]>
Commit | Line | Data |
---|---|---|
549b4930 ML |
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 | ||
24 | static int da_command_address; | |
25 | static int da_command_code; | |
26 | static struct calling_interface_buffer *buffer; | |
27 | struct platform_device *platform_device; | |
28 | static DEFINE_MUTEX(smm_mutex); | |
29 | ||
30 | static 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 | }; | |
59 | MODULE_DEVICE_TABLE(dmi, dell_device_table); | |
60 | ||
88404dfe | 61 | static void parse_da_table(const struct dmi_header *dm) |
549b4930 ML |
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 | ||
88404dfe | 76 | static void find_cmd_address(const struct dmi_header *dm, void *dummy) |
549b4930 ML |
77 | { |
78 | switch (dm->type) { | |
79 | case 0xda: /* Calling interface */ | |
80 | parse_da_table(dm); | |
81 | break; | |
82 | } | |
83 | } | |
84 | ||
85 | int 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 | ||
da1f607e ML |
105 | /* When enabled this indicates that SMM won't work */ |
106 | static 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 | ||
88404dfe | 131 | int init_dell_smbios_smm(void) |
549b4930 ML |
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 | ||
da1f607e ML |
144 | if (test_wsmt_enabled()) { |
145 | pr_debug("Disabling due to WSMT enabled\n"); | |
146 | ret = -ENODEV; | |
147 | goto fail_wsmt; | |
148 | } | |
149 | ||
549b4930 ML |
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 | ||
167 | fail_register: | |
168 | platform_device_del(platform_device); | |
169 | ||
170 | fail_platform_device_add: | |
171 | platform_device_put(platform_device); | |
172 | ||
da1f607e | 173 | fail_wsmt: |
549b4930 ML |
174 | fail_platform_device_alloc: |
175 | free_page((unsigned long)buffer); | |
176 | return ret; | |
177 | } | |
178 | ||
88404dfe | 179 | void exit_dell_smbios_smm(void) |
549b4930 ML |
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 | } |