@@ -816,30 +816,46 @@ ENDPROC(fail)
* 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 */
+ /* 6) 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)
#ifdef CONFIG_EARLY_PRINTK
/*
@@ -120,6 +120,37 @@ 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
@@ -207,6 +207,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
@@ -488,8 +488,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)
{