4f8197
From 59820aacf5f9bcfde98753fde79ca2c779d755aa Mon Sep 17 00:00:00 2001
4f8197
From: Mark Salter <msalter@redhat.com>
4f8197
Date: Tue, 24 Feb 2015 23:42:05 -0500
4f8197
Subject: [PATCH] Support SMBIOS 3.0 64-bit header
4f8197
4f8197
The SMBIOS 64-bit header adds suport for a 64-bit base address for the
4f8197
SMBIOS tables. This patch adds code to parse deal with older 32-bit
4f8197
headers and well as the 64-bit header. It also fixes 32-bit address
4f8197
assumptions in a number of places.
4f8197
4f8197
Signed-off-by: Mark Salter <msalter@redhat.com>
4f8197
---
4f8197
 dmidecode.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++-------------
4f8197
 1 file changed, 112 insertions(+), 31 deletions(-)
4f8197
4f8197
diff --git a/dmidecode.c b/dmidecode.c
4f8197
index fee3af1..6865cd7 100644
4f8197
--- a/dmidecode.c
4f8197
+++ b/dmidecode.c
4f8197
@@ -4277,7 +4277,10 @@ static u16 get_smbios_version(unsigned char *smbios)
4f8197
 {
4f8197
 	u16 ver;
4f8197
 
4f8197
-	ver = (smbios[0x06] << 8) + smbios[0x07];
4f8197
+	if (!memcmp(smbios, "_SM3_", 5))
4f8197
+		ver = (smbios[0x07] << 8) + smbios[0x08];
4f8197
+	else
4f8197
+		ver = (smbios[0x06] << 8) + smbios[0x07];
4f8197
 
4f8197
 	/* Some BIOS report weird SMBIOS version, fix that up */
4f8197
 	switch (ver) {
4f8197
@@ -4348,7 +4351,33 @@ static void dmi_table_dump(u8 *buf, u16 len)
4f8197
 	write_dump(32, len, buf, opt.dumpfile, 0);
4f8197
 }
4f8197
 
4f8197
-static void dmi_table(u32 base, u8 *buf, u16 len, u16 num, u16 ver)
4f8197
+static u16 dmi_table_count(u8 *buf, u16 len)
4f8197
+{
4f8197
+	u8 *data;
4f8197
+	u16 i = 0;
4f8197
+
4f8197
+	data = buf;
4f8197
+	while (data+4 <= buf + len) /* 4 is the length of an SMBIOS structure header */
4f8197
+	{
4f8197
+		struct dmi_header h;
4f8197
+
4f8197
+		++i;
4f8197
+
4f8197
+		to_dmi_header(&h, data);
4f8197
+
4f8197
+		if (h.length < 4)
4f8197
+			break;
4f8197
+
4f8197
+		/* look for the next handle */
4f8197
+		data += h.length;
4f8197
+		while (data - buf + 1 < len && (data[0] != 0 || data[1] != 0))
4f8197
+			data++;
4f8197
+		data += 2;
4f8197
+	}
4f8197
+	return i;
4f8197
+}
4f8197
+
4f8197
+static void dmi_table(u64 base, u8 *buf, u16 len, u16 num, u16 ver)
4f8197
 {
4f8197
 	u8 *data;
4f8197
 	int i = 0;
4f8197
@@ -4372,8 +4401,13 @@ static void dmi_table(u32 base, u8 *buf, u16 len, u16 num, u16 ver)
4f8197
 		{
4f8197
 			printf("%u structures occupying %u bytes.\n",
4f8197
 				num, len);
4f8197
-			if (!(opt.flags & FLAG_FROM_DUMP))
4f8197
-				printf("Table at 0x%08X.\n", base);
4f8197
+			if (!(opt.flags & FLAG_FROM_DUMP)) {
4f8197
+				if (base.h)
4f8197
+					printf("Table at 0x%08X%08X.\n",
4f8197
+					       base.h, base.l);
4f8197
+				else
4f8197
+					printf("Table at 0x%08X.\n", base.l);
4f8197
+			}
4f8197
 		}
4f8197
 		printf("\n");
4f8197
 	}
4f8197
@@ -4460,15 +4494,19 @@ static void dmi_table(u32 base, u8 *buf, u16 len, u16 num, u16 ver)
4f8197
 /*
4f8197
  * Build a crafted entry point with table address hard-coded to 32,
4f8197
  * as this is where we will put it in the output file. We adjust the
4f8197
- * DMI checksum appropriately. The SMBIOS checksum needs no adjustment.
4f8197
+ * checksum appropriately.
4f8197
  */
4f8197
-static void overwrite_dmi_address(u8 *buf)
4f8197
+static void overwrite_address(u8 *cksum, u8 *addr, int addr_len)
4f8197
 {
4f8197
-	buf[0x05] += buf[0x08] + buf[0x09] + buf[0x0A] + buf[0x0B] - 32;
4f8197
-	buf[0x08] = 32;
4f8197
-	buf[0x09] = 0;
4f8197
-	buf[0x0A] = 0;
4f8197
-	buf[0x0B] = 0;
4f8197
+	int i;
4f8197
+
4f8197
+	*cksum += addr[0] - 32;
4f8197
+	addr[0] = 32;
4f8197
+
4f8197
+	for (i = 1; i < addr_len; ++i) {
4f8197
+		*cksum += addr[i];
4f8197
+		addr[i] = 0;
4f8197
+	}
4f8197
 }
4f8197
 
4f8197
 /**
4f8197
@@ -4478,36 +4516,51 @@ static void overwrite_dmi_address(u8 *buf)
4f8197
 static void smbios_in_dumpfile(unsigned char *smbios)
4f8197
 {
4f8197
 	u8 crafted[32];
4f8197
+	u8 len;
4f8197
 
4f8197
 	memcpy(crafted, smbios, 32);
4f8197
-	overwrite_dmi_address(crafted + 0x10);
4f8197
+	if (!memcmp(smbios, "_SM3_", 5)) {
4f8197
+		overwrite_address(crafted + 0x05, crafted + 0x10, 8);
4f8197
+		len = crafted[0x06];
4f8197
+	} else {
4f8197
+		overwrite_address(crafted + 0x15, crafted + 0x18, 4);
4f8197
+		len = crafted[0x05];
4f8197
+	}
4f8197
 
4f8197
 	if (!(opt.flags & FLAG_QUIET))
4f8197
 		printf("# Writing %d bytes to %s.\n", crafted[0x05],
4f8197
 		       opt.dumpfile);
4f8197
 
4f8197
-	write_dump(0, crafted[0x05], crafted, opt.dumpfile, 1);
4f8197
+	write_dump(0, len, crafted, opt.dumpfile, 1);
4f8197
 }
4f8197
 
4f8197
 static int smbios_decode(u8 *buf, const char *devmem)
4f8197
 {
4f8197
 	u16 ver;
4f8197
-	u16 len;
4f8197
-	u32 base;
4f8197
+	u16 len, num;
4f8197
+	u64 base;
4f8197
 	u8 *dmibuf;
4f8197
 
4f8197
-	if (!checksum(buf, buf[0x05])
4f8197
-	 || memcmp(buf + 0x10, "_DMI_", 5) != 0
4f8197
-	 || !checksum(buf + 0x10, 0x0F))
4f8197
-		return 0;
4f8197
+	if (!memcmp(buf, "_SM3_", 5)) {
4f8197
+		if (!checksum(buf, buf[0x06]))
4f8197
+			return 0;
4f8197
+		base = QWORD(buf + 0x10);
4f8197
+		len = WORD(buf + 0x0C);
4f8197
+	} else {
4f8197
+		if (!checksum(buf, buf[0x05])
4f8197
+		    || memcmp(buf + 0x10, "_DMI_", 5) != 0
4f8197
+		    || !checksum(buf + 0x10, 0x0F))
4f8197
+			return 0;
4f8197
+		base.h = 0;
4f8197
+		base.l = DWORD(buf + 0x18);
4f8197
+		len = WORD(buf + 0x16);
4f8197
+	}
4f8197
 
4f8197
 	ver = get_smbios_version(buf);
4f8197
-
4f8197
-	base = DWORD(buf + 0x18);
4f8197
-	len = WORD(buf + 0x16);
4f8197
-	dmibuf = mem_chunk(base, len, devmem);
4f8197
+	dmibuf = mem_chunk(base.l, len, devmem);
4f8197
 	if (!dmibuf) {
4f8197
 		fprintf(stderr, "Table is unreachable, sorry."
4f8197
+
4f8197
 #ifndef USE_MMAP
4f8197
 			" Try compiling dmidecode with -DUSE_MMAP."
4f8197
 #endif
4f8197
@@ -4515,7 +4568,13 @@ static int smbios_decode(u8 *buf, const char *devmem)
4f8197
 		return 0;
4f8197
 	}
4f8197
 
4f8197
-	dmi_table(base, dmibuf, len, WORD(buf + 0x1C), ver);
4f8197
+	/* version 3 64-bit header doesn't have number of tables */
4f8197
+	if (!memcmp(buf, "_SM3_", 5))
4f8197
+		num = dmi_table_count(dmibuf, len);
4f8197
+	else
4f8197
+		num = WORD(buf + 0x1C);
4f8197
+
4f8197
+	dmi_table(base, dmibuf, len, num, ver);
4f8197
 
4f8197
 	free(dmibuf);
4f8197
 
4f8197
@@ -4529,6 +4588,7 @@ static void dmifs_smbios_decode(void)
4f8197
 {
4f8197
 	u16 ver;
4f8197
 	u8 *smbios;
4f8197
+	u64 base;
4f8197
 	struct dmi_table *dt;
4f8197
 
4f8197
 	dt = dmi_get_table();
4f8197
@@ -4538,8 +4598,28 @@ static void dmifs_smbios_decode(void)
4f8197
 	smbios = dt->smbios;
4f8197
 
4f8197
 	ver = get_smbios_version(dt->smbios);
4f8197
-	dmi_table(DWORD(smbios + 0x18), dmi_get_raw_data(dt->dmi_list),
4f8197
-		  WORD(smbios + 0x16), WORD(smbios + 0x1C), ver);
4f8197
+
4f8197
+	if (!memcmp(smbios, "_SM3_", 5)) {
4f8197
+		base = QWORD(smbios + 0x10);
4f8197
+		dmi_table(base, dmi_get_raw_data(dt->dmi_list),
4f8197
+			  dt->dmi_size, dt->dmi_count, ver);
4f8197
+		if (opt.flags & FLAG_DUMP_BIN) {
4f8197
+			/*
4f8197
+			 * Table length in SMBIOS 3.0 header is a maximum size.
4f8197
+			 * Make it exact to avoid warnings/errors when decoding
4f8197
+			 * from file we are dumping.
4f8197
+			 */
4f8197
+			dt->smbios[0x05] += dt->smbios[0x0c] + dt->smbios[0x0d];
4f8197
+			dt->smbios[0x0c] = dt->dmi_size;
4f8197
+			dt->smbios[0x0d] = dt->dmi_size >> 8;
4f8197
+			dt->smbios[0x05] -= dt->smbios[0x0c] + dt->smbios[0x0d];
4f8197
+		}
4f8197
+	} else {
4f8197
+		base.h = 0;
4f8197
+		base.l = DWORD(smbios + 0x18);
4f8197
+		dmi_table(base, dmi_get_raw_data(dt->dmi_list),
4f8197
+			  WORD(smbios + 0x16), WORD(smbios + 0x1C), ver);
4f8197
+	}
4f8197
 
4f8197
 	if (opt.flags & FLAG_DUMP_BIN)
4f8197
 		smbios_in_dumpfile(dt->smbios);
4f8197
@@ -4550,7 +4630,7 @@ static void dmifs_smbios_decode(void)
4f8197
 static int legacy_decode(u8 *buf, const char *devmem)
4f8197
 {
4f8197
 	u16 len;
4f8197
-	u32 base;
4f8197
+	u64 base;
4f8197
 	u8 *dmibuf;
4f8197
 
4f8197
 	if (!checksum(buf, 0x0F))
4f8197
@@ -4561,8 +4641,9 @@ static int legacy_decode(u8 *buf, const char *devmem)
4f8197
 			buf[0x0E] >> 4, buf[0x0E] & 0x0F);
4f8197
 
4f8197
 	len = WORD(buf + 0x06);
4f8197
-	base = DWORD(buf + 0x08);
4f8197
-	dmibuf = mem_chunk(base, len, devmem);
4f8197
+	base.h = 0;
4f8197
+	base.l = DWORD(buf + 0x08);
4f8197
+	dmibuf = mem_chunk(base.l, len, devmem);
4f8197
 	if (!dmibuf) {
4f8197
 		fprintf(stderr, "Table is unreachable, sorry."
4f8197
 #ifndef USE_MMAP
4f8197
@@ -4582,7 +4663,7 @@ static int legacy_decode(u8 *buf, const char *devmem)
4f8197
 		u8 crafted[16];
4f8197
 
4f8197
 		memcpy(crafted, buf, 16);
4f8197
-		overwrite_dmi_address(crafted);
4f8197
+		overwrite_address(crafted + 0x05, crafted + 0x08, 4);
4f8197
 
4f8197
 		printf("# Writing %d bytes to %s.\n", 0x0F, opt.dumpfile);
4f8197
 		write_dump(0, 0x0F, crafted, opt.dumpfile, 1);
4f8197
@@ -4689,7 +4770,7 @@ int main(int argc, char * const argv[])
4f8197
 			goto exit_free;
4f8197
 		}
4f8197
 
4f8197
-		if (memcmp(buf, "_SM_", 4) == 0)
4f8197
+		if (memcmp(buf, "_SM_", 4) == 0 || memcmp(buf, "_SM3_", 5) == 0)
4f8197
 		{
4f8197
 			if (smbios_decode(buf, opt.dumpfile))
4f8197
 				found++;
4f8197
-- 
4f8197
1.8.3.1
4f8197