Skip to content

Commit 6e121df

Browse files
linuswRussell King (Oracle)
authored andcommitted
ARM: 9090/1: Map the lowmem and kernel separately
Using our knowledge of where the physical kernel sections start and end we can split mapping of lowmem and kernel apart. This is helpful when you want to place the kernel independently from lowmem and not be limited to putting it into lowmem only, but also into places such as the VMALLOC area. We extensively rewrite the lowmem mapping code to account for all cases where the kernel image overlaps with the lowmem in different ways. This is helpful to handle situations which occur when the kernel is loaded in different places and makes it possible to place the kernel in a more random manner which is done with e.g. KASLR. We sprinkle some comments with illustrations and pr_debug() over it so it is also very evident to readers what is happening. We now use the kernel_sec_start and kernel_sec_end instead of relying on __pa() (phys_to_virt) to provide this. This is helpful if we want to resolve physical-to-virtual and virtual-to-physical mappings at runtime rather than compiletime, especially if we are not using patch phys to virt. Signed-off-by: Linus Walleij <[email protected]> Signed-off-by: Russell King <[email protected]>
1 parent a91da54 commit 6e121df

File tree

1 file changed

+111
-33
lines changed

1 file changed

+111
-33
lines changed

arch/arm/mm/mmu.c

Lines changed: 111 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,64 +1459,133 @@ static void __init kmap_init(void)
14591459

