diff options
| -rw-r--r-- | Documentation/ABI/testing/sysfs-firmware-dmi-entries (renamed from Documentation/ABI/testing/sysfs-firmware-dmi) | 2 | ||||
| -rw-r--r-- | Documentation/ABI/testing/sysfs-firmware-dmi-tables | 22 | ||||
| -rw-r--r-- | MAINTAINERS | 2 | ||||
| -rw-r--r-- | drivers/firmware/dmi-sysfs.c | 17 | ||||
| -rw-r--r-- | drivers/firmware/dmi_scan.c | 123 | ||||
| -rw-r--r-- | include/linux/dmi.h | 4 |
6 files changed, 141 insertions, 29 deletions
diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi b/Documentation/ABI/testing/sysfs-firmware-dmi-entries index c78f9ab01e56..210ad44b95a5 100644 --- a/Documentation/ABI/testing/sysfs-firmware-dmi +++ b/Documentation/ABI/testing/sysfs-firmware-dmi-entries | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | What: /sys/firmware/dmi/ | 1 | What: /sys/firmware/dmi/entries/ |
| 2 | Date: February 2011 | 2 | Date: February 2011 |
| 3 | Contact: Mike Waychison <mikew@google.com> | 3 | Contact: Mike Waychison <mikew@google.com> |
| 4 | Description: | 4 | Description: |
diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi-tables b/Documentation/ABI/testing/sysfs-firmware-dmi-tables new file mode 100644 index 000000000000..ff3cac8ed0bd --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-dmi-tables | |||
| @@ -0,0 +1,22 @@ | |||
| 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. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 73cfd4b0f539..25916c00d7a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -3337,6 +3337,8 @@ F: drivers/hwmon/dme1737.c | |||
| 3337 | DMI/SMBIOS SUPPORT | 3337 | DMI/SMBIOS SUPPORT |
| 3338 | M: Jean Delvare <jdelvare@suse.de> | 3338 | M: Jean Delvare <jdelvare@suse.de> |
| 3339 | S: Maintained | 3339 | S: Maintained |
| 3340 | T: quilt http://jdelvare.nerim.net/devel/linux/jdelvare-dmi/ | ||
| 3341 | F: Documentation/ABI/testing/sysfs-firmware-dmi-tables | ||
| 3340 | F: drivers/firmware/dmi-id.c | 3342 | F: drivers/firmware/dmi-id.c |
| 3341 | F: drivers/firmware/dmi_scan.c | 3343 | F: drivers/firmware/dmi_scan.c |
| 3342 | F: include/linux/dmi.h | 3344 | F: include/linux/dmi.h |
diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index e0f1cb3d3598..ef76e5eecf0b 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c | |||
| @@ -566,7 +566,6 @@ static struct kobj_type dmi_sysfs_entry_ktype = { | |||
| 566 | .default_attrs = dmi_sysfs_entry_attrs, | 566 | .default_attrs = dmi_sysfs_entry_attrs, |
| 567 | }; | 567 | }; |
| 568 | 568 | ||
| 569 | static struct kobject *dmi_kobj; | ||
| 570 | static struct kset *dmi_kset; | 569 | static struct kset *dmi_kset; |
| 571 | 570 | ||
| 572 | /* Global count of all instances seen. Only for setup */ | 571 | /* Global count of all instances seen. Only for setup */ |
| @@ -648,17 +647,20 @@ static void cleanup_entry_list(void) | |||
| 648 | 647 | ||
| 649 | static int __init dmi_sysfs_init(void) | 648 | static int __init dmi_sysfs_init(void) |
| 650 | { | 649 | { |
| 651 | int error = -ENOMEM; | 650 | int error; |
| 652 | int val; | 651 | int val; |
| 653 | 652 | ||
| 654 | /* Set up our directory */ | 653 | if (!dmi_kobj) { |
| 655 | dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); | 654 | pr_err("dmi-sysfs: dmi entry is absent.\n"); |
| 656 | if (!dmi_kobj) | 655 | error = -ENODATA; |
| 657 | goto err; | 656 | goto err; |
| 657 | } | ||
| 658 | 658 | ||
| 659 | dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); | 659 | dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); |
| 660 | if (!dmi_kset) | 660 | if (!dmi_kset) { |
| 661 | error = -ENOMEM; | ||
| 661 | goto err; | 662 | goto err; |
| 663 | } | ||
| 662 | 664 | ||
| 663 | val = 0; | 665 | val = 0; |
| 664 | error = dmi_walk(dmi_sysfs_register_handle, &val); | 666 | error = dmi_walk(dmi_sysfs_register_handle, &val); |
| @@ -675,7 +677,6 @@ static int __init dmi_sysfs_init(void) | |||
| 675 | err: | 677 | err: |
| 676 | cleanup_entry_list(); | 678 | cleanup_entry_list(); |
| 677 | kset_unregister(dmi_kset); | 679 | kset_unregister(dmi_kset); |
| 678 | kobject_put(dmi_kobj); | ||
| 679 | return error; | 680 | return error; |
| 680 | } | 681 | } |
| 681 | 682 | ||
| @@ -685,8 +686,6 @@ static void __exit dmi_sysfs_exit(void) | |||
| 685 | pr_debug("dmi-sysfs: unloading.\n"); | 686 | pr_debug("dmi-sysfs: unloading.\n"); |
| 686 | cleanup_entry_list(); | 687 | cleanup_entry_list(); |
| 687 | kset_unregister(dmi_kset); | 688 | kset_unregister(dmi_kset); |
| 688 | kobject_del(dmi_kobj); | ||
| 689 | kobject_put(dmi_kobj); | ||
| 690 | } | 689 | } |
| 691 | 690 | ||
| 692 | module_init(dmi_sysfs_init); | 691 | module_init(dmi_sysfs_init); |
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 97b1616aa391..ac1ce4a73edf 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c | |||
| @@ -10,6 +10,9 @@ | |||
| 10 | #include <asm/dmi.h> | 10 | #include <asm/dmi.h> |
| 11 | #include <asm/unaligned.h> | 11 | #include <asm/unaligned.h> |
| 12 | 12 | ||
| 13 | struct kobject *dmi_kobj; | ||
| 14 | EXPORT_SYMBOL_GPL(dmi_kobj); | ||
| 15 | |||
| 13 | /* | 16 | /* |
| 14 | * DMI stands for "Desktop Management Interface". It is part | 17 | * DMI stands for "Desktop Management Interface". It is part |
| 15 | * of and an antecedent to, SMBIOS, which stands for System | 18 | * of and an antecedent to, SMBIOS, which stands for System |
| @@ -20,6 +23,9 @@ static const char dmi_empty_string[] = " "; | |||
| 20 | static u32 dmi_ver __initdata; | 23 | static u32 dmi_ver __initdata; |
| 21 | static u32 dmi_len; | 24 | static u32 dmi_len; |
| 22 | static u16 dmi_num; | 25 | static u16 dmi_num; |
| 26 | static u8 smbios_entry_point[32]; | ||
| 27 | static int smbios_entry_point_size; | ||
| 28 | |||
| 23 | /* | 29 | /* |
| 24 | * Catch too early calls to dmi_check_system(): | 30 | * Catch too early calls to dmi_check_system(): |
| 25 | */ | 31 | */ |
| @@ -80,18 +86,18 @@ static const char * __init dmi_string(const struct dmi_header *dm, u8 s) | |||
| 80 | * We have to be cautious here. We have seen BIOSes with DMI pointers | 86 | * We have to be cautious here. We have seen BIOSes with DMI pointers |
| 81 | * pointing to completely the wrong place for example | 87 | * pointing to completely the wrong place for example |
| 82 | */ | 88 | */ |
| 83 | static void dmi_table(u8 *buf, | 89 | static void dmi_decode_table(u8 *buf, |
| 84 | void (*decode)(const struct dmi_header *, void *), | 90 | void (*decode)(const struct dmi_header *, void *), |
| 85 | void *private_data) | 91 | void *private_data) |
| 86 | { | 92 | { |
| 87 | u8 *data = buf; | 93 | u8 *data = buf; |
| 88 | int i = 0; | 94 | int i = 0; |
| 89 | 95 | ||
| 90 | /* | 96 | /* |
| 91 | * Stop when we have seen all the items the table claimed to have | 97 | * Stop when we have seen all the items the table claimed to have |
| 92 | * (SMBIOS < 3.0 only) OR we reach an end-of-table marker OR we run | 98 | * (SMBIOS < 3.0 only) OR we reach an end-of-table marker (SMBIOS |
| 93 | * off the end of the table (should never happen but sometimes does | 99 | * >= 3.0 only) OR we run off the end of the table (should never |
| 94 | * on bogus implementations.) | 100 | * happen but sometimes does on bogus implementations.) |
| 95 | */ | 101 | */ |
| 96 | while ((!dmi_num || i < dmi_num) && | 102 | while ((!dmi_num || i < dmi_num) && |
| 97 | (data - buf + sizeof(struct dmi_header)) <= dmi_len) { | 103 | (data - buf + sizeof(struct dmi_header)) <= dmi_len) { |
| @@ -108,15 +114,24 @@ static void dmi_table(u8 *buf, | |||
| 108 | if (data - buf < dmi_len - 1) | 114 | if (data - buf < dmi_len - 1) |
| 109 | decode(dm, private_data); | 115 | decode(dm, private_data); |
| 110 | 116 | ||
| 117 | data += 2; | ||
| 118 | i++; | ||
| 119 | |||
| 111 | /* | 120 | /* |
| 112 | * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0] | 121 | * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0] |
| 122 | * For tables behind a 64-bit entry point, we have no item | ||
| 123 | * count and no exact table length, so stop on end-of-table | ||
| 124 | * marker. For tables behind a 32-bit entry point, we have | ||
| 125 | * seen OEM structures behind the end-of-table marker on | ||
| 126 | * some systems, so don't trust it. | ||
| 113 | */ | 127 | */ |
| 114 | if (dm->type == DMI_ENTRY_END_OF_TABLE) | 128 | if (!dmi_num && dm->type == DMI_ENTRY_END_OF_TABLE) |
| 115 | break; | 129 | break; |
| 116 | |||
| 117 | data += 2; | ||
| 118 | i++; | ||
| 119 | } | 130 | } |
| 131 | |||
| 132 | /* Trim DMI table length if needed */ | ||
| 133 | if (dmi_len > data - buf) | ||
| 134 | dmi_len = data - buf; | ||
| 120 | } | 135 | } |
| 121 | 136 | ||
| 122 | static phys_addr_t dmi_base; | 137 | static phys_addr_t dmi_base; |
| @@ -125,16 +140,17 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, | |||
| 125 | void *)) | 140 | void *)) |
| 126 | { | 141 | { |
| 127 | u8 *buf; | 142 | u8 *buf; |
| 143 | u32 orig_dmi_len = dmi_len; | ||
| 128 | 144 | ||
| 129 | buf = dmi_early_remap(dmi_base, dmi_len); | 145 | buf = dmi_early_remap(dmi_base, orig_dmi_len); |
| 130 | if (buf == NULL) | 146 | if (buf == NULL) |
| 131 | return -1; | 147 | return -1; |
| 132 | 148 | ||
| 133 | dmi_table(buf, decode, NULL); | 149 | dmi_decode_table(buf, decode, NULL); |
| 134 | 150 | ||
| 135 | add_device_randomness(buf, dmi_len); | 151 | add_device_randomness(buf, dmi_len); |
| 136 | 152 | ||
| 137 | dmi_early_unmap(buf, dmi_len); | 153 | dmi_early_unmap(buf, orig_dmi_len); |
| 138 | return 0; | 154 | return 0; |
| 139 | } | 155 | } |
| 140 | 156 | ||
| @@ -478,17 +494,19 @@ static int __init dmi_present(const u8 *buf) | |||
| 478 | if (memcmp(buf, "_SM_", 4) == 0 && | 494 | if (memcmp(buf, "_SM_", 4) == 0 && |
| 479 | buf[5] < 32 && dmi_checksum(buf, buf[5])) { | 495 | buf[5] < 32 && dmi_checksum(buf, buf[5])) { |
| 480 | smbios_ver = get_unaligned_be16(buf + 6); | 496 | smbios_ver = get_unaligned_be16(buf + 6); |
| 497 | smbios_entry_point_size = buf[5]; | ||
| 498 | memcpy(smbios_entry_point, buf, smbios_entry_point_size); | ||
| 481 | 499 | ||
| 482 | /* Some BIOS report weird SMBIOS version, fix that up */ | 500 | /* Some BIOS report weird SMBIOS version, fix that up */ |
| 483 | switch (smbios_ver) { | 501 | switch (smbios_ver) { |
| 484 | case 0x021F: | 502 | case 0x021F: |
| 485 | case 0x0221: | 503 | case 0x0221: |
| 486 | pr_debug("SMBIOS version fixup(2.%d->2.%d)\n", | 504 | pr_debug("SMBIOS version fixup (2.%d->2.%d)\n", |
| 487 | smbios_ver & 0xFF, 3); | 505 | smbios_ver & 0xFF, 3); |
| 488 | smbios_ver = 0x0203; | 506 | smbios_ver = 0x0203; |
| 489 | break; | 507 | break; |
| 490 | case 0x0233: | 508 | case 0x0233: |
| 491 | pr_debug("SMBIOS version fixup(2.%d->2.%d)\n", 51, 6); | 509 | pr_debug("SMBIOS version fixup (2.%d->2.%d)\n", 51, 6); |
| 492 | smbios_ver = 0x0206; | 510 | smbios_ver = 0x0206; |
| 493 | break; | 511 | break; |
| 494 | } | 512 | } |
| @@ -512,6 +530,9 @@ static int __init dmi_present(const u8 *buf) | |||
| 512 | pr_info("SMBIOS %d.%d present.\n", | 530 | pr_info("SMBIOS %d.%d present.\n", |
| 513 | dmi_ver >> 8, dmi_ver & 0xFF); | 531 | dmi_ver >> 8, dmi_ver & 0xFF); |
| 514 | } else { | 532 | } else { |
| 533 | smbios_entry_point_size = 15; | ||
| 534 | memcpy(smbios_entry_point, buf, | ||
| 535 | smbios_entry_point_size); | ||
| 515 | pr_info("Legacy DMI %d.%d present.\n", | 536 | pr_info("Legacy DMI %d.%d present.\n", |
| 516 | dmi_ver >> 8, dmi_ver & 0xFF); | 537 | dmi_ver >> 8, dmi_ver & 0xFF); |
| 517 | } | 538 | } |
| @@ -533,11 +554,12 @@ static int __init dmi_smbios3_present(const u8 *buf) | |||
| 533 | { | 554 | { |
| 534 | if (memcmp(buf, "_SM3_", 5) == 0 && | 555 | if (memcmp(buf, "_SM3_", 5) == 0 && |
| 535 | buf[6] < 32 && dmi_checksum(buf, buf[6])) { | 556 | buf[6] < 32 && dmi_checksum(buf, buf[6])) { |
| 536 | dmi_ver = get_unaligned_be32(buf + 6); | 557 | dmi_ver = get_unaligned_be32(buf + 6) & 0xFFFFFF; |
| 537 | dmi_ver &= 0xFFFFFF; | ||
| 538 | dmi_num = 0; /* No longer specified */ | 558 | dmi_num = 0; /* No longer specified */ |
| 539 | dmi_len = get_unaligned_le32(buf + 12); | 559 | dmi_len = get_unaligned_le32(buf + 12); |
| 540 | dmi_base = get_unaligned_le64(buf + 16); | 560 | dmi_base = get_unaligned_le64(buf + 16); |
| 561 | smbios_entry_point_size = buf[6]; | ||
| 562 | memcpy(smbios_entry_point, buf, smbios_entry_point_size); | ||
| 541 | 563 | ||
| 542 | if (dmi_walk_early(dmi_decode) == 0) { | 564 | if (dmi_walk_early(dmi_decode) == 0) { |
| 543 | pr_info("SMBIOS %d.%d.%d present.\n", | 565 | pr_info("SMBIOS %d.%d.%d present.\n", |
| @@ -629,6 +651,71 @@ void __init dmi_scan_machine(void) | |||
| 629 | dmi_initialized = 1; | 651 | dmi_initialized = 1; |
| 630 | } | 652 | } |
| 631 | 653 | ||
| 654 | static ssize_t raw_table_read(struct file *file, struct kobject *kobj, | ||
| 655 | struct bin_attribute *attr, char *buf, | ||
| 656 | loff_t pos, size_t count) | ||
| 657 | { | ||
| 658 | memcpy(buf, attr->private + pos, count); | ||
| 659 | return count; | ||
| 660 | } | ||
| 661 | |||
| 662 | static BIN_ATTR(smbios_entry_point, S_IRUSR, raw_table_read, NULL, 0); | ||
| 663 | static BIN_ATTR(DMI, S_IRUSR, raw_table_read, NULL, 0); | ||
| 664 | |||
| 665 | static int __init dmi_init(void) | ||
| 666 | { | ||
| 667 | struct kobject *tables_kobj; | ||
| 668 | u8 *dmi_table; | ||
| 669 | int ret = -ENOMEM; | ||
| 670 | |||
| 671 | if (!dmi_available) { | ||
| 672 | ret = -ENODATA; | ||
| 673 | goto err; | ||
| 674 | } | ||
| 675 | |||
| 676 | /* | ||
| 677 | * Set up dmi directory at /sys/firmware/dmi. This entry should stay | ||
| 678 | * even after farther error, as it can be used by other modules like | ||
| 679 | * dmi-sysfs. | ||
| 680 | */ | ||
| 681 | dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); | ||
| 682 | if (!dmi_kobj) | ||
| 683 | goto err; | ||
| 684 | |||
| 685 | tables_kobj = kobject_create_and_add("tables", dmi_kobj); | ||
| 686 | if (!tables_kobj) | ||
| 687 | goto err; | ||
| 688 | |||
| 689 | dmi_table = dmi_remap(dmi_base, dmi_len); | ||
| 690 | if (!dmi_table) | ||
| 691 | goto err_tables; | ||
| 692 | |||
| 693 | bin_attr_smbios_entry_point.size = smbios_entry_point_size; | ||
| 694 | bin_attr_smbios_entry_point.private = smbios_entry_point; | ||
| 695 | ret = sysfs_create_bin_file(tables_kobj, &bin_attr_smbios_entry_point); | ||
| 696 | if (ret) | ||
| 697 | goto err_unmap; | ||
| 698 | |||
| 699 | bin_attr_DMI.size = dmi_len; | ||
| 700 | bin_attr_DMI.private = dmi_table; | ||
| 701 | ret = sysfs_create_bin_file(tables_kobj, &bin_attr_DMI); | ||
| 702 | if (!ret) | ||
| 703 | return 0; | ||
| 704 | |||
| 705 | sysfs_remove_bin_file(tables_kobj, | ||
| 706 | &bin_attr_smbios_entry_point); | ||
| 707 | err_unmap: | ||
| 708 | dmi_unmap(dmi_table); | ||
| 709 | err_tables: | ||
| 710 | kobject_del(tables_kobj); | ||
| 711 | kobject_put(tables_kobj); | ||
| 712 | err: | ||
| 713 | pr_err("dmi: Firmware registration failed.\n"); | ||
| 714 | |||
| 715 | return ret; | ||
| 716 | } | ||
| 717 | subsys_initcall(dmi_init); | ||
| 718 | |||
| 632 | /** | 719 | /** |
| 633 | * dmi_set_dump_stack_arch_desc - set arch description for dump_stack() | 720 | * dmi_set_dump_stack_arch_desc - set arch description for dump_stack() |
| 634 | * | 721 | * |
| @@ -897,7 +984,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *, void *), | |||
| 897 | if (buf == NULL) | 984 | if (buf == NULL) |
| 898 | return -1; | 985 | return -1; |
| 899 | 986 | ||
| 900 | dmi_table(buf, decode, private_data); | 987 | dmi_decode_table(buf, decode, private_data); |
| 901 | 988 | ||
| 902 | dmi_unmap(buf); | 989 | dmi_unmap(buf); |
| 903 | return 0; | 990 | return 0; |
diff --git a/include/linux/dmi.h b/include/linux/dmi.h index f820f0a336c9..5055ac34142d 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #define __DMI_H__ | 2 | #define __DMI_H__ |
| 3 | 3 | ||
| 4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
| 5 | #include <linux/kobject.h> | ||
| 5 | #include <linux/mod_devicetable.h> | 6 | #include <linux/mod_devicetable.h> |
| 6 | 7 | ||
| 7 | /* enum dmi_field is in mod_devicetable.h */ | 8 | /* enum dmi_field is in mod_devicetable.h */ |
| @@ -74,7 +75,7 @@ struct dmi_header { | |||
| 74 | u8 type; | 75 | u8 type; |
| 75 | u8 length; | 76 | u8 length; |
| 76 | u16 handle; | 77 | u16 handle; |
| 77 | }; | 78 | } __packed; |
| 78 | 79 | ||
| 79 | struct dmi_device { | 80 | struct dmi_device { |
| 80 | struct list_head list; | 81 | struct list_head list; |
| @@ -93,6 +94,7 @@ struct dmi_dev_onboard { | |||
| 93 | int devfn; | 94 | int devfn; |
| 94 | }; | 95 | }; |
| 95 | 96 | ||
| 97 | extern struct kobject *dmi_kobj; | ||
| 96 | extern int dmi_check_system(const struct dmi_system_id *list); | 98 | extern int dmi_check_system(const struct dmi_system_id *list); |
| 97 | const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list); | 99 | const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list); |
| 98 | extern const char * dmi_get_system_info(int field); | 100 | extern const char * dmi_get_system_info(int field); |
