1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Common functions for kernel modules using Dell SMBIOS 4 * 5 * Copyright (c) Red Hat <mjg@redhat.com> 6 * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com> 7 * Copyright (c) 2014 Pali Rohár <pali@kernel.org> 8 * 9 * Based on documentation in the libsmbios package: 10 * Copyright (C) 2005-2014 Dell Inc. 11 */ 12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 14 #include <linux/container_of.h> 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/capability.h> 18 #include <linux/dmi.h> 19 #include <linux/err.h> 20 #include <linux/mutex.h> 21 #include <linux/platform_device.h> 22 #include <linux/slab.h> 23 #include "dell-smbios.h" 24 25 static u32 da_supported_commands; 26 static int da_num_tokens; 27 static struct platform_device *platform_device; 28 static struct calling_interface_token *da_tokens; 29 static struct token_sysfs_data *token_entries; 30 static struct attribute **token_attrs; 31 static DEFINE_MUTEX(smbios_mutex); 32 33 struct token_sysfs_data { 34 struct device_attribute location_attr; 35 struct device_attribute value_attr; 36 struct calling_interface_token *token; 37 }; 38 39 struct smbios_device { 40 struct list_head list; 41 struct device *device; 42 int (*call_fn)(struct calling_interface_buffer *arg); 43 }; 44 45 struct smbios_call { 46 u32 need_capability; 47 int cmd_class; 48 int cmd_select; 49 }; 50 51 /* calls that are whitelisted for given capabilities */ 52 static struct smbios_call call_whitelist[] = { 53 /* generally tokens are allowed, but may be further filtered or 54 * restricted by token blacklist or whitelist 55 */ 56 {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_STD}, 57 {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_AC}, 58 {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_BAT}, 59 {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD}, 60 {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_AC}, 61 {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT}, 62 /* used by userspace: fwupdate */ 63 {CAP_SYS_ADMIN, CLASS_ADMIN_PROP, SELECT_ADMIN_PROP}, 64 /* used by userspace: fwupd */ 65 {CAP_SYS_ADMIN, CLASS_INFO, SELECT_DOCK}, 66 {CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE, SELECT_FLASH_INTERFACE}, 67 }; 68 69 /* calls that are explicitly blacklisted */ 70 static struct smbios_call call_blacklist[] = { 71 {0x0000, 1, 7}, /* manufacturing use */ 72 {0x0000, 6, 5}, /* manufacturing use */ 73 {0x0000, 11, 3}, /* write once */ 74 {0x0000, 11, 7}, /* write once */ 75 {0x0000, 11, 11}, /* write once */ 76 {0x0000, 19, -1}, /* diagnostics */ 77 /* handled by kernel: dell-laptop */ 78 {0x0000, CLASS_INFO, SELECT_RFKILL}, 79 {0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT}, 80 }; 81 82 struct token_range { 83 u32 need_capability; 84 u16 min; 85 u16 max; 86 }; 87 88 /* tokens that are whitelisted for given capabilities */ 89 static struct token_range token_whitelist[] = { 90 /* used by userspace: fwupdate */ 91 {CAP_SYS_ADMIN, CAPSULE_EN_TOKEN, CAPSULE_DIS_TOKEN}, 92 /* can indicate to userspace that WMI is needed */ 93 {0x0000, WSMT_EN_TOKEN, WSMT_DIS_TOKEN} 94 }; 95 96 /* tokens that are explicitly blacklisted */ 97 static struct token_range token_blacklist[] = { 98 {0x0000, 0x0058, 0x0059}, /* ME use */ 99 {0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */ 100 {0x0000, 0x013A, 0x01FF}, /* sata shadow copy */ 101 {0x0000, 0x0175, 0x0176}, /* write once */ 102 {0x0000, 0x0195, 0x0197}, /* diagnostics */ 103 {0x0000, 0x01DC, 0x01DD}, /* manufacturing use */ 104 {0x0000, 0x027D, 0x0284}, /* diagnostics */ 105 {0x0000, 0x02E3, 0x02E3}, /* manufacturing use */ 106 {0x0000, 0x02FF, 0x02FF}, /* manufacturing use */ 107 {0x0000, 0x0300, 0x0302}, /* manufacturing use */ 108 {0x0000, 0x0325, 0x0326}, /* manufacturing use */ 109 {0x0000, 0x0332, 0x0335}, /* fan control */ 110 {0x0000, 0x0350, 0x0350}, /* manufacturing use */ 111 {0x0000, 0x0363, 0x0363}, /* manufacturing use */ 112 {0x0000, 0x0368, 0x0368}, /* manufacturing use */ 113 {0x0000, 0x03F6, 0x03F7}, /* manufacturing use */ 114 {0x0000, 0x049E, 0x049F}, /* manufacturing use */ 115 {0x0000, 0x04A0, 0x04A3}, /* disagnostics */ 116 {0x0000, 0x04E6, 0x04E7}, /* manufacturing use */ 117 {0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */ 118 {0x0000, 0x9000, 0x9001}, /* internal BIOS use */ 119 {0x0000, 0xA000, 0xBFFF}, /* write only */ 120 {0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */ 121 /* handled by kernel: dell-laptop */ 122 {0x0000, BRIGHTNESS_TOKEN, BRIGHTNESS_TOKEN}, 123 {0x0000, KBD_LED_OFF_TOKEN, KBD_LED_AUTO_TOKEN}, 124 {0x0000, KBD_LED_AC_TOKEN, KBD_LED_AC_TOKEN}, 125 {0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN}, 126 {0x0000, KBD_LED_AUTO_100_TOKEN, KBD_LED_AUTO_100_TOKEN}, 127 {0x0000, GLOBAL_MIC_MUTE_ENABLE, GLOBAL_MIC_MUTE_DISABLE}, 128 }; 129 130 static LIST_HEAD(smbios_device_list); 131 132 int dell_smbios_error(int value) 133 { 134 switch (value) { 135 case 0: /* Completed successfully */ 136 return 0; 137 case -1: /* Completed with error */ 138 return -EIO; 139 case -2: /* Function not supported */ 140 return -ENXIO; 141 default: /* Unknown error */ 142 return -EINVAL; 143 } 144 } 145 EXPORT_SYMBOL_GPL(dell_smbios_error); 146 147 int dell_smbios_register_device(struct device *d, void *call_fn) 148 { 149 struct smbios_device *priv; 150 151 priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL); 152 if (!priv) 153 return -ENOMEM; 154 get_device(d); 155 priv->device = d; 156 priv->call_fn = call_fn; 157 mutex_lock(&smbios_mutex); 158 list_add_tail(&priv->list, &smbios_device_list); 159 mutex_unlock(&smbios_mutex); 160 dev_dbg(d, "Added device: %s\n", d->driver->name); 161 return 0; 162 } 163 EXPORT_SYMBOL_GPL(dell_smbios_register_device); 164 165 void dell_smbios_unregister_device(struct device *d) 166 { 167 struct smbios_device *priv; 168 169 mutex_lock(&smbios_mutex); 170 list_for_each_entry(priv, &smbios_device_list, list) { 171 if (priv->device == d) { 172 list_del(&priv->list); 173 put_device(d); 174 break; 175 } 176 } 177 mutex_unlock(&smbios_mutex); 178 dev_dbg(d, "Remove device: %s\n", d->driver->name); 179 } 180 EXPORT_SYMBOL_GPL(dell_smbios_unregister_device); 181 182 int dell_smbios_call_filter(struct device *d, 183 struct calling_interface_buffer *buffer) 184 { 185 u16 t = 0; 186 int i; 187 188 /* can't make calls over 30 */ 189 if (buffer->cmd_class > 30) { 190 dev_dbg(d, "class too big: %u\n", buffer->cmd_class); 191 return -EINVAL; 192 } 193 194 /* supported calls on the particular system */ 195 if (!(da_supported_commands & (1 << buffer->cmd_class))) { 196 dev_dbg(d, "invalid command, supported commands: 0x%8x\n", 197 da_supported_commands); 198 return -EINVAL; 199 } 200 201 /* match against call blacklist */ 202 for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) { 203 if (buffer->cmd_class != call_blacklist[i].cmd_class) 204 continue; 205 if (buffer->cmd_select != call_blacklist[i].cmd_select && 206 call_blacklist[i].cmd_select != -1) 207 continue; 208 dev_dbg(d, "blacklisted command: %u/%u\n", 209 buffer->cmd_class, buffer->cmd_select); 210 return -EINVAL; 211 } 212 213 /* if a token call, find token ID */ 214 215 if ((buffer->cmd_class == CLASS_TOKEN_READ || 216 buffer->cmd_class == CLASS_TOKEN_WRITE) && 217 buffer->cmd_select < 3) { 218 /* tokens enabled ? */ 219 if (!da_tokens) { 220 dev_dbg(d, "no token support on this system\n"); 221 return -EINVAL; 222 } 223 224 /* find the matching token ID */ 225 for (i = 0; i < da_num_tokens; i++) { 226 if (da_tokens[i].location != buffer->input[0]) 227 continue; 228 t = da_tokens[i].tokenID; 229 break; 230 } 231 232 /* token call; but token didn't exist */ 233 if (!t) { 234 dev_dbg(d, "token at location %04x doesn't exist\n", 235 buffer->input[0]); 236 return -EINVAL; 237 } 238 239 /* match against token blacklist */ 240 for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) { 241 if (!token_blacklist[i].min || !token_blacklist[i].max) 242 continue; 243 if (t >= token_blacklist[i].min && 244 t <= token_blacklist[i].max) 245 return -EINVAL; 246 } 247 248 /* match against token whitelist */ 249 for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) { 250 if (!token_whitelist[i].min || !token_whitelist[i].max) 251 continue; 252 if (t < token_whitelist[i].min || 253 t > token_whitelist[i].max) 254 continue; 255 if (!token_whitelist[i].need_capability || 256 capable(token_whitelist[i].need_capability)) { 257 dev_dbg(d, "whitelisted token: %x\n", t); 258 return 0; 259 } 260 261 } 262 } 263 /* match against call whitelist */ 264 for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) { 265 if (buffer->cmd_class != call_whitelist[i].cmd_class) 266 continue; 267 if (buffer->cmd_select != call_whitelist[i].cmd_select) 268 continue; 269 if (!call_whitelist[i].need_capability || 270 capable(call_whitelist[i].need_capability)) { 271 dev_dbg(d, "whitelisted capable command: %u/%u\n", 272 buffer->cmd_class, buffer->cmd_select); 273 return 0; 274 } 275 dev_dbg(d, "missing capability %d for %u/%u\n", 276 call_whitelist[i].need_capability, 277 buffer->cmd_class, buffer->cmd_select); 278 279 } 280 281 /* not in a whitelist, only allow processes with capabilities */ 282 if (capable(CAP_SYS_RAWIO)) { 283 dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n", 284 buffer->cmd_class, buffer->cmd_select); 285 return 0; 286 } 287 288 return -EACCES; 289 } 290 EXPORT_SYMBOL_GPL(dell_smbios_call_filter); 291 292 int dell_smbios_call(struct calling_interface_buffer *buffer) 293 { 294 int (*call_fn)(struct calling_interface_buffer *) = NULL; 295 struct device *selected_dev = NULL; 296 struct smbios_device *priv; 297 int ret; 298 299 mutex_lock(&smbios_mutex); 300 list_for_each_entry(priv, &smbios_device_list, list) { 301 if (!selected_dev || priv->device->id >= selected_dev->id) { 302 dev_dbg(priv->device, "Trying device ID: %d\n", 303 priv->device->id); 304 call_fn = priv->call_fn; 305 selected_dev = priv->device; 306 } 307 } 308 309 if (!selected_dev) { 310 ret = -ENODEV; 311 pr_err("No dell-smbios drivers are loaded\n"); 312 goto out_smbios_call; 313 } 314 315 ret = call_fn(buffer); 316 317 out_smbios_call: 318 mutex_unlock(&smbios_mutex); 319 return ret; 320 } 321 EXPORT_SYMBOL_GPL(dell_smbios_call); 322 323 struct calling_interface_token *dell_smbios_find_token(int tokenid) 324 { 325 int i; 326 327 if (!da_tokens) 328 return NULL; 329 330 for (i = 0; i < da_num_tokens; i++) { 331 if (da_tokens[i].tokenID == tokenid) 332 return &da_tokens[i]; 333 } 334 335 return NULL; 336 } 337 EXPORT_SYMBOL_GPL(dell_smbios_find_token); 338 339 static BLOCKING_NOTIFIER_HEAD(dell_laptop_chain_head); 340 341 int dell_laptop_register_notifier(struct notifier_block *nb) 342 { 343 return blocking_notifier_chain_register(&dell_laptop_chain_head, nb); 344 } 345 EXPORT_SYMBOL_GPL(dell_laptop_register_notifier); 346 347 int dell_laptop_unregister_notifier(struct notifier_block *nb) 348 { 349 return blocking_notifier_chain_unregister(&dell_laptop_chain_head, nb); 350 } 351 EXPORT_SYMBOL_GPL(dell_laptop_unregister_notifier); 352 353 void dell_laptop_call_notifier(unsigned long action, void *data) 354 { 355 blocking_notifier_call_chain(&dell_laptop_chain_head, action, data); 356 } 357 EXPORT_SYMBOL_GPL(dell_laptop_call_notifier); 358 359 static void __init parse_da_table(const struct dmi_header *dm) 360 { 361 /* Final token is a terminator, so we don't want to copy it */ 362 int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1; 363 struct calling_interface_token *new_da_tokens; 364 struct calling_interface_structure *table = 365 container_of(dm, struct calling_interface_structure, header); 366 367 /* 368 * 4 bytes of table header, plus 7 bytes of Dell header 369 * plus at least 6 bytes of entry 370 */ 371 372 if (dm->length < 17) 373 return; 374 375 da_supported_commands = table->supportedCmds; 376 377 new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) * 378 sizeof(struct calling_interface_token), 379 GFP_KERNEL); 380 381 if (!new_da_tokens) 382 return; 383 da_tokens = new_da_tokens; 384 385 memcpy(da_tokens+da_num_tokens, table->tokens, 386 sizeof(struct calling_interface_token) * tokens); 387 388 da_num_tokens += tokens; 389 } 390 391 static void zero_duplicates(struct device *dev) 392 { 393 int i, j; 394 395 for (i = 0; i < da_num_tokens; i++) { 396 if (da_tokens[i].tokenID == 0) 397 continue; 398 for (j = i+1; j < da_num_tokens; j++) { 399 if (da_tokens[j].tokenID == 0) 400 continue; 401 if (da_tokens[i].tokenID == da_tokens[j].tokenID) { 402 dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n", 403 da_tokens[j].tokenID, 404 da_tokens[j].location, 405 da_tokens[j].value); 406 da_tokens[j].tokenID = 0; 407 } 408 } 409 } 410 } 411 412 static void __init find_tokens(const struct dmi_header *dm, void *dummy) 413 { 414 switch (dm->type) { 415 case 0xd4: /* Indexed IO */ 416 case 0xd5: /* Protected Area Type 1 */ 417 case 0xd6: /* Protected Area Type 2 */ 418 break; 419 case 0xda: /* Calling interface */ 420 parse_da_table(dm); 421 break; 422 } 423 } 424 425 static ssize_t location_show(struct device *dev, 426 struct device_attribute *attr, char *buf) 427 { 428 struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, location_attr); 429 430 if (!capable(CAP_SYS_ADMIN)) 431 return -EPERM; 432 433 return sysfs_emit(buf, "%08x", data->token->location); 434 } 435 436 static ssize_t value_show(struct device *dev, 437 struct device_attribute *attr, char *buf) 438 { 439 struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, value_attr); 440 441 if (!capable(CAP_SYS_ADMIN)) 442 return -EPERM; 443 444 return sysfs_emit(buf, "%08x", data->token->value); 445 } 446 447 static struct attribute_group smbios_attribute_group = { 448 .name = "tokens" 449 }; 450 451 static struct platform_driver platform_driver = { 452 .driver = { 453 .name = "dell-smbios", 454 }, 455 }; 456 457 static int build_tokens_sysfs(struct platform_device *dev) 458 { 459 char *location_name; 460 char *value_name; 461 int ret; 462 int i, j; 463 464 token_entries = kcalloc(da_num_tokens, sizeof(*token_entries), GFP_KERNEL); 465 if (!token_entries) 466 return -ENOMEM; 467 468 /* need to store both location and value + terminator*/ 469 token_attrs = kcalloc((2 * da_num_tokens) + 1, sizeof(*token_attrs), GFP_KERNEL); 470 if (!token_attrs) 471 goto out_allocate_attrs; 472 473 for (i = 0, j = 0; i < da_num_tokens; i++) { 474 /* skip empty */ 475 if (da_tokens[i].tokenID == 0) 476 continue; 477 478 token_entries[i].token = &da_tokens[i]; 479 480 /* add location */ 481 location_name = kasprintf(GFP_KERNEL, "%04x_location", 482 da_tokens[i].tokenID); 483 if (location_name == NULL) 484 goto out_unwind_strings; 485 486 sysfs_attr_init(&token_entries[i].location_attr.attr); 487 token_entries[i].location_attr.attr.name = location_name; 488 token_entries[i].location_attr.attr.mode = 0444; 489 token_entries[i].location_attr.show = location_show; 490 token_attrs[j++] = &token_entries[i].location_attr.attr; 491 492 /* add value */ 493 value_name = kasprintf(GFP_KERNEL, "%04x_value", 494 da_tokens[i].tokenID); 495 if (!value_name) { 496 kfree(location_name); 497 goto out_unwind_strings; 498 } 499 500 sysfs_attr_init(&token_entries[i].value_attr.attr); 501 token_entries[i].value_attr.attr.name = value_name; 502 token_entries[i].value_attr.attr.mode = 0444; 503 token_entries[i].value_attr.show = value_show; 504 token_attrs[j++] = &token_entries[i].value_attr.attr; 505 } 506 smbios_attribute_group.attrs = token_attrs; 507 508 ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group); 509 if (ret) 510 goto out_unwind_strings; 511 return 0; 512 513 out_unwind_strings: 514 while (i--) { 515 kfree(token_entries[i].location_attr.attr.name); 516 kfree(token_entries[i].value_attr.attr.name); 517 } 518 kfree(token_attrs); 519 out_allocate_attrs: 520 kfree(token_entries); 521 522 return -ENOMEM; 523 } 524 525 static void free_group(struct platform_device *pdev) 526 { 527 int i; 528 529 sysfs_remove_group(&pdev->dev.kobj, 530 &smbios_attribute_group); 531 for (i = 0; i < da_num_tokens; i++) { 532 kfree(token_entries[i].location_attr.attr.name); 533 kfree(token_entries[i].value_attr.attr.name); 534 } 535 kfree(token_attrs); 536 kfree(token_entries); 537 } 538 539 static int __init dell_smbios_init(void) 540 { 541 int ret, wmi, smm; 542 543 if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) && 544 !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) { 545 pr_err("Unable to run on non-Dell system\n"); 546 return -ENODEV; 547 } 548 549 dmi_walk(find_tokens, NULL); 550 551 ret = platform_driver_register(&platform_driver); 552 if (ret) 553 goto fail_platform_driver; 554 555 platform_device = platform_device_alloc("dell-smbios", 0); 556 if (!platform_device) { 557 ret = -ENOMEM; 558 goto fail_platform_device_alloc; 559 } 560 ret = platform_device_add(platform_device); 561 if (ret) 562 goto fail_platform_device_add; 563 564 /* register backends */ 565 wmi = init_dell_smbios_wmi(); 566 if (wmi) 567 pr_debug("Failed to initialize WMI backend: %d\n", wmi); 568 smm = init_dell_smbios_smm(); 569 if (smm) 570 pr_debug("Failed to initialize SMM backend: %d\n", smm); 571 if (wmi && smm) { 572 pr_err("No SMBIOS backends available (wmi: %d, smm: %d)\n", 573 wmi, smm); 574 ret = -ENODEV; 575 goto fail_create_group; 576 } 577 578 if (da_tokens) { 579 /* duplicate tokens will cause problems building sysfs files */ 580 zero_duplicates(&platform_device->dev); 581 582 ret = build_tokens_sysfs(platform_device); 583 if (ret) 584 goto fail_sysfs; 585 } 586 587 return 0; 588 589 fail_sysfs: 590 free_group(platform_device); 591 592 fail_create_group: 593 platform_device_del(platform_device); 594 595 fail_platform_device_add: 596 platform_device_put(platform_device); 597 598 fail_platform_device_alloc: 599 platform_driver_unregister(&platform_driver); 600 601 fail_platform_driver: 602 kfree(da_tokens); 603 return ret; 604 } 605 606 static void __exit dell_smbios_exit(void) 607 { 608 exit_dell_smbios_wmi(); 609 exit_dell_smbios_smm(); 610 mutex_lock(&smbios_mutex); 611 if (platform_device) { 612 if (da_tokens) 613 free_group(platform_device); 614 platform_device_unregister(platform_device); 615 platform_driver_unregister(&platform_driver); 616 } 617 kfree(da_tokens); 618 mutex_unlock(&smbios_mutex); 619 } 620 621 module_init(dell_smbios_init); 622 module_exit(dell_smbios_exit); 623 624 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 625 MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>"); 626 MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); 627 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); 628 MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS"); 629 MODULE_LICENSE("GPL"); 630