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