diff options
Diffstat (limited to 'drivers/firmware/efi/libstub/arm64.c')
| -rw-r--r-- | drivers/firmware/efi/libstub/arm64.c | 58 | 
1 files changed, 44 insertions, 14 deletions
diff --git a/drivers/firmware/efi/libstub/arm64.c b/drivers/firmware/efi/libstub/arm64.c index 399770266372..446e35eaf3d9 100644 --- a/drivers/firmware/efi/libstub/arm64.c +++ b/drivers/firmware/efi/libstub/arm64.c @@ -9,6 +9,7 @@  #include <linux/efi.h>  #include <asm/efi.h> +#include <asm/image.h>  #include <asm/memory.h>  #include <asm/sysreg.h> @@ -16,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) @@ -65,9 +89,10 @@ efi_status_t check_platform_features(void)  #define DCTYPE	"cvau"  #endif +u32 __weak code_size; +  void efi_cache_sync_image(unsigned long image_base, -			  unsigned long alloc_size, -			  unsigned long code_size) +			  unsigned long alloc_size)  {  	u32 ctr = read_cpuid_effective_cachetype();  	u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr, @@ -75,16 +100,21 @@ void efi_cache_sync_image(unsigned long image_base,  	/* only perform the cache maintenance if needed for I/D coherency */  	if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) { +		unsigned long base = image_base; +		unsigned long size = code_size; +  		do { -			asm("dc " DCTYPE ", %0" :: "r"(image_base)); -			image_base += lsize; -			code_size -= lsize; -		} while (code_size >= lsize); +			asm("dc " DCTYPE ", %0" :: "r"(base)); +			base += lsize; +			size -= lsize; +		} while (size >= lsize);  	}  	asm("ic ialluis");  	dsb(ish);  	isb(); + +	efi_remap_image(image_base, alloc_size, code_size);  }  unsigned long __weak primary_entry_offset(void)  |