ARM: mpu: add early bring-up code for the ARMv7 PMSA-compliant MPU
authorJonathan Austin <jonathan.austin@arm.com>
Fri, 22 Feb 2013 17:48:56 +0000 (17:48 +0000)
committerJonathan Austin <jonathan.austin@arm.com>
Fri, 7 Jun 2013 16:02:51 +0000 (17:02 +0100)
This patch adds initial support for using the MPU, which is necessary for
SMP operation on PMSAv7 processors because it is the only way to ensure
memory is shared. This is an initial patch and full SMP support is added
later in this series.

The setup of the MPU is performed in a way analagous to that for the MMU:
Very early initialisation before the C environment is brought up, followed
by a sanity check and more complete initialisation in C.

This patch provides the simplest possible memory region configuration:
MPU_PROBE_REGION: Reserved for probing MPU details, not enabled
MPU_BG_REGION: A 'background' region that specifies all memory strongly ordered
MPU_RAM_REGION: A single shared, cacheable, normal region for the valid RAM.

In this early initialisation code we simply map the whole of the address
space with the BG_REGION and (at least) the kernel with the RAM_REGION. The
MPU has region alignment constraints that require us to round past the end
of the kernel.

As region 2 has a higher priority than region 1, it overrides the strongly-
ordered behaviour for RAM only.

Subsequent patches will add more complete initialisation from the C-world
and support for bringing up secondary CPUs.

Signed-off-by: Jonathan Austin <jonathan.austin@arm.com>
Reviewed-by: Will Deacon <will.deacon@arm.com>
CC: Hyok S. Choi <hyok.choi@samsung.com>
arch/arm/include/asm/mpu.h
arch/arm/kernel/head-nommu.S

index bd48c0cb544ebbee75fc699db0c44b2efa895bc6..001483465aa4fa26b50ce3b1e3984062d8b9192d 100644 (file)
@@ -50,6 +50,9 @@
 /* Maximum number of regions Linux is interested in */
 #define MPU_MAX_REGIONS                16
 
+#define MPU_DATA_SIDE          0
+#define MPU_INSTR_SIDE         1
+
 #ifndef __ASSEMBLY__
 
 struct mpu_rgn {
index 06ba9c8e62bee5c020aaff14aba69b654b7fd818..659912c495710e0391f6e604618081fdad17a808 100644 (file)
 #include <asm/assembler.h>
 #include <asm/ptrace.h>
 #include <asm/asm-offsets.h>
+#include <asm/memory.h>
 #include <asm/cp15.h>
 #include <asm/thread_info.h>
 #include <asm/v7m.h>
+#include <asm/mpu.h>
 
 /*
  * Kernel startup entry point.
@@ -63,6 +65,17 @@ ENTRY(stext)
        movs    r10, r5                         @ invalid processor (r5=0)?
        beq     __error_p                               @ yes, error 'p'
 
+#ifdef CONFIG_ARM_MPU
+       /* Calculate the size of a region covering just the kernel */
+       ldr     r5, =PHYS_OFFSET                @ Region start: PHYS_OFFSET
+       ldr     r6, =(_end)                     @ Cover whole kernel
+       sub     r6, r6, r5                      @ 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
+       bl      __setup_mpu
+#endif
        ldr     r13, =__mmap_switched           @ address to jump to after
                                                @ initialising sctlr
        adr     lr, BSYM(1f)                    @ return (PIC) address
@@ -147,4 +160,78 @@ __after_proc_init:
 ENDPROC(__after_proc_init)
        .ltorg
 
+#ifdef CONFIG_ARM_MPU
+
+
+/* Set which MPU region should be programmed */
+.macro set_region_nr tmp, rgnr
+       mov     \tmp, \rgnr                     @ Use static region numbers
+       mcr     p15, 0, \tmp, c6, c2, 0         @ Write RGNR
+.endm
+
+/* Setup a single MPU region, either D or I side (D-side for unified) */
+.macro setup_region bar, acr, sr, side = MPU_DATA_SIDE
+       mcr     p15, 0, \bar, c6, c1, (0 + \side)       @ I/DRBAR
+       mcr     p15, 0, \acr, c6, c1, (4 + \side)       @ I/DRACR
+       mcr     p15, 0, \sr, c6, c1, (2 + \side)                @ I/DRSR
+.endm
+
+/*
+ * Setup the MPU and initial MPU Regions. We create the following regions:
+ * Region 0: Use this for probing the MPU details, so leave disabled.
+ * Region 1: Background region - covers the whole of RAM as strongly ordered
+ * Region 2: Normal, Shared, cacheable for RAM. From PHYS_OFFSET, size from r6
+ *
+ * r6: Value to be written to DRSR (and IRSR if required) for MPU_RAM_REGION
+*/
+
+ENTRY(__setup_mpu)
+
+       /* Probe for v7 PMSA compliance */
+       mrc     p15, 0, r0, c0, c1, 4           @ Read ID_MMFR0
+       and     r0, r0, #(MMFR0_PMSA)           @ PMSA field
+       teq     r0, #(MMFR0_PMSAv7)             @ PMSA v7
+       bne     __error_p                       @ Fail: ARM_MPU on NOT v7 PMSA
+
+       /* Determine whether the D/I-side memory map is unified. We set the
+        * flags here and continue to use them for the rest of this function */
+       mrc     p15, 0, r0, c0, c0, 4           @ MPUIR
+       ands    r5, r0, #MPUIR_DREGION_SZMASK   @ 0 size d region => No MPU
+       beq     __error_p                       @ Fail: ARM_MPU and no MPU
+       tst     r0, #MPUIR_nU                   @ MPUIR_nU = 0 for unified
+
+       /* Setup second region first to free up r6 */
+       set_region_nr r0, #MPU_RAM_REGION
+       isb
+       /* Full access from PL0, PL1, shared for CONFIG_SMP, cacheable */
+       ldr     r0, =PHYS_OFFSET                @ RAM starts at PHYS_OFFSET
+       ldr     r5,=(MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL)
+
+       setup_region r0, r5, r6, MPU_DATA_SIDE  @ PHYS_OFFSET, shared, enabled
+       beq     1f                              @ Memory-map not unified
+       setup_region r0, r5, r6, MPU_INSTR_SIDE @ PHYS_OFFSET, shared, enabled
+1:     isb
+
+       /* First/background region */
+       set_region_nr r0, #MPU_BG_REGION
+       isb
+       /* Execute Never,  strongly ordered, inaccessible to PL0, rw PL1  */
+       mov     r0, #0                          @ BG region starts at 0x0
+       ldr     r5,=(MPU_ACR_XN | MPU_RGN_STRONGLY_ORDERED | MPU_AP_PL1RW_PL0NA)
+       mov     r6, #MPU_RSR_ALL_MEM            @ 4GB region, enabled
+
+       setup_region r0, r5, r6, MPU_DATA_SIDE  @ 0x0, BG region, enabled
+       beq     2f                              @ Memory-map not unified
+       setup_region r0, r5, r6, MPU_INSTR_SIDE @ 0x0, BG region, enabled
+2:     isb
+
+       /* Enable the MPU */
+       mrc     p15, 0, r0, c1, c0, 0           @ Read SCTLR
+       bic     r0, r0, #CR_BR                  @ Disable the 'default mem-map'
+       orr     r0, r0, #CR_M                   @ Set SCTRL.M (MPU on)
+       mcr     p15, 0, r0, c1, c0, 0           @ Enable MPU
+       isb
+       mov pc,lr
+ENDPROC(__setup_mpu)
+#endif
 #include "head-common.S"