Skip to content

Commit 468afbf

Browse files
author
Gavin Shan
committed
KVM: arm64: Fix tcr_el2 initialisation in hVHE mode
JIRA: https://issues.redhat.com/browse/RHEL-82298 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 <maz@kernel.org> Cc: Oliver Upton <oliver.upton@linux.dev> Fixes: ad744e8 ("arm64: Allow arm64_sw.hvhe on command line") Signed-off-by: Will Deacon <will@kernel.org> Link: https://lore.kernel.org/r/20250214133724.13179-1-will@kernel.org Signed-off-by: Marc Zyngier <maz@kernel.org> (cherry picked from commit 102c51c) Signed-off-by: Gavin Shan <gshan@redhat.com>
1 parent f3452fd commit 468afbf

File tree

2 files changed

+8
-9
lines changed

2 files changed

+8
-9
lines changed

arch/arm64/include/asm/kvm_arm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
#define TCR_EL2_IRGN0_MASK TCR_IRGN0_MASK
118118
#define TCR_EL2_T0SZ_MASK 0x3f
119119
#define TCR_EL2_MASK (TCR_EL2_TG0_MASK | TCR_EL2_SH0_MASK | \
120-
TCR_EL2_ORGN0_MASK | TCR_EL2_IRGN0_MASK | TCR_EL2_T0SZ_MASK)
120+
TCR_EL2_ORGN0_MASK | TCR_EL2_IRGN0_MASK)
121121

122122
/* VTCR_EL2 Registers bits */
123123
#define VTCR_EL2_DS TCR_EL2_DS

arch/arm64/kvm/arm.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,7 +1986,7 @@ static int kvm_init_vector_slots(void)
19861986
static void __init cpu_prepare_hyp_mode(int cpu, u32 hyp_va_bits)
19871987
{
19881988
struct kvm_nvhe_init_params *params = per_cpu_ptr_nvhe_sym(kvm_init_params, cpu);
1989-
unsigned long tcr, ips;
1989+
unsigned long tcr;
19901990

19911991
/*
19921992
* Calculate the raw per-cpu offset without a translation from the
@@ -2000,19 +2000,18 @@ static void __init cpu_prepare_hyp_mode(int cpu, u32 hyp_va_bits)
20002000
params->mair_el2 = read_sysreg(mair_el1);
20012001

20022002
tcr = read_sysreg(tcr_el1);
2003-
ips = FIELD_GET(TCR_IPS_MASK, tcr);
20042003
if (cpus_have_final_cap(ARM64_KVM_HVHE)) {
2004+
tcr &= ~(TCR_HD | TCR_HA | TCR_A1 | TCR_T0SZ_MASK);
20052005
tcr |= TCR_EPD1_MASK;
20062006
} else {
2007+
unsigned long ips = FIELD_GET(TCR_IPS_MASK, tcr);
2008+
20072009
tcr &= TCR_EL2_MASK;
2008-
tcr |= TCR_EL2_RES1;
2010+
tcr |= TCR_EL2_RES1 | FIELD_PREP(TCR_EL2_PS_MASK, ips);
2011+
if (lpa2_is_enabled())
2012+
tcr |= TCR_EL2_DS;
20092013
}
2010-
tcr &= ~TCR_T0SZ_MASK;
20112014
tcr |= TCR_T0SZ(hyp_va_bits);
2012-
tcr &= ~TCR_EL2_PS_MASK;
2013-
tcr |= FIELD_PREP(TCR_EL2_PS_MASK, ips);
2014-
if (lpa2_is_enabled())
2015-
tcr |= TCR_EL2_DS;
20162015
params->tcr_el2 = tcr;
20172016

20182017
params->pgd_pa = kvm_mmu_get_httbr();

0 commit comments

Comments
 (0)