arm64: Fix early handling of FEAT_E2H0 not being implemented
authorMarc Zyngier <maz@kernel.org>
Thu, 21 Mar 2024 11:54:14 +0000 (11:54 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Mon, 1 Apr 2024 08:33:29 +0000 (01:33 -0700)
Commit 3944382fa6f2 introduced checks for the FEAT_E2H0 not being
implemented. However, the check is absolutely wrong and makes a
point it testing a bit that is guaranteed to be zero.

On top of that, the detection happens way too late, after the
init_el2_state has done its job.

This went undetected because the HW this was tested on has E2H being
RAO/WI, and not RES1. However, the bug shows up when run as a nested
guest, where HCR_EL2.E2H is not necessarily set to 1. As a result,
booting the kernel in hVHE mode fails with timer accesses being
cought in a trap loop (which was fun to debug).

Fix the check for ID_AA64MMFR4_EL1.E2H0, and set the HCR_EL2.E2H bit
early so that it can be checked by the rest of the init sequence.

With this, hVHE works again in a NV environment that doesn't have
FEAT_E2H0.

Fixes: 3944382fa6f2 ("arm64: Treat HCR_EL2.E2H as RES1 when ID_AA64MMFR4_EL1.E2H0 is negative")
Signed-off-by: Marc Zyngier <maz@kernel.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/20240321115414.3169115-1-maz@kernel.org
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kernel/head.S

index ce08b74..06234c3 100644 (file)
@@ -291,6 +291,21 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
        blr     x2
 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
 
@@ -303,22 +318,10 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
 
        mov_q   x1, INIT_SCTLR_EL1_MMU_OFF
 
-       /*
-        * Compliant CPUs advertise their VHE-onlyness with
-        * ID_AA64MMFR4_EL1.E2H0 < 0. HCR_EL2.E2H can be
-        * RES1 in that case.
-        *
-        * Fruity CPUs seem to have HCR_EL2.E2H set to RES1, but
-        * don't advertise it (they predate this relaxation).
-        */
-       mrs_s   x0, SYS_ID_AA64MMFR4_EL1
-       ubfx    x0, x0, #ID_AA64MMFR4_EL1_E2H0_SHIFT, #ID_AA64MMFR4_EL1_E2H0_WIDTH
-       tbnz    x0, #(ID_AA64MMFR4_EL1_E2H0_SHIFT + ID_AA64MMFR4_EL1_E2H0_WIDTH - 1), 1f
-
        mrs     x0, hcr_el2
        and     x0, x0, #HCR_E2H
        cbz     x0, 2f
-1:
+
        /* Set a sane SCTLR_EL1, the VHE way */
        pre_disable_mmu_workaround
        msr_s   SYS_SCTLR_EL12, x1