Skip to content

Commit 5c9d9a1

Browse files
murzinvRussell King
authored andcommitted
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 <[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 9fcb01a commit 5c9d9a1

File tree

2 files changed

+149
-46
lines changed

2 files changed

+149
-46
lines changed

arch/arm/include/asm/mpu.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
/* MPU D/I Size Register fields */
1616
#define MPU_RSR_SZ 1
1717
#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
1823

1924
/* The D/I RSR value for an enabled region spanning the whole of memory */
2025
#define MPU_RSR_ALL_MEM 63

arch/arm/mm/pmsa-v7.c

Lines changed: 144 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* ARM PMSAv7 supporting functions.
55
*/
66

7+
#include <linux/bitops.h>
78
#include <linux/memblock.h>
89

910
#include <asm/cp15.h>
@@ -12,9 +13,20 @@
1213

1314
#include "mm.h"
1415

16+
struct region {
17+
phys_addr_t base;
18+
phys_addr_t size;
19+
unsigned long subreg;
20+
};
21+
22+
static struct region __initdata mem[MPU_MAX_REGIONS];
23+
1524
static unsigned int __initdata mpu_min_region_order;
1625
static unsigned int __initdata mpu_max_regions;
1726

27+
static int __init __mpu_min_region_order(void);
28+
static int __init __mpu_max_regions(void);
29+
1830
#ifndef CONFIG_CPU_V7M
1931

2032
#define DRBAR __ACCESS_CP15(c6, 0, c1, 0)
@@ -130,19 +142,120 @@ static int __init mpu_present(void)
130142
return ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7);
131143
}
132144

145+
static 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+
188+
static 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+
133229
/* MPU initialisation functions */
134230
void __init adjust_lowmem_bounds_mpu(void)
135231
{
136232
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;
138234
struct memblock_region *reg;
139235
bool first = true;
140236
phys_addr_t mem_start;
141237
phys_addr_t mem_end;
238+
unsigned int mem_max_regions;
239+
int num, i;
142240

143241
if (!mpu_present())
144242
return;
145243

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
146259
for_each_memblock(memory, reg) {
147260
if (first) {
148261
/*
@@ -168,40 +281,23 @@ void __init adjust_lowmem_bounds_mpu(void)
168281
}
169282
}
170283

171-
/*
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;
284+
num = allocate_region(mem_start, specified_mem_size, mem_max_regions, mem);
187285

188-
/* The actual region size is the smaller of the two */
189-
aligned_region_size = aligned_region_size < rounded_mem_size
190-
? aligned_region_size + 1
191-
: rounded_mem_size + 1;
286+
for (i = 0; i < num; i++) {
287+
unsigned long subreg = mem[i].size / MPU_NR_SUBREGS;
192288

193-
if (aligned_region_size != specified_mem_size) {
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);
289+
total_mem_size += mem[i].size - subreg * hweight_long(mem[i].subreg);
198290

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);
200293
}
201294

202-
pr_debug("MPU Region from %pa size %pa (end %pa))\n",
203-
&phys_offset, &aligned_region_size, &mem_end);
204-
295+
if (total_mem_size != specified_mem_size) {
296+
pr_warn("Truncating memory from %pa to %pa (MPU region constraints)",
297+
&specified_mem_size, &total_mem_size);
298+
memblock_remove(mem_start + total_mem_size,
299+
specified_mem_size - total_mem_size);
300+
}
205301
}
206302

207303
static int __init __mpu_max_regions(void)
@@ -258,7 +354,8 @@ static int __init __mpu_min_region_order(void)
258354
}
259355

260356
static 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)
262359
{
263360
u32 size_data;
264361

@@ -275,6 +372,7 @@ static int __init mpu_setup_region(unsigned int number, phys_addr_t start,
275372

276373
/* Writing N to bits 5:1 (RSR_SZ) specifies region size 2^N+1 */
277374
size_data = ((size_order - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN;
375+
size_data |= subregions << MPU_RSR_SD;
278376

279377
dsb(); /* Ensure all previous data accesses occur with old mappings */
280378
rgnr_write(number);
@@ -308,33 +406,33 @@ static int __init mpu_setup_region(unsigned int number, phys_addr_t start,
308406
*/
309407
void __init mpu_setup(void)
310408
{
311-
int region = 0, err = 0;
409+
int i, region = 0, err = 0;
312410

313411
if (!mpu_present())
314412
return;
315413

316-
/* Free-up MPU_PROBE_REGION */
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) */
414+
/* Setup MPU (order is important) */
323415

324416
/* Background */
325417
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);
327420

328421
/* RAM */
329-
err |= mpu_setup_region(region++, PHYS_OFFSET,
330-
ilog2(memblock.memory.regions[0].size),
331-
MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL);
422+
for (i = 0; i < ARRAY_SIZE(mem); i++) {
423+
if (!mem[i].size)
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+
}
332430

333431
/* Vectors */
334432
#ifndef CONFIG_CPU_V7M
335-
err |= mpu_setup_region(region++, vectors_base,
336-
ilog2(2 * PAGE_SIZE),
337-
MPU_AP_PL1RW_PL0NA | MPU_RGN_NORMAL);
433+
err |= mpu_setup_region(region++, vectors_base, ilog2(2 * PAGE_SIZE),
434+
MPU_AP_PL1RW_PL0NA | MPU_RGN_NORMAL,
435+
0);
338436
#endif
339437
if (err) {
340438
panic("MPU region initialization failure! %d", err);

0 commit comments

Comments
 (0)