| .. | .. |
|---|
| 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); |
|---|