Commit 7a68b55f authored by Mark Rutland's avatar Mark Rutland Committed by Marc Zyngier
Browse files

KVM: arm64: Initialize HCR_EL2.E2H early



On CPUs without FEAT_E2H0, HCR_EL2.E2H is RES1, but may reset to an
UNKNOWN value out of reset and consequently may not read as 1 unless it
has been explicitly initialized.

We handled this for the head.S boot code in commits:

  3944382f ("arm64: Treat HCR_EL2.E2H as RES1 when ID_AA64MMFR4_EL1.E2H0 is negative")
  b3320142 ("arm64: Fix early handling of FEAT_E2H0 not being implemented")

Unfortunately, we forgot to apply a similar fix to the KVM PSCI entry
points used when relaying CPU_ON, CPU_SUSPEND, and SYSTEM SUSPEND. When
KVM is entered via these entry points, the value of HCR_EL2.E2H may be
consumed before it has been initialized (e.g. by the 'init_el2_state'
macro).

Initialize HCR_EL2.E2H early in these paths such that it can be consumed
reliably. The existing code in head.S is factored out into a new
'init_el2_hcr' macro, and this is used in the __kvm_hyp_init_cpu()
function common to all the relevant PSCI entry points.

For clarity, I've tweaked the assembly used to check whether
ID_AA64MMFR4_EL1.E2H0 is negative. The bitfield is extracted as a signed
value, and this is checked with a signed-greater-or-equal (GE) comparison.

As the hyp code will reconfigure HCR_EL2 later in ___kvm_hyp_init(), all
bits other than E2H are initialized to zero in __kvm_hyp_init_cpu().

Fixes: 3944382f ("arm64: Treat HCR_EL2.E2H as RES1 when ID_AA64MMFR4_EL1.E2H0 is negative")
Fixes: b3320142 ("arm64: Fix early handling of FEAT_E2H0 not being implemented")
Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
Cc: Ahmed Genidi <ahmed.genidi@arm.com>
Cc: Ben Horgan <ben.horgan@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Leo Yan <leo.yan@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oliver.upton@linux.dev>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250227180526.1204723-2-mark.rutland@arm.com


[maz: fixed LT->GE thinko]
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent fa808ed4
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -16,6 +16,32 @@
#include <asm/sysreg.h>
#include <linux/irqchip/arm-gic-v3.h>

.macro init_el2_hcr	val
	mov_q	x0, \val

	/*
	 * Compliant CPUs advertise their VHE-onlyness with
	 * ID_AA64MMFR4_EL1.E2H0 < 0. On such CPUs HCR_EL2.E2H is RES1, but it
	 * can reset into an UNKNOWN state and might not read as 1 until it has
	 * been initialized explicitly.
	 *
	 * Fruity CPUs seem to have HCR_EL2.E2H set to RAO/WI, but
	 * don't advertise it (they predate this relaxation).
	 *
	 * Initalize HCR_EL2.E2H so that later code can rely upon HCR_EL2.E2H
	 * indicating whether the CPU is running in E2H mode.
	 */
	mrs_s	x1, SYS_ID_AA64MMFR4_EL1
	sbfx	x1, x1, #ID_AA64MMFR4_EL1_E2H0_SHIFT, #ID_AA64MMFR4_EL1_E2H0_WIDTH
	cmp	x1, #0
	b.ge	.LnVHE_\@

	orr	x0, x0, #HCR_E2H
.LnVHE_\@:
	msr	hcr_el2, x0
	isb
.endm

.macro __init_el2_sctlr
	mov_q	x0, INIT_SCTLR_EL2_MMU_OFF
	msr	sctlr_el2, x0
+1 −18
Original line number Diff line number Diff line
@@ -298,25 +298,8 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
	msr	sctlr_el2, x0
	isb
0:
	mov_q	x0, HCR_HOST_NVHE_FLAGS

	/*
	 * Compliant CPUs advertise their VHE-onlyness with
	 * ID_AA64MMFR4_EL1.E2H0 < 0. HCR_EL2.E2H can be
	 * RES1 in that case. Publish the E2H bit early so that
	 * it can be picked up by the init_el2_state macro.
	 *
	 * Fruity CPUs seem to have HCR_EL2.E2H set to RAO/WI, but
	 * don't advertise it (they predate this relaxation).
	 */
	mrs_s	x1, SYS_ID_AA64MMFR4_EL1
	tbz	x1, #(ID_AA64MMFR4_EL1_E2H0_SHIFT + ID_AA64MMFR4_EL1_E2H0_WIDTH - 1), 1f

	orr	x0, x0, #HCR_E2H
1:
	msr	hcr_el2, x0
	isb

	init_el2_hcr	HCR_HOST_NVHE_FLAGS
	init_el2_state

	/* Hypervisor stub */
+7 −1
Original line number Diff line number Diff line
@@ -73,8 +73,12 @@ __do_hyp_init:
	eret
SYM_CODE_END(__kvm_hyp_init)

/*
 * Initialize EL2 CPU state to sane values.
 *
 * HCR_EL2.E2H must have been initialized already.
 */
SYM_CODE_START_LOCAL(__kvm_init_el2_state)
	/* Initialize EL2 CPU state to sane values. */
	init_el2_state				// Clobbers x0..x2
	finalise_el2_state
	ret
@@ -206,6 +210,8 @@ SYM_CODE_START_LOCAL(__kvm_hyp_init_cpu)

2:	msr	SPsel, #1			// We want to use SP_EL{1,2}

	init_el2_hcr	0

	bl	__kvm_init_el2_state

	__init_el2_nvhe_prepare_eret