aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Murzin <vladimir.murzin@arm.com>2017-10-16 07:52:35 -0400
committerRussell King <rmk+kernel@armlinux.org.uk>2017-10-23 11:58:31 -0400
commit877ec119dbbf9576953efc457ede5243621ad6eb (patch)
tree14b0e2a738222f9f046d1c3ebbb1fc6580dc2298
parent59b6359dd92d18f5dc04b14a4c926fa08ab66f7c (diff)
ARM: 8706/1: NOMMU: Move out MPU setup in separate module
Having MPU handling code in dedicated module makes it easier to enhance/maintain it. Tested-by: Szemző András <sza@esh.hu> Tested-by: Alexandre TORGUE <alexandre.torgue@st.com> Tested-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r--arch/arm/include/asm/mpu.h16
-rw-r--r--arch/arm/mm/Makefile1
-rw-r--r--arch/arm/mm/nommu.c254
-rw-r--r--arch/arm/mm/pmsa-v7.c262
4 files changed, 276 insertions, 257 deletions
diff --git a/arch/arm/include/asm/mpu.h b/arch/arm/include/asm/mpu.h
index c3247cc2fe08..edec5cf48471 100644
--- a/arch/arm/include/asm/mpu.h
+++ b/arch/arm/include/asm/mpu.h
@@ -1,8 +1,6 @@
1#ifndef __ARM_MPU_H 1#ifndef __ARM_MPU_H
2#define __ARM_MPU_H 2#define __ARM_MPU_H
3 3
4#ifdef CONFIG_ARM_MPU
5
6/* MPUIR layout */ 4/* MPUIR layout */
7#define MPUIR_nU 1 5#define MPUIR_nU 1
8#define MPUIR_DREGION 8 6#define MPUIR_DREGION 8
@@ -69,8 +67,18 @@ struct mpu_rgn_info {
69}; 67};
70extern struct mpu_rgn_info mpu_rgn_info; 68extern struct mpu_rgn_info mpu_rgn_info;
71 69
72#endif /* __ASSEMBLY__ */ 70#ifdef CONFIG_ARM_MPU
71
72extern void __init adjust_lowmem_bounds_mpu(void);
73extern void __init mpu_setup(void);
73 74
74#endif /* CONFIG_ARM_MPU */ 75#else
76
77static inline void adjust_lowmem_bounds_mpu(void) {}
78static inline void mpu_setup(void) {}
79
80#endif /* !CONFIG_ARM_MPU */
81
82#endif /* __ASSEMBLY__ */
75 83
76#endif 84#endif
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index 950d19babb5f..bdb2ec11a2e3 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MMU) += fault-armv.o flush.o idmap.o ioremap.o \
9 9
10ifneq ($(CONFIG_MMU),y) 10ifneq ($(CONFIG_MMU),y)
11obj-y += nommu.o 11obj-y += nommu.o
12obj-$(CONFIG_ARM_MPU) += pmsa-v7.o
12endif 13endif
13 14
14obj-$(CONFIG_ARM_PTDUMP) += dump.o 15obj-$(CONFIG_ARM_PTDUMP) += dump.o
diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c
index 3b8e728cc944..4c56b567cb47 100644
--- a/arch/arm/mm/nommu.c
+++ b/arch/arm/mm/nommu.c
@@ -27,259 +27,7 @@ unsigned long vectors_base;
27 27
28#ifdef CONFIG_ARM_MPU 28#ifdef CONFIG_ARM_MPU
29struct mpu_rgn_info mpu_rgn_info; 29struct mpu_rgn_info mpu_rgn_info;
30 30#endif
31/* Region number */
32static void rgnr_write(u32 v)
33{
34 asm("mcr p15, 0, %0, c6, c2, 0" : : "r" (v));
35}
36
37/* Data-side / unified region attributes */
38
39/* Region access control register */
40static void dracr_write(u32 v)
41{
42 asm("mcr p15, 0, %0, c6, c1, 4" : : "r" (v));
43}
44
45/* Region size register */
46static void drsr_write(u32 v)
47{
48 asm("mcr p15, 0, %0, c6, c1, 2" : : "r" (v));
49}
50
51/* Region base address register */
52static void drbar_write(u32 v)
53{
54 asm("mcr p15, 0, %0, c6, c1, 0" : : "r" (v));
55}
56
57static u32 drbar_read(void)
58{
59 u32 v;
60 asm("mrc p15, 0, %0, c6, c1, 0" : "=r" (v));
61 return v;
62}
63/* Optional instruction-side region attributes */
64
65/* I-side Region access control register */
66static void iracr_write(u32 v)
67{
68 asm("mcr p15, 0, %0, c6, c1, 5" : : "r" (v));
69}
70
71/* I-side Region size register */
72static void irsr_write(u32 v)
73{
74 asm("mcr p15, 0, %0, c6, c1, 3" : : "r" (v));
75}
76
77/* I-side Region base address register */
78static void irbar_write(u32 v)
79{
80 asm("mcr p15, 0, %0, c6, c1, 1" : : "r" (v));
81}
82
83static unsigned long irbar_read(void)
84{
85 unsigned long v;
86 asm("mrc p15, 0, %0, c6, c1, 1" : "=r" (v));
87 return v;
88}
89
90/* MPU initialisation functions */
91void __init adjust_lowmem_bounds_mpu(void)
92{
93 phys_addr_t phys_offset = PHYS_OFFSET;
94 phys_addr_t aligned_region_size, specified_mem_size, rounded_mem_size;
95 struct memblock_region *reg;
96 bool first = true;
97 phys_addr_t mem_start;
98 phys_addr_t mem_end;
99
100 for_each_memblock(memory, reg) {
101 if (first) {
102 /*
103 * Initially only use memory continuous from
104 * PHYS_OFFSET */
105 if (reg->base != phys_offset)
106 panic("First memory bank must be contiguous from PHYS_OFFSET");
107
108 mem_start = reg->base;
109 mem_end = reg->base + reg->size;
110 specified_mem_size = reg->size;
111 first = false;
112 } else {
113 /*
114 * memblock auto merges contiguous blocks, remove
115 * all blocks afterwards in one go (we can't remove
116 * blocks separately while iterating)
117 */
118 pr_notice("Ignoring RAM after %pa, memory at %pa ignored\n",
119 &mem_end, &reg->base);
120 memblock_remove(reg->base, 0 - reg->base);
121 break;
122 }
123 }
124
125 /*
126 * MPU has curious alignment requirements: Size must be power of 2, and
127 * region start must be aligned to the region size
128 */
129 if (phys_offset != 0)
130 pr_info("PHYS_OFFSET != 0 => MPU Region size constrained by alignment requirements\n");
131
132 /*
133 * Maximum aligned region might overflow phys_addr_t if phys_offset is
134 * 0. Hence we keep everything below 4G until we take the smaller of
135 * the aligned_region_size and rounded_mem_size, one of which is
136 * guaranteed to be smaller than the maximum physical address.
137 */
138 aligned_region_size = (phys_offset - 1) ^ (phys_offset);
139 /* Find the max power-of-two sized region that fits inside our bank */
140 rounded_mem_size = (1 << __fls(specified_mem_size)) - 1;
141
142 /* The actual region size is the smaller of the two */
143 aligned_region_size = aligned_region_size < rounded_mem_size
144 ? aligned_region_size + 1
145 : rounded_mem_size + 1;
146
147 if (aligned_region_size != specified_mem_size) {
148 pr_warn("Truncating memory from %pa to %pa (MPU region constraints)",
149 &specified_mem_size, &aligned_region_size);
150 memblock_remove(mem_start + aligned_region_size,
151 specified_mem_size - aligned_region_size);
152
153 mem_end = mem_start + aligned_region_size;
154 }
155
156 pr_debug("MPU Region from %pa size %pa (end %pa))\n",
157 &phys_offset, &aligned_region_size, &mem_end);
158
159}
160
161static int mpu_present(void)
162{
163 return ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7);
164}
165
166static int mpu_max_regions(void)
167{
168 /*
169 * We don't support a different number of I/D side regions so if we
170 * have separate instruction and data memory maps then return
171 * whichever side has a smaller number of supported regions.
172 */
173 u32 dregions, iregions, mpuir;
174 mpuir = read_cpuid(CPUID_MPUIR);
175
176 dregions = iregions = (mpuir & MPUIR_DREGION_SZMASK) >> MPUIR_DREGION;
177
178 /* Check for separate d-side and i-side memory maps */
179 if (mpuir & MPUIR_nU)
180 iregions = (mpuir & MPUIR_IREGION_SZMASK) >> MPUIR_IREGION;
181
182 /* Use the smallest of the two maxima */
183 return min(dregions, iregions);
184}
185
186static int mpu_iside_independent(void)
187{
188 /* MPUIR.nU specifies whether there is *not* a unified memory map */
189 return read_cpuid(CPUID_MPUIR) & MPUIR_nU;
190}
191
192static int mpu_min_region_order(void)
193{
194 u32 drbar_result, irbar_result;
195 /* We've kept a region free for this probing */
196 rgnr_write(MPU_PROBE_REGION);
197 isb();
198 /*
199 * As per ARM ARM, write 0xFFFFFFFC to DRBAR to find the minimum
200 * region order
201 */
202 drbar_write(0xFFFFFFFC);
203 drbar_result = irbar_result = drbar_read();
204 drbar_write(0x0);
205 /* If the MPU is non-unified, we use the larger of the two minima*/
206 if (mpu_iside_independent()) {
207 irbar_write(0xFFFFFFFC);
208 irbar_result = irbar_read();
209 irbar_write(0x0);
210 }
211 isb(); /* Ensure that MPU region operations have completed */
212 /* Return whichever result is larger */
213 return __ffs(max(drbar_result, irbar_result));
214}
215
216static int mpu_setup_region(unsigned int number, phys_addr_t start,
217 unsigned int size_order, unsigned int properties)
218{
219 u32 size_data;
220
221 /* We kept a region free for probing resolution of MPU regions*/
222 if (number > mpu_max_regions() || number == MPU_PROBE_REGION)
223 return -ENOENT;
224
225 if (size_order > 32)
226 return -ENOMEM;
227
228 if (size_order < mpu_min_region_order())
229 return -ENOMEM;
230
231 /* Writing N to bits 5:1 (RSR_SZ) specifies region size 2^N+1 */
232 size_data = ((size_order - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN;
233
234 dsb(); /* Ensure all previous data accesses occur with old mappings */
235 rgnr_write(number);
236 isb();
237 drbar_write(start);
238 dracr_write(properties);
239 isb(); /* Propagate properties before enabling region */
240 drsr_write(size_data);
241
242 /* Check for independent I-side registers */
243 if (mpu_iside_independent()) {
244 irbar_write(start);
245 iracr_write(properties);
246 isb();
247 irsr_write(size_data);
248 }
249 isb();
250
251 /* Store region info (we treat i/d side the same, so only store d) */
252 mpu_rgn_info.rgns[number].dracr = properties;
253 mpu_rgn_info.rgns[number].drbar = start;
254 mpu_rgn_info.rgns[number].drsr = size_data;
255 return 0;
256}
257
258/*
259* Set up default MPU regions, doing nothing if there is no MPU
260*/
261void __init mpu_setup(void)
262{
263 int region_err;
264 if (!mpu_present())
265 return;
266
267 region_err = mpu_setup_region(MPU_RAM_REGION, PHYS_OFFSET,
268 ilog2(memblock.memory.regions[0].size),
269 MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL);
270 if (region_err) {
271 panic("MPU region initialization failure! %d", region_err);
272 } else {
273 pr_info("Using ARMv7 PMSA Compliant MPU. "
274 "Region independence: %s, Max regions: %d\n",
275 mpu_iside_independent() ? "Yes" : "No",
276 mpu_max_regions());
277 }
278}
279#else
280static void adjust_lowmem_bounds_mpu(void) {}
281static void __init mpu_setup(void) {}
282#endif /* CONFIG_ARM_MPU */
283 31
284#ifdef CONFIG_CPU_CP15 32#ifdef CONFIG_CPU_CP15
285#ifdef CONFIG_CPU_HIGH_VECTOR 33#ifdef CONFIG_CPU_HIGH_VECTOR
diff --git a/arch/arm/mm/pmsa-v7.c b/arch/arm/mm/pmsa-v7.c
new file mode 100644
index 000000000000..cc987715457d
--- /dev/null
+++ b/arch/arm/mm/pmsa-v7.c
@@ -0,0 +1,262 @@
1/*
2 * Based on linux/arch/arm/mm/nommu.c
3 *
4 * ARM PMSAv7 supporting functions.
5 */
6
7#include <linux/memblock.h>
8
9#include <asm/cp15.h>
10#include <asm/cputype.h>
11#include <asm/mpu.h>
12
13#include "mm.h"
14
15/* Region number */
16static void rgnr_write(u32 v)
17{
18 asm("mcr p15, 0, %0, c6, c2, 0" : : "r" (v));
19}
20
21/* Data-side / unified region attributes */
22
23/* Region access control register */
24static void dracr_write(u32 v)
25{
26 asm("mcr p15, 0, %0, c6, c1, 4" : : "r" (v));
27}
28
29/* Region size register */
30static void drsr_write(u32 v)
31{
32 asm("mcr p15, 0, %0, c6, c1, 2" : : "r" (v));
33}
34
35/* Region base address register */
36static void drbar_write(u32 v)
37{
38 asm("mcr p15, 0, %0, c6, c1, 0" : : "r" (v));
39}
40
41static u32 drbar_read(void)
42{
43 u32 v;
44 asm("mrc p15, 0, %0, c6, c1, 0" : "=r" (v));
45 return v;
46}
47/* Optional instruction-side region attributes */
48
49/* I-side Region access control register */
50static void iracr_write(u32 v)
51{
52 asm("mcr p15, 0, %0, c6, c1, 5" : : "r" (v));
53}
54
55/* I-side Region size register */
56static void irsr_write(u32 v)
57{
58 asm("mcr p15, 0, %0, c6, c1, 3" : : "r" (v));
59}
60
61/* I-side Region base address register */
62static void irbar_write(u32 v)
63{
64 asm("mcr p15, 0, %0, c6, c1, 1" : : "r" (v));
65}
66
67static unsigned long irbar_read(void)
68{
69 unsigned long v;
70 asm("mrc p15, 0, %0, c6, c1, 1" : "=r" (v));
71 return v;
72}
73
74/* MPU initialisation functions */
75void __init adjust_lowmem_bounds_mpu(void)
76{
77 phys_addr_t phys_offset = PHYS_OFFSET;
78 phys_addr_t aligned_region_size, specified_mem_size, rounded_mem_size;
79 struct memblock_region *reg;
80 bool first = true;
81 phys_addr_t mem_start;
82 phys_addr_t mem_end;
83
84 for_each_memblock(memory, reg) {
85 if (first) {
86 /*
87 * Initially only use memory continuous from
88 * PHYS_OFFSET */
89 if (reg->base != phys_offset)
90 panic("First memory bank must be contiguous from PHYS_OFFSET");
91
92 mem_start = reg->base;
93 mem_end = reg->base + reg->size;
94 specified_mem_size = reg->size;
95 first = false;
96 } else {
97 /*
98 * memblock auto merges contiguous blocks, remove
99 * all blocks afterwards in one go (we can't remove
100 * blocks separately while iterating)
101 */
102 pr_notice("Ignoring RAM after %pa, memory at %pa ignored\n",
103 &mem_end, &reg->base);
104 memblock_remove(reg->base, 0 - reg->base);
105 break;
106 }
107 }
108
109 /*
110 * MPU has curious alignment requirements: Size must be power of 2, and
111 * region start must be aligned to the region size
112 */
113 if (phys_offset != 0)
114 pr_info("PHYS_OFFSET != 0 => MPU Region size constrained by alignment requirements\n");
115
116 /*
117 * Maximum aligned region might overflow phys_addr_t if phys_offset is
118 * 0. Hence we keep everything below 4G until we take the smaller of
119 * the aligned_region_size and rounded_mem_size, one of which is
120 * guaranteed to be smaller than the maximum physical address.
121 */
122 aligned_region_size = (phys_offset - 1) ^ (phys_offset);
123 /* Find the max power-of-two sized region that fits inside our bank */
124 rounded_mem_size = (1 << __fls(specified_mem_size)) - 1;
125
126 /* The actual region size is the smaller of the two */
127 aligned_region_size = aligned_region_size < rounded_mem_size
128 ? aligned_region_size + 1
129 : rounded_mem_size + 1;
130
131 if (aligned_region_size != specified_mem_size) {
132 pr_warn("Truncating memory from %pa to %pa (MPU region constraints)",
133 &specified_mem_size, &aligned_region_size);
134 memblock_remove(mem_start + aligned_region_size,
135 specified_mem_size - aligned_region_size);
136
137 mem_end = mem_start + aligned_region_size;
138 }
139
140 pr_debug("MPU Region from %pa size %pa (end %pa))\n",
141 &phys_offset, &aligned_region_size, &mem_end);
142
143}
144
145static int mpu_present(void)
146{
147 return ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7);
148}
149
150static int mpu_max_regions(void)
151{
152 /*
153 * We don't support a different number of I/D side regions so if we
154 * have separate instruction and data memory maps then return
155 * whichever side has a smaller number of supported regions.
156 */
157 u32 dregions, iregions, mpuir;
158 mpuir = read_cpuid(CPUID_MPUIR);
159
160 dregions = iregions = (mpuir & MPUIR_DREGION_SZMASK) >> MPUIR_DREGION;
161
162 /* Check for separate d-side and i-side memory maps */
163 if (mpuir & MPUIR_nU)
164 iregions = (mpuir & MPUIR_IREGION_SZMASK) >> MPUIR_IREGION;
165
166 /* Use the smallest of the two maxima */
167 return min(dregions, iregions);
168}
169
170static int mpu_iside_independent(void)
171{
172 /* MPUIR.nU specifies whether there is *not* a unified memory map */
173 return read_cpuid(CPUID_MPUIR) & MPUIR_nU;
174}
175
176static int mpu_min_region_order(void)
177{
178 u32 drbar_result, irbar_result;
179 /* We've kept a region free for this probing */
180 rgnr_write(MPU_PROBE_REGION);
181 isb();
182 /*
183 * As per ARM ARM, write 0xFFFFFFFC to DRBAR to find the minimum
184 * region order
185 */
186 drbar_write(0xFFFFFFFC);
187 drbar_result = irbar_result = drbar_read();
188 drbar_write(0x0);
189 /* If the MPU is non-unified, we use the larger of the two minima*/
190 if (mpu_iside_independent()) {
191 irbar_write(0xFFFFFFFC);
192 irbar_result = irbar_read();
193 irbar_write(0x0);
194 }
195 isb(); /* Ensure that MPU region operations have completed */
196 /* Return whichever result is larger */
197 return __ffs(max(drbar_result, irbar_result));
198}
199
200static int mpu_setup_region(unsigned int number, phys_addr_t start,
201 unsigned int size_order, unsigned int properties)
202{
203 u32 size_data;
204
205 /* We kept a region free for probing resolution of MPU regions*/
206 if (number > mpu_max_regions() || number == MPU_PROBE_REGION)
207 return -ENOENT;
208
209 if (size_order > 32)
210 return -ENOMEM;
211
212 if (size_order < mpu_min_region_order())
213 return -ENOMEM;
214
215 /* Writing N to bits 5:1 (RSR_SZ) specifies region size 2^N+1 */
216 size_data = ((size_order - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN;
217
218 dsb(); /* Ensure all previous data accesses occur with old mappings */
219 rgnr_write(number);
220 isb();
221 drbar_write(start);
222 dracr_write(properties);
223 isb(); /* Propagate properties before enabling region */
224 drsr_write(size_data);
225
226 /* Check for independent I-side registers */
227 if (mpu_iside_independent()) {
228 irbar_write(start);
229 iracr_write(properties);
230 isb();
231 irsr_write(size_data);
232 }
233 isb();
234
235 /* Store region info (we treat i/d side the same, so only store d) */
236 mpu_rgn_info.rgns[number].dracr = properties;
237 mpu_rgn_info.rgns[number].drbar = start;
238 mpu_rgn_info.rgns[number].drsr = size_data;
239 return 0;
240}
241
242/*
243* Set up default MPU regions, doing nothing if there is no MPU
244*/
245void __init mpu_setup(void)
246{
247 int region_err;
248 if (!mpu_present())
249 return;
250
251 region_err = mpu_setup_region(MPU_RAM_REGION, PHYS_OFFSET,
252 ilog2(memblock.memory.regions[0].size),
253 MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL);
254 if (region_err) {
255 panic("MPU region initialization failure! %d", region_err);
256 } else {
257 pr_info("Using ARMv7 PMSA Compliant MPU. "
258 "Region independence: %s, Max regions: %d\n",
259 mpu_iside_independent() ? "Yes" : "No",
260 mpu_max_regions());
261 }
262}