2 * Common functions for kernel modules using Dell SMBIOS
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>
8 * Based on documentation in the libsmbios package:
9 * Copyright (C) 2005-2014 Dell Inc.
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/capability.h>
20 #include <linux/dmi.h>
21 #include <linux/err.h>
22 #include <linux/mutex.h>
23 #include <linux/platform_device.h>
24 #include <linux/slab.h>
25 #include "dell-smbios.h"
27 static u32 da_supported_commands
;
28 static int da_num_tokens
;
29 static struct platform_device
*platform_device
;
30 static struct calling_interface_token
*da_tokens
;
31 static struct device_attribute
*token_location_attrs
;
32 static struct device_attribute
*token_value_attrs
;
33 static struct attribute
**token_attrs
;
34 static DEFINE_MUTEX(smbios_mutex
);
36 struct smbios_device
{
37 struct list_head list
;
38 struct device
*device
;
39 int (*call_fn
)(struct calling_interface_buffer
*arg
);
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
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
},
66 /* calls that are explicitly blacklisted */
67 static struct smbios_call call_blacklist
[] = {
68 {0x0000, 1, 7}, /* manufacturing use */
69 {0x0000, 6, 5}, /* manufacturing use */
70 {0x0000, 11, 3}, /* write once */
71 {0x0000, 11, 7}, /* 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
},
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
}
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
},
127 static LIST_HEAD(smbios_device_list
);
129 int dell_smbios_error(int value
)
132 case 0: /* Completed successfully */
134 case -1: /* Completed with error */
136 case -2: /* Function not supported */
138 default: /* Unknown error */
142 EXPORT_SYMBOL_GPL(dell_smbios_error
);
144 int dell_smbios_register_device(struct device
*d
, void *call_fn
)
146 struct smbios_device
*priv
;
148 priv
= devm_kzalloc(d
, sizeof(struct smbios_device
), GFP_KERNEL
);
153 priv
->call_fn
= call_fn
;
154 mutex_lock(&smbios_mutex
);
155 list_add_tail(&priv
->list
, &smbios_device_list
);
156 mutex_unlock(&smbios_mutex
);
157 dev_dbg(d
, "Added device: %s\n", d
->driver
->name
);
160 EXPORT_SYMBOL_GPL(dell_smbios_register_device
);
162 void dell_smbios_unregister_device(struct device
*d
)
164 struct smbios_device
*priv
;
166 mutex_lock(&smbios_mutex
);
167 list_for_each_entry(priv
, &smbios_device_list
, list
) {
168 if (priv
->device
== d
) {
169 list_del(&priv
->list
);
174 mutex_unlock(&smbios_mutex
);
175 dev_dbg(d
, "Remove device: %s\n", d
->driver
->name
);
177 EXPORT_SYMBOL_GPL(dell_smbios_unregister_device
);
179 int dell_smbios_call_filter(struct device
*d
,
180 struct calling_interface_buffer
*buffer
)
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
);
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
);
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
)
202 if (buffer
->cmd_select
!= call_blacklist
[i
].cmd_select
&&
203 call_blacklist
[i
].cmd_select
!= -1)
205 dev_dbg(d
, "blacklisted command: %u/%u\n",
206 buffer
->cmd_class
, buffer
->cmd_select
);
210 /* if a token call, find token ID */
212 if ((buffer
->cmd_class
== CLASS_TOKEN_READ
||
213 buffer
->cmd_class
== CLASS_TOKEN_WRITE
) &&
214 buffer
->cmd_select
< 3) {
215 /* tokens enabled ? */
217 dev_dbg(d
, "no token support on this system\n");
221 /* find the matching token ID */
222 for (i
= 0; i
< da_num_tokens
; i
++) {
223 if (da_tokens
[i
].location
!= buffer
->input
[0])
225 t
= da_tokens
[i
].tokenID
;
229 /* token call; but token didn't exist */
231 dev_dbg(d
, "token at location %04x doesn't exist\n",
236 /* match against token blacklist */
237 for (i
= 0; i
< ARRAY_SIZE(token_blacklist
); i
++) {
238 if (!token_blacklist
[i
].min
|| !token_blacklist
[i
].max
)
240 if (t
>= token_blacklist
[i
].min
&&
241 t
<= token_blacklist
[i
].max
)
245 /* match against token whitelist */
246 for (i
= 0; i
< ARRAY_SIZE(token_whitelist
); i
++) {
247 if (!token_whitelist
[i
].min
|| !token_whitelist
[i
].max
)
249 if (t
< token_whitelist
[i
].min
||
250 t
> token_whitelist
[i
].max
)
252 if (!token_whitelist
[i
].need_capability
||
253 capable(token_whitelist
[i
].need_capability
)) {
254 dev_dbg(d
, "whitelisted token: %x\n", t
);
260 /* match against call whitelist */
261 for (i
= 0; i
< ARRAY_SIZE(call_whitelist
); i
++) {
262 if (buffer
->cmd_class
!= call_whitelist
[i
].cmd_class
)
264 if (buffer
->cmd_select
!= call_whitelist
[i
].cmd_select
)
266 if (!call_whitelist
[i
].need_capability
||
267 capable(call_whitelist
[i
].need_capability
)) {
268 dev_dbg(d
, "whitelisted capable command: %u/%u\n",
269 buffer
->cmd_class
, buffer
->cmd_select
);
272 dev_dbg(d
, "missing capability %d for %u/%u\n",
273 call_whitelist
[i
].need_capability
,
274 buffer
->cmd_class
, buffer
->cmd_select
);
278 /* not in a whitelist, only allow processes with capabilities */
279 if (capable(CAP_SYS_RAWIO
)) {
280 dev_dbg(d
, "Allowing %u/%u due to CAP_SYS_RAWIO\n",
281 buffer
->cmd_class
, buffer
->cmd_select
);
287 EXPORT_SYMBOL_GPL(dell_smbios_call_filter
);
289 int dell_smbios_call(struct calling_interface_buffer
*buffer
)
291 int (*call_fn
)(struct calling_interface_buffer
*) = NULL
;
292 struct device
*selected_dev
= NULL
;
293 struct smbios_device
*priv
;
296 mutex_lock(&smbios_mutex
);
297 list_for_each_entry(priv
, &smbios_device_list
, list
) {
298 if (!selected_dev
|| priv
->device
->id
>= selected_dev
->id
) {
299 dev_dbg(priv
->device
, "Trying device ID: %d\n",
301 call_fn
= priv
->call_fn
;
302 selected_dev
= priv
->device
;
308 pr_err("No dell-smbios drivers are loaded\n");
309 goto out_smbios_call
;
312 ret
= call_fn(buffer
);
315 mutex_unlock(&smbios_mutex
);
318 EXPORT_SYMBOL_GPL(dell_smbios_call
);
320 struct calling_interface_token
*dell_smbios_find_token(int tokenid
)
327 for (i
= 0; i
< da_num_tokens
; i
++) {
328 if (da_tokens
[i
].tokenID
== tokenid
)
329 return &da_tokens
[i
];
334 EXPORT_SYMBOL_GPL(dell_smbios_find_token
);
336 static BLOCKING_NOTIFIER_HEAD(dell_laptop_chain_head
);
338 int dell_laptop_register_notifier(struct notifier_block
*nb
)
340 return blocking_notifier_chain_register(&dell_laptop_chain_head
, nb
);
342 EXPORT_SYMBOL_GPL(dell_laptop_register_notifier
);
344 int dell_laptop_unregister_notifier(struct notifier_block
*nb
)
346 return blocking_notifier_chain_unregister(&dell_laptop_chain_head
, nb
);
348 EXPORT_SYMBOL_GPL(dell_laptop_unregister_notifier
);
350 void dell_laptop_call_notifier(unsigned long action
, void *data
)
352 blocking_notifier_call_chain(&dell_laptop_chain_head
, action
, data
);
354 EXPORT_SYMBOL_GPL(dell_laptop_call_notifier
);
356 static void __init
parse_da_table(const struct dmi_header
*dm
)
358 /* Final token is a terminator, so we don't want to copy it */
359 int tokens
= (dm
->length
-11)/sizeof(struct calling_interface_token
)-1;
360 struct calling_interface_token
*new_da_tokens
;
361 struct calling_interface_structure
*table
=
362 container_of(dm
, struct calling_interface_structure
, header
);
365 * 4 bytes of table header, plus 7 bytes of Dell header
366 * plus at least 6 bytes of entry
372 da_supported_commands
= table
->supportedCmds
;
374 new_da_tokens
= krealloc(da_tokens
, (da_num_tokens
+ tokens
) *
375 sizeof(struct calling_interface_token
),
380 da_tokens
= new_da_tokens
;
382 memcpy(da_tokens
+da_num_tokens
, table
->tokens
,
383 sizeof(struct calling_interface_token
) * tokens
);
385 da_num_tokens
+= tokens
;
388 static void zero_duplicates(struct device
*dev
)
392 for (i
= 0; i
< da_num_tokens
; i
++) {
393 if (da_tokens
[i
].tokenID
== 0)
395 for (j
= i
+1; j
< da_num_tokens
; j
++) {
396 if (da_tokens
[j
].tokenID
== 0)
398 if (da_tokens
[i
].tokenID
== da_tokens
[j
].tokenID
) {
399 dev_dbg(dev
, "Zeroing dup token ID %x(%x/%x)\n",
400 da_tokens
[j
].tokenID
,
401 da_tokens
[j
].location
,
403 da_tokens
[j
].tokenID
= 0;
409 static void __init
find_tokens(const struct dmi_header
*dm
, void *dummy
)
412 case 0xd4: /* Indexed IO */
413 case 0xd5: /* Protected Area Type 1 */
414 case 0xd6: /* Protected Area Type 2 */
416 case 0xda: /* Calling interface */
422 static int match_attribute(struct device
*dev
,
423 struct device_attribute
*attr
)
427 for (i
= 0; i
< da_num_tokens
* 2; i
++) {
430 if (strcmp(token_attrs
[i
]->name
, attr
->attr
.name
) == 0)
433 dev_dbg(dev
, "couldn't match: %s\n", attr
->attr
.name
);
437 static ssize_t
location_show(struct device
*dev
,
438 struct device_attribute
*attr
, char *buf
)
442 if (!capable(CAP_SYS_ADMIN
))
445 i
= match_attribute(dev
, attr
);
447 return scnprintf(buf
, PAGE_SIZE
, "%08x", da_tokens
[i
].location
);
451 static ssize_t
value_show(struct device
*dev
,
452 struct device_attribute
*attr
, char *buf
)
456 if (!capable(CAP_SYS_ADMIN
))
459 i
= match_attribute(dev
, attr
);
461 return scnprintf(buf
, PAGE_SIZE
, "%08x", da_tokens
[i
].value
);
465 static struct attribute_group smbios_attribute_group
= {
469 static struct platform_driver platform_driver
= {
471 .name
= "dell-smbios",
475 static int build_tokens_sysfs(struct platform_device
*dev
)
483 /* (number of tokens + 1 for null terminated */
484 size
= sizeof(struct device_attribute
) * (da_num_tokens
+ 1);
485 token_location_attrs
= kzalloc(size
, GFP_KERNEL
);
486 if (!token_location_attrs
)
488 token_value_attrs
= kzalloc(size
, GFP_KERNEL
);
489 if (!token_value_attrs
)
490 goto out_allocate_value
;
492 /* need to store both location and value + terminator*/
493 size
= sizeof(struct attribute
*) * ((2 * da_num_tokens
) + 1);
494 token_attrs
= kzalloc(size
, GFP_KERNEL
);
496 goto out_allocate_attrs
;
498 for (i
= 0, j
= 0; i
< da_num_tokens
; i
++) {
500 if (da_tokens
[i
].tokenID
== 0)
503 location_name
= kasprintf(GFP_KERNEL
, "%04x_location",
504 da_tokens
[i
].tokenID
);
505 if (location_name
== NULL
)
506 goto out_unwind_strings
;
507 sysfs_attr_init(&token_location_attrs
[i
].attr
);
508 token_location_attrs
[i
].attr
.name
= location_name
;
509 token_location_attrs
[i
].attr
.mode
= 0444;
510 token_location_attrs
[i
].show
= location_show
;
511 token_attrs
[j
++] = &token_location_attrs
[i
].attr
;
514 value_name
= kasprintf(GFP_KERNEL
, "%04x_value",
515 da_tokens
[i
].tokenID
);
516 if (value_name
== NULL
)
517 goto loop_fail_create_value
;
518 sysfs_attr_init(&token_value_attrs
[i
].attr
);
519 token_value_attrs
[i
].attr
.name
= value_name
;
520 token_value_attrs
[i
].attr
.mode
= 0444;
521 token_value_attrs
[i
].show
= value_show
;
522 token_attrs
[j
++] = &token_value_attrs
[i
].attr
;
525 loop_fail_create_value
:
526 kfree(location_name
);
527 goto out_unwind_strings
;
529 smbios_attribute_group
.attrs
= token_attrs
;
531 ret
= sysfs_create_group(&dev
->dev
.kobj
, &smbios_attribute_group
);
533 goto out_unwind_strings
;
538 kfree(token_location_attrs
[i
].attr
.name
);
539 kfree(token_value_attrs
[i
].attr
.name
);
543 kfree(token_value_attrs
);
545 kfree(token_location_attrs
);
550 static void free_group(struct platform_device
*pdev
)
554 sysfs_remove_group(&pdev
->dev
.kobj
,
555 &smbios_attribute_group
);
556 for (i
= 0; i
< da_num_tokens
; i
++) {
557 kfree(token_location_attrs
[i
].attr
.name
);
558 kfree(token_value_attrs
[i
].attr
.name
);
561 kfree(token_value_attrs
);
562 kfree(token_location_attrs
);
565 static int __init
dell_smbios_init(void)
569 if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING
, "Dell System", NULL
) &&
570 !dmi_find_device(DMI_DEV_TYPE_OEM_STRING
, "www.dell.com", NULL
)) {
571 pr_err("Unable to run on non-Dell system\n");
575 dmi_walk(find_tokens
, NULL
);
577 ret
= platform_driver_register(&platform_driver
);
579 goto fail_platform_driver
;
581 platform_device
= platform_device_alloc("dell-smbios", 0);
582 if (!platform_device
) {
584 goto fail_platform_device_alloc
;
586 ret
= platform_device_add(platform_device
);
588 goto fail_platform_device_add
;
590 /* register backends */
591 wmi
= init_dell_smbios_wmi();
593 pr_debug("Failed to initialize WMI backend: %d\n", wmi
);
594 smm
= init_dell_smbios_smm();
596 pr_debug("Failed to initialize SMM backend: %d\n", smm
);
598 pr_err("No SMBIOS backends available (wmi: %d, smm: %d)\n",
600 goto fail_create_group
;
604 /* duplicate tokens will cause problems building sysfs files */
605 zero_duplicates(&platform_device
->dev
);
607 ret
= build_tokens_sysfs(platform_device
);
615 free_group(platform_device
);
618 platform_device_del(platform_device
);
620 fail_platform_device_add
:
621 platform_device_put(platform_device
);
623 fail_platform_device_alloc
:
624 platform_driver_unregister(&platform_driver
);
626 fail_platform_driver
:
631 static void __exit
dell_smbios_exit(void)
633 exit_dell_smbios_wmi();
634 exit_dell_smbios_smm();
635 mutex_lock(&smbios_mutex
);
636 if (platform_device
) {
638 free_group(platform_device
);
639 platform_device_unregister(platform_device
);
640 platform_driver_unregister(&platform_driver
);
643 mutex_unlock(&smbios_mutex
);
646 module_init(dell_smbios_init
);
647 module_exit(dell_smbios_exit
);
649 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
650 MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
651 MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
652 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
653 MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS");
654 MODULE_LICENSE("GPL");