18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SMI methods for use with dell-smbios 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) Red Hat <mjg@redhat.com> 68c2ecf20Sopenharmony_ci * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com> 78c2ecf20Sopenharmony_ci * Copyright (c) 2014 Pali Rohár <pali@kernel.org> 88c2ecf20Sopenharmony_ci * Copyright (c) 2017 Dell Inc. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/dmi.h> 138c2ecf20Sopenharmony_ci#include <linux/gfp.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include "dcdbas.h" 198c2ecf20Sopenharmony_ci#include "dell-smbios.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int da_command_address; 228c2ecf20Sopenharmony_cistatic int da_command_code; 238c2ecf20Sopenharmony_cistatic struct calling_interface_buffer *buffer; 248c2ecf20Sopenharmony_cistatic struct platform_device *platform_device; 258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(smm_mutex); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const struct dmi_system_id dell_device_table[] __initconst = { 288c2ecf20Sopenharmony_ci { 298c2ecf20Sopenharmony_ci .ident = "Dell laptop", 308c2ecf20Sopenharmony_ci .matches = { 318c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 328c2ecf20Sopenharmony_ci DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 338c2ecf20Sopenharmony_ci }, 348c2ecf20Sopenharmony_ci }, 358c2ecf20Sopenharmony_ci { 368c2ecf20Sopenharmony_ci .matches = { 378c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 388c2ecf20Sopenharmony_ci DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/ 398c2ecf20Sopenharmony_ci }, 408c2ecf20Sopenharmony_ci }, 418c2ecf20Sopenharmony_ci { 428c2ecf20Sopenharmony_ci .matches = { 438c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 448c2ecf20Sopenharmony_ci DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/ 458c2ecf20Sopenharmony_ci }, 468c2ecf20Sopenharmony_ci }, 478c2ecf20Sopenharmony_ci { 488c2ecf20Sopenharmony_ci .ident = "Dell Computer Corporation", 498c2ecf20Sopenharmony_ci .matches = { 508c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 518c2ecf20Sopenharmony_ci DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 528c2ecf20Sopenharmony_ci }, 538c2ecf20Sopenharmony_ci }, 548c2ecf20Sopenharmony_ci { } 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(dmi, dell_device_table); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void parse_da_table(const struct dmi_header *dm) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct calling_interface_structure *table = 618c2ecf20Sopenharmony_ci container_of(dm, struct calling_interface_structure, header); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least 648c2ecf20Sopenharmony_ci * 6 bytes of entry 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci if (dm->length < 17) 678c2ecf20Sopenharmony_ci return; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci da_command_address = table->cmdIOAddress; 708c2ecf20Sopenharmony_ci da_command_code = table->cmdIOCode; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void find_cmd_address(const struct dmi_header *dm, void *dummy) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci switch (dm->type) { 768c2ecf20Sopenharmony_ci case 0xda: /* Calling interface */ 778c2ecf20Sopenharmony_ci parse_da_table(dm); 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int dell_smbios_smm_call(struct calling_interface_buffer *input) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct smi_cmd command; 858c2ecf20Sopenharmony_ci size_t size; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci size = sizeof(struct calling_interface_buffer); 888c2ecf20Sopenharmony_ci command.magic = SMI_CMD_MAGIC; 898c2ecf20Sopenharmony_ci command.command_address = da_command_address; 908c2ecf20Sopenharmony_ci command.command_code = da_command_code; 918c2ecf20Sopenharmony_ci command.ebx = virt_to_phys(buffer); 928c2ecf20Sopenharmony_ci command.ecx = 0x42534931; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci mutex_lock(&smm_mutex); 958c2ecf20Sopenharmony_ci memcpy(buffer, input, size); 968c2ecf20Sopenharmony_ci dcdbas_smi_request(&command); 978c2ecf20Sopenharmony_ci memcpy(input, buffer, size); 988c2ecf20Sopenharmony_ci mutex_unlock(&smm_mutex); 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* When enabled this indicates that SMM won't work */ 1038c2ecf20Sopenharmony_cistatic bool test_wsmt_enabled(void) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct calling_interface_token *wsmt; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* if token doesn't exist, SMM will work */ 1088c2ecf20Sopenharmony_ci wsmt = dell_smbios_find_token(WSMT_EN_TOKEN); 1098c2ecf20Sopenharmony_ci if (!wsmt) 1108c2ecf20Sopenharmony_ci return false; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* If token exists, try to access over SMM but set a dummy return. 1138c2ecf20Sopenharmony_ci * - If WSMT disabled it will be overwritten by SMM 1148c2ecf20Sopenharmony_ci * - If WSMT enabled then dummy value will remain 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci buffer->cmd_class = CLASS_TOKEN_READ; 1178c2ecf20Sopenharmony_ci buffer->cmd_select = SELECT_TOKEN_STD; 1188c2ecf20Sopenharmony_ci memset(buffer, 0, sizeof(struct calling_interface_buffer)); 1198c2ecf20Sopenharmony_ci buffer->input[0] = wsmt->location; 1208c2ecf20Sopenharmony_ci buffer->output[0] = 99; 1218c2ecf20Sopenharmony_ci dell_smbios_smm_call(buffer); 1228c2ecf20Sopenharmony_ci if (buffer->output[0] == 99) 1238c2ecf20Sopenharmony_ci return true; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return false; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciint init_dell_smbios_smm(void) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int ret; 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * Allocate buffer below 4GB for SMI data--only 32-bit physical addr 1338c2ecf20Sopenharmony_ci * is passed to SMI handler. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); 1368c2ecf20Sopenharmony_ci if (!buffer) 1378c2ecf20Sopenharmony_ci return -ENOMEM; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci dmi_walk(find_cmd_address, NULL); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (test_wsmt_enabled()) { 1428c2ecf20Sopenharmony_ci pr_debug("Disabling due to WSMT enabled\n"); 1438c2ecf20Sopenharmony_ci ret = -ENODEV; 1448c2ecf20Sopenharmony_ci goto fail_wsmt; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci platform_device = platform_device_alloc("dell-smbios", 1); 1488c2ecf20Sopenharmony_ci if (!platform_device) { 1498c2ecf20Sopenharmony_ci ret = -ENOMEM; 1508c2ecf20Sopenharmony_ci goto fail_platform_device_alloc; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ret = platform_device_add(platform_device); 1548c2ecf20Sopenharmony_ci if (ret) 1558c2ecf20Sopenharmony_ci goto fail_platform_device_add; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci ret = dell_smbios_register_device(&platform_device->dev, 1588c2ecf20Sopenharmony_ci &dell_smbios_smm_call); 1598c2ecf20Sopenharmony_ci if (ret) 1608c2ecf20Sopenharmony_ci goto fail_register; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cifail_register: 1658c2ecf20Sopenharmony_ci platform_device_del(platform_device); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cifail_platform_device_add: 1688c2ecf20Sopenharmony_ci platform_device_put(platform_device); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cifail_wsmt: 1718c2ecf20Sopenharmony_cifail_platform_device_alloc: 1728c2ecf20Sopenharmony_ci free_page((unsigned long)buffer); 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_civoid exit_dell_smbios_smm(void) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci if (platform_device) { 1798c2ecf20Sopenharmony_ci dell_smbios_unregister_device(&platform_device->dev); 1808c2ecf20Sopenharmony_ci platform_device_unregister(platform_device); 1818c2ecf20Sopenharmony_ci free_page((unsigned long)buffer); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci} 184