Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: dmi_scan: add SBMIOS entry and DMI tables

Some utils, like dmidecode and smbios, need to access SMBIOS entry
table area in order to get information like SMBIOS version, size, etc.
Currently it's done via /dev/mem. But for situation when /dev/mem
usage is disabled, the utils have to use dmi sysfs instead, which
doesn't represent SMBIOS entry and adds code/delay redundancy when direct
access for table is needed.

So this patch creates dmi/tables and adds SMBIOS entry point to allow
utils in question to work correctly without /dev/mem. Also patch adds
raw dmi table to simplify dmi table processing in user space, as
proposed by Jean Delvare.

Tested-by: Roy Franz <roy.franz@linaro.org>
Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@globallogic.com>
Signed-off-by: Jean Delvare <jdelvare@suse.de>

authored by

Ivan Khoronzhuk and committed by
Jean Delvare
d7f96f97 6e0ad59e

+111 -9
+22
Documentation/ABI/testing/sysfs-firmware-dmi-tables
··· 1 + What: /sys/firmware/dmi/tables/ 2 + Date: April 2015 3 + Contact: Ivan Khoronzhuk <ivan.khoronzhuk@globallogic.com> 4 + Description: 5 + The firmware provides DMI structures as a packed list of 6 + data referenced by a SMBIOS table entry point. The SMBIOS 7 + entry point contains general information, like SMBIOS 8 + version, DMI table size, etc. The structure, content and 9 + size of SMBIOS entry point is dependent on SMBIOS version. 10 + The format of SMBIOS entry point and DMI structures 11 + can be read in SMBIOS specification. 12 + 13 + The dmi/tables provides raw SMBIOS entry point and DMI tables 14 + through sysfs as an alternative to utilities reading them 15 + from /dev/mem. The raw SMBIOS entry point and DMI table are 16 + presented as binary attributes and are accessible via: 17 + 18 + /sys/firmware/dmi/tables/smbios_entry_point 19 + /sys/firmware/dmi/tables/DMI 20 + 21 + The complete DMI information can be obtained using these two 22 + tables.
+1
MAINTAINERS
··· 3289 3289 M: Jean Delvare <jdelvare@suse.de> 3290 3290 S: Maintained 3291 3291 T: quilt http://jdelvare.nerim.net/devel/linux/jdelvare-dmi/ 3292 + F: Documentation/ABI/testing/sysfs-firmware-dmi-tables 3292 3293 F: drivers/firmware/dmi-id.c 3293 3294 F: drivers/firmware/dmi_scan.c 3294 3295 F: include/linux/dmi.h
+8 -9
drivers/firmware/dmi-sysfs.c
··· 566 566 .default_attrs = dmi_sysfs_entry_attrs, 567 567 }; 568 568 569 - static struct kobject *dmi_kobj; 570 569 static struct kset *dmi_kset; 571 570 572 571 /* Global count of all instances seen. Only for setup */ ··· 647 648 648 649 static int __init dmi_sysfs_init(void) 649 650 { 650 - int error = -ENOMEM; 651 + int error; 651 652 int val; 652 653 653 - /* Set up our directory */ 654 - dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); 655 - if (!dmi_kobj) 654 + if (!dmi_kobj) { 655 + pr_err("dmi-sysfs: dmi entry is absent.\n"); 656 + error = -ENODATA; 656 657 goto err; 658 + } 657 659 658 660 dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); 659 - if (!dmi_kset) 661 + if (!dmi_kset) { 662 + error = -ENOMEM; 660 663 goto err; 664 + } 661 665 662 666 val = 0; 663 667 error = dmi_walk(dmi_sysfs_register_handle, &val); ··· 677 675 err: 678 676 cleanup_entry_list(); 679 677 kset_unregister(dmi_kset); 680 - kobject_put(dmi_kobj); 681 678 return error; 682 679 } 683 680 ··· 686 685 pr_debug("dmi-sysfs: unloading.\n"); 687 686 cleanup_entry_list(); 688 687 kset_unregister(dmi_kset); 689 - kobject_del(dmi_kobj); 690 - kobject_put(dmi_kobj); 691 688 } 692 689 693 690 module_init(dmi_sysfs_init);
+78
drivers/firmware/dmi_scan.c
··· 10 10 #include <asm/dmi.h> 11 11 #include <asm/unaligned.h> 12 12 13 + struct kobject *dmi_kobj; 14 + EXPORT_SYMBOL_GPL(dmi_kobj); 15 + 13 16 /* 14 17 * DMI stands for "Desktop Management Interface". It is part 15 18 * of and an antecedent to, SMBIOS, which stands for System ··· 23 20 static u32 dmi_ver __initdata; 24 21 static u32 dmi_len; 25 22 static u16 dmi_num; 23 + static u8 smbios_entry_point[32]; 24 + static int smbios_entry_point_size; 25 + 26 26 /* 27 27 * Catch too early calls to dmi_check_system(): 28 28 */ ··· 494 488 if (memcmp(buf, "_SM_", 4) == 0 && 495 489 buf[5] < 32 && dmi_checksum(buf, buf[5])) { 496 490 smbios_ver = get_unaligned_be16(buf + 6); 491 + smbios_entry_point_size = buf[5]; 492 + memcpy(smbios_entry_point, buf, smbios_entry_point_size); 497 493 498 494 /* Some BIOS report weird SMBIOS version, fix that up */ 499 495 switch (smbios_ver) { ··· 530 522 pr_info("SMBIOS %d.%d present.\n", 531 523 dmi_ver >> 8, dmi_ver & 0xFF); 532 524 } else { 525 + smbios_entry_point_size = 15; 526 + memcpy(smbios_entry_point, buf, 527 + smbios_entry_point_size); 533 528 pr_info("Legacy DMI %d.%d present.\n", 534 529 dmi_ver >> 8, dmi_ver & 0xFF); 535 530 } ··· 559 548 dmi_num = 0; /* No longer specified */ 560 549 dmi_len = get_unaligned_le32(buf + 12); 561 550 dmi_base = get_unaligned_le64(buf + 16); 551 + smbios_entry_point_size = buf[6]; 552 + memcpy(smbios_entry_point, buf, smbios_entry_point_size); 562 553 563 554 if (dmi_walk_early(dmi_decode) == 0) { 564 555 pr_info("SMBIOS %d.%d.%d present.\n", ··· 651 638 out: 652 639 dmi_initialized = 1; 653 640 } 641 + 642 + static ssize_t raw_table_read(struct file *file, struct kobject *kobj, 643 + struct bin_attribute *attr, char *buf, 644 + loff_t pos, size_t count) 645 + { 646 + memcpy(buf, attr->private + pos, count); 647 + return count; 648 + } 649 + 650 + static BIN_ATTR(smbios_entry_point, S_IRUSR, raw_table_read, NULL, 0); 651 + static BIN_ATTR(DMI, S_IRUSR, raw_table_read, NULL, 0); 652 + 653 + static int __init dmi_init(void) 654 + { 655 + struct kobject *tables_kobj; 656 + u8 *dmi_table; 657 + int ret = -ENOMEM; 658 + 659 + if (!dmi_available) { 660 + ret = -ENODATA; 661 + goto err; 662 + } 663 + 664 + /* 665 + * Set up dmi directory at /sys/firmware/dmi. This entry should stay 666 + * even after farther error, as it can be used by other modules like 667 + * dmi-sysfs. 668 + */ 669 + dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); 670 + if (!dmi_kobj) 671 + goto err; 672 + 673 + tables_kobj = kobject_create_and_add("tables", dmi_kobj); 674 + if (!tables_kobj) 675 + goto err; 676 + 677 + dmi_table = dmi_remap(dmi_base, dmi_len); 678 + if (!dmi_table) 679 + goto err_tables; 680 + 681 + bin_attr_smbios_entry_point.size = smbios_entry_point_size; 682 + bin_attr_smbios_entry_point.private = smbios_entry_point; 683 + ret = sysfs_create_bin_file(tables_kobj, &bin_attr_smbios_entry_point); 684 + if (ret) 685 + goto err_unmap; 686 + 687 + bin_attr_DMI.size = dmi_len; 688 + bin_attr_DMI.private = dmi_table; 689 + ret = sysfs_create_bin_file(tables_kobj, &bin_attr_DMI); 690 + if (!ret) 691 + return 0; 692 + 693 + sysfs_remove_bin_file(tables_kobj, 694 + &bin_attr_smbios_entry_point); 695 + err_unmap: 696 + dmi_unmap(dmi_table); 697 + err_tables: 698 + kobject_del(tables_kobj); 699 + kobject_put(tables_kobj); 700 + err: 701 + pr_err("dmi: Firmware registration failed.\n"); 702 + 703 + return ret; 704 + } 705 + subsys_initcall(dmi_init); 654 706 655 707 /** 656 708 * dmi_set_dump_stack_arch_desc - set arch description for dump_stack()
+2
include/linux/dmi.h
··· 2 2 #define __DMI_H__ 3 3 4 4 #include <linux/list.h> 5 + #include <linux/kobject.h> 5 6 #include <linux/mod_devicetable.h> 6 7 7 8 /* enum dmi_field is in mod_devicetable.h */ ··· 94 93 int devfn; 95 94 }; 96 95 96 + extern struct kobject *dmi_kobj; 97 97 extern int dmi_check_system(const struct dmi_system_id *list); 98 98 const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list); 99 99 extern const char * dmi_get_system_info(int field);