Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.16-rc1 196 lines 4.9 kB view raw
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 15#include <linux/dmi.h> 16#include <linux/gfp.h> 17#include <linux/io.h> 18#include <linux/module.h> 19#include <linux/mutex.h> 20#include <linux/platform_device.h> 21#include "../../firmware/dcdbas.h" 22#include "dell-smbios.h" 23 24static int da_command_address; 25static int da_command_code; 26static struct calling_interface_buffer *buffer; 27struct platform_device *platform_device; 28static DEFINE_MUTEX(smm_mutex); 29 30static const struct dmi_system_id dell_device_table[] __initconst = { 31 { 32 .ident = "Dell laptop", 33 .matches = { 34 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 35 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 36 }, 37 }, 38 { 39 .matches = { 40 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 41 DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/ 42 }, 43 }, 44 { 45 .matches = { 46 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 47 DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/ 48 }, 49 }, 50 { 51 .ident = "Dell Computer Corporation", 52 .matches = { 53 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 54 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 55 }, 56 }, 57 { } 58}; 59MODULE_DEVICE_TABLE(dmi, dell_device_table); 60 61static void __init parse_da_table(const struct dmi_header *dm) 62{ 63 struct calling_interface_structure *table = 64 container_of(dm, struct calling_interface_structure, header); 65 66 /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least 67 * 6 bytes of entry 68 */ 69 if (dm->length < 17) 70 return; 71 72 da_command_address = table->cmdIOAddress; 73 da_command_code = table->cmdIOCode; 74} 75 76static void __init find_cmd_address(const struct dmi_header *dm, void *dummy) 77{ 78 switch (dm->type) { 79 case 0xda: /* Calling interface */ 80 parse_da_table(dm); 81 break; 82 } 83} 84 85int dell_smbios_smm_call(struct calling_interface_buffer *input) 86{ 87 struct smi_cmd command; 88 size_t size; 89 90 size = sizeof(struct calling_interface_buffer); 91 command.magic = SMI_CMD_MAGIC; 92 command.command_address = da_command_address; 93 command.command_code = da_command_code; 94 command.ebx = virt_to_phys(buffer); 95 command.ecx = 0x42534931; 96 97 mutex_lock(&smm_mutex); 98 memcpy(buffer, input, size); 99 dcdbas_smi_request(&command); 100 memcpy(input, buffer, size); 101 mutex_unlock(&smm_mutex); 102 return 0; 103} 104 105/* When enabled this indicates that SMM won't work */ 106static bool test_wsmt_enabled(void) 107{ 108 struct calling_interface_token *wsmt; 109 110 /* if token doesn't exist, SMM will work */ 111 wsmt = dell_smbios_find_token(WSMT_EN_TOKEN); 112 if (!wsmt) 113 return false; 114 115 /* If token exists, try to access over SMM but set a dummy return. 116 * - If WSMT disabled it will be overwritten by SMM 117 * - If WSMT enabled then dummy value will remain 118 */ 119 buffer->cmd_class = CLASS_TOKEN_READ; 120 buffer->cmd_select = SELECT_TOKEN_STD; 121 memset(buffer, 0, sizeof(struct calling_interface_buffer)); 122 buffer->input[0] = wsmt->location; 123 buffer->output[0] = 99; 124 dell_smbios_smm_call(buffer); 125 if (buffer->output[0] == 99) 126 return true; 127 128 return false; 129} 130 131static int __init dell_smbios_smm_init(void) 132{ 133 int ret; 134 /* 135 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr 136 * is passed to SMI handler. 137 */ 138 buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); 139 if (!buffer) 140 return -ENOMEM; 141 142 dmi_walk(find_cmd_address, NULL); 143 144 if (test_wsmt_enabled()) { 145 pr_debug("Disabling due to WSMT enabled\n"); 146 ret = -ENODEV; 147 goto fail_wsmt; 148 } 149 150 platform_device = platform_device_alloc("dell-smbios", 1); 151 if (!platform_device) { 152 ret = -ENOMEM; 153 goto fail_platform_device_alloc; 154 } 155 156 ret = platform_device_add(platform_device); 157 if (ret) 158 goto fail_platform_device_add; 159 160 ret = dell_smbios_register_device(&platform_device->dev, 161 &dell_smbios_smm_call); 162 if (ret) 163 goto fail_register; 164 165 return 0; 166 167fail_register: 168 platform_device_del(platform_device); 169 170fail_platform_device_add: 171 platform_device_put(platform_device); 172 173fail_wsmt: 174fail_platform_device_alloc: 175 free_page((unsigned long)buffer); 176 return ret; 177} 178 179static void __exit dell_smbios_smm_exit(void) 180{ 181 if (platform_device) { 182 dell_smbios_unregister_device(&platform_device->dev); 183 platform_device_unregister(platform_device); 184 free_page((unsigned long)buffer); 185 } 186} 187 188subsys_initcall(dell_smbios_smm_init); 189module_exit(dell_smbios_smm_exit); 190 191MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 192MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>"); 193MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); 194MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); 195MODULE_DESCRIPTION("Dell SMBIOS communications over SMI"); 196MODULE_LICENSE("GPL");