diff options
-rw-r--r-- | drivers/platform/x86/dell-smbios.c | 192 | ||||
-rw-r--r-- | drivers/platform/x86/dell-smbios.h | 11 |
2 files changed, 203 insertions, 0 deletions
diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c index 2229d44cb92c..d99edd803c19 100644 --- a/drivers/platform/x86/dell-smbios.c +++ b/drivers/platform/x86/dell-smbios.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | #include "dell-smbios.h" | 25 | #include "dell-smbios.h" |
26 | 26 | ||
27 | static u32 da_supported_commands; | ||
27 | static int da_num_tokens; | 28 | static int da_num_tokens; |
28 | static struct platform_device *platform_device; | 29 | static struct platform_device *platform_device; |
29 | static struct calling_interface_token *da_tokens; | 30 | static struct calling_interface_token *da_tokens; |
@@ -38,6 +39,91 @@ struct smbios_device { | |||
38 | int (*call_fn)(struct calling_interface_buffer *); | 39 | int (*call_fn)(struct calling_interface_buffer *); |
39 | }; | 40 | }; |
40 | 41 | ||
42 | struct smbios_call { | ||
43 | u32 need_capability; | ||
44 | int cmd_class; | ||
45 | int cmd_select; | ||
46 | }; | ||
47 | |||
48 | /* calls that are whitelisted for given capabilities */ | ||
49 | static struct smbios_call call_whitelist[] = { | ||
50 | /* generally tokens are allowed, but may be further filtered or | ||
51 | * restricted by token blacklist or whitelist | ||
52 | */ | ||
53 | {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_STD}, | ||
54 | {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_AC}, | ||
55 | {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_BAT}, | ||
56 | {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD}, | ||
57 | {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_AC}, | ||
58 | {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT}, | ||
59 | /* used by userspace: fwupdate */ | ||
60 | {CAP_SYS_ADMIN, CLASS_ADMIN_PROP, SELECT_ADMIN_PROP}, | ||
61 | /* used by userspace: fwupd */ | ||
62 | {CAP_SYS_ADMIN, CLASS_INFO, SELECT_DOCK}, | ||
63 | {CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE, SELECT_FLASH_INTERFACE}, | ||
64 | }; | ||
65 | |||
66 | /* calls that are explicitly blacklisted */ | ||
67 | static struct smbios_call call_blacklist[] = { | ||
68 | {0x0000, 01, 07}, /* manufacturing use */ | ||
69 | {0x0000, 06, 05}, /* manufacturing use */ | ||
70 | {0x0000, 11, 03}, /* write once */ | ||
71 | {0x0000, 11, 07}, /* write once */ | ||
72 | {0x0000, 11, 11}, /* write once */ | ||
73 | {0x0000, 19, -1}, /* diagnostics */ | ||
74 | /* handled by kernel: dell-laptop */ | ||
75 | {0x0000, CLASS_INFO, SELECT_RFKILL}, | ||
76 | {0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT}, | ||
77 | }; | ||
78 | |||
79 | struct token_range { | ||
80 | u32 need_capability; | ||
81 | u16 min; | ||
82 | u16 max; | ||
83 | }; | ||
84 | |||
85 | /* tokens that are whitelisted for given capabilities */ | ||
86 | static struct token_range token_whitelist[] = { | ||
87 | /* used by userspace: fwupdate */ | ||
88 | {CAP_SYS_ADMIN, CAPSULE_EN_TOKEN, CAPSULE_DIS_TOKEN}, | ||
89 | /* can indicate to userspace that WMI is needed */ | ||
90 | {0x0000, WSMT_EN_TOKEN, WSMT_DIS_TOKEN} | ||
91 | }; | ||
92 | |||
93 | /* tokens that are explicitly blacklisted */ | ||
94 | static struct token_range token_blacklist[] = { | ||
95 | {0x0000, 0x0058, 0x0059}, /* ME use */ | ||
96 | {0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */ | ||
97 | {0x0000, 0x013A, 0x01FF}, /* sata shadow copy */ | ||
98 | {0x0000, 0x0175, 0x0176}, /* write once */ | ||
99 | {0x0000, 0x0195, 0x0197}, /* diagnostics */ | ||
100 | {0x0000, 0x01DC, 0x01DD}, /* manufacturing use */ | ||
101 | {0x0000, 0x027D, 0x0284}, /* diagnostics */ | ||
102 | {0x0000, 0x02E3, 0x02E3}, /* manufacturing use */ | ||
103 | {0x0000, 0x02FF, 0x02FF}, /* manufacturing use */ | ||
104 | {0x0000, 0x0300, 0x0302}, /* manufacturing use */ | ||
105 | {0x0000, 0x0325, 0x0326}, /* manufacturing use */ | ||
106 | {0x0000, 0x0332, 0x0335}, /* fan control */ | ||
107 | {0x0000, 0x0350, 0x0350}, /* manufacturing use */ | ||
108 | {0x0000, 0x0363, 0x0363}, /* manufacturing use */ | ||
109 | {0x0000, 0x0368, 0x0368}, /* manufacturing use */ | ||
110 | {0x0000, 0x03F6, 0x03F7}, /* manufacturing use */ | ||
111 | {0x0000, 0x049E, 0x049F}, /* manufacturing use */ | ||
112 | {0x0000, 0x04A0, 0x04A3}, /* disagnostics */ | ||
113 | {0x0000, 0x04E6, 0x04E7}, /* manufacturing use */ | ||
114 | {0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */ | ||
115 | {0x0000, 0x9000, 0x9001}, /* internal BIOS use */ | ||
116 | {0x0000, 0xA000, 0xBFFF}, /* write only */ | ||
117 | {0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */ | ||
118 | /* handled by kernel: dell-laptop */ | ||
119 | {0x0000, BRIGHTNESS_TOKEN, BRIGHTNESS_TOKEN}, | ||
120 | {0x0000, KBD_LED_OFF_TOKEN, KBD_LED_AUTO_TOKEN}, | ||
121 | {0x0000, KBD_LED_AC_TOKEN, KBD_LED_AC_TOKEN}, | ||
122 | {0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN}, | ||
123 | {0x0000, KBD_LED_AUTO_100_TOKEN, KBD_LED_AUTO_100_TOKEN}, | ||
124 | {0x0000, GLOBAL_MIC_MUTE_ENABLE, GLOBAL_MIC_MUTE_DISABLE}, | ||
125 | }; | ||
126 | |||
41 | static LIST_HEAD(smbios_device_list); | 127 | static LIST_HEAD(smbios_device_list); |
42 | 128 | ||
43 | int dell_smbios_error(int value) | 129 | int dell_smbios_error(int value) |
@@ -90,6 +176,110 @@ void dell_smbios_unregister_device(struct device *d) | |||
90 | } | 176 | } |
91 | EXPORT_SYMBOL_GPL(dell_smbios_unregister_device); | 177 | EXPORT_SYMBOL_GPL(dell_smbios_unregister_device); |
92 | 178 | ||
179 | int dell_smbios_call_filter(struct device *d, | ||
180 | struct calling_interface_buffer *buffer) | ||
181 | { | ||
182 | u16 t = 0; | ||
183 | int i; | ||
184 | |||
185 | /* can't make calls over 30 */ | ||
186 | if (buffer->cmd_class > 30) { | ||
187 | dev_dbg(d, "class too big: %u\n", buffer->cmd_class); | ||
188 | return -EINVAL; | ||
189 | } | ||
190 | |||
191 | /* supported calls on the particular system */ | ||
192 | if (!(da_supported_commands & (1 << buffer->cmd_class))) { | ||
193 | dev_dbg(d, "invalid command, supported commands: 0x%8x\n", | ||
194 | da_supported_commands); | ||
195 | return -EINVAL; | ||
196 | } | ||
197 | |||
198 | /* match against call blacklist */ | ||
199 | for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) { | ||
200 | if (buffer->cmd_class != call_blacklist[i].cmd_class) | ||
201 | continue; | ||
202 | if (buffer->cmd_select != call_blacklist[i].cmd_select && | ||
203 | call_blacklist[i].cmd_select != -1) | ||
204 | continue; | ||
205 | dev_dbg(d, "blacklisted command: %u/%u\n", | ||
206 | buffer->cmd_class, buffer->cmd_select); | ||
207 | return -EINVAL; | ||
208 | } | ||
209 | |||
210 | /* if a token call, find token ID */ | ||
211 | |||
212 | if ((buffer->cmd_class == CLASS_TOKEN_READ || | ||
213 | buffer->cmd_class == CLASS_TOKEN_WRITE) && | ||
214 | buffer->cmd_select < 3) { | ||
215 | /* find the matching token ID */ | ||
216 | for (i = 0; i < da_num_tokens; i++) { | ||
217 | if (da_tokens[i].location != buffer->input[0]) | ||
218 | continue; | ||
219 | t = da_tokens[i].tokenID; | ||
220 | break; | ||
221 | } | ||
222 | |||
223 | /* token call; but token didn't exist */ | ||
224 | if (!t) { | ||
225 | dev_dbg(d, "token at location %04x doesn't exist\n", | ||
226 | buffer->input[0]); | ||
227 | return -EINVAL; | ||
228 | } | ||
229 | |||
230 | /* match against token blacklist */ | ||
231 | for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) { | ||
232 | if (!token_blacklist[i].min || !token_blacklist[i].max) | ||
233 | continue; | ||
234 | if (t >= token_blacklist[i].min && | ||
235 | t <= token_blacklist[i].max) | ||
236 | return -EINVAL; | ||
237 | } | ||
238 | |||
239 | /* match against token whitelist */ | ||
240 | for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) { | ||
241 | if (!token_whitelist[i].min || !token_whitelist[i].max) | ||
242 | continue; | ||
243 | if (t < token_whitelist[i].min || | ||
244 | t > token_whitelist[i].max) | ||
245 | continue; | ||
246 | if (!token_whitelist[i].need_capability || | ||
247 | capable(token_whitelist[i].need_capability)) { | ||
248 | dev_dbg(d, "whitelisted token: %x\n", t); | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | } | ||
253 | } | ||
254 | /* match against call whitelist */ | ||
255 | for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) { | ||
256 | if (buffer->cmd_class != call_whitelist[i].cmd_class) | ||
257 | continue; | ||
258 | if (buffer->cmd_select != call_whitelist[i].cmd_select) | ||
259 | continue; | ||
260 | if (!call_whitelist[i].need_capability || | ||
261 | capable(call_whitelist[i].need_capability)) { | ||
262 | dev_dbg(d, "whitelisted capable command: %u/%u\n", | ||
263 | buffer->cmd_class, buffer->cmd_select); | ||
264 | return 0; | ||
265 | } | ||
266 | dev_dbg(d, "missing capability %d for %u/%u\n", | ||
267 | call_whitelist[i].need_capability, | ||
268 | buffer->cmd_class, buffer->cmd_select); | ||
269 | |||
270 | } | ||
271 | |||
272 | /* not in a whitelist, only allow processes with capabilities */ | ||
273 | if (capable(CAP_SYS_RAWIO)) { | ||
274 | dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n", | ||
275 | buffer->cmd_class, buffer->cmd_select); | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | return -EACCES; | ||
280 | } | ||
281 | EXPORT_SYMBOL_GPL(dell_smbios_call_filter); | ||
282 | |||
93 | int dell_smbios_call(struct calling_interface_buffer *buffer) | 283 | int dell_smbios_call(struct calling_interface_buffer *buffer) |
94 | { | 284 | { |
95 | int (*call_fn)(struct calling_interface_buffer *) = NULL; | 285 | int (*call_fn)(struct calling_interface_buffer *) = NULL; |
@@ -168,6 +358,8 @@ static void __init parse_da_table(const struct dmi_header *dm) | |||
168 | if (dm->length < 17) | 358 | if (dm->length < 17) |
169 | return; | 359 | return; |
170 | 360 | ||
361 | da_supported_commands = table->supportedCmds; | ||
362 | |||
171 | new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) * | 363 | new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) * |
172 | sizeof(struct calling_interface_token), | 364 | sizeof(struct calling_interface_token), |
173 | GFP_KERNEL); | 365 | GFP_KERNEL); |
diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h index 07effdc7ae8b..91e8004d48ba 100644 --- a/drivers/platform/x86/dell-smbios.h +++ b/drivers/platform/x86/dell-smbios.h | |||
@@ -26,9 +26,14 @@ | |||
26 | #define SELECT_TOKEN_AC 2 | 26 | #define SELECT_TOKEN_AC 2 |
27 | #define CLASS_KBD_BACKLIGHT 4 | 27 | #define CLASS_KBD_BACKLIGHT 4 |
28 | #define SELECT_KBD_BACKLIGHT 11 | 28 | #define SELECT_KBD_BACKLIGHT 11 |
29 | #define CLASS_FLASH_INTERFACE 7 | ||
30 | #define SELECT_FLASH_INTERFACE 3 | ||
31 | #define CLASS_ADMIN_PROP 10 | ||
32 | #define SELECT_ADMIN_PROP 3 | ||
29 | #define CLASS_INFO 17 | 33 | #define CLASS_INFO 17 |
30 | #define SELECT_RFKILL 11 | 34 | #define SELECT_RFKILL 11 |
31 | #define SELECT_APP_REGISTRATION 3 | 35 | #define SELECT_APP_REGISTRATION 3 |
36 | #define SELECT_DOCK 22 | ||
32 | 37 | ||
33 | /* Tokens used in kernel drivers, any of these | 38 | /* Tokens used in kernel drivers, any of these |
34 | * should be filtered from userspace access | 39 | * should be filtered from userspace access |
@@ -44,6 +49,10 @@ | |||
44 | #define KBD_LED_AUTO_100_TOKEN 0x02F6 | 49 | #define KBD_LED_AUTO_100_TOKEN 0x02F6 |
45 | #define GLOBAL_MIC_MUTE_ENABLE 0x0364 | 50 | #define GLOBAL_MIC_MUTE_ENABLE 0x0364 |
46 | #define GLOBAL_MIC_MUTE_DISABLE 0x0365 | 51 | #define GLOBAL_MIC_MUTE_DISABLE 0x0365 |
52 | |||
53 | /* tokens whitelisted to userspace use */ | ||
54 | #define CAPSULE_EN_TOKEN 0x0461 | ||
55 | #define CAPSULE_DIS_TOKEN 0x0462 | ||
47 | #define WSMT_EN_TOKEN 0x04EC | 56 | #define WSMT_EN_TOKEN 0x04EC |
48 | #define WSMT_DIS_TOKEN 0x04ED | 57 | #define WSMT_DIS_TOKEN 0x04ED |
49 | 58 | ||
@@ -80,6 +89,8 @@ int dell_smbios_register_device(struct device *d, void *call_fn); | |||
80 | void dell_smbios_unregister_device(struct device *d); | 89 | void dell_smbios_unregister_device(struct device *d); |
81 | 90 | ||
82 | int dell_smbios_error(int value); | 91 | int dell_smbios_error(int value); |
92 | int dell_smbios_call_filter(struct device *d, | ||
93 | struct calling_interface_buffer *buffer); | ||
83 | int dell_smbios_call(struct calling_interface_buffer *buffer); | 94 | int dell_smbios_call(struct calling_interface_buffer *buffer); |
84 | 95 | ||
85 | struct calling_interface_token *dell_smbios_find_token(int tokenid); | 96 | struct calling_interface_token *dell_smbios_find_token(int tokenid); |