diff --git a/xen/arch/arm/arm64/head.S b/xen/arch/arm/arm64/head.S index 76c3affe11..19a53d0a03 100644 --- a/xen/arch/arm/arm64/head.S +++ b/xen/arch/arm/arm64/head.S @@ -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 - msr TTBR0_EL2, x0 + /* 2) Turn off MMU */ + mrs x1, SCTLR_EL2 + bic x1, x1, #SCTLR_Axx_ELx_M + msr SCTLR_EL2, x1 + 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 */ + /* + * 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 + + /* + * 5) Flush I-cache + * This should not be necessary but it is kept for safety. + */ + ic iallu + isb + + /* 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 /* diff --git a/xen/arch/arm/arm64/mm.c b/xen/arch/arm/arm64/mm.c index 56b9e9b8d3..78b7c7eb00 100644 --- a/xen/arch/arm/arm64/mm.c +++ b/xen/arch/arm/arm64/mm.c @@ -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 diff --git a/xen/arch/arm/include/asm/mm.h b/xen/arch/arm/include/asm/mm.h index 23dec574eb..4262165ce2 100644 --- a/xen/arch/arm/include/asm/mm.h +++ b/xen/arch/arm/include/asm/mm.h @@ -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 diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c index b7104d8d33..74f6ff2c6f 100644 --- a/xen/arch/arm/mm.c +++ b/xen/arch/arm/mm.c @@ -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) {