Skip to content

Commit 102c51c

Browse files
willdeaconMarc Zyngier
authored and
Marc Zyngier
committed
KVM: arm64: Fix tcr_el2 initialisation in hVHE mode
When not running in VHE mode, cpu_prepare_hyp_mode() computes the value of TCR_EL2 using the host's TCR_EL1 settings as a starting point. For nVHE, this amounts to masking out everything apart from the TG0, SH0, ORGN0, IRGN0 and T0SZ fields before setting the RES1 bits, shifting the IPS field down to the PS field and setting DS if LPA2 is enabled. Unfortunately, for hVHE, things go slightly wonky: EPD1 is correctly set to disable walks via TTBR1_EL2 but then the T1SZ and IPS fields are corrupted when we mistakenly attempt to initialise the PS and DS fields in their E2H=0 positions. Furthermore, many fields are retained from TCR_EL1 which should not be propagated to TCR_EL2. Notably, this means we can end up with A1 set despite not initialising TTBR1_EL2 at all. This has been shown to cause unexpected translation faults at EL2 with pKVM due to TLB invalidation not taking effect when running with a non-zero ASID. Fix the TCR_EL2 initialisation code to set PS and DS only when E2H=0, masking out HD, HA and A1 when E2H=1. Cc: Marc Zyngier <[email protected]> Cc: Oliver Upton <[email protected]> Fixes: ad744e8 ("arm64: Allow arm64_sw.hvhe on command line") Signed-off-by: Will Deacon <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Marc Zyngier <[email protected]>
1 parent 0ad2507 commit 102c51c

File tree

2 files changed

+8
-9
lines changed

2 files changed

+8
-9
lines changed

arch/arm64/include/asm/kvm_arm.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119
#define TCR_EL2_IRGN0_MASK TCR_IRGN0_MASK
120120
#define TCR_EL2_T0SZ_MASK 0x3f
121121
#define TCR_EL2_MASK (TCR_EL2_TG0_MASK | TCR_EL2_SH0_MASK | \
122-
TCR_EL2_ORGN0_MASK | TCR_EL2_IRGN0_MASK | TCR_EL2_T0SZ_MASK)
122+
TCR_EL2_ORGN0_MASK | TCR_EL2_IRGN0_MASK)
123123

124124
/* VTCR_EL2 Registers bits */
125125
#define VTCR_EL2_DS TCR_EL2_DS

arch/arm64/kvm/arm.c

+7-8
Original file line numberDiff line numberDiff line change
@@ -1980,7 +1980,7 @@ static int kvm_init_vector_slots(void)
19801980
static void __init cpu_prepare_hyp_mode(int cpu, u32 hyp_va_bits)
19811981
{
19821982
struct kvm_nvhe_init_params *params = per_cpu_ptr_nvhe_sym(kvm_init_params, cpu);
1983-
unsigned long tcr, ips;
1983+
unsigned long tcr;
19841984

19851985
/*
19861986
* Calculate the raw per-cpu offset without a translation from the
@@ -1994,19 +1994,18 @@ static void __init cpu_prepare_hyp_mode(int cpu, u32 hyp_va_bits)
19941994
params->mair_el2 = read_sysreg(mair_el1);
19951995

19961996
tcr = read_sysreg(tcr_el1);
1997-
ips = FIELD_GET(TCR_IPS_MASK, tcr);
19981997
if (cpus_have_final_cap(ARM64_KVM_HVHE)) {
1998+
tcr &= ~(TCR_HD | TCR_HA | TCR_A1 | TCR_T0SZ_MASK);
19991999
tcr |= TCR_EPD1_MASK;
20002000
} else {
2001+
unsigned long ips = FIELD_GET(TCR_IPS_MASK, tcr);
2002+
20012003
tcr &= TCR_EL2_MASK;
2002-
tcr |= TCR_EL2_RES1;
2004+
tcr |= TCR_EL2_RES1 | FIELD_PREP(TCR_EL2_PS_MASK, ips);
2005+
if (lpa2_is_enabled())
2006+
tcr |= TCR_EL2_DS;
20032007
}
2004-
tcr &= ~TCR_T0SZ_MASK;
20052008
tcr |= TCR_T0SZ(hyp_va_bits);
2006-
tcr &= ~TCR_EL2_PS_MASK;
2007-
tcr |= FIELD_PREP(TCR_EL2_PS_MASK, ips);
2008-
if (lpa2_is_enabled())
2009-
tcr |= TCR_EL2_DS;
20102009
params->tcr_el2 = tcr;
20112010

20122011
params->pgd_pa = kvm_mmu_get_httbr();

0 commit comments

Comments
 (0)