Skip to content

Commit bd914a9

Browse files
author
Marc Zyngier
committed
KVM: arm64: nv: Don't adjust PSTATE.M when L2 is nesting
We currently check for HCR_EL2.NV being set to decide whether we need to repaint PSTATE.M to say EL2 instead of EL1 on exit. However, this isn't correct when L2 is itself a hypervisor, and that L1 as set its own HCR_EL2.NV. That's because we "flatten" the state and inherit parts of the guest's own setup. In that case, we shouldn't adjust PSTATE.M, as this is really EL1 for both us and the guest. Instead of trying to try and work out how we ended-up with HCR_EL2.NV being set by introspecting both the host and guest states, use a per-CPU flag to remember the context (HYP or not), and use that information to decide whether PSTATE needs tweaking. Reviewed-by: Oliver Upton <oliver.upton@linux.dev> Link: https://lore.kernel.org/r/20250514103501.2225951-7-maz@kernel.org Signed-off-by: Marc Zyngier <maz@kernel.org>
1 parent 85bba00 commit bd914a9

File tree

2 files changed

+20
-2
lines changed

2 files changed

+20
-2
lines changed

arch/arm64/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ struct kvm_host_data {
654654
#define KVM_HOST_DATA_FLAG_HAS_TRBE 1
655655
#define KVM_HOST_DATA_FLAG_TRBE_ENABLED 4
656656
#define KVM_HOST_DATA_FLAG_EL1_TRACING_CONFIGURED 5
657+
#define KVM_HOST_DATA_FLAG_VCPU_IN_HYP_CONTEXT 6
657658
unsigned long flags;
658659

659660
struct kvm_cpu_context host_ctxt;

arch/arm64/kvm/hyp/vhe/switch.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,23 @@ static u64 __compute_hcr(struct kvm_vcpu *vcpu)
5353
if (!vcpu_has_nv(vcpu))
5454
return hcr;
5555

56+
/*
57+
* We rely on the invariant that a vcpu entered from HYP
58+
* context must also exit in the same context, as only an ERET
59+
* instruction can kick us out of it, and we obviously trap
60+
* that sucker. PSTATE.M will get fixed-up on exit.
61+
*/
5662
if (is_hyp_ctxt(vcpu)) {
63+
host_data_set_flag(VCPU_IN_HYP_CONTEXT);
64+
5765
hcr |= HCR_NV | HCR_NV2 | HCR_AT | HCR_TTLB;
5866

5967
if (!vcpu_el2_e2h_is_set(vcpu))
6068
hcr |= HCR_NV1;
6169

6270
write_sysreg_s(vcpu->arch.ctxt.vncr_array, SYS_VNCR_EL2);
71+
} else {
72+
host_data_clear_flag(VCPU_IN_HYP_CONTEXT);
6373
}
6474

6575
return hcr | (__vcpu_sys_reg(vcpu, HCR_EL2) & ~NV_HCR_GUEST_EXCLUDE);
@@ -568,9 +578,12 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
568578

569579
/*
570580
* If we were in HYP context on entry, adjust the PSTATE view
571-
* so that the usual helpers work correctly.
581+
* so that the usual helpers work correctly. This enforces our
582+
* invariant that the guest's HYP context status is preserved
583+
* across a run.
572584
*/
573-
if (vcpu_has_nv(vcpu) && (read_sysreg(hcr_el2) & HCR_NV)) {
585+
if (vcpu_has_nv(vcpu) &&
586+
unlikely(host_data_test_flag(VCPU_IN_HYP_CONTEXT))) {
574587
u64 mode = *vcpu_cpsr(vcpu) & (PSR_MODE_MASK | PSR_MODE32_BIT);
575588

576589
switch (mode) {
@@ -586,6 +599,10 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
586599
*vcpu_cpsr(vcpu) |= mode;
587600
}
588601

602+
/* Apply extreme paranoia! */
603+
BUG_ON(vcpu_has_nv(vcpu) &&
604+
!!host_data_test_flag(VCPU_IN_HYP_CONTEXT) != is_hyp_ctxt(vcpu));
605+
589606
return __fixup_guest_exit(vcpu, exit_code, hyp_exit_handlers);
590607
}
591608

0 commit comments

Comments
 (0)