xen/arm64: mm: Rework switch_ttbr()

At the moment, switch_ttbr() is switching the TTBR whilst the MMU is
still on.

Switching TTBR is like replacing existing mappings with new ones. So
we need to follow the break-before-make sequence.

In this case, it means the MMU needs to be switched off while the
TTBR is updated. In order to disable the MMU, we need to first
jump to an identity mapping.

Rename switch_ttbr() to switch_ttbr_id() and create an helper on
top to temporary map the identity mapping and call switch_ttbr()
via the identity address.

switch_ttbr_id() is now reworked to temporarily turn off the MMU
before updating the TTBR.

We also need to make sure the helper switch_ttbr() is part of the
identity mapping. So move _end_boot past it.

The arm32 code will use a different approach. So this issue is for now
only resolved on arm64.

Signed-off-by: Julien Grall <jgrall@amazon.com>
Reviewed-by: Luca Fancellu <luca.fancellu@arm.com>
Reviewed-by: Michal Orzel <michal.orzel@amd.com>
Reviewed-by: Bertrand Marquis <bertrand.marquis@arm.com>
Tested-by: Henry Wang <Henry.Wang@arm.com>
This commit is contained in:
Julien Grall committed 2023-04-19 19:39:58 +01:00
1 parent 1c78d76b67
commit c4b72c4a54
4 files changed
+66 -19

No files matched your search

+33 -17
View File
@@ -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
/*
+31
View File
@@ -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
+2
View File
@@ -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
-2
View File
@@ -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)
{
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.