Skip to content

Commit fc43026

Browse files
author
Ard Biesheuvel
committed
dmi: add support for SMBIOS 3.0 64-bit entry point
The DMTF SMBIOS reference spec v3.0.0 defines a new 64-bit entry point, which enables support for SMBIOS structure tables residing at a physical offset over 4 GB. This is especially important for upcoming arm64 platforms whose system RAM resides entirely above the 4 GB boundary. For the UEFI case, this code attempts to detect the new SMBIOS 3.0 header magic at the offset passed in the SMBIOS3_TABLE_GUID UEFI configuration table. If this configuration table is not provided, or if we fail to parse the header, we fall back to using the legacy SMBIOS_TABLE_GUID configuration table. This is in line with the spec, that allows both configuration tables to be provided, but mandates that they must point to the same structure table, unless the version pointed to by the 64-bit entry point is a superset of the 32-bit one. For the non-UEFI case, the detection logic is modified to look for the SMBIOS 3.0 header magic before it looks for the legacy header magic. Note that this patch is based on version 3.0.0d [draft] of the specification, which is expected not to deviate from the final version in ways that would affect the correctness of this implementation. Tested-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> Acked-by: Leif Lindholm <leif.lindholm@linaro.org> Tested-by: Leif Lindholm <leif.lindholm@linaro.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Tony Luck <tony.luck@intel.com> Acked-by: Matt Fleming <matt.fleming@intel.com> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
1 parent e1ccbbc commit fc43026

File tree

1 file changed

+72
-7
lines changed

1 file changed

+72
-7
lines changed

drivers/firmware/dmi_scan.c

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ static void dmi_table(u8 *buf, int len, int num,
9292
while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) {
9393
const struct dmi_header *dm = (const struct dmi_header *)data;
9494

95+
/*
96+
* 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]
97+
*/
98+
if (dm->type == DMI_ENTRY_END_OF_TABLE)
99+
break;
100+
95101
/*
96102
* We want to know the total length (formatted area and
97103
* strings) before decoding to make sure we won't run off the
@@ -107,7 +113,7 @@ static void dmi_table(u8 *buf, int len, int num,
107113
}
108114
}
109115

110-
static u32 dmi_base;
116+
static phys_addr_t dmi_base;
111117
static u16 dmi_len;
112118
static u16 dmi_num;
113119

@@ -467,7 +473,7 @@ static int __init dmi_present(const u8 *buf)
467473

468474
if (memcmp(buf, "_SM_", 4) == 0 &&
469475
buf[5] < 32 && dmi_checksum(buf, buf[5])) {
470-
smbios_ver = (buf[6] << 8) + buf[7];
476+
smbios_ver = get_unaligned_be16(buf + 6);
471477

472478
/* Some BIOS report weird SMBIOS version, fix that up */
473479
switch (smbios_ver) {
@@ -489,10 +495,9 @@ static int __init dmi_present(const u8 *buf)
489495
buf += 16;
490496

491497
if (memcmp(buf, "_DMI_", 5) == 0 && dmi_checksum(buf, 15)) {
492-
dmi_num = (buf[13] << 8) | buf[12];
493-
dmi_len = (buf[7] << 8) | buf[6];
494-
dmi_base = (buf[11] << 24) | (buf[10] << 16) |
495-
(buf[9] << 8) | buf[8];
498+
dmi_num = get_unaligned_le16(buf + 12);
499+
dmi_len = get_unaligned_le16(buf + 6);
500+
dmi_base = get_unaligned_le32(buf + 8);
496501

497502
if (dmi_walk_early(dmi_decode) == 0) {
498503
if (smbios_ver) {
@@ -514,12 +519,72 @@ static int __init dmi_present(const u8 *buf)
514519
return 1;
515520
}
516521

522+
/*
523+
* Check for the SMBIOS 3.0 64-bit entry point signature. Unlike the legacy
524+
* 32-bit entry point, there is no embedded DMI header (_DMI_) in here.
525+
*/
526+
static int __init dmi_smbios3_present(const u8 *buf)
527+
{
528+
if (memcmp(buf, "_SM3_", 5) == 0 &&
529+
buf[6] < 32 && dmi_checksum(buf, buf[6])) {
530+
dmi_ver = get_unaligned_be16(buf + 7);
531+
dmi_len = get_unaligned_le32(buf + 12);
532+
dmi_base = get_unaligned_le64(buf + 16);
533+
534+
/*
535+
* The 64-bit SMBIOS 3.0 entry point no longer has a field
536+
* containing the number of structures present in the table.
537+
* Instead, it defines the table size as a maximum size, and
538+
* relies on the end-of-table structure type (#127) to be used
539+
* to signal the end of the table.
540+
* So let's define dmi_num as an upper bound as well: each
541+
* structure has a 4 byte header, so dmi_len / 4 is an upper
542+
* bound for the number of structures in the table.
543+
*/
544+
dmi_num = dmi_len / 4;
545+
546+
if (dmi_walk_early(dmi_decode) == 0) {
547+
pr_info("SMBIOS %d.%d present.\n",
548+
dmi_ver >> 8, dmi_ver & 0xFF);
549+
dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string));
550+
pr_debug("DMI: %s\n", dmi_ids_string);
551+
return 0;
552+
}
553+
}
554+
return 1;
555+
}
556+
517557
void __init dmi_scan_machine(void)
518558
{
519559
char __iomem *p, *q;
520560
char buf[32];
521561

522562
if (efi_enabled(EFI_CONFIG_TABLES)) {
563+
/*
564+
* According to the DMTF SMBIOS reference spec v3.0.0, it is
565+
* allowed to define both the 64-bit entry point (smbios3) and
566+
* the 32-bit entry point (smbios), in which case they should
567+
* either both point to the same SMBIOS structure table, or the
568+
* table pointed to by the 64-bit entry point should contain a
569+
* superset of the table contents pointed to by the 32-bit entry
570+
* point (section 5.2)
571+
* This implies that the 64-bit entry point should have
572+
* precedence if it is defined and supported by the OS. If we
573+
* have the 64-bit entry point, but fail to decode it, fall
574+
* back to the legacy one (if available)
575+
*/
576+
if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) {
577+
p = dmi_early_remap(efi.smbios3, 32);
578+
if (p == NULL)
579+
goto error;
580+
memcpy_fromio(buf, p, 32);
581+
dmi_early_unmap(p, 32);
582+
583+
if (!dmi_smbios3_present(buf)) {
584+
dmi_available = 1;
585+
goto out;
586+
}
587+
}
523588
if (efi.smbios == EFI_INVALID_TABLE_ADDR)
524589
goto error;
525590

@@ -552,7 +617,7 @@ void __init dmi_scan_machine(void)
552617
memset(buf, 0, 16);
553618
for (q = p; q < p + 0x10000; q += 16) {
554619
memcpy_fromio(buf + 16, q, 16);
555-
if (!dmi_present(buf)) {
620+
if (!dmi_smbios3_present(buf) || !dmi_present(buf)) {
556621
dmi_available = 1;
557622
dmi_early_unmap(p, 0x10000);
558623
goto out;

0 commit comments

Comments
 (0)