From: Ard Biesheuvel <ardb@kernel.org>
Date: Tue, 28 Feb 2023 17:00:49 +0100
Subject: arm64: efi: Use SMBIOS processor ID to key off Altra quirk
Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git#urgent
Git-commit: 22d9e53df5aa26f775d96222fbb049569720349f
Patch-mainline: Queued in subsystem maintainer repository
References: bsc#1208750

Instead of using the SMBIOS type 1 record 'family' field, which is often
modified by OEMs, use the type 4 'processor ID' field, which is always
set to the same value on all known Altra EFI systems in the field, and
is more likely to be left alone, given that it is based on the SOC id
SMCCC API call which is implemented in secure firmware.

Fixes: 550b33cfd4452968 ("arm64: efi: Force the use of ...")
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 drivers/firmware/efi/libstub/arm64.c   |   12 +++++------
 drivers/firmware/efi/libstub/efistub.h |   34 +++++++++++++++++++++++++++++++++
 drivers/firmware/efi/libstub/smbios.c  |   14 +++++++++++--
 3 files changed, 52 insertions(+), 8 deletions(-)

--- a/drivers/firmware/efi/libstub/arm64.c
+++ b/drivers/firmware/efi/libstub/arm64.c
@@ -16,16 +16,16 @@
 
 static bool system_needs_vamap(void)
 {
-	const u8 *type1_family = efi_get_smbios_string(1, family);
+	const struct efi_smbios_type4_record *record;
 
 	/*
 	 * Ampere eMAG, Altra, and Altra Max machines crash in SetTime() if
-	 * SetVirtualAddressMap() has not been called prior.
+	 * SetVirtualAddressMap() has not been called prior. These systems can
+	 * be identified by the SMCCC soc ID, which is conveniently exposed via
+	 * the type 4 SMBIOS records.
 	 */
-	if (!type1_family || (
-	    strcmp(type1_family, "eMAG") &&
-	    strcmp(type1_family, "Altra") &&
-	    strcmp(type1_family, "Altra Max")))
+	record = (struct efi_smbios_type4_record *)efi_get_smbios_record(4);
+	if (!record || memcmp(record->processor_id, "\x1\0\x16\xA\xA1\0\0\0", 8))
 		return false;
 
 	efi_warn("Working around broken SetVirtualAddressMap()\n");
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -1054,6 +1054,8 @@ struct efi_smbios_record {
 	u16	handle;
 };
 
+const struct efi_smbios_record *efi_get_smbios_record(u8 type);
+
 struct efi_smbios_type1_record {
 	struct efi_smbios_record	header;
 
@@ -1067,6 +1069,38 @@ struct efi_smbios_type1_record {
 	u8				family;
 };
 
+struct efi_smbios_type4_record {
+	struct efi_smbios_record	header;
+
+	u8				socket;
+	u8				processor_type;
+	u8				processor_family;
+	u8				processor_manufacturer;
+	u8				processor_id[8];
+	u8				processor_version;
+	u8				voltage;
+	u16				external_clock;
+	u16				max_speed;
+	u16				current_speed;
+	u8				status;
+	u8				processor_upgrade;
+	u16				l1_cache_handle;
+	u16				l2_cache_handle;
+	u16				l3_cache_handle;
+	u8				serial_number;
+	u8				asset_tag;
+	u8				part_number;
+	u8				core_count;
+	u8				enabled_core_count;
+	u8				thread_count;
+	u16				processor_characteristics;
+	u16				processor_family2;
+	u16				core_count2;
+	u16				enabled_core_count2;
+	u16				thread_count2;
+	u16				thread_enabled;
+};
+
 #define efi_get_smbios_string(__type, __name) ({			\
 	int size = sizeof(struct efi_smbios_type ## __type ## _record);	\
 	int off = offsetof(struct efi_smbios_type ## __type ## _record,	\
--- a/drivers/firmware/efi/libstub/smbios.c
+++ b/drivers/firmware/efi/libstub/smbios.c
@@ -22,19 +22,29 @@ struct efi_smbios_protocol {
 	u8 minor_version;
 };
 
-const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize)
+const struct efi_smbios_record *efi_get_smbios_record(u8 type)
 {
 	struct efi_smbios_record *record;
 	efi_smbios_protocol_t *smbios;
 	efi_status_t status;
 	u16 handle = 0xfffe;
-	const u8 *strtable;
 
 	status = efi_bs_call(locate_protocol, &EFI_SMBIOS_PROTOCOL_GUID, NULL,
 			     (void **)&smbios) ?:
 		 efi_call_proto(smbios, get_next, &handle, &type, &record, NULL);
 	if (status != EFI_SUCCESS)
 		return NULL;
+	return record;
+}
+
+const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize)
+{
+	const struct efi_smbios_record *record;
+	const u8 *strtable;
+
+	record = efi_get_smbios_record(type);
+	if (!record)
+		return NULL;
 
 	strtable = (u8 *)record + recsize;
 	for (int i = 1; i < ((u8 *)record)[offset]; i++) {
