Skip to content

Commit a0995c0

Browse files
murzinvRussell King
authored and
Russell King
committed
ARM: 8708/1: NOMMU: Rework MPU to be mostly done in C
Currently, there are several issues with how MPU is setup: 1. We won't boot if MPU is missing 2. We won't boot if use XIP 3. Further extension of MPU setup requires asm skills The 1st point can be relaxed, so we can continue with boot CPU even if MPU is missed and fail boot for secondaries only. To address the 2nd point we could create region covering CONFIG_XIP_PHYS_ADDR - _end and that might work for the first stage of MPU enable, but due to MPU's alignment requirement we could cover too much, IOW we need more flexibility in how we're partitioning memory regions... and it'd be hardly possible to archive because of the 3rd point. This patch is trying to address 1st and 3rd issues and paves the path for 2nd and further improvements. The most visible change introduced with this patch is that we start using mpu_rgn_info array (as it was supposed?), so change in MPU setup done by boot CPU is recorded there and feed to secondaries. It allows us to keep minimal region setup for boot CPU and do the rest in C. Since we start programming MPU regions in C evaluation of MPU constrains (number of regions supported and minimal region order) can be done once, which in turn open possibility to free-up "probe" region early. Tested-by: Szemző András <[email protected]> Tested-by: Alexandre TORGUE <[email protected]> Tested-by: Benjamin Gaignard <[email protected]> Signed-off-by: Vladimir Murzin <[email protected]> Signed-off-by: Russell King <[email protected]>
1 parent e8b47e1 commit a0995c0

File tree

6 files changed

+127
-40
lines changed

6 files changed

+127
-40
lines changed

arch/arm/include/asm/mpu.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ struct mpu_rgn {
6262
};
6363

6464
struct mpu_rgn_info {
65-
u32 mpuir;
65+
unsigned int used;
6666
struct mpu_rgn rgns[MPU_MAX_REGIONS];
6767
};
6868
extern struct mpu_rgn_info mpu_rgn_info;

arch/arm/include/asm/smp.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ asmlinkage void secondary_start_kernel(void);
6060
*/
6161
struct secondary_data {
6262
union {
63-
unsigned long mpu_rgn_szr;
63+
struct mpu_rgn_info *mpu_rgn_info;
6464
u64 pgdir;
6565
};
6666
unsigned long swapper_pg_dir;

arch/arm/kernel/asm-offsets.c

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <asm/mach/arch.h>
2424
#include <asm/thread_info.h>
2525
#include <asm/memory.h>
26+
#include <asm/mpu.h>
2627
#include <asm/procinfo.h>
2728
#include <asm/suspend.h>
2829
#include <asm/vdso_datapage.h>
@@ -186,6 +187,16 @@ int main(void)
186187
BLANK();
187188
#ifdef CONFIG_VDSO
188189
DEFINE(VDSO_DATA_SIZE, sizeof(union vdso_data_store));
190+
#endif
191+
BLANK();
192+
#ifdef CONFIG_ARM_MPU
193+
DEFINE(MPU_RNG_INFO_RNGS, offsetof(struct mpu_rgn_info, rgns));
194+
DEFINE(MPU_RNG_INFO_USED, offsetof(struct mpu_rgn_info, used));
195+
196+
DEFINE(MPU_RNG_SIZE, sizeof(struct mpu_rgn));
197+
DEFINE(MPU_RGN_DRBAR, offsetof(struct mpu_rgn, drbar));
198+
DEFINE(MPU_RGN_DRSR, offsetof(struct mpu_rgn, drsr));
199+
DEFINE(MPU_RGN_DRACR, offsetof(struct mpu_rgn, dracr));
189200
#endif
190201
return 0;
191202
}

arch/arm/kernel/head-nommu.S

+62-18
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
#include <linux/linkage.h>
1515
#include <linux/init.h>
16+
#include <linux/errno.h>
1617

1718
#include <asm/assembler.h>
1819
#include <asm/ptrace.h>
@@ -110,8 +111,8 @@ ENTRY(secondary_startup)
110111

