Commit eb684408 authored by Ard Biesheuvel's avatar Ard Biesheuvel
Browse files

arm64: efi: Use SMBIOS processor version to key off Ampere quirk



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: 550b33cf ("arm64: efi: Force the use of ...")
Tested-by: default avatarAndrea Righi <andrea.righi@canonical.com>
Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
parent 34343eb0
Loading
Loading
Loading
Loading
+31 −8
Original line number Diff line number Diff line
@@ -16,22 +16,45 @@

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;

	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)
{
	u64 tg;
+38 −3
Original line number Diff line number Diff line
@@ -1074,6 +1074,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;

@@ -1087,14 +1089,47 @@ 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);

void efi_remap_image(unsigned long image_base, unsigned alloc_size,
		     unsigned long code_size);
+11 −2
Original line number Diff line number Diff line
@@ -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++) {