Skip to content

Commit 8636483

Browse files
Marc ZyngierOliver Upton
authored andcommitted
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>
1 parent 9e27085 commit 8636483

5 files changed

Lines changed: 36 additions & 2 deletions

File tree

arch/arm64/include/asm/kvm_asm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ void kvm_get_kimage_voffset(struct alt_instr *alt,
300300
__le32 *origptr, __le32 *updptr, int nr_inst);
301301
void kvm_compute_final_ctr_el0(struct alt_instr *alt,
302302
__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);
303305
void __noreturn __cold nvhe_hyp_panic_handler(u64 esr, u64 spsr, u64 elr_virt,
304306
u64 elr_phys, u64 par, uintptr_t vcpu, u64 far, u64 hpfar);
305307

arch/arm64/include/asm/sysreg.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@
9191
*/
9292
#define pstate_field(op1, op2) ((op1) << Op1_shift | (op2) << Op2_shift)
9393
#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))
9596

9697
#define PSTATE_PAN pstate_field(0, 4)
9798
#define PSTATE_UAO pstate_field(0, 3)

arch/arm64/kernel/image-vars.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ KVM_NVHE_ALIAS(kvm_patch_vector_branch);
8686
KVM_NVHE_ALIAS(kvm_update_va_mask);
8787
KVM_NVHE_ALIAS(kvm_get_kimage_voffset);
8888
KVM_NVHE_ALIAS(kvm_compute_final_ctr_el0);
89+
KVM_NVHE_ALIAS(kvm_pan_patch_el2_entry);
8990
KVM_NVHE_ALIAS(spectre_bhb_patch_loop_iter);
9091
KVM_NVHE_ALIAS(spectre_bhb_patch_loop_mitigation_enable);
9192
KVM_NVHE_ALIAS(spectre_bhb_patch_wa3);

arch/arm64/kvm/hyp/entry.S

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ SYM_INNER_LABEL(__guest_exit, SYM_L_GLOBAL)
126126

127127
add x1, x1, #VCPU_CONTEXT
128128

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
130132

131133
// Store the guest regs x2 and x3
132134
stp x2, x3, [x1, #CPU_XREG_OFFSET(2)]

arch/arm64/kvm/va_layout.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,31 @@ void kvm_compute_final_ctr_el0(struct alt_instr *alt,
296296
generate_mov_q(read_sanitised_ftr_reg(SYS_CTR_EL0),
297297
origptr, updptr, nr_inst);
298298
}
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+
}

0 commit comments

Comments
 (0)