diff --git a/xen/arch/x86/dmi_scan.c b/xen/arch/x86/dmi_scan.c index 500133ac35..187c01e495 100644 --- a/xen/arch/x86/dmi_scan.c +++ b/xen/arch/x86/dmi_scan.c @@ -38,6 +38,18 @@ struct __packed smbios_eps { struct dmi_eps dmi; }; +struct __packed smbios3_eps { + char anchor[5]; /* "_SM3_" */ + u8 checksum; + u8 length; + u8 major, minor; + u8 docrev; + u8 revision; + u8 _rsrvd_; + u32 max_size; + u64 address; +}; + struct dmi_header { u8 type; @@ -45,6 +57,53 @@ struct dmi_header u16 handle; }; +enum dmi_entry_type { + DMI_ENTRY_BIOS = 0, + DMI_ENTRY_SYSTEM, + DMI_ENTRY_BASEBOARD, + DMI_ENTRY_CHASSIS, + DMI_ENTRY_PROCESSOR, + DMI_ENTRY_MEM_CONTROLLER, + DMI_ENTRY_MEM_MODULE, + DMI_ENTRY_CACHE, + DMI_ENTRY_PORT_CONNECTOR, + DMI_ENTRY_SYSTEM_SLOT, + DMI_ENTRY_ONBOARD_DEVICE, + DMI_ENTRY_OEMSTRINGS, + DMI_ENTRY_SYSCONF, + DMI_ENTRY_BIOS_LANG, + DMI_ENTRY_GROUP_ASSOC, + DMI_ENTRY_SYSTEM_EVENT_LOG, + DMI_ENTRY_PHYS_MEM_ARRAY, + DMI_ENTRY_MEM_DEVICE, + DMI_ENTRY_32_MEM_ERROR, + DMI_ENTRY_MEM_ARRAY_MAPPED_ADDR, + DMI_ENTRY_MEM_DEV_MAPPED_ADDR, + DMI_ENTRY_BUILTIN_POINTING_DEV, + DMI_ENTRY_PORTABLE_BATTERY, + DMI_ENTRY_SYSTEM_RESET, + DMI_ENTRY_HW_SECURITY, + DMI_ENTRY_SYSTEM_POWER_CONTROLS, + DMI_ENTRY_VOLTAGE_PROBE, + DMI_ENTRY_COOLING_DEV, + DMI_ENTRY_TEMP_PROBE, + DMI_ENTRY_ELECTRICAL_CURRENT_PROBE, + DMI_ENTRY_OOB_REMOTE_ACCESS, + DMI_ENTRY_BIS_ENTRY, + DMI_ENTRY_SYSTEM_BOOT, + DMI_ENTRY_MGMT_DEV, + DMI_ENTRY_MGMT_DEV_COMPONENT, + DMI_ENTRY_MGMT_DEV_THRES, + DMI_ENTRY_MEM_CHANNEL, + DMI_ENTRY_IPMI_DEV, + DMI_ENTRY_SYS_POWER_SUPPLY, + DMI_ENTRY_ADDITIONAL, + DMI_ENTRY_ONBOARD_DEV_EXT, + DMI_ENTRY_MGMT_CONTROLLER_HOST, + DMI_ENTRY_INACTIVE = 126, + DMI_ENTRY_END_OF_TABLE = 127, +}; + #undef DMI_DEBUG #ifdef DMI_DEBUG @@ -74,7 +133,8 @@ static char * __init dmi_string(struct dmi_header *dm, u8 s) * pointing to completely the wrong place for example */ -static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dmi_header *)) +static int __init dmi_table(paddr_t base, u32 len, int num, + void (*decode)(struct dmi_header *)) { u8 *buf; struct dmi_header *dm; @@ -92,7 +152,7 @@ static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dm * OR we run off the end of the table (also happens) */ - while(itype == DMI_ENTRY_END_OF_TABLE) + break; data+=2; i++; } @@ -127,16 +189,28 @@ static inline bool_t __init dmi_checksum(const void __iomem *buf, static u32 __initdata efi_dmi_address; static u32 __initdata efi_dmi_size; +static u64 __initdata efi_smbios3_address; +static u32 __initdata efi_smbios3_size; /* * Important: This function gets called while still in EFI * (pseudo-)physical mode. */ -void __init dmi_efi_get_table(void *smbios) +void __init dmi_efi_get_table(const void *smbios, const void *smbios3) { - struct smbios_eps *eps = smbios; + const struct smbios_eps *eps = smbios; + const struct smbios3_eps *eps3 = smbios3; - if (memcmp(eps->anchor, "_SM_", 4) && + if (eps3 && memcmp(eps3->anchor, "_SM3_", 5) && + eps3->length >= sizeof(*eps3) && + dmi_checksum(eps3, eps3->length)) { + efi_smbios3_address = eps3->address; + efi_smbios3_size = eps3->max_size; + return; + } + + if (eps && memcmp(eps->anchor, "_SM_", 4) && + eps->length >= sizeof(*eps) && dmi_checksum(eps, eps->length) && memcmp(eps->dmi.anchor, "_DMI_", 5) == 0 && dmi_checksum(&eps->dmi, sizeof(eps->dmi))) { @@ -145,99 +219,190 @@ void __init dmi_efi_get_table(void *smbios) } } -int __init dmi_get_table(u32 *base, u32 *len) +const char *__init dmi_get_table(paddr_t *base, u32 *len) { - struct dmi_eps eps; - char __iomem *p, *q; + static unsigned int __initdata instance; if (efi_enabled) { - if (!efi_dmi_size) - return -1; - *base = efi_dmi_address; - *len = efi_dmi_size; - return 0; - } + if (efi_smbios3_size && !(instance & 1)) { + *base = efi_smbios3_address; + *len = efi_smbios3_size; + instance |= 1; + return "SMBIOSv3"; + } + if (efi_dmi_size && !(instance & 2)) { + *base = efi_dmi_address; + *len = efi_dmi_size; + instance |= 2; + return "DMI"; + } + } else { + char __iomem *p = maddr_to_virt(0xF0000), *q; + union { + struct dmi_eps dmi; + struct smbios3_eps smbios3; + } eps; - p = maddr_to_virt(0xF0000); - for (q = p; q < p + 0x10000; q += 16) { - memcpy_fromio(&eps, q, 15); - if (memcmp(eps.anchor, "_DMI_", 5) == 0 && - dmi_checksum(&eps, sizeof(eps))) { - *base = eps.address; - *len = eps.size; - return 0; + for (q = p; q <= p + 0x10000 - sizeof(eps.dmi); q += 16) { + memcpy_fromio(&eps, q, sizeof(eps.dmi)); + if (!(instance & 1) && + memcmp(eps.dmi.anchor, "_DMI_", 5) == 0 && + dmi_checksum(&eps.dmi, sizeof(eps.dmi))) { + *base = eps.dmi.address; + *len = eps.dmi.size; + instance |= 1; + return "DMI"; + } + + BUILD_BUG_ON(sizeof(eps.smbios3) <= sizeof(eps.dmi)); + if ((instance & 2) || + q > p + 0x10000 - sizeof(eps.smbios3)) + continue; + memcpy_fromio(&eps.dmi + 1, q + sizeof(eps.dmi), + sizeof(eps.smbios3) - sizeof(eps.dmi)); + if (!memcmp(eps.smbios3.anchor, "_SM3_", 5) && + eps.smbios3.length >= sizeof(eps.smbios3) && + q <= p + 0x10000 - eps.smbios3.length && + dmi_checksum(q, eps.smbios3.length)) { + *base = eps.smbios3.address; + *len = eps.smbios3.max_size; + instance |= 2; + return "SMBIOSv3"; + } } } - return -1; + return NULL; } +typedef union { + const struct smbios_eps __iomem *legacy; + const struct smbios3_eps __iomem *v3; +} smbios_eps_u __attribute__((transparent_union)); + static int __init _dmi_iterate(const struct dmi_eps *dmi, - const struct smbios_eps __iomem *smbios, + const smbios_eps_u smbios, void (*decode)(struct dmi_header *)) { - u16 num = dmi->num_structures; - u16 len = dmi->size; - u32 base = dmi->address; + int num; + u32 len; + paddr_t base; - /* - * DMI version 0.0 means that the real version is taken from - * the SMBIOS version, which we may not know at this point. - */ - if (dmi->revision) - printk(KERN_INFO "DMI %d.%d present.\n", - dmi->revision >> 4, dmi->revision & 0x0f); - else if (!smbios) - printk(KERN_INFO "DMI present.\n"); - dmi_printk((KERN_INFO "%d structures occupying %d bytes.\n", - num, len)); - dmi_printk((KERN_INFO "DMI table at 0x%08X.\n", base)); + if (!dmi) { + num = -1; + len = smbios.v3->max_size; + base = smbios.v3->address; + printk(KERN_INFO "SMBIOS %d.%d present.\n", + smbios.v3->major, smbios.v3->minor); + dmi_printk((KERN_INFO "SMBIOS v3 table at 0x%"PRIpaddr".\n", base)); + } else { + num = dmi->num_structures; + len = dmi->size; + base = dmi->address; + + /* + * DMI version 0.0 means that the real version is taken from + * the SMBIOS version, which we may not know at this point. + */ + if (dmi->revision) + printk(KERN_INFO "DMI %d.%d present.\n", + dmi->revision >> 4, dmi->revision & 0x0f); + else if (!smbios.legacy) + printk(KERN_INFO "DMI present.\n"); + dmi_printk((KERN_INFO "%d structures occupying %u bytes.\n", + num, len)); + dmi_printk((KERN_INFO "DMI table at 0x%08X.\n", (u32)base)); + } return dmi_table(base, len, num, decode); } static int __init dmi_iterate(void (*decode)(struct dmi_header *)) { - struct dmi_eps eps; + struct dmi_eps dmi; + struct smbios3_eps smbios3; char __iomem *p, *q; + dmi.size = 0; + smbios3.length = 0; + p = maddr_to_virt(0xF0000); for (q = p; q < p + 0x10000; q += 16) { - memcpy_fromio(&eps, q, sizeof(eps)); - if (memcmp(eps.anchor, "_DMI_", 5) == 0 && - dmi_checksum(&eps, sizeof(eps))) - return _dmi_iterate(&eps, NULL, decode); + if (!dmi.size) { + memcpy_fromio(&dmi, q, sizeof(dmi)); + if (memcmp(dmi.anchor, "_DMI_", 5) || + !dmi_checksum(&dmi, sizeof(dmi))) + dmi.size = 0; + } + if (!smbios3.length && + q <= p + 0x10000 - sizeof(smbios3)) { + memcpy_fromio(&smbios3, q, sizeof(smbios3)); + if (memcmp(smbios3.anchor, "_SM3_", 5) || + smbios3.length < sizeof(smbios3) || + q < p + 0x10000 - smbios3.length || + !dmi_checksum(q, smbios3.length)) + smbios3.length = 0; + } } + + if (smbios3.length) + return _dmi_iterate(NULL, &smbios3, decode); + if (dmi.size) + return _dmi_iterate(&dmi, NULL, decode); return -1; } static int __init dmi_efi_iterate(void (*decode)(struct dmi_header *)) { - struct smbios_eps eps; - const struct smbios_eps __iomem *p; int ret = -1; - if (efi.smbios == EFI_INVALID_TABLE_ADDR) - return -1; + while (efi.smbios3 != EFI_INVALID_TABLE_ADDR) { + struct smbios3_eps eps; + const struct smbios3_eps __iomem *p; - p = bt_ioremap(efi.smbios, sizeof(eps)); - if (!p) - return -1; - memcpy_fromio(&eps, p, sizeof(eps)); - bt_iounmap(p, sizeof(eps)); + p = bt_ioremap(efi.smbios3, sizeof(eps)); + if (!p) + break; + memcpy_fromio(&eps, p, sizeof(eps)); + bt_iounmap(p, sizeof(eps)); - if (memcmp(eps.anchor, "_SM_", 4)) - return -1; + if (memcmp(eps.anchor, "_SM3_", 5) || + eps.length < sizeof(eps)) + break; - p = bt_ioremap(efi.smbios, eps.length); - if (!p) - return -1; - if (dmi_checksum(p, eps.length) && - memcmp(eps.dmi.anchor, "_DMI_", 5) == 0 && - dmi_checksum(&eps.dmi, sizeof(eps.dmi))) { - printk(KERN_INFO "SMBIOS %d.%d present.\n", - eps.major, eps.minor); - ret = _dmi_iterate(&eps.dmi, p, decode); + p = bt_ioremap(efi.smbios3, eps.length); + if (!p) + break; + if (dmi_checksum(p, eps.length)) + ret = _dmi_iterate(NULL, p, decode); + bt_iounmap(p, eps.length); + break; + } + + if (ret != 0 && efi.smbios != EFI_INVALID_TABLE_ADDR) { + struct smbios_eps eps; + const struct smbios_eps __iomem *p; + + p = bt_ioremap(efi.smbios, sizeof(eps)); + if (!p) + return -1; + memcpy_fromio(&eps, p, sizeof(eps)); + bt_iounmap(p, sizeof(eps)); + + if (memcmp(eps.anchor, "_SM_", 4) || + eps.length < sizeof(eps)) + return -1; + + p = bt_ioremap(efi.smbios, eps.length); + if (!p) + return -1; + if (dmi_checksum(p, eps.length) && + memcmp(eps.dmi.anchor, "_DMI_", 5) == 0 && + dmi_checksum(&eps.dmi, sizeof(eps.dmi))) { + printk(KERN_INFO "SMBIOS %d.%d present.\n", + eps.major, eps.minor); + ret = _dmi_iterate(&eps.dmi, p, decode); + } + bt_iounmap(p, eps.length); } - bt_iounmap(p, eps.length); return ret; } @@ -476,7 +641,7 @@ static void __init dmi_decode(struct dmi_header *dm) switch(dm->type) { - case 0: + case DMI_ENTRY_BIOS: dmi_printk(("BIOS Vendor: %s\n", dmi_string(dm, data[4]))); dmi_save_ident(dm, DMI_BIOS_VENDOR, 4); @@ -487,7 +652,7 @@ static void __init dmi_decode(struct dmi_header *dm) dmi_string(dm, data[8]))); dmi_save_ident(dm, DMI_BIOS_DATE, 8); break; - case 1: + case DMI_ENTRY_SYSTEM: dmi_printk(("System Vendor: %s\n", dmi_string(dm, data[4]))); dmi_save_ident(dm, DMI_SYS_VENDOR, 4); @@ -500,7 +665,7 @@ static void __init dmi_decode(struct dmi_header *dm) dmi_printk(("Serial Number: %s\n", dmi_string(dm, data[7]))); break; - case 2: + case DMI_ENTRY_BASEBOARD: dmi_printk(("Board Vendor: %s\n", dmi_string(dm, data[4]))); dmi_save_ident(dm, DMI_BOARD_VENDOR, 4); diff --git a/xen/arch/x86/e820.c b/xen/arch/x86/e820.c index bf84bae63f..743a375e60 100644 --- a/xen/arch/x86/e820.c +++ b/xen/arch/x86/e820.c @@ -504,11 +504,19 @@ static uint64_t __init mtrr_top_of_ram(void) static void __init reserve_dmi_region(void) { - u32 base, len; - if ( (dmi_get_table(&base, &len) == 0) && ((base + len) > base) && - reserve_e820_ram(&e820, base, base + len) ) - printk("WARNING: DMI table located in E820 RAM %08x-%08x. Fixed.\n", - base, base+len); + for ( ; ; ) + { + paddr_t base; + u32 len; + const char *what = dmi_get_table(&base, &len); + + if ( !what ) + break; + if ( ((base + len) > base) && + reserve_e820_ram(&e820, base, base + len) ) + printk("WARNING: %s table located in E820 RAM %"PRIpaddr"-%"PRIpaddr". Fixed.\n", + what, base, base + len); + } } static void __init machine_specific_memory_setup( diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c index ac6881ea93..f5e179beac 100644 --- a/xen/common/efi/boot.c +++ b/xen/common/efi/boot.c @@ -32,6 +32,8 @@ /* Using SetVirtualAddressMap() is incompatible with kexec: */ #undef USE_SET_VIRTUAL_ADDRESS_MAP +#define SMBIOS3_TABLE_GUID \ + { 0xf2fd1544, 0x9794, 0x4a2c, {0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94} } #define SHIM_LOCK_PROTOCOL_GUID \ { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} } @@ -993,6 +995,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) static EFI_GUID __initdata acpi_guid = ACPI_TABLE_GUID; static EFI_GUID __initdata mps_guid = MPS_TABLE_GUID; static EFI_GUID __initdata smbios_guid = SMBIOS_TABLE_GUID; + static EFI_GUID __initdata smbios3_guid = SMBIOS3_TABLE_GUID; if ( match_guid(&acpi2_guid, &efi_ct[i].VendorGuid) ) efi.acpi20 = (long)efi_ct[i].VendorTable; @@ -1002,11 +1005,15 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) efi.mps = (long)efi_ct[i].VendorTable; if ( match_guid(&smbios_guid, &efi_ct[i].VendorGuid) ) efi.smbios = (long)efi_ct[i].VendorTable; + if ( match_guid(&smbios3_guid, &efi_ct[i].VendorGuid) ) + efi.smbios3 = (long)efi_ct[i].VendorTable; } #ifndef CONFIG_ARM /* TODO - disabled until implemented on ARM */ - if (efi.smbios != EFI_INVALID_TABLE_ADDR) - dmi_efi_get_table((void *)(long)efi.smbios); + dmi_efi_get_table(efi.smbios != EFI_INVALID_TABLE_ADDR + ? (void *)(long)efi.smbios : NULL, + efi.smbios3 != EFI_INVALID_TABLE_ADDR + ? (void *)(long)efi.smbios3 : NULL); #endif /* Collect PCI ROM contents. */ diff --git a/xen/common/efi/runtime.c b/xen/common/efi/runtime.c index c840e083e6..7ed5bfabd4 100644 --- a/xen/common/efi/runtime.c +++ b/xen/common/efi/runtime.c @@ -45,6 +45,7 @@ struct efi __read_mostly efi = { .acpi20 = EFI_INVALID_TABLE_ADDR, .mps = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, + .smbios3 = EFI_INVALID_TABLE_ADDR, }; const struct efi_pci_rom *__read_mostly efi_pci_roms; diff --git a/xen/include/xen/dmi.h b/xen/include/xen/dmi.h index 8205893162..df26d4b3e3 100644 --- a/xen/include/xen/dmi.h +++ b/xen/include/xen/dmi.h @@ -34,8 +34,8 @@ struct dmi_system_id { extern int dmi_check_system(struct dmi_system_id *list); extern void dmi_scan_machine(void); -extern int dmi_get_table(u32 *base, u32 *len); -extern void dmi_efi_get_table(void *); +extern const char *dmi_get_table(paddr_t *base, u32 *len); +extern void dmi_efi_get_table(const void *smbios, const void *smbios3); bool_t dmi_get_date(int field, int *yearp, int *monthp, int *dayp); extern void dmi_end_boot(void); diff --git a/xen/include/xen/efi.h b/xen/include/xen/efi.h index 5e02724572..48de8e0b09 100644 --- a/xen/include/xen/efi.h +++ b/xen/include/xen/efi.h @@ -15,6 +15,7 @@ struct efi { unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long smbios; /* SM BIOS table */ + unsigned long smbios3; /* SMBIOS v3 table */ }; extern struct efi efi;