arm64: efi: Use SMBIOS processor version to key off Ampere quirk
authorArd Biesheuvel <ardb@kernel.org>
Tue, 28 Feb 2023 16:00:49 +0000 (17:00 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 9 Jun 2023 08:34:29 +0000 (10:34 +0200)
commit eb684408f3ea4856639675d6465f0024e498e4b1 upstream.

Instead of using the SMBIOS type 1 record 'family' field, which is often
modified by OEMs, use the type 4 'processor ID' and 'processor version'
fields, which are set to a small set of probe-able values on all known
Ampere EFI systems in the field.

Fixes: 550b33cfd4452968 ("arm64: efi: Force the use of ...")
Tested-by: Andrea Righi <andrea.righi@canonical.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Jeremi Piotrowski <jpiotrowski@linux.microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/firmware/efi/libstub/arm64-stub.c
drivers/firmware/efi/libstub/efistub.h
drivers/firmware/efi/libstub/smbios.c

index 42282c5..e2f9056 100644 (file)
 
 static bool system_needs_vamap(void)
 {
-       const u8 *type1_family = efi_get_smbios_string(1, family);
+       const struct efi_smbios_type4_record *record;
+       const u32 __aligned(1) *socid;
+       const u8 *version;
 
        /*
         * Ampere eMAG, Altra, and Altra Max machines crash in SetTime() if
-        * SetVirtualAddressMap() has not been called prior.
+        * SetVirtualAddressMap() has not been called prior. Most Altra systems
+        * can be identified by the SMCCC soc ID, which is conveniently exposed
+        * via the type 4 SMBIOS records. Otherwise, test the processor version
+        * field. eMAG systems all appear to have the processor version field
+        * set to "eMAG".
         */
-       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)
                return false;
 
-       efi_warn("Working around broken SetVirtualAddressMap()\n");
-       return true;
+       socid = (u32 *)record->processor_id;
+       switch (*socid & 0xffff000f) {
+               static char const altra[] = "Ampere(TM) Altra(TM) Processor";
+               static char const emag[] = "eMAG";
+
+       default:
+               version = efi_get_smbios_string(&record->header, 4,
+                                               processor_version);
+               if (!version || (strncmp(version, altra, sizeof(altra) - 1) &&
+                                strncmp(version, emag, sizeof(emag) - 1)))
+                       break;
+
+               fallthrough;
+
+       case 0x0a160001:        // Altra
+       case 0x0a160002:        // Altra Max
+               efi_warn("Working around broken SetVirtualAddressMap()\n");
+               return true;
+       }
+
+       return false;
 }
 
 efi_status_t check_platform_features(void)
index 900df67..970e86e 100644 (file)
@@ -983,6 +983,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;
 
@@ -996,13 +998,46 @@ struct efi_smbios_type1_record {
        u8                              family;
 };
 
-#define efi_get_smbios_string(__type, __name) ({                       \
+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(__record, __type, __name) ({             \
        int size = sizeof(struct efi_smbios_type ## __type ## _record); \
        int off = offsetof(struct efi_smbios_type ## __type ## _record, \
                           __name);                                     \
-       __efi_get_smbios_string(__type, off, size);                     \
+       __efi_get_smbios_string((__record), __type, off, size);         \
 })
 
-const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize);
+const u8 *__efi_get_smbios_string(const struct efi_smbios_record *record,
+                                 u8 type, int offset, int recsize);
 
 #endif
index aadb422..f9c159c 100644 (file)
@@ -22,19 +22,28 @@ 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(const struct efi_smbios_record *record,
+                                 u8 type, int offset, int recsize)
+{
+       const u8 *strtable;
+
+       if (!record)
+               return NULL;
 
        strtable = (u8 *)record + record->length;
        for (int i = 1; i < ((u8 *)record)[offset]; i++) {