.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | #include <linux/types.h> |
---|
2 | 3 | #include <linux/string.h> |
---|
3 | 4 | #include <linux/init.h> |
---|
.. | .. |
---|
5 | 6 | #include <linux/ctype.h> |
---|
6 | 7 | #include <linux/dmi.h> |
---|
7 | 8 | #include <linux/efi.h> |
---|
8 | | -#include <linux/bootmem.h> |
---|
| 9 | +#include <linux/memblock.h> |
---|
9 | 10 | #include <linux/random.h> |
---|
10 | 11 | #include <asm/dmi.h> |
---|
11 | 12 | #include <asm/unaligned.h> |
---|
| 13 | + |
---|
| 14 | +#ifndef SMBIOS_ENTRY_POINT_SCAN_START |
---|
| 15 | +#define SMBIOS_ENTRY_POINT_SCAN_START 0xF0000 |
---|
| 16 | +#endif |
---|
12 | 17 | |
---|
13 | 18 | struct kobject *dmi_kobj; |
---|
14 | 19 | EXPORT_SYMBOL_GPL(dmi_kobj); |
---|
.. | .. |
---|
16 | 21 | /* |
---|
17 | 22 | * DMI stands for "Desktop Management Interface". It is part |
---|
18 | 23 | * of and an antecedent to, SMBIOS, which stands for System |
---|
19 | | - * Management BIOS. See further: http://www.dmtf.org/standards |
---|
| 24 | + * Management BIOS. See further: https://www.dmtf.org/standards |
---|
20 | 25 | */ |
---|
21 | 26 | static const char dmi_empty_string[] = ""; |
---|
22 | 27 | |
---|
.. | .. |
---|
34 | 39 | const char *bank; |
---|
35 | 40 | u64 size; /* bytes */ |
---|
36 | 41 | u16 handle; |
---|
| 42 | + u8 type; /* DDR2, DDR3, DDR4 etc */ |
---|
37 | 43 | } *dmi_memdev; |
---|
38 | 44 | static int dmi_memdev_nr; |
---|
39 | 45 | |
---|
.. | .. |
---|
178 | 184 | return; |
---|
179 | 185 | |
---|
180 | 186 | dmi_ident[slot] = p; |
---|
| 187 | +} |
---|
| 188 | + |
---|
| 189 | +static void __init dmi_save_release(const struct dmi_header *dm, int slot, |
---|
| 190 | + int index) |
---|
| 191 | +{ |
---|
| 192 | + const u8 *minor, *major; |
---|
| 193 | + char *s; |
---|
| 194 | + |
---|
| 195 | + /* If the table doesn't have the field, let's return */ |
---|
| 196 | + if (dmi_ident[slot] || dm->length < index) |
---|
| 197 | + return; |
---|
| 198 | + |
---|
| 199 | + minor = (u8 *) dm + index; |
---|
| 200 | + major = (u8 *) dm + index - 1; |
---|
| 201 | + |
---|
| 202 | + /* As per the spec, if the system doesn't support this field, |
---|
| 203 | + * the value is FF |
---|
| 204 | + */ |
---|
| 205 | + if (*major == 0xFF && *minor == 0xFF) |
---|
| 206 | + return; |
---|
| 207 | + |
---|
| 208 | + s = dmi_alloc(8); |
---|
| 209 | + if (!s) |
---|
| 210 | + return; |
---|
| 211 | + |
---|
| 212 | + sprintf(s, "%u.%u", *major, *minor); |
---|
| 213 | + |
---|
| 214 | + dmi_ident[slot] = s; |
---|
181 | 215 | } |
---|
182 | 216 | |
---|
183 | 217 | static void __init dmi_save_uuid(const struct dmi_header *dm, int slot, |
---|
.. | .. |
---|
390 | 424 | u64 bytes; |
---|
391 | 425 | u16 size; |
---|
392 | 426 | |
---|
393 | | - if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x12) |
---|
| 427 | + if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x13) |
---|
394 | 428 | return; |
---|
395 | 429 | if (nr >= dmi_memdev_nr) { |
---|
396 | 430 | pr_warn(FW_BUG "Too many DIMM entries in SMBIOS table\n"); |
---|
.. | .. |
---|
399 | 433 | dmi_memdev[nr].handle = get_unaligned(&dm->handle); |
---|
400 | 434 | dmi_memdev[nr].device = dmi_string(dm, d[0x10]); |
---|
401 | 435 | dmi_memdev[nr].bank = dmi_string(dm, d[0x11]); |
---|
| 436 | + dmi_memdev[nr].type = d[0x12]; |
---|
402 | 437 | |
---|
403 | 438 | size = get_unaligned((u16 *)&d[0xC]); |
---|
404 | 439 | if (size == 0) |
---|
.. | .. |
---|
416 | 451 | nr++; |
---|
417 | 452 | } |
---|
418 | 453 | |
---|
419 | | -void __init dmi_memdev_walk(void) |
---|
| 454 | +static void __init dmi_memdev_walk(void) |
---|
420 | 455 | { |
---|
421 | | - if (!dmi_available) |
---|
422 | | - return; |
---|
423 | | - |
---|
424 | 456 | if (dmi_walk_early(count_mem_devices) == 0 && dmi_memdev_nr) { |
---|
425 | 457 | dmi_memdev = dmi_alloc(sizeof(*dmi_memdev) * dmi_memdev_nr); |
---|
426 | 458 | if (dmi_memdev) |
---|
.. | .. |
---|
440 | 472 | dmi_save_ident(dm, DMI_BIOS_VENDOR, 4); |
---|
441 | 473 | dmi_save_ident(dm, DMI_BIOS_VERSION, 5); |
---|
442 | 474 | dmi_save_ident(dm, DMI_BIOS_DATE, 8); |
---|
| 475 | + dmi_save_release(dm, DMI_BIOS_RELEASE, 21); |
---|
| 476 | + dmi_save_release(dm, DMI_EC_FIRMWARE_RELEASE, 23); |
---|
443 | 477 | break; |
---|
444 | 478 | case 1: /* System Information */ |
---|
445 | 479 | dmi_save_ident(dm, DMI_SYS_VENDOR, 4); |
---|
.. | .. |
---|
614 | 648 | return 1; |
---|
615 | 649 | } |
---|
616 | 650 | |
---|
617 | | -void __init dmi_scan_machine(void) |
---|
| 651 | +static void __init dmi_scan_machine(void) |
---|
618 | 652 | { |
---|
619 | 653 | char __iomem *p, *q; |
---|
620 | 654 | char buf[32]; |
---|
.. | .. |
---|
663 | 697 | return; |
---|
664 | 698 | } |
---|
665 | 699 | } else if (IS_ENABLED(CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK)) { |
---|
666 | | - p = dmi_early_remap(0xF0000, 0x10000); |
---|
| 700 | + p = dmi_early_remap(SMBIOS_ENTRY_POINT_SCAN_START, 0x10000); |
---|
667 | 701 | if (p == NULL) |
---|
668 | 702 | goto error; |
---|
669 | 703 | |
---|
.. | .. |
---|
769 | 803 | subsys_initcall(dmi_init); |
---|
770 | 804 | |
---|
771 | 805 | /** |
---|
772 | | - * dmi_set_dump_stack_arch_desc - set arch description for dump_stack() |
---|
| 806 | + * dmi_setup - scan and setup DMI system information |
---|
773 | 807 | * |
---|
774 | | - * Invoke dump_stack_set_arch_desc() with DMI system information so that |
---|
775 | | - * DMI identifiers are printed out on task dumps. Arch boot code should |
---|
776 | | - * call this function after dmi_scan_machine() if it wants to print out DMI |
---|
777 | | - * identifiers on task dumps. |
---|
| 808 | + * Scan the DMI system information. This setups DMI identifiers |
---|
| 809 | + * (dmi_system_id) for printing it out on task dumps and prepares |
---|
| 810 | + * DIMM entry information (dmi_memdev_info) from the SMBIOS table |
---|
| 811 | + * for using this when reporting memory errors. |
---|
778 | 812 | */ |
---|
779 | | -void __init dmi_set_dump_stack_arch_desc(void) |
---|
| 813 | +void __init dmi_setup(void) |
---|
780 | 814 | { |
---|
| 815 | + dmi_scan_machine(); |
---|
| 816 | + if (!dmi_available) |
---|
| 817 | + return; |
---|
| 818 | + |
---|
| 819 | + dmi_memdev_walk(); |
---|
781 | 820 | dump_stack_set_arch_desc("%s", dmi_ids_string); |
---|
782 | 821 | } |
---|
783 | 822 | |
---|
.. | .. |
---|
841 | 880 | * returns non zero or we hit the end. Callback function is called for |
---|
842 | 881 | * each successful match. Returns the number of matches. |
---|
843 | 882 | * |
---|
844 | | - * dmi_scan_machine must be called before this function is called. |
---|
| 883 | + * dmi_setup must be called before this function is called. |
---|
845 | 884 | */ |
---|
846 | 885 | int dmi_check_system(const struct dmi_system_id *list) |
---|
847 | 886 | { |
---|
.. | .. |
---|
871 | 910 | * Walk the blacklist table until the first match is found. Return the |
---|
872 | 911 | * pointer to the matching entry or NULL if there's no match. |
---|
873 | 912 | * |
---|
874 | | - * dmi_scan_machine must be called before this function is called. |
---|
| 913 | + * dmi_setup must be called before this function is called. |
---|
875 | 914 | */ |
---|
876 | 915 | const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list) |
---|
877 | 916 | { |
---|
.. | .. |
---|
1125 | 1164 | return ~0ull; |
---|
1126 | 1165 | } |
---|
1127 | 1166 | EXPORT_SYMBOL_GPL(dmi_memdev_size); |
---|
| 1167 | + |
---|
| 1168 | +/** |
---|
| 1169 | + * dmi_memdev_type - get the memory type |
---|
| 1170 | + * @handle: DMI structure handle |
---|
| 1171 | + * |
---|
| 1172 | + * Return the DMI memory type of the module in the slot associated with the |
---|
| 1173 | + * given DMI handle, or 0x0 if no such DMI handle exists. |
---|
| 1174 | + */ |
---|
| 1175 | +u8 dmi_memdev_type(u16 handle) |
---|
| 1176 | +{ |
---|
| 1177 | + int n; |
---|
| 1178 | + |
---|
| 1179 | + if (dmi_memdev) { |
---|
| 1180 | + for (n = 0; n < dmi_memdev_nr; n++) { |
---|
| 1181 | + if (handle == dmi_memdev[n].handle) |
---|
| 1182 | + return dmi_memdev[n].type; |
---|
| 1183 | + } |
---|
| 1184 | + } |
---|
| 1185 | + return 0x0; /* Not a valid value */ |
---|
| 1186 | +} |
---|
| 1187 | +EXPORT_SYMBOL_GPL(dmi_memdev_type); |
---|
| 1188 | + |
---|
| 1189 | +/** |
---|
| 1190 | + * dmi_memdev_handle - get the DMI handle of a memory slot |
---|
| 1191 | + * @slot: slot number |
---|
| 1192 | + * |
---|
| 1193 | + * Return the DMI handle associated with a given memory slot, or %0xFFFF |
---|
| 1194 | + * if there is no such slot. |
---|
| 1195 | + */ |
---|
| 1196 | +u16 dmi_memdev_handle(int slot) |
---|
| 1197 | +{ |
---|
| 1198 | + if (dmi_memdev && slot >= 0 && slot < dmi_memdev_nr) |
---|
| 1199 | + return dmi_memdev[slot].handle; |
---|
| 1200 | + |
---|
| 1201 | + return 0xffff; /* Not a valid value */ |
---|
| 1202 | +} |
---|
| 1203 | +EXPORT_SYMBOL_GPL(dmi_memdev_handle); |
---|