Skip to content

Commit

Permalink
arm64: efi: Use SMBIOS processor version to key off Ampere quirk
Browse files Browse the repository at this point in the history
commit eb68440 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: 550b33c ("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>
  • Loading branch information
ardbiesheuvel authored and gregkh committed Jun 9, 2023
1 parent b026755 commit e8631d8
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 13 deletions.
39 changes: 31 additions & 8 deletions drivers/firmware/efi/libstub/arm64-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,43 @@

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)
Expand Down
41 changes: 38 additions & 3 deletions drivers/firmware/efi/libstub/efistub.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
13 changes: 11 additions & 2 deletions drivers/firmware/efi/libstub/smbios.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down

0 comments on commit e8631d8

Please sign in to comment.