[PATCH v3 8/8] ARM: NOMMU: Support MPU in XIP configuration
Alexandre Torgue
alexandre.torgue at st.com
Mon Sep 25 05:54:30 PDT 2017
Hi Vlad,
On 09/25/2017 11:05 AM, Vladimir Murzin wrote:
> Currently, there is assumption in early MPU setup code that kernel
> image is located in RAM, which is obviously not true for XIP. To run
> code from ROM we need to make sure that it is covered by MPU. However,
> due to we allocate regions (semi-)dynamically we can run into issue of
> trimming region we are running from in case ROM spawns several MPU
> regions. To help deal with that we enforce minimum alignments for start
> end end of XIP address space as 1MB and 128Kb correspondingly.
>
> Signed-off-by: Vladimir Murzin <vladimir.murzin at arm.com>
Thanks for the XIP fix.
Tested-by: Alexandre TORGUE <alexandre.torgue at st.com>
regards
Alex
> ---
> arch/arm/Kconfig-nommu | 2 +-
> arch/arm/include/asm/mpu.h | 3 ++-
> arch/arm/kernel/head-nommu.S | 20 ++++++++++++++++
> arch/arm/kernel/vmlinux-xip.lds.S | 23 ++++++++++++++++++
> arch/arm/mm/pmsa-v7.c | 49 +++++++++++++++++++++++++++++++++++----
> 5 files changed, 90 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm/Kconfig-nommu b/arch/arm/Kconfig-nommu
> index 930e000..0fad7d9 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 5db37a6..56ec026 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 8a0718f..2ce4455 100644
> --- a/arch/arm/kernel/head-nommu.S
> +++ b/arch/arm/kernel/head-nommu.S
> @@ -279,6 +279,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 8265b11..c6f7056 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 <linux/sizes.h>
> +
> #include <asm-generic/vmlinux.lds.h>
> #include <asm/cache.h>
> #include <asm/thread_info.h>
> @@ -205,6 +207,9 @@ SECTIONS
> PERCPU_SECTION(L1_CACHE_BYTES)
> #endif
>
> +#ifdef CONFIG_ARM_MPU
> + . = ALIGN(SZ_128K);
> +#endif
> _exiprom = .; /* End of XIP ROM area */
> __data_loc = ALIGN(4); /* location in binary */
> . = PAGE_OFFSET + TEXT_OFFSET;
> @@ -322,3 +327,21 @@ ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")
> */
> ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & PAGE_MASK) <= PAGE_SIZE,
> "HYP init code too big or misaligned")
> +
> +#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 c1f1fc7..27434d7 100644
> --- a/arch/arm/mm/pmsa-v7.c
> +++ b/arch/arm/mm/pmsa-v7.c
> @@ -7,9 +7,11 @@
> #include <linux/bitops.h>
> #include <linux/memblock.h>
>
> +#include <asm/cacheflush.h>
> #include <asm/cp15.h>
> #include <asm/cputype.h>
> #include <asm/mpu.h>
> +#include <asm/sections.h>
>
> #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);
>
More information about the linux-arm-kernel
mailing list