diff --git a/arch/arm/Kconfig-nommu b/arch/arm/Kconfig-nommu index 930e000489a8..0fad7d943630 100644 --- a/arch/arm/Kconfig-nommu +++ b/arch/arm/Kconfig-nommu @@ -52,7 +52,7 @@ config REMAP_VECTORS_TO_RAM config ARM_MPU bool 'Use the ARM v7 PMSA Compliant MPU' - depends on !XIP_KERNEL && (CPU_V7 || CPU_V7M) + depends on CPU_V7 || CPU_V7M default y if CPU_V7 help Some ARM systems without an MMU have instead a Memory Protection diff --git a/arch/arm/include/asm/mpu.h b/arch/arm/include/asm/mpu.h index 5db37a6ef3cb..56ec02617f58 100644 --- a/arch/arm/include/asm/mpu.h +++ b/arch/arm/include/asm/mpu.h @@ -41,6 +41,7 @@ #endif /* Access permission bits of ACR (only define those that we use)*/ +#define MPU_AP_PL1RO_PL0NA (0x5 << 8) #define MPU_AP_PL1RW_PL0RW (0x3 << 8) #define MPU_AP_PL1RW_PL0R0 (0x2 << 8) #define MPU_AP_PL1RW_PL0NA (0x1 << 8) @@ -49,7 +50,7 @@ #define MPU_PROBE_REGION 0 #define MPU_BG_REGION 1 #define MPU_RAM_REGION 2 -#define MPU_VECTORS_REGION 3 +#define MPU_ROM_REGION 3 /* Maximum number of regions Linux is interested in */ #define MPU_MAX_REGIONS 16 diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index 0d64b8ba7e9c..2e38f85b757a 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -258,6 +258,26 @@ M_CLASS(ldr r0, [r12, #MPU_TYPE]) setup_region r0, r5, r6, MPU_INSTR_SIDE r12 @ 0x0, BG region, enabled 2: isb +#ifdef CONFIG_XIP_KERNEL + set_region_nr r0, #MPU_ROM_REGION, r12 + isb + + ldr r5,=(MPU_AP_PL1RO_PL0NA | MPU_RGN_NORMAL) + + ldr r0, =CONFIG_XIP_PHYS_ADDR @ ROM start + ldr r6, =(_exiprom) @ ROM end + sub r6, r6, r0 @ Minimum size of region to map + clz r6, r6 @ Region size must be 2^N... + rsb r6, r6, #31 @ ...so round up region size + lsl r6, r6, #MPU_RSR_SZ @ Put size in right field + orr r6, r6, #(1 << MPU_RSR_EN) @ Set region enabled bit + + setup_region r0, r5, r6, MPU_DATA_SIDE, r12 @ XIP_PHYS_ADDR, shared, enabled + beq 3f @ Memory-map not unified + setup_region r0, r5, r6, MPU_INSTR_SIDE, r12 @ XIP_PHYS_ADDR, shared, enabled +3: isb +#endif + /* Enable the MPU */ AR_CLASS(mrc p15, 0, r0, c1, c0, 0) @ Read SCTLR AR_CLASS(bic r0, r0, #CR_BR) @ Disable the 'default mem-map' diff --git a/arch/arm/kernel/vmlinux-xip.lds.S b/arch/arm/kernel/vmlinux-xip.lds.S index 7a844310085e..74c93879532a 100644 --- a/arch/arm/kernel/vmlinux-xip.lds.S +++ b/arch/arm/kernel/vmlinux-xip.lds.S @@ -6,6 +6,8 @@ /* No __ro_after_init data in the .rodata section - which will always be ro */ #define RO_AFTER_INIT_DATA +#include + #include #include #include @@ -187,6 +189,9 @@ SECTIONS INIT_RAM_FS } +#ifdef CONFIG_ARM_MPU + . = ALIGN(SZ_128K); +#endif _exiprom = .; /* End of XIP ROM area */ /* @@ -314,3 +319,21 @@ ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & PAGE_MASK) <= PAGE_SIZE, */ ASSERT((_end - __bss_start) >= 12288, ".bss too small for CONFIG_XIP_DEFLATED_DATA") #endif + +#ifdef CONFIG_ARM_MPU +/* + * Due to PMSAv7 restriction on base address and size we have to + * enforce minimal alignment restrictions. It was seen that weaker + * alignment restriction on _xiprom will likely force XIP address + * space spawns multiple MPU regions thus it is likely we run in + * situation when we are reprogramming MPU region we run on with + * something which doesn't cover reprogramming code itself, so as soon + * as we update MPU settings we'd immediately try to execute straight + * from background region which is XN. + * It seem that alignment in 1M should suit most users. + * _exiprom is aligned as 1/8 of 1M so can be covered by subregion + * disable + */ +ASSERT(!(_xiprom & (SZ_1M - 1)), "XIP start address may cause MPU programming issues") +ASSERT(!(_exiprom & (SZ_128K - 1)), "XIP end address may cause MPU programming issues") +#endif diff --git a/arch/arm/mm/pmsa-v7.c b/arch/arm/mm/pmsa-v7.c index ef204634a16e..106ae1c435a3 100644 --- a/arch/arm/mm/pmsa-v7.c +++ b/arch/arm/mm/pmsa-v7.c @@ -7,9 +7,11 @@ #include #include +#include #include #include #include +#include #include "mm.h" @@ -20,6 +22,9 @@ struct region { }; static struct region __initdata mem[MPU_MAX_REGIONS]; +#ifdef CONFIG_XIP_KERNEL +static struct region __initdata xip[MPU_MAX_REGIONS]; +#endif static unsigned int __initdata mpu_min_region_order; static unsigned int __initdata mpu_max_regions; @@ -229,7 +234,6 @@ static int __init allocate_region(phys_addr_t base, phys_addr_t size, /* MPU initialisation functions */ void __init adjust_lowmem_bounds_mpu(void) { - phys_addr_t phys_offset = PHYS_OFFSET; phys_addr_t specified_mem_size, total_mem_size = 0; struct memblock_region *reg; bool first = true; @@ -256,8 +260,19 @@ void __init adjust_lowmem_bounds_mpu(void) /* ... and one for vectors */ mem_max_regions--; #endif + +#ifdef CONFIG_XIP_KERNEL + /* plus some regions to cover XIP ROM */ + num = allocate_region(CONFIG_XIP_PHYS_ADDR, __pa(_exiprom) - CONFIG_XIP_PHYS_ADDR, + mem_max_regions, xip); + + mem_max_regions -= num; +#endif + for_each_memblock(memory, reg) { if (first) { + phys_addr_t phys_offset = PHYS_OFFSET; + /* * Initially only use memory continuous from * PHYS_OFFSET */ @@ -355,7 +370,7 @@ static int __init __mpu_min_region_order(void) static int __init mpu_setup_region(unsigned int number, phys_addr_t start, unsigned int size_order, unsigned int properties, - unsigned int subregions) + unsigned int subregions, bool need_flush) { u32 size_data; @@ -374,6 +389,9 @@ static int __init mpu_setup_region(unsigned int number, phys_addr_t start, size_data = ((size_order - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN; size_data |= subregions << MPU_RSR_SD; + if (need_flush) + flush_cache_all(); + dsb(); /* Ensure all previous data accesses occur with old mappings */ rgnr_write(number); isb(); @@ -416,7 +434,28 @@ void __init mpu_setup(void) /* Background */ err |= mpu_setup_region(region++, 0, 32, MPU_ACR_XN | MPU_RGN_STRONGLY_ORDERED | MPU_AP_PL1RW_PL0NA, - 0); + 0, false); + +#ifdef CONFIG_XIP_KERNEL + /* ROM */ + for (i = 0; i < ARRAY_SIZE(xip); i++) { + /* + * In case we overwrite RAM region we set earlier in + * head-nommu.S (which is cachable) all subsequent + * data access till we setup RAM bellow would be done + * with BG region (which is uncachable), thus we need + * to clean and invalidate cache. + */ + bool need_flush = region == MPU_RAM_REGION; + + if (!xip[i].size) + continue; + + err |= mpu_setup_region(region++, xip[i].base, ilog2(xip[i].size), + MPU_AP_PL1RO_PL0NA | MPU_RGN_NORMAL, + xip[i].subreg, need_flush); + } +#endif /* RAM */ for (i = 0; i < ARRAY_SIZE(mem); i++) { @@ -425,14 +464,14 @@ void __init mpu_setup(void) err |= mpu_setup_region(region++, mem[i].base, ilog2(mem[i].size), MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL, - mem[i].subreg); + mem[i].subreg, false); } /* Vectors */ #ifndef CONFIG_CPU_V7M err |= mpu_setup_region(region++, vectors_base, ilog2(2 * PAGE_SIZE), MPU_AP_PL1RW_PL0NA | MPU_RGN_NORMAL, - 0); + 0, false); #endif if (err) { panic("MPU region initialization failure! %d", err);