aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Murzin <vladimir.murzin@arm.com>2017-10-16 07:59:15 -0400
committerRussell King <rmk+kernel@armlinux.org.uk>2017-10-23 11:59:23 -0400
commit5c9d9a1b3a540779ba3f2d5e81150b2d92dcb74a (patch)
treea2f5677540c7556b3bba3293055f11291bc3e96f
parent9fcb01a9f54c28062a73a545c29137a4cc104c72 (diff)
ARM: 8712/1: NOMMU: Use more MPU regions to cover memory
PMSAv7 defines curious alignment requirements to the regions: - size must be power of 2, and - region start must be aligned to the region size Because of that we currently adjust lowmem bounds plus we assign only one MPU region to cover memory all these lead to significant amount of memory could be wasted. As an example, consider 64Mb of memory at 0x70000000 - it fits alignment requirements nicely; now, imagine that 2Mb of memory is reserved for coherent DMA allocation, so now Linux is expected to see 62Mb of memory... and here annoying thing happens - memory gets truncated to 32Mb (we've lost 30Mb!), i.e. MPU layout looks like: 0: base 0x70000000, size 0x2000000 This patch tries to allocate as much as possible MPU slots to minimise amount of truncated memory. Moreover, with this patch MPU subregions starting to get used. MPU subregions allow us reduce the number of MPU slots used. For example given above, MPU layout looks like: 0: base 0x70000000, size 0x2000000 1: base 0x72000000, size 0x1000000 2: base 0x73000000, size 0x1000000, disable subreg 7 (0x73e00000 - 0x73ffffff) Where without subregions we'd get: 0: base 0x70000000, size 0x2000000 1: base 0x72000000, size 0x1000000 2: base 0x73000000, size 0x800000 3: base 0x73800000, size 0x400000 4: base 0x73c00000, size 0x200000 To achieve better layout we fist try to cover specified memory as is (maybe with help of subregions) and if we failed, we truncate memory to fit alignment requirements (so it occupies one MPU slot) and perform one more attempt with the reminder, and so on till we either cover all memory or run out of MPU slots. 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.h5
-rw-r--r--arch/arm/mm/pmsa-v7.c190
2 files changed, 149 insertions, 46 deletions
diff --git a/arch/arm/include/asm/mpu.h b/arch/arm/include/asm/mpu.h
index 403462e1df9d..5db37a6ef3cb 100644
--- a/arch/arm/include/asm/mpu.h
+++ b/arch/arm/include/asm/mpu.h
@@ -15,6 +15,11 @@
15/* MPU D/I Size Register fields */ 15/* MPU D/I Size Register fields */
16#define MPU_RSR_SZ 1 16#define MPU_RSR_SZ 1
17#define MPU_RSR_EN 0 17#define MPU_RSR_EN 0
18#define MPU_RSR_SD 8
19
20/* Number of subregions (SD) */
21#define MPU_NR_SUBREGS 8
22#define MPU_MIN_SUBREG_SIZE 256
18 23
19/* The D/I RSR value for an enabled region spanning the whole of memory */ 24/* The D/I RSR value for an enabled region spanning the whole of memory */
20#define MPU_RSR_ALL_MEM 63 25#define MPU_RSR_ALL_MEM 63
diff --git a/arch/arm/mm/pmsa-v7.c b/arch/arm/mm/pmsa-v7.c
index 06e2a853cab9..ef204634a16e 100644
--- a/arch/arm/mm/pmsa-v7.c
+++ b/arch/arm/mm/pmsa-v7.c
@@ -4,6 +4,7 @@
4 * ARM PMSAv7 supporting functions. 4 * ARM PMSAv7 supporting functions.
5 */ 5 */
6 6
7#include <linux/bitops.h>
7#include <linux/memblock.h> 8#include <linux/memblock.h>
8 9
9#include <asm/cp15.h> 10#include <asm/cp15.h>
@@ -12,9 +13,20 @@
12 13
13#include "mm.h" 14#include "mm.h"
14 15
16struct region {
17 phys_addr_t base;
18 phys_addr_t size;
19 unsigned long subreg;
20};
21
22static struct region __initdata mem[MPU_MAX_REGIONS];
23
15static unsigned int __initdata mpu_min_region_order; 24static unsigned int __initdata mpu_min_region_order;
16static unsigned int __initdata mpu_max_regions; 25static unsigned int __initdata mpu_max_regions;
17 26
27static int __init __mpu_min_region_order(void);
28static int __init __mpu_max_regions(void);
29
18#ifndef CONFIG_CPU_V7M 30#ifndef CONFIG_CPU_V7M
19 31
20#define DRBAR __ACCESS_CP15(c6, 0, c1, 0) 32#define DRBAR __ACCESS_CP15(c6, 0, c1, 0)
@@ -130,19 +142,120 @@ static int __init mpu_present(void)
130 return ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7); 142 return ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7);
131} 143}
132 144
145static bool __init try_split_region(phys_addr_t base, phys_addr_t size, struct region *region)
146{
147 unsigned long subreg, bslots, sslots;
148 phys_addr_t abase = base & ~(size - 1);
149 phys_addr_t asize = base + size - abase;
150 phys_addr_t p2size = 1 << __fls(asize);
151 phys_addr_t bdiff, sdiff;
152
153 if (p2size != asize)
154 p2size *= 2;
155
156 bdiff = base - abase;
157 sdiff = p2size - asize;
158 subreg = p2size / MPU_NR_SUBREGS;
159
160 if ((bdiff % subreg) || (sdiff % subreg))
161 return false;
162
163 bslots = bdiff / subreg;
164 sslots = sdiff / subreg;
165
166 if (bslots || sslots) {
167 int i;
168
169 if (subreg < MPU_MIN_SUBREG_SIZE)
170 return false;
171
172 if (bslots + sslots > MPU_NR_SUBREGS)
173 return false;
174
175 for (i = 0; i < bslots; i++)
176 _set_bit(i, &region->subreg);
177
178 for (i = 1; i <= sslots; i++)
179 _set_bit(MPU_NR_SUBREGS - i, &region->subreg);
180 }
181
182 region->base = abase;
183 region->size = p2size;
184
185 return true;
186}
187
188static int __init allocate_region(phys_addr_t base, phys_addr_t size,
189 unsigned int limit, struct region *regions)
190{
191 int count = 0;
192 phys_addr_t diff = size;
193 int attempts = MPU_MAX_REGIONS;
194
195 while (diff) {
196 /* Try cover region as is (maybe with help of subregions) */
197 if (try_split_region(base, size, &regions[count])) {
198 count++;
199 base += size;
200 diff -= size;
201 size = diff;
202 } else {
203 /*
204 * Maximum aligned region might overflow phys_addr_t
205 * if "base" is 0. Hence we keep everything below 4G
206 * until we take the smaller of the aligned region
207 * size ("asize") and rounded region size ("p2size"),
208 * one of which is guaranteed to be smaller than the
209 * maximum physical address.
210 */
211 phys_addr_t asize = (base - 1) ^ base;
212 phys_addr_t p2size = (1 << __fls(diff)) - 1;
213
214 size = asize < p2size ? asize + 1 : p2size + 1;
215 }
216
217 if (count > limit)
218 break;
219
220 if (!attempts)
221 break;
222
223 attempts--;
224 }
225
226 return count;
227}
228
133/* MPU initialisation functions */ 229/* MPU initialisation functions */
134void __init adjust_lowmem_bounds_mpu(void) 230void __init adjust_lowmem_bounds_mpu(void)
135{ 231{
136 phys_addr_t phys_offset = PHYS_OFFSET; 232 phys_addr_t phys_offset = PHYS_OFFSET;
137 phys_addr_t aligned_region_size, specified_mem_size, rounded_mem_size; 233 phys_addr_t specified_mem_size, total_mem_size = 0;
138 struct memblock_region *reg; 234 struct memblock_region *reg;
139 bool first = true; 235 bool first = true;
140 phys_addr_t mem_start; 236 phys_addr_t mem_start;
141 phys_addr_t mem_end; 237 phys_addr_t mem_end;
238 unsigned int mem_max_regions;
239 int num, i;
142 240
143 if (!mpu_present()) 241 if (!mpu_present())
144 return; 242 return;
145 243
244 /* Free-up MPU_PROBE_REGION */
245 mpu_min_region_order = __mpu_min_region_order();
246
247 /* How many regions are supported */
248 mpu_max_regions = __mpu_max_regions();
249
250 mem_max_regions = min((unsigned int)MPU_MAX_REGIONS, mpu_max_regions);
251
252 /* We need to keep one slot for background region */
253 mem_max_regions--;
254
255#ifndef CONFIG_CPU_V7M
256 /* ... and one for vectors */
257 mem_max_regions--;
258#endif
146 for_each_memblock(memory, reg) { 259 for_each_memblock(memory, reg) {
147 if (first) { 260 if (first) {
148 /* 261 /*
@@ -168,40 +281,23 @@ void __init adjust_lowmem_bounds_mpu(void)
168 } 281 }
169 } 282 }
170 283
171 /* 284 num = allocate_region(mem_start, specified_mem_size, mem_max_regions, mem);
172 * MPU has curious alignment requirements: Size must be power of 2, and
173 * region start must be aligned to the region size
174 */
175 if (phys_offset != 0)
176 pr_info("PHYS_OFFSET != 0 => MPU Region size constrained by alignment requirements\n");
177
178 /*
179 * Maximum aligned region might overflow phys_addr_t if phys_offset is
180 * 0. Hence we keep everything below 4G until we take the smaller of
181 * the aligned_region_size and rounded_mem_size, one of which is
182 * guaranteed to be smaller than the maximum physical address.
183 */
184 aligned_region_size = (phys_offset - 1) ^ (phys_offset);
185 /* Find the max power-of-two sized region that fits inside our bank */
186 rounded_mem_size = (1 << __fls(specified_mem_size)) - 1;
187 285
188 /* The actual region size is the smaller of the two */ 286 for (i = 0; i < num; i++) {
189 aligned_region_size = aligned_region_size < rounded_mem_size 287 unsigned long subreg = mem[i].size / MPU_NR_SUBREGS;
190 ? aligned_region_size + 1
191 : rounded_mem_size + 1;
192 288
193 if (aligned_region_size != specified_mem_size) { 289 total_mem_size += mem[i].size - subreg * hweight_long(mem[i].subreg);
194 pr_warn("Truncating memory from %pa to %pa (MPU region constraints)",
195 &specified_mem_size, &aligned_region_size);
196 memblock_remove(mem_start + aligned_region_size,
197 specified_mem_size - aligned_region_size);
198 290
199 mem_end = mem_start + aligned_region_size; 291 pr_debug("MPU: base %pa size %pa disable subregions: %*pbl\n",
292 &mem[i].base, &mem[i].size, MPU_NR_SUBREGS, &mem[i].subreg);
200 } 293 }
201 294
202 pr_debug("MPU Region from %pa size %pa (end %pa))\n", 295 if (total_mem_size != specified_mem_size) {
203 &phys_offset, &aligned_region_size, &mem_end); 296 pr_warn("Truncating memory from %pa to %pa (MPU region constraints)",
204 297 &specified_mem_size, &total_mem_size);
298 memblock_remove(mem_start + total_mem_size,
299 specified_mem_size - total_mem_size);
300 }
205} 301}
206 302
207static int __init __mpu_max_regions(void) 303static int __init __mpu_max_regions(void)
@@ -258,7 +354,8 @@ static int __init __mpu_min_region_order(void)
258} 354}
259 355
260static int __init mpu_setup_region(unsigned int number, phys_addr_t start, 356static int __init mpu_setup_region(unsigned int number, phys_addr_t start,
261 unsigned int size_order, unsigned int properties) 357 unsigned int size_order, unsigned int properties,
358 unsigned int subregions)
262{ 359{
263 u32 size_data; 360 u32 size_data;
264 361
@@ -275,6 +372,7 @@ static int __init mpu_setup_region(unsigned int number, phys_addr_t start,
275 372
276 /* Writing N to bits 5:1 (RSR_SZ) specifies region size 2^N+1 */ 373 /* Writing N to bits 5:1 (RSR_SZ) specifies region size 2^N+1 */
277 size_data = ((size_order - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN; 374 size_data = ((size_order - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN;
375 size_data |= subregions << MPU_RSR_SD;
278 376
279 dsb(); /* Ensure all previous data accesses occur with old mappings */ 377 dsb(); /* Ensure all previous data accesses occur with old mappings */
280 rgnr_write(number); 378 rgnr_write(number);
@@ -308,33 +406,33 @@ static int __init mpu_setup_region(unsigned int number, phys_addr_t start,
308*/ 406*/
309void __init mpu_setup(void) 407void __init mpu_setup(void)
310{ 408{
311 int region = 0, err = 0; 409 int i, region = 0, err = 0;
312 410
313 if (!mpu_present()) 411 if (!mpu_present())
314 return; 412 return;
315 413
316 /* Free-up MPU_PROBE_REGION */ 414 /* Setup MPU (order is important) */
317 mpu_min_region_order = __mpu_min_region_order();
318
319 /* How many regions are supported */
320 mpu_max_regions = __mpu_max_regions();
321
322 /* Now setup MPU (order is important) */
323 415
324 /* Background */ 416 /* Background */
325 err |= mpu_setup_region(region++, 0, 32, 417 err |= mpu_setup_region(region++, 0, 32,
326 MPU_ACR_XN | MPU_RGN_STRONGLY_ORDERED | MPU_AP_PL1RW_PL0NA); 418 MPU_ACR_XN | MPU_RGN_STRONGLY_ORDERED | MPU_AP_PL1RW_PL0NA,
419 0);
327 420
328 /* RAM */ 421 /* RAM */
329 err |= mpu_setup_region(region++, PHYS_OFFSET, 422 for (i = 0; i < ARRAY_SIZE(mem); i++) {
330 ilog2(memblock.memory.regions[0].size), 423 if (!mem[i].size)
331 MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL); 424 continue;
425
426 err |= mpu_setup_region(region++, mem[i].base, ilog2(mem[i].size),
427 MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL,
428 mem[i].subreg);
429 }
332 430
333 /* Vectors */ 431 /* Vectors */
334#ifndef CONFIG_CPU_V7M 432#ifndef CONFIG_CPU_V7M
335 err |= mpu_setup_region(region++, vectors_base, 433 err |= mpu_setup_region(region++, vectors_base, ilog2(2 * PAGE_SIZE),
336 ilog2(2 * PAGE_SIZE), 434 MPU_AP_PL1RW_PL0NA | MPU_RGN_NORMAL,
337 MPU_AP_PL1RW_PL0NA | MPU_RGN_NORMAL); 435 0);
338#endif 436#endif
339 if (err) { 437 if (err) {
340 panic("MPU region initialization failure! %d", err); 438 panic("MPU region initialization failure! %d", err);