Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

KVM: arm64: Don't blindly set set PSTATE.PAN on guest exit

We set PSTATE.PAN to 1 on exiting from a guest if PAN support has
been compiled in and that it exists on the HW. However, this is not
necessarily correct.

In a nVHE configuration, there is no notion of PAN at EL2, so setting
PSTATE.PAN to anything is pointless.

Furthermore, not setting PAN to 0 when CONFIG_ARM64_PAN isn't set
means we run with the *guest's* PSTATE.PAN (which might be set to 1),
and we will explode on the next userspace access. Yes, the architecture
is delightful in that particular corner.

Fix the whole thing by always setting PAN to something when running
VHE (which implies PAN support), and only ignore it when running nVHE.

Reported-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://msgid.link/20260107124600.2736328-1-maz@kernel.org
Signed-off-by: Oliver Upton <oupton@kernel.org>

authored by

Marc Zyngier and committed by
Oliver Upton
86364832 9e27085c

+36 -2
+2
arch/arm64/include/asm/kvm_asm.h
··· 300 300 __le32 *origptr, __le32 *updptr, int nr_inst); 301 301 void kvm_compute_final_ctr_el0(struct alt_instr *alt, 302 302 __le32 *origptr, __le32 *updptr, int nr_inst); 303 + void kvm_pan_patch_el2_entry(struct alt_instr *alt, 304 + __le32 *origptr, __le32 *updptr, int nr_inst); 303 305 void __noreturn __cold nvhe_hyp_panic_handler(u64 esr, u64 spsr, u64 elr_virt, 304 306 u64 elr_phys, u64 par, uintptr_t vcpu, u64 far, u64 hpfar); 305 307
+2 -1
arch/arm64/include/asm/sysreg.h
··· 91 91 */ 92 92 #define pstate_field(op1, op2) ((op1) << Op1_shift | (op2) << Op2_shift) 93 93 #define PSTATE_Imm_shift CRm_shift 94 - #define SET_PSTATE(x, r) __emit_inst(0xd500401f | PSTATE_ ## r | ((!!x) << PSTATE_Imm_shift)) 94 + #define ENCODE_PSTATE(x, r) (0xd500401f | PSTATE_ ## r | ((!!x) << PSTATE_Imm_shift)) 95 + #define SET_PSTATE(x, r) __emit_inst(ENCODE_PSTATE(x, r)) 95 96 96 97 #define PSTATE_PAN pstate_field(0, 4) 97 98 #define PSTATE_UAO pstate_field(0, 3)
+1
arch/arm64/kernel/image-vars.h
··· 86 86 KVM_NVHE_ALIAS(kvm_update_va_mask); 87 87 KVM_NVHE_ALIAS(kvm_get_kimage_voffset); 88 88 KVM_NVHE_ALIAS(kvm_compute_final_ctr_el0); 89 + KVM_NVHE_ALIAS(kvm_pan_patch_el2_entry); 89 90 KVM_NVHE_ALIAS(spectre_bhb_patch_loop_iter); 90 91 KVM_NVHE_ALIAS(spectre_bhb_patch_loop_mitigation_enable); 91 92 KVM_NVHE_ALIAS(spectre_bhb_patch_wa3);
+3 -1
arch/arm64/kvm/hyp/entry.S
··· 126 126 127 127 add x1, x1, #VCPU_CONTEXT 128 128 129 - ALTERNATIVE(nop, SET_PSTATE_PAN(1), ARM64_HAS_PAN, CONFIG_ARM64_PAN) 129 + alternative_cb ARM64_ALWAYS_SYSTEM, kvm_pan_patch_el2_entry 130 + nop 131 + alternative_cb_end 130 132 131 133 // Store the guest regs x2 and x3 132 134 stp x2, x3, [x1, #CPU_XREG_OFFSET(2)]
+28
arch/arm64/kvm/va_layout.c
··· 296 296 generate_mov_q(read_sanitised_ftr_reg(SYS_CTR_EL0), 297 297 origptr, updptr, nr_inst); 298 298 } 299 + 300 + void kvm_pan_patch_el2_entry(struct alt_instr *alt, 301 + __le32 *origptr, __le32 *updptr, int nr_inst) 302 + { 303 + /* 304 + * If we're running at EL1 without hVHE, then SCTLR_EL2.SPAN means 305 + * nothing to us (it is RES1), and we don't need to set PSTATE.PAN 306 + * to anything useful. 307 + */ 308 + if (!is_kernel_in_hyp_mode() && !cpus_have_cap(ARM64_KVM_HVHE)) 309 + return; 310 + 311 + /* 312 + * Leap of faith: at this point, we must be running VHE one way or 313 + * another, and FEAT_PAN is required to be implemented. If KVM 314 + * explodes at runtime because your system does not abide by this 315 + * requirement, call your favourite HW vendor, they have screwed up. 316 + * 317 + * We don't expect hVHE to access any userspace mapping, so always 318 + * set PSTATE.PAN on enty. Same thing if we have PAN enabled on an 319 + * EL2 kernel. Only force it to 0 if we have not configured PAN in 320 + * the kernel (and you know this is really silly). 321 + */ 322 + if (cpus_have_cap(ARM64_KVM_HVHE) || IS_ENABLED(CONFIG_ARM64_PAN)) 323 + *updptr = cpu_to_le32(ENCODE_PSTATE(1, PAN)); 324 + else 325 + *updptr = cpu_to_le32(ENCODE_PSTATE(0, PAN)); 326 + }