111112
#ifdef CONFIG_ARM_MPU
112113
/* Use MPU region info supplied by __cpu_up */
113-
ldr r6, [r7] @ get secondary_data.mpu_szr
114-
bl __setup_mpu @ Initialize the MPU
114+
ldr r6, [r7] @ get secondary_data.mpu_rgn_info
115+
bl __secondary_setup_mpu @ Initialize the MPU
115116
#endif
116117

117118
badr lr, 1f @ return (PIC) address
@@ -204,13 +205,13 @@ ENTRY(__setup_mpu)
204205
mrc p15, 0, r0, c0, c1, 4 @ Read ID_MMFR0
205206
and r0, r0, #(MMFR0_PMSA) @ PMSA field
206207
teq r0, #(MMFR0_PMSAv7) @ PMSA v7
207-
bne __error_p @ Fail: ARM_MPU on NOT v7 PMSA
208+
bxne lr
208209

209210
/* Determine whether the D/I-side memory map is unified. We set the
210211
* flags here and continue to use them for the rest of this function */
211212
mrc p15, 0, r0, c0, c0, 4 @ MPUIR
212213
ands r5, r0, #MPUIR_DREGION_SZMASK @ 0 size d region => No MPU
213-
beq __error_p @ Fail: ARM_MPU and no MPU
214+
bxeq lr
214215
tst r0, #MPUIR_nU @ MPUIR_nU = 0 for unified
215216

216217
/* Setup second region first to free up r6 */
@@ -238,27 +239,70 @@ ENTRY(__setup_mpu)
238239
setup_region r0, r5, r6, MPU_INSTR_SIDE @ 0x0, BG region, enabled
239240
2: isb
240241

