aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2015-04-04 11:58:38 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2015-06-01 18:46:33 -0400
commitd8dc7fbd53eeb329a1dda5a19df7058b9c1c413e (patch)
tree9350da1b44869917801c2445f1faad7aecf23986
parentc0b759d87eab301af0380f5459057656178e78cf (diff)
ARM: re-implement physical address space switching
Re-implement the physical address space switching to be architecturally compliant. This involves flushing the caches, disabling the MMU, and only then updating the page tables. Once that is complete, the system can be brought back up again. Since we disable the MMU, we need to do the update in assembly code. Luckily, the entries which need updating are fairly trivial, and are all setup by the early assembly code. We can merely adjust each entry by the delta required. Not only does this fix the code to be architecturally compliant, but it fixes a couple of bugs too: 1. The original code would only ever update the first L2 entry covering a fraction of the kernel; the remainder were left untouched. 2. The L2 entries covering the DTB blob were likewise untouched. This solution fixes up all entries. Tested-by: Murali Karicheri <m-karicheri2@ti.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/mm/Kconfig4
-rw-r--r--arch/arm/mm/Makefile1
-rw-r--r--arch/arm/mm/mmu.c124
-rw-r--r--arch/arm/mm/pv-fixup-asm.S88
4 files changed, 133 insertions, 84 deletions
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index b4f92b9a13ac..4dc661e2d3a6 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -624,6 +624,10 @@ config ARM_LPAE
624 624
625 If unsure, say N. 625 If unsure, say N.
626 626
627config ARM_PV_FIXUP
628 def_bool y
629 depends on ARM_LPAE && ARM_PATCH_PHYS_VIRT && ARCH_KEYSTONE
630
627config ARCH_PHYS_ADDR_T_64BIT 631config ARCH_PHYS_ADDR_T_64BIT
628 def_bool ARM_LPAE 632 def_bool ARM_LPAE
629 633
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index d3afdf9eb65a..4cc1ec9f6bb0 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MODULES) += proc-syms.o
18obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o 18obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o
19obj-$(CONFIG_HIGHMEM) += highmem.o 19obj-$(CONFIG_HIGHMEM) += highmem.o
20obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o 20obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
21obj-$(CONFIG_ARM_PV_FIXUP) += pv-fixup-asm.o
21 22
22obj-$(CONFIG_CPU_ABRT_NOMMU) += abort-nommu.o 23obj-$(CONFIG_CPU_ABRT_NOMMU) += abort-nommu.o
23obj-$(CONFIG_CPU_ABRT_EV4) += abort-ev4.o 24obj-$(CONFIG_CPU_ABRT_EV4) += abort-ev4.o
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 0e5ed87221dd..a5d3180a6886 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -1387,7 +1387,11 @@ static void __init map_lowmem(void)
1387 } 1387 }
1388} 1388}
1389 1389
1390#if defined(CONFIG_ARM_LPAE) && defined(CONFIG_ARM_PATCH_PHYS_VIRT) 1390#ifdef CONFIG_ARM_PV_FIXUP
1391extern unsigned long __atags_pointer;
1392typedef void pgtables_remap(long long offset, unsigned long pgd, void *bdata);
1393pgtables_remap lpae_pgtables_remap_asm;
1394
1391/* 1395/*
1392 * early_paging_init() recreates boot time page table setup, allowing machines 1396 * early_paging_init() recreates boot time page table setup, allowing machines
1393 * to switch over to a high (>4G) address space on LPAE systems 1397 * to switch over to a high (>4G) address space on LPAE systems
@@ -1395,35 +1399,30 @@ static void __init map_lowmem(void)
1395void __init early_paging_init(const struct machine_desc *mdesc, 1399void __init early_paging_init(const struct machine_desc *mdesc,
1396 struct proc_info_list *procinfo) 1400 struct proc_info_list *procinfo)
1397{ 1401{
1398 pmdval_t pmdprot = procinfo->__cpu_mm_mmu_flags; 1402 pgtables_remap *lpae_pgtables_remap;
1399 unsigned long map_start, map_end; 1403 unsigned long pa_pgd;
1404 unsigned int cr, ttbcr;
1400 long long offset; 1405 long long offset;
1401 pgd_t *pgd0, *pgdk; 1406 void *boot_data;
1402 pud_t *pud0, *pudk, *pud_start;
1403 pmd_t *pmd0, *pmdk;
1404 phys_addr_t phys;
1405 int i;
1406 1407
1407 if (!mdesc->pv_fixup) 1408 if (!mdesc->pv_fixup)
1408 return; 1409 return;
1409 1410
1410 /* remap kernel code and data */
1411 map_start = init_mm.start_code & PMD_MASK;
1412 map_end = ALIGN(init_mm.brk, PMD_SIZE);
1413
1414 /* get a handle on things... */
1415 pgd0 = pgd_offset_k(0);
1416 pud_start = pud0 = pud_offset(pgd0, 0);
1417 pmd0 = pmd_offset(pud0, 0);
1418
1419 pgdk = pgd_offset_k(map_start);
1420 pudk = pud_offset(pgdk, map_start);
1421 pmdk = pmd_offset(pudk, map_start);
1422
1423 offset = mdesc->pv_fixup(); 1411 offset = mdesc->pv_fixup();
1424 if (offset == 0) 1412 if (offset == 0)
1425 return; 1413 return;
1426 1414
1415 /*
1416 * Get the address of the remap function in the 1:1 identity
1417 * mapping setup by the early page table assembly code. We
1418 * must get this prior to the pv update. The following barrier
1419 * ensures that this is complete before we fixup any P:V offsets.
1420 */
1421 lpae_pgtables_remap = (pgtables_remap *)(unsigned long)__pa(lpae_pgtables_remap_asm);
1422 pa_pgd = __pa(swapper_pg_dir);
1423 boot_data = __va(__atags_pointer);
1424 barrier();
1425
1427 pr_info("Switching physical address space to 0x%08llx\n", 1426 pr_info("Switching physical address space to 0x%08llx\n",
1428 (u64)PHYS_OFFSET + offset); 1427 (u64)PHYS_OFFSET + offset);
1429 1428
@@ -1436,75 +1435,32 @@ void __init early_paging_init(const struct machine_desc *mdesc,
1436 (&__pv_table_end - &__pv_table_begin) << 2); 1435 (&__pv_table_end - &__pv_table_begin) << 2);
1437 1436
1438 /* 1437 /*
1439 * Cache cleaning operations for self-modifying code 1438 * We changing not only the virtual to physical mapping, but also
1440 * We should clean the entries by MVA but running a 1439 * the physical addresses used to access memory. We need to flush
1441 * for loop over every pv_table entry pointer would 1440 * all levels of cache in the system with caching disabled to
1442 * just complicate the code. 1441 * ensure that all data is written back, and nothing is prefetched
1443 */ 1442 * into the caches. We also need to prevent the TLB walkers
1444 flush_cache_louis(); 1443 * allocating into the caches too. Note that this is ARMv7 LPAE
1445 dsb(ishst); 1444 * specific.
1446 isb();
1447
1448 /*
1449 * FIXME: This code is not architecturally compliant: we modify
1450 * the mappings in-place, indeed while they are in use by this
1451 * very same code. This may lead to unpredictable behaviour of
1452 * the CPU.
1453 *
1454 * Even modifying the mappings in a separate page table does
1455 * not resolve this.
1456 *
1457 * The architecture strongly recommends that when a mapping is
1458 * changed, that it is changed by first going via an invalid
1459 * mapping and back to the new mapping. This is to ensure that
1460 * no TLB conflicts (caused by the TLB having more than one TLB
1461 * entry match a translation) can occur. However, doing that
1462 * here will result in unmapping the code we are running.
1463 */
1464 pr_warn("WARNING: unsafe modification of in-place page tables - tainting kernel\n");
1465 add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
1466
1467 /*
1468 * Remap level 1 table. This changes the physical addresses
1469 * used to refer to the level 2 page tables to the high
1470 * physical address alias, leaving everything else the same.
1471 */
1472 for (i = 0; i < PTRS_PER_PGD; pud0++, i++) {
1473 set_pud(pud0,
1474 __pud(__pa(pmd0) | PMD_TYPE_TABLE | L_PGD_SWAPPER));
1475 pmd0 += PTRS_PER_PMD;
1476 }
1477
1478 /*
1479 * Remap the level 2 table, pointing the mappings at the high
1480 * physical address alias of these pages.
1481 */
1482 phys = __pa(map_start);
1483 do {
1484 *pmdk++ = __pmd(phys | pmdprot);
1485 phys += PMD_SIZE;
1486 } while (phys < map_end);
1487
1488 /*
1489 * Ensure that the above updates are flushed out of the cache.
1490 * This is not strictly correct; on a system where the caches
1491 * are coherent with each other, but the MMU page table walks
1492 * may not be coherent, flush_cache_all() may be a no-op, and
1493 * this will fail.
1494 */ 1445 */
1446 cr = get_cr();
1447 set_cr(cr & ~(CR_I | CR_C));
1448 asm("mrc p15, 0, %0, c2, c0, 2" : "=r" (ttbcr));
1449 asm volatile("mcr p15, 0, %0, c2, c0, 2"
1450 : : "r" (ttbcr & ~(3 << 8 | 3 << 10)));
1495 flush_cache_all(); 1451 flush_cache_all();
1496 1452
1497 /* 1453 /*
1498 * Re-write the TTBR values to point them at the high physical 1454 * Fixup the page tables - this must be in the idmap region as
1499 * alias of the page tables. We expect __va() will work on 1455 * we need to disable the MMU to do this safely, and hence it
1500 * cpu_get_pgd(), which returns the value of TTBR0. 1456 * needs to be assembly. It's fairly simple, as we're using the
1457 * temporary tables setup by the initial assembly code.
1501 */ 1458 */
1502 cpu_switch_mm(pgd0, &init_mm); 1459 lpae_pgtables_remap(offset, pa_pgd, boot_data);
1503 cpu_set_ttbr(1, __pa(pgd0) + TTBR1_OFFSET);
1504 1460
1505 /* Finally flush any stale TLB values. */ 1461 /* Re-enable the caches and cacheable TLB walks */
1506 local_flush_bp_all(); 1462 asm volatile("mcr p15, 0, %0, c2, c0, 2" : : "r" (ttbcr));
1507 local_flush_tlb_all(); 1463 set_cr(cr);
1508} 1464}
1509 1465
1510#else 1466#else
diff --git a/arch/arm/mm/pv-fixup-asm.S b/arch/arm/mm/pv-fixup-asm.S
new file mode 100644
index 000000000000..1867f3e43016
--- /dev/null
+++ b/arch/arm/mm/pv-fixup-asm.S
@@ -0,0 +1,88 @@
1/*
2 * Copyright (C) 2015 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This assembly is required to safely remap the physical address space
9 * for Keystone 2
10 */
11#include <linux/linkage.h>
12#include <asm/asm-offsets.h>
13#include <asm/cp15.h>
14#include <asm/memory.h>
15#include <asm/pgtable.h>
16
17 .section ".idmap.text", "ax"
18
19#define L1_ORDER 3
20#define L2_ORDER 3
21
22ENTRY(lpae_pgtables_remap_asm)
23 stmfd sp!, {r4-r8, lr}
24
25 mrc p15, 0, r8, c1, c0, 0 @ read control reg
26 bic ip, r8, #CR_M @ disable caches and MMU
27 mcr p15, 0, ip, c1, c0, 0
28 dsb
29 isb
30
31 /* Update level 2 entries covering the kernel */
32 ldr r6, =(_end - 1)
33 add r7, r2, #0x1000
34 add r6, r7, r6, lsr #SECTION_SHIFT - L2_ORDER
35 add r7, r7, #PAGE_OFFSET >> (SECTION_SHIFT - L2_ORDER)
361: ldrd r4, [r7]
37 adds r4, r4, r0
38 adc r5, r5, r1
39 strd r4, [r7], #1 << L2_ORDER
40 cmp r7, r6
41 bls 1b
42
43 /* Update level 2 entries for the boot data */
44 add r7, r2, #0x1000
45 add r7, r7, r3, lsr #SECTION_SHIFT - L2_ORDER
46 bic r7, r7, #(1 << L2_ORDER) - 1
47 ldrd r4, [r7]
48 adds r4, r4, r0
49 adc r5, r5, r1
50 strd r4, [r7], #1 << L2_ORDER
51 ldrd r4, [r7]
52 adds r4, r4, r0
53 adc r5, r5, r1
54 strd r4, [r7]
55
56 /* Update level 1 entries */
57 mov r6, #4
58 mov r7, r2
592: ldrd r4, [r7]
60 adds r4, r4, r0
61 adc r5, r5, r1
62 strd r4, [r7], #1 << L1_ORDER
63 subs r6, r6, #1
64 bne 2b
65
66 mrrc p15, 0, r4, r5, c2 @ read TTBR0
67 adds r4, r4, r0 @ update physical address
68 adc r5, r5, r1
69 mcrr p15, 0, r4, r5, c2 @ write back TTBR0
70 mrrc p15, 1, r4, r5, c2 @ read TTBR1
71 adds r4, r4, r0 @ update physical address
72 adc r5, r5, r1
73 mcrr p15, 1, r4, r5, c2 @ write back TTBR1
74
75 dsb
76
77 mov ip, #0
78 mcr p15, 0, ip, c7, c5, 0 @ I+BTB cache invalidate
79 mcr p15, 0, ip, c8, c7, 0 @ local_flush_tlb_all()
80 dsb
81 isb
82
83 mcr p15, 0, r8, c1, c0, 0 @ re-enable MMU
84 dsb
85 isb
86
87 ldmfd sp!, {r4-r8, pc}
88ENDPROC(lpae_pgtables_remap_asm)