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