From 367c85c472869b75eaf770d4be0b360e30710b95 Mon Sep 17 00:00:00 2001
From: Jeffrey Bastian <jbastian@redhat.com>
Date: Tue, 29 Sep 2020 13:28:16 +0200
Subject: [PATCH] lscpu: use SMBIOS tables on ARM for lscpu

ARM SBBR (Sever Base Boot Requirements) require SMBIOS tables, and
SMBIOS Type 4 describes the CPU manufacturer and model name (among other
details).  If SMBIOS Type 4 is present, use it to extract these strings.

Example output (before and after the patch) on an HP m400, Lenovo HR330A,
and HPE Apollo 70:

[root@hp-m400 ~]# /usr/bin/lscpu | grep -i -e vendor -e model -e stepping
Vendor ID:           APM
Model:               1
Model name:          X-Gene
Stepping:            0x0
[root@hp-m400 ~]# ./lscpu | grep -i -e vendor -e model -e stepping
Vendor ID:                       AppliedMicro
Model:                           1
Model name:                      X-Gene
Stepping:                        0x0

[root@lenovo-hr330a ~]# /usr/bin/lscpu | grep -i -e vendor -e model -e stepping
Vendor ID:           APM
Model:               2
Model name:          X-Gene
Stepping:            0x3
[root@lenovo-hr330a ~]# ./lscpu | grep -i -e vendor -e model -e stepping
Vendor ID:                       Ampere(TM)
Model:                           2
Model name:                      eMAG
Stepping:                        0x3

[root@hpe-apollo-70 ~]# /usr/bin/lscpu | grep -i -e vendor -e model -e stepping
Vendor ID:           Cavium
Model:               1
Model name:          ThunderX2 99xx
Stepping:            0x1
[root@hpe-apollo-70 ~]# ./lscpu | grep -i -e vendor -e model -e stepping
Vendor ID:                       Cavium Inc.
Model:                           1
Model name:                      Cavium ThunderX2(R) CPU CN9980 v2.1 @ 2.20GHz
Stepping:                        0x1

[kzak@redhat.com: - move dmi_header to lscpu.h
                  - make arm_cpu_smbios() more robust for failed
		    open() and read()
                  - use original arm_cpu_decode() also on failed
		    arm_cpu_smbios()]

Signed-off-by: Jeffrey Bastian <jbastian@redhat.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
---
 sys-utils/lscpu-arm.c | 120 ++++++++++++++++++++++++++++++++----------
 sys-utils/lscpu-dmi.c |  41 +--------------
 sys-utils/lscpu.h     |  36 +++++++++++++
 3 files changed, 128 insertions(+), 69 deletions(-)

diff --git a/sys-utils/lscpu-arm.c b/sys-utils/lscpu-arm.c
index aa7d826..ed77df0 100644
--- a/sys-utils/lscpu-arm.c
+++ b/sys-utils/lscpu-arm.c
@@ -22,7 +22,15 @@
  *  - Linux kernel: arch/armX/include/asm/cputype.h
  *  - GCC sources: config/arch/arch-cores.def
  *  - Ancient wisdom