14601460
static void __init map_lowmem(void)
14611461
{
1462-
phys_addr_t kernel_x_start = round_down(__pa(KERNEL_START), SECTION_SIZE);
1463-
phys_addr_t kernel_x_end = round_up(__pa(__init_end), SECTION_SIZE);
14641462
phys_addr_t start, end;
14651463
u64 i;
14661464

14671465
/* Map all the lowmem memory banks. */
14681466
for_each_mem_range(i, &start, &end) {
14691467
struct map_desc map;
14701468

1469+
pr_debug("map lowmem start: 0x%08llx, end: 0x%08llx\n",
1470+
(long long)start, (long long)end);
14711471
if (end > arm_lowmem_limit)
14721472
end = arm_lowmem_limit;
14731473
if (start >= end)
14741474
break;
14751475

1476-
if (end < kernel_x_start) {
1477-
map.pfn = __phys_to_pfn(start);
1478-
map.virtual = __phys_to_virt(start);
1479-
map.length = end - start;
1480-
map.type = MT_MEMORY_RWX;
1476+
/*
1477+
* If our kernel image is in the VMALLOC area we need to remove
1478+
* the kernel physical memory from lowmem since the kernel will
1479+
* be mapped separately.
1480+
*
1481+
* The kernel will typically be at the very start of lowmem,
1482+
* but any placement relative to memory ranges is possible.
1483+
*
1484+
* If the memblock contains the kernel, we have to chisel out
1485+
* the kernel memory from it and map each part separately. We
1486+
* get 6 different theoretical cases:
1487+
*
1488+
* +--------+ +--------+
1489+
* +-- start --+ +--------+ | Kernel | | Kernel |
1490+
* | | | Kernel | | case 2 | | case 5 |
1491+
* | | | case 1 | +--------+ | | +--------+
1492+
* | Memory | +--------+ | | | Kernel |
1493+
* | range | +--------+ | | | case 6 |
1494+
* | | | Kernel | +--------+ | | +--------+
1495+
* | | | case 3 | | Kernel | | |
1496+
* +-- end ----+ +--------+ | case 4 | | |
1497+
* +--------+ +--------+
1498+
*/
14811499

1482-
create_mapping(&map);
1483-
} else if (start >= kernel_x_end) {
1484-
map.pfn = __phys_to_pfn(start);
1485-
map.virtual = __phys_to_virt(start);
1486-
map.length = end - start;
1487-
map.type = MT_MEMORY_RW;
1500+
/* Case 5: kernel covers range, don't map anything, should be rare */
1501+
if ((start > kernel_sec_start) && (end < kernel_sec_end))
1502+
break;
14881503

1489-
create_mapping(&map);
1490-
} else {
1491-
/* This better cover the entire kernel */
1492-
if (start < kernel_x_start) {
1504+
/* Cases where the kernel is starting inside the range */
1505+
if ((kernel_sec_start >= start) && (kernel_sec_start <= end)) {
1506+
/* Case 6: kernel is embedded in the range, we need two mappings */
1507+
if ((start < kernel_sec_start) && (end > kernel_sec_end)) {
1508+
/* Map memory below the kernel */
14931509
map.pfn = __phys_to_pfn(start);
14941510
map.virtual = __phys_to_virt(start);
1495-
map.length = kernel_x_start - start;
1511+
map.length = kernel_sec_start - start;
14961512
map.type = MT_MEMORY_RW;
1497-
14981513
create_mapping(&map);
1499-
}
1500-
1501-
map.pfn = __phys_to_pfn(kernel_x_start);
1502-
map.virtual = __phys_to_virt(kernel_x_start);
1503-
map.length = kernel_x_end - kernel_x_start;
1504-
map.type = MT_MEMORY_RWX;
1505-
1506-
create_mapping(&map);
1507-
1508-
if (kernel_x_end < end) {
1509-
map.pfn = __phys_to_pfn(kernel_x_end);
1510-
map.virtual = __phys_to_virt(kernel_x_end);
1511-
map.length = end - kernel_x_end;
1514+
/* Map memory above the kernel */
1515+
map.pfn = __phys_to_pfn(kernel_sec_end);
1516+
map.virtual = __phys_to_virt(kernel_sec_end);
1517+
map.length = end - kernel_sec_end;
15121518
map.type = MT_MEMORY_RW;
1513-
15141519
create_mapping(&map);
1520+
break;
15151521
}
1522+
/* Case 1: kernel and range start at the same address, should be common */
1523+
if (kernel_sec_start == start)
1524+
start = kernel_sec_end;
1525+
/* Case 3: kernel and range end at the same address, should be rare */
1526+
if (kernel_sec_end == end)
1527+
end = kernel_sec_start;
1528+
} else if ((kernel_sec_start < start) && (kernel_sec_end > start) && (kernel_sec_end < end)) {
1529+
/* Case 2: kernel ends inside range, starts below it */
1530+
start = kernel_sec_end;
1531+
} else if ((kernel_sec_start > start) && (kernel_sec_start < end) && (kernel_sec_end > end)) {
1532+
/* Case 4: kernel starts inside range, ends above it */
1533+
end = kernel_sec_start;
15161534
}
1535+
map.pfn = __phys_to_pfn(start);
1536+
map.virtual = __phys_to_virt(start);
1537+
map.length = end - start;
1538+
map.type = MT_MEMORY_RW;
1539+
create_mapping(&map);
15171540
}
15181541
}
15191542

1543+
static void __init map_kernel(void)
1544+
{
1545+
/*
1546+
* We use the well known kernel section start and end and split the area in the
1547+
* middle like this:
1548+
* . .
1549+
* | RW memory |
1550+
* +----------------+ kernel_x_start
1551+
* | Executable |
1552+
* | kernel memory |
1553+
* +----------------+ kernel_x_end / kernel_nx_start
1554+
* | Non-executable |
1555+
* | kernel memory |
1556+
* +----------------+ kernel_nx_end
1557+
* | RW memory |
1558+
* . .
1559+
*
1560+
* Notice that we are dealing with section sized mappings here so all of this
1561+
* will be bumped to the closest section boundary. This means that some of the
1562+
* non-executable part of the kernel memory is actually mapped as executable.
1563+
* This will only persist until we turn on proper memory management later on
1564+
* and we remap the whole kernel with page granularity.
1565+
*/
1566+
phys_addr_t kernel_x_start = kernel_sec_start;
1567+
phys_addr_t kernel_x_end = round_up(__pa(__init_end), SECTION_SIZE);
1568+
phys_addr_t kernel_nx_start = kernel_x_end;
1569+
phys_addr_t kernel_nx_end = kernel_sec_end;
1570+
struct map_desc map;
1571+
1572+
map.pfn = __phys_to_pfn(kernel_x_start);
1573+
map.virtual = __phys_to_virt(kernel_x_start);
1574+
map.length = kernel_x_end - kernel_x_start;
1575+
map.type = MT_MEMORY_RWX;
1576+
create_mapping(&map);
1577+
1578+
/* If the nx part is small it may end up covered by the tail of the RWX section */
1579+
if (kernel_x_end == kernel_nx_end)
1580+
return;
1581+
1582+
map.pfn = __phys_to_pfn(kernel_nx_start);
1583+
map.virtual = __phys_to_virt(kernel_nx_start);
1584+
map.length = kernel_nx_end - kernel_nx_start;
1585+
map.type = MT_MEMORY_RW;
1586+
create_mapping(&map);
1587+
}
1588+
15201589
#ifdef CONFIG_ARM_PV_FIXUP
15211590
typedef void pgtables_remap(long long offset, unsigned long pgd);
15221591
pgtables_remap lpae_pgtables_remap_asm;
@@ -1647,9 +1716,18 @@ void __init paging_init(const struct machine_desc *mdesc)
16471716
{
16481717
void *zero_page;
16491718

1719+
pr_debug("physical kernel sections: 0x%08x-0x%08x\n",
1720+
kernel_sec_start, kernel_sec_end);
1721+
16501722
prepare_page_table();
16511723
map_lowmem();
16521724
memblock_set_current_limit(arm_lowmem_limit);
1725+
pr_debug("lowmem limit is %08llx\n", (long long)arm_lowmem_limit);
1726+
/*
1727+
* After this point early_alloc(), i.e. the memblock allocator, can
1728+
* be used
1729+
*/
1730+
map_kernel();
16531731
dma_contiguous_remap();
16541732
early_fixmap_shutdown();
16551733
devicemaps_init(mdesc);

0 commit comments

Comments
 (0)