241-
/* Vectors region */
242-
set_region_nr r0, #MPU_VECTORS_REGION
242+
/* Enable the MPU */
243+
mrc p15, 0, r0, c1, c0, 0 @ Read SCTLR
244+
bic r0, r0, #CR_BR @ Disable the 'default mem-map'
245+
orr r0, r0, #CR_M @ Set SCTRL.M (MPU on)
246+
mcr p15, 0, r0, c1, c0, 0 @ Enable MPU
247+
isb
248+
249+
ret lr
250+
ENDPROC(__setup_mpu)
251+
252+
#ifdef CONFIG_SMP
253+
/*
254+
* r6: pointer at mpu_rgn_info
255+
*/
256+
257+
ENTRY(__secondary_setup_mpu)
258+
/* Probe for v7 PMSA compliance */
259+
mrc p15, 0, r0, c0, c1, 4 @ Read ID_MMFR0
260+
and r0, r0, #(MMFR0_PMSA) @ PMSA field
261+
teq r0, #(MMFR0_PMSAv7) @ PMSA v7
262+
bne __error_p
263+
264+
/* Determine whether the D/I-side memory map is unified. We set the
265+
* flags here and continue to use them for the rest of this function */
266+
mrc p15, 0, r0, c0, c0, 4 @ MPUIR
267+
ands r5, r0, #MPUIR_DREGION_SZMASK @ 0 size d region => No MPU
268+
beq __error_p
269+
270+
ldr r4, [r6, #MPU_RNG_INFO_USED]
271+
mov r5, #MPU_RNG_SIZE
272+
add r3, r6, #MPU_RNG_INFO_RNGS
273+
mla r3, r4, r5, r3
274+
275+
1:
276+
tst r0, #MPUIR_nU @ MPUIR_nU = 0 for unified
277+
sub r3, r3, #MPU_RNG_SIZE
278+
sub r4, r4, #1
279+
280+
set_region_nr r0, r4
243281
isb
244-
/* Shared, inaccessible to PL0, rw PL1 */
245-
mov r0, #CONFIG_VECTORS_BASE @ Cover from VECTORS_BASE
246-
ldr r5,=(MPU_AP_PL1RW_PL0NA | MPU_RGN_NORMAL)
247-
/* Writing N to bits 5:1 (RSR_SZ) --> region size 2^N+1 */
248-
mov r6, #(((2 * PAGE_SHIFT - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN)
249282

250-
setup_region r0, r5, r6, MPU_DATA_SIDE @ VECTORS_BASE, PL0 NA, enabled
251-
beq 3f @ Memory-map not unified
252-
setup_region r0, r5, r6, MPU_INSTR_SIDE @ VECTORS_BASE, PL0 NA, enabled
253-
3: isb
283+
ldr r0, [r3, #MPU_RGN_DRBAR]
284+
ldr r6, [r3, #MPU_RGN_DRSR]
285+
ldr r5, [r3, #MPU_RGN_DRACR]
286+
287+
setup_region r0, r5, r6, MPU_DATA_SIDE
288+
beq 2f
289+
setup_region r0, r5, r6, MPU_INSTR_SIDE
290+
2: isb
291+
292+
mrc p15, 0, r0, c0, c0, 4 @ Reevaluate the MPUIR
293+
cmp r4, #0
294+
bgt 1b
254295

255296
/* Enable the MPU */
256297
mrc p15, 0, r0, c1, c0, 0 @ Read SCTLR
257-
bic r0, r0, #CR_BR @ Disable the 'default mem-map'
298+
bic r0, r0, #CR_BR @ Disable the 'default mem-map'
258299
orr r0, r0, #CR_M @ Set SCTRL.M (MPU on)
259300
mcr p15, 0, r0, c1, c0, 0 @ Enable MPU
260301
isb
302+
261303
ret lr
262-
ENDPROC(__setup_mpu)
263-
#endif
304+
ENDPROC(__secondary_setup_mpu)
305+
306+
#endif /* CONFIG_SMP */
307+
#endif /* CONFIG_ARM_MPU */
264308
#include "head-common.S"

arch/arm/kernel/smp.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
114114
*/
115115
secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;
116116
#ifdef CONFIG_ARM_MPU
117-
secondary_data.mpu_rgn_szr = mpu_rgn_info.rgns[MPU_RAM_REGION].drsr;
117+
secondary_data.mpu_rgn_info = &mpu_rgn_info;
118118
#endif
119119

120120
#ifdef CONFIG_MMU

arch/arm/mm/pmsa-v7.c

+51-19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
#include "mm.h"
1414

15+
static unsigned int __initdata mpu_min_region_order;
16+
static unsigned int __initdata mpu_max_regions;
17+
1518
#define DRBAR __ACCESS_CP15(c6, 0, c1, 0)
1619
#define IRBAR __ACCESS_CP15(c6, 0, c1, 1)
1720
#define DRSR __ACCESS_CP15(c6, 0, c1, 2)
@@ -75,6 +78,11 @@ static inline u32 irbar_read(void)
7578
return read_sysreg(IRBAR);
7679
}
7780

81+
static int __init mpu_present(void)
82+
{
83+
return ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7);
84+
}
85+
7886
/* MPU initialisation functions */
7987
void __init adjust_lowmem_bounds_mpu(void)
8088
{
@@ -85,6 +93,9 @@ void __init adjust_lowmem_bounds_mpu(void)
8593
phys_addr_t mem_start;
8694
phys_addr_t mem_end;
8795

96+
if (!mpu_present())
97+
return;
98+
8899
for_each_memblock(memory, reg) {
89100
if (first) {
90101
/*
@@ -146,19 +157,15 @@ void __init adjust_lowmem_bounds_mpu(void)
146157

147158
}
148159

149-
static int mpu_present(void)
150-
{
151-
return ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7);
152-
}
153-
154-
static int mpu_max_regions(void)
160+
static int __init __mpu_max_regions(void)
155161
{
156162
/*
157163
* We don't support a different number of I/D side regions so if we
158164
* have separate instruction and data memory maps then return
159165
* whichever side has a smaller number of supported regions.
160166
*/
161167
u32 dregions, iregions, mpuir;
168+
162169
mpuir = read_cpuid(CPUID_MPUIR);
163170

164171
dregions = iregions = (mpuir & MPUIR_DREGION_SZMASK) >> MPUIR_DREGION;
@@ -171,15 +178,16 @@ static int mpu_max_regions(void)
171178
return min(dregions, iregions);
172179
}
173180

174-
static int mpu_iside_independent(void)
181+
static int __init mpu_iside_independent(void)
175182
{
176183
/* MPUIR.nU specifies whether there is *not* a unified memory map */
177184
return read_cpuid(CPUID_MPUIR) & MPUIR_nU;
178185
}
179186

180-
static int mpu_min_region_order(void)
187+
static int __init __mpu_min_region_order(void)
181188
{
182189
u32 drbar_result, irbar_result;
190+
183191
/* We've kept a region free for this probing */
184192
rgnr_write(MPU_PROBE_REGION);
185193
isb();
@@ -198,22 +206,24 @@ static int mpu_min_region_order(void)
198206
}
199207
isb(); /* Ensure that MPU region operations have completed */
200208
/* Return whichever result is larger */
209+
201210
return __ffs(max(drbar_result, irbar_result));
202211
}
203212

204-
static int mpu_setup_region(unsigned int number, phys_addr_t start,
213+
static int __init mpu_setup_region(unsigned int number, phys_addr_t start,
205214
unsigned int size_order, unsigned int properties)
206215
{
207216
u32 size_data;
208217

209218
/* We kept a region free for probing resolution of MPU regions*/
210-
if (number > mpu_max_regions() || number == MPU_PROBE_REGION)
219+
if (number > mpu_max_regions
220+
|| number >= MPU_MAX_REGIONS)
211221
return -ENOENT;
212222

213223
if (size_order > 32)
214224
return -ENOMEM;
215225

216-
if (size_order < mpu_min_region_order())
226+
if (size_order < mpu_min_region_order)
217227
return -ENOMEM;
218228

219229
/* Writing N to bits 5:1 (RSR_SZ) specifies region size 2^N+1 */
@@ -240,6 +250,9 @@ static int mpu_setup_region(unsigned int number, phys_addr_t start,
240250
mpu_rgn_info.rgns[number].dracr = properties;
241251
mpu_rgn_info.rgns[number].drbar = start;
242252
mpu_rgn_info.rgns[number].drsr = size_data;
253+
254+
mpu_rgn_info.used++;
255+
243256
return 0;
244257
}
245258

@@ -248,19 +261,38 @@ static int mpu_setup_region(unsigned int number, phys_addr_t start,
248261
*/
249262
void __init mpu_setup(void)
250263
{
251-
int region_err;
264+
int region = 0, err = 0;
265+
252266
if (!mpu_present())
253267
return;
254268

255-
region_err = mpu_setup_region(MPU_RAM_REGION, PHYS_OFFSET,
256-
ilog2(memblock.memory.regions[0].size),
257-
MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL);
258-
if (region_err) {
259-
panic("MPU region initialization failure! %d", region_err);
269+
/* Free-up MPU_PROBE_REGION */
270+
mpu_min_region_order = __mpu_min_region_order();
271+
272+
/* How many regions are supported */
273+
mpu_max_regions = __mpu_max_regions();
274+
275+
/* Now setup MPU (order is important) */
276+
277+
/* Background */
278+
err |= mpu_setup_region(region++, 0, 32,
279+
MPU_ACR_XN | MPU_RGN_STRONGLY_ORDERED | MPU_AP_PL1RW_PL0NA);
280+
281+
/* RAM */
282+
err |= mpu_setup_region(region++, PHYS_OFFSET,
283+
ilog2(memblock.memory.regions[0].size),
284+
MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL);
285+
286+
/* Vectors */
287+
err |= mpu_setup_region(region++, vectors_base,
288+
ilog2(2 * PAGE_SIZE),
289+
MPU_AP_PL1RW_PL0NA | MPU_RGN_NORMAL);
290+
if (err) {
291+
panic("MPU region initialization failure! %d", err);
260292
} else {
261293
pr_info("Using ARMv7 PMSA Compliant MPU. "
262-
"Region independence: %s, Max regions: %d\n",
294+
"Region independence: %s, Used %d of %d regions\n",
263295
mpu_iside_independent() ? "Yes" : "No",
264-
mpu_max_regions());
296+
mpu_rgn_info.used, mpu_max_regions);
265297
}
266298
}

0 commit comments

Comments
 (0)