+ *  - SMBIOS tables (if applicable)
  */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #include "lscpu.h"
 
 struct id_part {
@@ -201,42 +209,45 @@ static const struct hw_impl hw_implementer[] = {
     { -1,   unknown_part, "unknown" },
 };
 
-void arm_cpu_decode(struct lscpu_desc *desc)
+static void __arm_cpu_decode(struct lscpu_desc *desc)
 {
-	int j, impl, part;
+	int j, impl = 0;
 	const struct id_part *parts = NULL;
 	char *end;
 
-	if (desc->vendor == NULL || desc->model == NULL)
-		return;
-	if ((strncmp(desc->vendor,"0x",2) != 0 || strncmp(desc->model,"0x",2) ))
-		return;
-
-	errno = 0;
-	impl = (int) strtol(desc->vendor, &end, 0);
-	if (errno || desc->vendor == end)
-		return;
-
-	errno = 0;
-	part = (int) strtol(desc->model, &end, 0);
-	if (errno || desc->model == end)
-		return;
-
-	for (j = 0; hw_implementer[j].id != -1; j++) {
-		if (hw_implementer[j].id == impl) {
-			parts = hw_implementer[j].parts;
-			desc->vendor = (char *) hw_implementer[j].name;
-			break;
-		}
+	if (desc->vendor && startswith(desc->vendor, "0x")) {
+		errno = 0;
+		impl = (int) strtol(desc->vendor, &end, 0);
+		if (errno || desc->vendor == end)
+			return;
 	}
 
-	if (parts == NULL)
-		return;
+	/* model and modelname */
+	if (impl && desc->model && startswith(desc->model, "0x")) {
+		int part;
+
+		errno = 0;
+
+		part = (int) strtol(desc->model, &end, 0);
+		if (errno || desc->model == end)
+			return;
+
+		for (j = 0; hw_implementer[j].id != -1; j++) {
+			if (hw_implementer[j].id == impl) {
+				parts = hw_implementer[j].parts;
+				desc->vendor = (char *) hw_implementer[j].name;
+				break;
+			}
+		}
+
+		if (parts == NULL)
+			return;
 
-	for (j = 0; parts[j].id != -1; j++) {
-		if (parts[j].id == part) {
-			desc->modelname = (char *) parts[j].name;
-			break;
+		for (j = 0; parts[j].id != -1; j++) {
+			if (parts[j].id == part) {
+				desc->modelname = (char *) parts[j].name;
+				break;
+			}
 		}
 	}
 
@@ -259,3 +270,54 @@ void arm_cpu_decode(struct lscpu_desc *desc)
 		desc->stepping = xstrdup(buf);
 	}
 }
+
+#define PROC_MFR_OFFSET		0x07
+#define PROC_VERSION_OFFSET	0x10
+
+static int __arm_cpu_smbios(struct lscpu_desc *desc)
+{
+	uint8_t data[8192];
+	char buf[128], *str;
+	struct lscpu_dmi_header h;
+	int fd;
+	ssize_t rs;
+
+	fd = open(_PATH_SYS_DMI_TYPE4, O_RDONLY);
+	if (fd < 0)
+		return fd;
+
+	rs = read_all(fd, (char *) data, 8192);
+	close(fd);
+
+	if (rs == -1)
+		return -1;
+
+	to_dmi_header(&h, data);
+
+	str = dmi_string(&h, data[PROC_MFR_OFFSET]);
+	if (str) {
+		xstrncpy(buf, str, 127);
+		desc->vendor = xstrdup(buf);
+	}
+
+	str = dmi_string(&h, data[PROC_VERSION_OFFSET]);
+	if (str) {
+		xstrncpy(buf, str, 127);
+		desc->modelname = xstrdup(buf);
+	}
+
+	return 0;
+}
+
+void arm_cpu_decode(struct lscpu_desc *desc)
+{
+	int rc = -1;
+
+	/* use SMBIOS Type 4 data if available,
+	 * else fall back to manual decoding using the tables above */
+	if (access(_PATH_SYS_DMI_TYPE4, R_OK) == 0)
+		rc = __arm_cpu_smbios(desc);
+
+	if (rc)
+		__arm_cpu_decode(desc);
+}
diff --git a/sys-utils/lscpu-dmi.c b/sys-utils/lscpu-dmi.c
index edf0f31..8263ce9 100644
--- a/sys-utils/lscpu-dmi.c
+++ b/sys-utils/lscpu-dmi.c
@@ -29,19 +29,9 @@
 
 #include "lscpu.h"
 
-#define _PATH_SYS_DMI	 "/sys/firmware/dmi/tables/DMI"
-
 #define WORD(x) (uint16_t)(*(const uint16_t *)(x))
 #define DWORD(x) (uint32_t)(*(const uint32_t *)(x))
 
-struct dmi_header
-{
-	uint8_t type;
-	uint8_t length;
-	uint16_t handle;
-	uint8_t *data;
-};
-
 static void *get_mem_chunk(size_t base, size_t len, const char *devmem)
 {
 	void *p = NULL;
@@ -66,35 +56,6 @@ nothing:
 	return NULL;
 }
 
-static void to_dmi_header(struct dmi_header *h, uint8_t *data)
-{
-	h->type = data[0];
-	h->length = data[1];
-	memcpy(&h->handle, data + 2, sizeof(h->handle));
-	h->data = data;
-}
-
-static char *dmi_string(const struct dmi_header *dm, uint8_t s)
-{
-	char *bp = (char *)dm->data;
-
-	if (s == 0)
-		return NULL;
-
-	bp += dm->length;
-	while (s > 1 && *bp)
-	{
-		bp += strlen(bp);
-		bp++;
-		s--;
-	}
-
-	if (!*bp)
-		return NULL;
-
-	return bp;
-}
-
 static int hypervisor_from_dmi_table(uint32_t base, uint16_t len,
 				uint16_t num, const char *devmem)
 {
@@ -113,7 +74,7 @@ static int hypervisor_from_dmi_table(uint32_t base, uint16_t len,
 	 /* 4 is the length of an SMBIOS structure header */
 	while (i < num && data + 4 <= buf + len) {
 		uint8_t *next;
-		struct dmi_header h;
+		struct lscpu_dmi_header h;
 
 		to_dmi_header(&h, data);
 
diff --git a/sys-utils/lscpu.h b/sys-utils/lscpu.h
index 5bbdb34..e602b0a 100644
--- a/sys-utils/lscpu.h
+++ b/sys-utils/lscpu.h
@@ -211,4 +211,40 @@ struct lscpu_modifier {
 extern int read_hypervisor_dmi(void);
 extern void arm_cpu_decode(struct lscpu_desc *desc);
 
+#define _PATH_SYS_DMI		"/sys/firmware/dmi/tables/DMI"
+#define _PATH_SYS_DMI_TYPE4	"/sys/firmware/dmi/entries/4-0/raw"
+
+struct lscpu_dmi_header
+{
+	uint8_t type;
+	uint8_t length;
+	uint16_t handle;
+	uint8_t *data;
+};
+
+static inline void to_dmi_header(struct lscpu_dmi_header *h, uint8_t *data)
+{
+	h->type = data[0];
+	h->length = data[1];
+	memcpy(&h->handle, data + 2, sizeof(h->handle));
+	h->data = data;
+}
+
+static inline char *dmi_string(const struct lscpu_dmi_header *dm, uint8_t s)
+{
+	char *bp = (char *)dm->data;
+
+	if (!s || !bp)
+		return NULL;
+
+	bp += dm->length;
+	while (s > 1 && *bp) {
+		bp += strlen(bp);
+		bp++;
+		s--;
+	}
+
+	return !*bp ? NULL : bp;
+}
+
 #endif /* LSCPU_H */
-- 
2.33.0

