armv8: Add ARMv8 MPU configuration logic
Armv8r64 is the first Armv8 platform that only has a PMSA at the current exception level. The architecture supplement for Armv8r64 describes new fields in ID_AA64MMFR0_EL1 which can be used to detect whether a VMSA or PMSA is present. These fields are RES0 on Armv8a. Add logic to read these fields and, for the protection of the memory used by U-Boot, initialize the MPU instead of the MMU during init, then clear the MPU regions before transition to the next stage. Provide a default (blank) MPU memory map, which can be overridden by board configurations. Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com>
This commit is contained in:
		
							parent
							
								
									37a757e227
								
							
						
					
					
						commit
						2f5b7b7490
					
				|  | @ -15,6 +15,7 @@ | ||||||
| #include <asm/global_data.h> | #include <asm/global_data.h> | ||||||
| #include <asm/system.h> | #include <asm/system.h> | ||||||
| #include <asm/armv8/mmu.h> | #include <asm/armv8/mmu.h> | ||||||
|  | #include <asm/armv8/mpu.h> | ||||||
| 
 | 
 | ||||||
| DECLARE_GLOBAL_DATA_PTR; | DECLARE_GLOBAL_DATA_PTR; | ||||||
| 
 | 
 | ||||||
|  | @ -365,6 +366,86 @@ __weak u64 get_page_table_size(void) | ||||||
| 	return size; | 	return size; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void mpu_clear_regions(void) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; mpu_mem_map[i].end || mpu_mem_map[i].attrs; i++) { | ||||||
|  | 		setup_el2_mpu_region(i, 0, 0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct mpu_region default_mpu_mem_map[] = {{0,}}; | ||||||
|  | __weak struct mpu_region *mpu_mem_map = default_mpu_mem_map; | ||||||
|  | 
 | ||||||
|  | static void mpu_setup(void) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	if (current_el() != 2) { | ||||||
|  | 		panic("MPU configuration is only supported at EL2"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	set_sctlr(get_sctlr() & ~(CR_M | CR_WXN)); | ||||||
|  | 
 | ||||||
|  | 	asm volatile("msr MAIR_EL2, %0" : : "r" MEMORY_ATTRIBUTES); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; mpu_mem_map[i].end || mpu_mem_map[i].attrs; i++) { | ||||||
|  | 		setup_el2_mpu_region(i, | ||||||
|  | 			PRBAR_ADDRESS(mpu_mem_map[i].start) | ||||||
|  | 				| PRBAR_OUTER_SH | PRBAR_AP_RW_ANY, | ||||||
|  | 			PRLAR_ADDRESS(mpu_mem_map[i].end) | ||||||
|  | 				| mpu_mem_map[i].attrs | PRLAR_EN_BIT | ||||||
|  | 			); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	set_sctlr(get_sctlr() | CR_M); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool el_has_mmu(void) | ||||||
|  | { | ||||||
|  | 	uint64_t id_aa64mmfr0; | ||||||
|  | 	asm volatile("mrs %0, id_aa64mmfr0_el1" | ||||||
|  | 			: "=r" (id_aa64mmfr0) : : "cc"); | ||||||
|  | 	uint64_t msa = id_aa64mmfr0 & ID_AA64MMFR0_EL1_MSA_MASK; | ||||||
|  | 	uint64_t msa_frac = id_aa64mmfr0 & ID_AA64MMFR0_EL1_MSA_FRAC_MASK; | ||||||
|  | 
 | ||||||
|  | 	switch (msa) { | ||||||
|  | 		case ID_AA64MMFR0_EL1_MSA_VMSA: | ||||||
|  | 			/*
 | ||||||
|  | 			 * VMSA supported in all translation regimes. | ||||||
|  | 			 * No support for PMSA. | ||||||
|  | 			 */ | ||||||
|  | 			return true; | ||||||
|  | 		case ID_AA64MMFR0_EL1_MSA_USE_FRAC: | ||||||
|  | 			/* See MSA_frac for the supported MSAs. */ | ||||||
|  | 			switch (msa_frac) { | ||||||
|  | 				case ID_AA64MMFR0_EL1_MSA_FRAC_NO_PMSA: | ||||||
|  | 					/*
 | ||||||
|  | 					 * PMSA not supported in any translation | ||||||
|  | 					 * regime. | ||||||
|  | 					 */ | ||||||
|  | 					return true; | ||||||
|  | 				case ID_AA64MMFR0_EL1_MSA_FRAC_VMSA: | ||||||
|  | 					/*
 | ||||||
|  | 					* PMSA supported in all translation | ||||||
|  | 					* regimes. No support for VMSA. | ||||||
|  | 					*/ | ||||||
|  | 				case ID_AA64MMFR0_EL1_MSA_FRAC_PMSA: | ||||||
|  | 					/*
 | ||||||
|  | 					 * PMSA supported in all translation | ||||||
|  | 					 * regimes. | ||||||
|  | 					 */ | ||||||
|  | 					return false; | ||||||
|  | 				default: | ||||||
|  | 					panic("Unsupported id_aa64mmfr0_el1 " \ | ||||||
|  | 						"MSA_frac value"); | ||||||
|  | 			} | ||||||
|  | 		default: | ||||||
|  | 			panic("Unsupported id_aa64mmfr0_el1 MSA value"); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void setup_pgtables(void) | void setup_pgtables(void) | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
|  | @ -479,8 +560,13 @@ void dcache_enable(void) | ||||||
| 	/* The data cache is not active unless the mmu is enabled */ | 	/* The data cache is not active unless the mmu is enabled */ | ||||||
| 	if (!(get_sctlr() & CR_M)) { | 	if (!(get_sctlr() & CR_M)) { | ||||||
| 		invalidate_dcache_all(); | 		invalidate_dcache_all(); | ||||||
| 		__asm_invalidate_tlb_all(); | 
 | ||||||
| 		mmu_setup(); | 		if (el_has_mmu()) { | ||||||
|  | 			__asm_invalidate_tlb_all(); | ||||||
|  | 			mmu_setup(); | ||||||
|  | 		} else { | ||||||
|  | 			mpu_setup(); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	set_sctlr(get_sctlr() | CR_C); | 	set_sctlr(get_sctlr() | CR_C); | ||||||
|  | @ -499,7 +585,11 @@ void dcache_disable(void) | ||||||
| 	set_sctlr(sctlr & ~(CR_C|CR_M)); | 	set_sctlr(sctlr & ~(CR_C|CR_M)); | ||||||
| 
 | 
 | ||||||
| 	flush_dcache_all(); | 	flush_dcache_all(); | ||||||
| 	__asm_invalidate_tlb_all(); | 
 | ||||||
|  | 	if (el_has_mmu()) | ||||||
|  | 		__asm_invalidate_tlb_all(); | ||||||
|  | 	else | ||||||
|  | 		mpu_clear_regions(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int dcache_status(void) | int dcache_status(void) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,61 @@ | ||||||
|  | /*
 | ||||||
|  |  * SPDX-License-Identifier: GPL-2.0+ | ||||||
|  |  * | ||||||
|  |  * (C) Copyright 2021 Arm Limited | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _ASM_ARMV8_MPU_H_ | ||||||
|  | #define _ASM_ARMV8_MPU_H_ | ||||||
|  | 
 | ||||||
|  | #include <asm/armv8/mmu.h> | ||||||
|  | #include <asm/barriers.h> | ||||||
|  | #include <linux/stringify.h> | ||||||
|  | 
 | ||||||
|  | #define PRSELR_EL2		S3_4_c6_c2_1 | ||||||
|  | #define PRBAR_EL2		S3_4_c6_c8_0 | ||||||
|  | #define PRLAR_EL2		S3_4_c6_c8_1 | ||||||
|  | #define MPUIR_EL2		S3_4_c0_c0_4 | ||||||
|  | 
 | ||||||
|  | #define PRBAR_ADDRESS(addr)	((addr) & ~(0x3fULL)) | ||||||
|  | 
 | ||||||
|  | /* Access permissions */ | ||||||
|  | #define PRBAR_AP(val)		(((val) & 0x3) << 2) | ||||||
|  | #define PRBAR_AP_RW_HYP		PRBAR_AP(0x0) | ||||||
|  | #define PRBAR_AP_RW_ANY		PRBAR_AP(0x1) | ||||||
|  | #define PRBAR_AP_RO_HYP		PRBAR_AP(0x2) | ||||||
|  | #define PRBAR_AP_RO_ANY		PRBAR_AP(0x3) | ||||||
|  | 
 | ||||||
|  | /* Shareability */ | ||||||
|  | #define PRBAR_SH(val)		(((val) & 0x3) << 4) | ||||||
|  | #define PRBAR_NON_SH		PRBAR_SH(0x0) | ||||||
|  | #define PRBAR_OUTER_SH		PRBAR_SH(0x2) | ||||||
|  | #define PRBAR_INNER_SH		PRBAR_SH(0x3) | ||||||
|  | 
 | ||||||
|  | /* Memory attribute (MAIR idx) */ | ||||||
|  | #define PRLAR_ATTRIDX(val)	(((val) & 0x7) << 1) | ||||||
|  | #define PRLAR_EN_BIT		(0x1) | ||||||
|  | #define PRLAR_ADDRESS(addr)	((addr) & ~(0x3fULL)) | ||||||
|  | 
 | ||||||
|  | #ifndef __ASSEMBLY__ | ||||||
|  | 
 | ||||||
|  | static inline void setup_el2_mpu_region(uint8_t region, uint64_t base, uint64_t limit) | ||||||
|  | { | ||||||
|  | 	asm volatile("msr " __stringify(PRSELR_EL2) ", %0" : : "r" (region)); | ||||||
|  | 	isb(); | ||||||
|  | 	asm volatile("msr " __stringify(PRBAR_EL2) ", %0" : : "r" (base)); | ||||||
|  | 	asm volatile("msr " __stringify(PRLAR_EL2) ", %0" : : "r" (limit)); | ||||||
|  | 	dsb(); | ||||||
|  | 	isb(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | struct mpu_region { | ||||||
|  | 	u64 start; | ||||||
|  | 	u64 end; | ||||||
|  | 	u64 attrs; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern struct mpu_region *mpu_mem_map; | ||||||
|  | 
 | ||||||
|  | #endif /* _ASM_ARMV8_MPU_H_ */ | ||||||
		Loading…
	
		Reference in New Issue