@@ -812,36 +812,52 @@ fail: PRINT("- Boot failed -\r\n")
b 1b
ENDPROC(fail)
-GLOBAL(_end_boot)
-
/*
* Switch TTBR
*
* x0 ttbr
- *
- * TODO: This code does not comply with break-before-make.
*/
-ENTRY(switch_ttbr)
- dsb sy /* Ensure the flushes happen before
- * continuing */
- isb /* Ensure synchronization with previous
- * changes to text */
- tlbi alle2 /* Flush hypervisor TLB */
- ic iallu /* Flush I-cache */
- dsb sy /* Ensure completion of TLB flush */
+ENTRY(switch_ttbr_id)
+ /* 1) Ensure any previous read/write have completed */
+ dsb ish
+ isb
+
+ /* 2) Turn off MMU */
+ mrs x1, SCTLR_EL2
+ bic x1, x1, #SCTLR_Axx_ELx_M
+ msr SCTLR_EL2, x1
+ isb
+
+ /*
+ * 3) Flush the TLBs.
+ * See asm/arm64/flushtlb.h for the explanation of the sequence.
+ */
+ dsb nshst
+ tlbi alle2
+ dsb nsh
+ isb
+
+ /* 4) Update the TTBR */
+ msr TTBR0_EL2, x0
isb
- msr TTBR0_EL2, x0
+ /*
+ * 5) Flush I-cache
+ * This should not be necessary but it is kept for safety.
+ */
+ ic iallu
+ isb
- isb /* Ensure synchronization with previous
- * changes to text */
- tlbi alle2 /* Flush hypervisor TLB */
- ic iallu /* Flush I-cache */
- dsb sy /* Ensure completion of TLB flush */
+ /* 5) Turn on the MMU */
+ mrs x1, SCTLR_EL2
+ orr x1, x1, #SCTLR_Axx_ELx_M /* Enable MMU */
+ msr SCTLR_EL2, x1
isb
ret
-ENDPROC(switch_ttbr)
+ENDPROC(switch_ttbr_id)
+
+GLOBAL(_end_boot)
#ifdef CONFIG_EARLY_PRINTK
/*
@@ -31,6 +31,15 @@ static void __init prepare_boot_identity_mapping(void)
lpae_t pte;
DECLARE_OFFSETS(id_offsets, id_addr);
+ /*
+ * We will be re-using the boot ID tables. They may not have been
+ * zeroed but they should be unlinked. So it is fine to use
+ * clear_page().
+ */
+ clear_page(boot_first_id);
+ clear_page(boot_second_id);
+ clear_page(boot_third_id);
+
if ( id_offsets[0] != 0 )
panic("Cannot handled ID mapping above 512GB\n");
@@ -111,6 +120,36 @@ void update_identity_mapping(bool enable)
BUG_ON(rc);
}
+extern void switch_ttbr_id(uint64_t ttbr);
+
+typedef void (switch_ttbr_fn)(uint64_t ttbr);
+
+void __init switch_ttbr(uint64_t ttbr)
+{
+ vaddr_t id_addr = virt_to_maddr(switch_ttbr_id);
+ switch_ttbr_fn *fn = (switch_ttbr_fn *)id_addr;
+ lpae_t pte;
+
+ /* Enable the identity mapping in the boot page tables */
+ update_identity_mapping(true);
+ /* Enable the identity mapping in the runtime page tables */
+ pte = pte_of_xenaddr((vaddr_t)switch_ttbr_id);
+ pte.pt.table = 1;
+ pte.pt.xn = 0;
+ pte.pt.ro = 1;
+ write_pte(&xen_third_id[third_table_offset(id_addr)], pte);
+
+ /* Switch TTBR */
+ fn(ttbr);
+
+ /*
+ * Disable the identity mapping in the runtime page tables.
+ * Note it is not necessary to disable it in the boot page tables
+ * because they are not going to be used by this CPU anymore.
+ */
+ update_identity_mapping(false);
+}
+
/*
* Local variables:
* mode: C
@@ -196,6 +196,8 @@ extern unsigned long total_pages;
extern void setup_pagetables(unsigned long boot_phys_offset);
/* Map FDT in boot pagetable */
extern void *early_fdt_map(paddr_t fdt_paddr);
+/* Switch to a new root page-tables */
+extern void switch_ttbr(uint64_t ttbr);
/* Remove early mappings */
extern void remove_early_mappings(void);
/* Allocate and initialise pagetables for a secondary CPU. Sets init_ttbr to the
@@ -485,8 +485,6 @@ static void xen_pt_enforce_wnx(void)
flush_xen_tlb_local();
}
-extern void switch_ttbr(uint64_t ttbr);
-
/* Clear a translation table and clean & invalidate the cache */
static void clear_table(void *table)
{
@@ -559,13 +557,17 @@ void __init setup_pagetables(unsigned long boot_phys_offset)
ttbr = (uintptr_t) cpu0_pgtable + phys_offset;
#endif
- switch_ttbr(ttbr);
-
- xen_pt_enforce_wnx();
-
+ /*
+ * This needs to be setup first so switch_ttbr() can enable the
+ * identity mapping.
+ */
#ifdef CONFIG_ARM_32
per_cpu(xen_pgtable, 0) = cpu0_pgtable;
#endif
+
+ switch_ttbr(ttbr);
+
+ xen_pt_enforce_wnx();
}
static void clear_boot_pagetables(void)