dell-smbios-smm.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * SMI methods for use with dell-smbios
  4. *
  5. * Copyright (c) Red Hat <[email protected]>
  6. * Copyright (c) 2014 Gabriele Mazzotta <[email protected]>
  7. * Copyright (c) 2014 Pali Rohár <[email protected]>
  8. * Copyright (c) 2017 Dell Inc.
  9. */
  10. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  11. #include <linux/dmi.h>
  12. #include <linux/gfp.h>
  13. #include <linux/io.h>
  14. #include <linux/module.h>
  15. #include <linux/mutex.h>
  16. #include <linux/platform_device.h>
  17. #include "dcdbas.h"
  18. #include "dell-smbios.h"
  19. static int da_command_address;
  20. static int da_command_code;
  21. static struct smi_buffer smi_buf;
  22. static struct calling_interface_buffer *buffer;
  23. static struct platform_device *platform_device;
  24. static DEFINE_MUTEX(smm_mutex);
  25. static void parse_da_table(const struct dmi_header *dm)
  26. {
  27. struct calling_interface_structure *table =
  28. container_of(dm, struct calling_interface_structure, header);
  29. /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
  30. * 6 bytes of entry
  31. */
  32. if (dm->length < 17)
  33. return;
  34. da_command_address = table->cmdIOAddress;
  35. da_command_code = table->cmdIOCode;
  36. }
  37. static void find_cmd_address(const struct dmi_header *dm, void *dummy)
  38. {
  39. switch (dm->type) {
  40. case 0xda: /* Calling interface */
  41. parse_da_table(dm);
  42. break;
  43. }
  44. }
  45. static int dell_smbios_smm_call(struct calling_interface_buffer *input)
  46. {
  47. struct smi_cmd command;
  48. size_t size;
  49. size = sizeof(struct calling_interface_buffer);
  50. command.magic = SMI_CMD_MAGIC;
  51. command.command_address = da_command_address;
  52. command.command_code = da_command_code;
  53. command.ebx = smi_buf.dma;
  54. command.ecx = 0x42534931;
  55. mutex_lock(&smm_mutex);
  56. memcpy(buffer, input, size);
  57. dcdbas_smi_request(&command);
  58. memcpy(input, buffer, size);
  59. mutex_unlock(&smm_mutex);
  60. return 0;
  61. }
  62. /* When enabled this indicates that SMM won't work */
  63. static bool test_wsmt_enabled(void)
  64. {
  65. struct calling_interface_token *wsmt;
  66. /* if token doesn't exist, SMM will work */
  67. wsmt = dell_smbios_find_token(WSMT_EN_TOKEN);
  68. if (!wsmt)
  69. return false;
  70. /* If token exists, try to access over SMM but set a dummy return.
  71. * - If WSMT disabled it will be overwritten by SMM
  72. * - If WSMT enabled then dummy value will remain
  73. */
  74. buffer->cmd_class = CLASS_TOKEN_READ;
  75. buffer->cmd_select = SELECT_TOKEN_STD;
  76. memset(buffer, 0, sizeof(struct calling_interface_buffer));
  77. buffer->input[0] = wsmt->location;
  78. buffer->output[0] = 99;
  79. dell_smbios_smm_call(buffer);
  80. if (buffer->output[0] == 99)
  81. return true;
  82. return false;
  83. }
  84. int init_dell_smbios_smm(void)
  85. {
  86. int ret;
  87. /*
  88. * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
  89. * is passed to SMI handler.
  90. */
  91. ret = dcdbas_smi_alloc(&smi_buf, PAGE_SIZE);
  92. if (ret)
  93. return ret;
  94. buffer = (void *)smi_buf.virt;
  95. dmi_walk(find_cmd_address, NULL);
  96. if (test_wsmt_enabled()) {
  97. pr_debug("Disabling due to WSMT enabled\n");
  98. ret = -ENODEV;
  99. goto fail_wsmt;
  100. }
  101. platform_device = platform_device_alloc("dell-smbios", 1);
  102. if (!platform_device) {
  103. ret = -ENOMEM;
  104. goto fail_platform_device_alloc;
  105. }
  106. ret = platform_device_add(platform_device);
  107. if (ret)
  108. goto fail_platform_device_add;
  109. ret = dell_smbios_register_device(&platform_device->dev,
  110. &dell_smbios_smm_call);
  111. if (ret)
  112. goto fail_register;
  113. return 0;
  114. fail_register:
  115. platform_device_del(platform_device);
  116. fail_platform_device_add:
  117. platform_device_put(platform_device);
  118. fail_wsmt:
  119. fail_platform_device_alloc:
  120. dcdbas_smi_free(&smi_buf);
  121. return ret;
  122. }
  123. void exit_dell_smbios_smm(void)
  124. {
  125. if (platform_device) {
  126. dell_smbios_unregister_device(&platform_device->dev);
  127. platform_device_unregister(platform_device);
  128. dcdbas_smi_free(&smi_buf);
  129. }
  130. }