dell-smbios-smm.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. * SMI methods for use with dell-smbios
  3. *
  4. * Copyright (c) Red Hat <mjg@redhat.com>
  5. * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
  6. * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
  7. * Copyright (c) 2017 Dell Inc.
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2 as
  11. * published by the Free Software Foundation.
  12. */
  13. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14. #include <linux/dmi.h>
  15. #include <linux/gfp.h>
  16. #include <linux/io.h>
  17. #include <linux/module.h>
  18. #include <linux/mutex.h>
  19. #include <linux/platform_device.h>
  20. #include "../../firmware/dcdbas.h"
  21. #include "dell-smbios.h"
  22. static int da_command_address;
  23. static int da_command_code;
  24. static struct calling_interface_buffer *buffer;
  25. static struct platform_device *platform_device;
  26. static DEFINE_MUTEX(smm_mutex);
  27. static const struct dmi_system_id dell_device_table[] __initconst = {
  28. {
  29. .ident = "Dell laptop",
  30. .matches = {
  31. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  32. DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
  33. },
  34. },
  35. {
  36. .matches = {
  37. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  38. DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
  39. },
  40. },
  41. {
  42. .matches = {
  43. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  44. DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
  45. },
  46. },
  47. {
  48. .ident = "Dell Computer Corporation",
  49. .matches = {
  50. DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
  51. DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
  52. },
  53. },
  54. { }
  55. };
  56. MODULE_DEVICE_TABLE(dmi, dell_device_table);
  57. static void parse_da_table(const struct dmi_header *dm)
  58. {
  59. struct calling_interface_structure *table =
  60. container_of(dm, struct calling_interface_structure, header);
  61. /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
  62. * 6 bytes of entry
  63. */
  64. if (dm->length < 17)
  65. return;
  66. da_command_address = table->cmdIOAddress;
  67. da_command_code = table->cmdIOCode;
  68. }
  69. static void find_cmd_address(const struct dmi_header *dm, void *dummy)
  70. {
  71. switch (dm->type) {
  72. case 0xda: /* Calling interface */
  73. parse_da_table(dm);
  74. break;
  75. }
  76. }
  77. static int dell_smbios_smm_call(struct calling_interface_buffer *input)
  78. {
  79. struct smi_cmd command;
  80. size_t size;
  81. size = sizeof(struct calling_interface_buffer);
  82. command.magic = SMI_CMD_MAGIC;
  83. command.command_address = da_command_address;
  84. command.command_code = da_command_code;
  85. command.ebx = virt_to_phys(buffer);
  86. command.ecx = 0x42534931;
  87. mutex_lock(&smm_mutex);
  88. memcpy(buffer, input, size);
  89. dcdbas_smi_request(&command);
  90. memcpy(input, buffer, size);
  91. mutex_unlock(&smm_mutex);
  92. return 0;
  93. }
  94. /* When enabled this indicates that SMM won't work */
  95. static bool test_wsmt_enabled(void)
  96. {
  97. struct calling_interface_token *wsmt;
  98. /* if token doesn't exist, SMM will work */
  99. wsmt = dell_smbios_find_token(WSMT_EN_TOKEN);
  100. if (!wsmt)
  101. return false;
  102. /* If token exists, try to access over SMM but set a dummy return.
  103. * - If WSMT disabled it will be overwritten by SMM
  104. * - If WSMT enabled then dummy value will remain
  105. */
  106. buffer->cmd_class = CLASS_TOKEN_READ;
  107. buffer->cmd_select = SELECT_TOKEN_STD;
  108. memset(buffer, 0, sizeof(struct calling_interface_buffer));
  109. buffer->input[0] = wsmt->location;
  110. buffer->output[0] = 99;
  111. dell_smbios_smm_call(buffer);
  112. if (buffer->output[0] == 99)
  113. return true;
  114. return false;
  115. }
  116. int init_dell_smbios_smm(void)
  117. {
  118. int ret;
  119. /*
  120. * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
  121. * is passed to SMI handler.
  122. */
  123. buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
  124. if (!buffer)
  125. return -ENOMEM;
  126. dmi_walk(find_cmd_address, NULL);
  127. if (test_wsmt_enabled()) {
  128. pr_debug("Disabling due to WSMT enabled\n");
  129. ret = -ENODEV;
  130. goto fail_wsmt;
  131. }
  132. platform_device = platform_device_alloc("dell-smbios", 1);
  133. if (!platform_device) {
  134. ret = -ENOMEM;
  135. goto fail_platform_device_alloc;
  136. }
  137. ret = platform_device_add(platform_device);
  138. if (ret)
  139. goto fail_platform_device_add;
  140. ret = dell_smbios_register_device(&platform_device->dev,
  141. &dell_smbios_smm_call);
  142. if (ret)
  143. goto fail_register;
  144. return 0;
  145. fail_register:
  146. platform_device_del(platform_device);
  147. fail_platform_device_add:
  148. platform_device_put(platform_device);
  149. fail_wsmt:
  150. fail_platform_device_alloc:
  151. free_page((unsigned long)buffer);
  152. return ret;
  153. }
  154. void exit_dell_smbios_smm(void)
  155. {
  156. if (platform_device) {
  157. dell_smbios_unregister_device(&platform_device->dev);
  158. platform_device_unregister(platform_device);
  159. free_page((unsigned long)buffer);
  160. }
  161. }