[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH] x86: support SMBIOS v3



While presumably of primary use to ARM64 (once the code gets
generalized), we should still support this more modern variant,
allowing for the actual DMI data to reside in memory above 4Gb.

While based on draft version 3.0.0d, it is assumed that the final
version of the specification will not render this implementation
invalid (not the least because Linux 3.19 already makes the same
assumption).

Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>

--- 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 d
  *     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, in
         *      OR we run off the end of the table (also happens)
         */
  
-       while(i<num && data-buf+sizeof(struct dmi_header)<=len)
+       while((num < 0 || i < num) && data-buf+sizeof(struct dmi_header)<=len)
        {
                dm=(struct dmi_header *)data;
                /*
@@ -105,6 +165,8 @@ static int __init dmi_table(u32 base, in
                        data++;
                if(data-buf<len-1)
                        decode(dm);
+               if (dm->type == DMI_ENTRY_END_OF_TABLE)
+                   break;
                data+=2;
                i++;
        }
@@ -127,16 +189,28 @@ static inline bool_t __init dmi_checksum
 
 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 (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 (memcmp(eps->anchor, "_SM_", 4) &&
+       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 *smbi
        }
 }
 
-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;
-       }
-
-       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;
+               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;
+
+               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;
+
+       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)
-               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));
+               /*
+                * 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
        
        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
                                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
                        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);
--- a/xen/arch/x86/e820.c
+++ b/xen/arch/x86/e820.c
@@ -504,11 +504,19 @@ static uint64_t __init mtrr_top_of_ram(v
 
 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(
--- 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_SY
         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_SY
               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. */
--- 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;
--- 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);
 
--- 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;


Attachment: x86-SMBIOSv3.patch
Description: Text document

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.