struct smbios_device { struct list_head list; struct device *device; int priority; int (*call_fn)(struct calling_interface_buffer *arg);
};
struct smbios_call {
u32 need_capability; int cmd_class; int cmd_select;
};
/* calls that are whitelisted for given capabilities */ staticstruct smbios_call call_whitelist[] = { /* generally tokens are allowed, but may be further filtered or * restricted by token blacklist or whitelist
*/
{CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_STD},
{CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_AC},
{CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_BAT},
{CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD},
{CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_AC},
{CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT}, /* used by userspace: fwupdate */
{CAP_SYS_ADMIN, CLASS_ADMIN_PROP, SELECT_ADMIN_PROP}, /* used by userspace: fwupd */
{CAP_SYS_ADMIN, CLASS_INFO, SELECT_DOCK},
{CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE, SELECT_FLASH_INTERFACE},
};
/* calls that are explicitly blacklisted */ staticstruct smbios_call call_blacklist[] = {
{0x0000, 1, 7}, /* manufacturing use */
{0x0000, 6, 5}, /* manufacturing use */
{0x0000, 11, 3}, /* write once */
{0x0000, 11, 7}, /* write once */
{0x0000, 11, 11}, /* write once */
{0x0000, 19, -1}, /* diagnostics */ /* handled by kernel: dell-laptop */
{0x0000, CLASS_INFO, SELECT_RFKILL},
{0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT},
{0x0000, CLASS_INFO, SELECT_THERMAL_MANAGEMENT},
};
/* tokens that are whitelisted for given capabilities */ staticstruct token_range token_whitelist[] = { /* used by userspace: fwupdate */
{CAP_SYS_ADMIN, CAPSULE_EN_TOKEN, CAPSULE_DIS_TOKEN}, /* can indicate to userspace that WMI is needed */
{0x0000, WSMT_EN_TOKEN, WSMT_DIS_TOKEN}
};
/* tokens that are explicitly blacklisted */ staticstruct token_range token_blacklist[] = {
{0x0000, 0x0058, 0x0059}, /* ME use */
{0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */
{0x0000, 0x013A, 0x01FF}, /* sata shadow copy */
{0x0000, 0x0175, 0x0176}, /* write once */
{0x0000, 0x0195, 0x0197}, /* diagnostics */
{0x0000, 0x01DC, 0x01DD}, /* manufacturing use */
{0x0000, 0x027D, 0x0284}, /* diagnostics */
{0x0000, 0x02E3, 0x02E3}, /* manufacturing use */
{0x0000, 0x02FF, 0x02FF}, /* manufacturing use */
{0x0000, 0x0300, 0x0302}, /* manufacturing use */
{0x0000, 0x0325, 0x0326}, /* manufacturing use */
{0x0000, 0x0332, 0x0335}, /* fan control */
{0x0000, 0x0350, 0x0350}, /* manufacturing use */
{0x0000, 0x0363, 0x0363}, /* manufacturing use */
{0x0000, 0x0368, 0x0368}, /* manufacturing use */
{0x0000, 0x03F6, 0x03F7}, /* manufacturing use */
{0x0000, 0x049E, 0x049F}, /* manufacturing use */
{0x0000, 0x04A0, 0x04A3}, /* disagnostics */
{0x0000, 0x04E6, 0x04E7}, /* manufacturing use */
{0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */
{0x0000, 0x9000, 0x9001}, /* internal BIOS use */
{0x0000, 0xA000, 0xBFFF}, /* write only */
{0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */ /* handled by kernel: dell-laptop */
{0x0000, BRIGHTNESS_TOKEN, BRIGHTNESS_TOKEN},
{0x0000, KBD_LED_OFF_TOKEN, KBD_LED_AUTO_TOKEN},
{0x0000, KBD_LED_AC_TOKEN, KBD_LED_AC_TOKEN},
{0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN},
{0x0000, KBD_LED_AUTO_100_TOKEN, KBD_LED_AUTO_100_TOKEN},
{0x0000, GLOBAL_MIC_MUTE_ENABLE, GLOBAL_MIC_MUTE_DISABLE},
};
static LIST_HEAD(smbios_device_list);
int dell_smbios_error(int value)
{ switch (value) { case 0: /* Completed successfully */ return 0; case -1: /* Completed with error */ return -EIO; case -2: /* Function not supported */ return -ENXIO; default: /* Unknown error */ return -EINVAL;
}
}
EXPORT_SYMBOL_GPL(dell_smbios_error);
int dell_smbios_register_device(struct device *d, int priority, void *call_fn)
{ struct smbios_device *priv;
int dell_smbios_call_filter(struct device *d, struct calling_interface_buffer *buffer)
{
u16 t = 0; int i;
/* can't make calls over 30 */ if (buffer->cmd_class > 30) {
dev_dbg(d, "class too big: %u\n", buffer->cmd_class); return -EINVAL;
}
/* supported calls on the particular system */ if (!(da_supported_commands & (1 << buffer->cmd_class))) {
dev_dbg(d, "invalid command, supported commands: 0x%8x\n",
da_supported_commands); return -EINVAL;
}
/* match against call blacklist */ for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) { if (buffer->cmd_class != call_blacklist[i].cmd_class) continue; if (buffer->cmd_select != call_blacklist[i].cmd_select &&
call_blacklist[i].cmd_select != -1) continue;
dev_dbg(d, "blacklisted command: %u/%u\n",
buffer->cmd_class, buffer->cmd_select); return -EINVAL;
}
/* if a token call, find token ID */
if ((buffer->cmd_class == CLASS_TOKEN_READ ||
buffer->cmd_class == CLASS_TOKEN_WRITE) &&
buffer->cmd_select < 3) { /* tokens enabled ? */ if (!da_tokens) {
dev_dbg(d, "no token support on this system\n"); return -EINVAL;
}
/* find the matching token ID */ for (i = 0; i < da_num_tokens; i++) { if (da_tokens[i].location != buffer->input[0]) continue;
t = da_tokens[i].tokenID; break;
}
/* token call; but token didn't exist */ if (!t) {
dev_dbg(d, "token at location %04x doesn't exist\n",
buffer->input[0]); return -EINVAL;
}
/* match against token blacklist */ for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) { if (!token_blacklist[i].min || !token_blacklist[i].max) continue; if (t >= token_blacklist[i].min &&
t <= token_blacklist[i].max) return -EINVAL;
}
/* match against token whitelist */ for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) { if (!token_whitelist[i].min || !token_whitelist[i].max) continue; if (t < token_whitelist[i].min ||
t > token_whitelist[i].max) continue; if (!token_whitelist[i].need_capability ||
capable(token_whitelist[i].need_capability)) {
dev_dbg(d, "whitelisted token: %x\n", t); return 0;
}
}
} /* match against call whitelist */ for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) { if (buffer->cmd_class != call_whitelist[i].cmd_class) continue; if (buffer->cmd_select != call_whitelist[i].cmd_select) continue; if (!call_whitelist[i].need_capability ||
capable(call_whitelist[i].need_capability)) {
dev_dbg(d, "whitelisted capable command: %u/%u\n",
buffer->cmd_class, buffer->cmd_select); return 0;
}
dev_dbg(d, "missing capability %d for %u/%u\n",
call_whitelist[i].need_capability,
buffer->cmd_class, buffer->cmd_select);
}
/* not in a whitelist, only allow processes with capabilities */ if (capable(CAP_SYS_RAWIO)) {
dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n",
buffer->cmd_class, buffer->cmd_select); return 0;
}
staticvoid __init parse_da_table(conststruct dmi_header *dm)
{ /* Final token is a terminator, so we don't want to copy it */ int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1; struct calling_interface_token *new_da_tokens; struct calling_interface_structure *table =
container_of(dm, struct calling_interface_structure, header);
/* * 4 bytes of table header, plus 7 bytes of Dell header * plus at least 6 bytes of entry
*/
staticint build_tokens_sysfs(struct platform_device *dev)
{ char *location_name; char *value_name; int ret; int i, j;
token_entries = kcalloc(da_num_tokens, sizeof(*token_entries), GFP_KERNEL); if (!token_entries) return -ENOMEM;
/* need to store both location and value + terminator*/
token_attrs = kcalloc((2 * da_num_tokens) + 1, sizeof(*token_attrs), GFP_KERNEL); if (!token_attrs) goto out_allocate_attrs;
for (i = 0, j = 0; i < da_num_tokens; i++) { /* skip empty */ if (da_tokens[i].tokenID == 0) continue;
ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group); if (ret) goto out_unwind_strings; return 0;
out_unwind_strings: while (i--) {
kfree(token_entries[i].location_attr.attr.name);
kfree(token_entries[i].value_attr.attr.name);
}
kfree(token_attrs);
out_allocate_attrs:
kfree(token_entries);
return -ENOMEM;
}
staticvoid free_group(struct platform_device *pdev)
{ int i;
sysfs_remove_group(&pdev->dev.kobj,
&smbios_attribute_group); for (i = 0; i < da_num_tokens; i++) {
kfree(token_entries[i].location_attr.attr.name);
kfree(token_entries[i].value_attr.attr.name);
}
kfree(token_attrs);
kfree(token_entries);
}
staticint __init dell_smbios_init(void)
{ int ret, wmi, smm;
if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Alienware", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) {
pr_err("Unable to run on non-Dell system\n"); return -ENODEV;
}
dmi_walk(find_tokens, NULL);
ret = platform_driver_register(&platform_driver); if (ret) goto fail_platform_driver;
platform_device = platform_device_alloc("dell-smbios", 0); if (!platform_device) {
ret = -ENOMEM; goto fail_platform_device_alloc;
}
ret = platform_device_add(platform_device); if (ret) goto fail_platform_device_add;
/* register backends */
wmi = init_dell_smbios_wmi(); if (wmi)
pr_debug("Failed to initialize WMI backend: %d\n", wmi);
smm = init_dell_smbios_smm(); if (smm)
pr_debug("Failed to initialize SMM backend: %d\n", smm); if (wmi && smm) {
pr_err("No SMBIOS backends available (wmi: %d, smm: %d)\n",
wmi, smm);
ret = -ENODEV; goto fail_create_group;
}
if (da_tokens) { /* duplicate tokens will cause problems building sysfs files */
zero_duplicates(&platform_device->dev);
ret = build_tokens_sysfs(platform_device); if (ret) goto fail_sysfs;
}
return 0;
fail_sysfs: if (!wmi)
exit_dell_smbios_wmi(); if (!smm)
exit_dell_smbios_smm();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.