[RFC PATCH v7 24/28] arm64: errata: Don't enable guest buffer if misprogrammed

Alexandru Elisei alexandru.elisei at arm.com
Thu Sep 3 09:06:19 PDT 2026


When PMBPTR_EL1 is programmed to be equal to or greater than
PMBLIMITR_EL1.Limit, SPE is enabled and PMBSR_EL1.S is zero, the CPU might
deadlock.

This doesn't affect correctly written software, like the SPE driver, but a
buggy KVM guest might trigger the erratum. The workaround for KVM is not to
enable the hardware buffer, and, as a consequence, profiling. According
to ARM DDI0487M.c, rule R_PGGHS, this is one of the acceptable
behaviours when the buffer is misprogrammed.

Several parts are affected:

* ARM Neoverse-N1 (MP050), SDEN v35, erratum 1978083
  https://support.arm.com/documentation/SDEN-885747/35-0
* ARM Neoverse-V1 (MP076), SDEN v23, erratum 1978082
  https://support.arm.com/documentation/SDEN-1401781/23-0
* ARM Cortex-X1 (MP077), SDEN v25, erratum 2004055
  https://support.arm.com/documentation/SDEN-1401782/25-0
* ARM Cortex-X1C (MP136), SDEN v20, erratum 2004056
  https://support.arm.com/documentation/SDEN-1707914/20-0
* ARM Cortex-A78 (MP102), SDEN v25, erratum 2004055
  https://support.arm.com/documentation/SDEN-1401784/25-0
* ARM Cortex-A78AE (MP105), SDEN v22, erratum 2004059
  https://support.arm.com/documentation/SDEN-1707912/22-0
* ARM Cortex-A78C (MP138), SDEN v20, erratum 2004056
  https://support.arm.com/documentation/SDEN-1707916/20-0

Signed-off-by: Alexandru Elisei <alexandru.elisei at arm.com>
---
 Documentation/arch/arm64/silicon-errata.rst | 14 +++++++++++
 arch/arm64/Kconfig                          | 25 ++++++++++++++++++++
 arch/arm64/include/asm/kvm_spe.h            |  1 +
 arch/arm64/kernel/cpu_errata.c              | 26 +++++++++++++++++++++
 arch/arm64/kvm/hyp/vhe/spe-sr.c             |  6 +++--
 arch/arm64/kvm/spe.c                        | 22 +++++++++++++++++
 arch/arm64/tools/cpucaps                    |  1 +
 drivers/perf/arm_spe_pmu.c                  |  3 +++
 include/linux/perf/arm_spe_pmu.h            |  1 +
 9 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst
index a29f8fbc16a8..a5477cae7d70 100644
--- a/Documentation/arch/arm64/silicon-errata.rst
+++ b/Documentation/arch/arm64/silicon-errata.rst
@@ -151,10 +151,14 @@ stable kernels.
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A78      | #3031174        | ARM64_ERRATUM_3023823       |
 +----------------+-----------------+-----------------+-----------------------------+
+| ARM            | Cortex-A78      | #2004055        | ARM64_ERRATUM_2004055       |
++----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A78AE    | #4193793        | ARM64_ERRATUM_4118414       |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A78AE    | #3031175        | ARM64_ERRATUM_3023823       |
 +----------------+-----------------+-----------------+-----------------------------+
+| ARM            | Cortex-A78AE    | #2004059        | ARM64_ERRATUM_2004055       |
++----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A78C     | #3324346,       | ARM64_ERRATUM_3194386       |
 |                |                 | #3324347        |                             |
 +----------------+-----------------+-----------------+-----------------------------+
@@ -163,6 +167,8 @@ stable kernels.
 | ARM            | Cortex-A78C     | #3031176,       | ARM64_ERRATUM_3023823       |
 |                |                 | #3031177        |                             |
 +----------------+-----------------+-----------------+-----------------------------+
+| ARM            | Cortex-A78C     | #2004056        | ARM64_ERRATUM_2004055       |
++----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A710     | #2119858        | ARM64_ERRATUM_2119858       |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A710     | #2054223        | ARM64_ERRATUM_2054223       |
@@ -189,12 +195,16 @@ stable kernels.
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-X1       | #3031174        | ARM64_ERRATUM_3023823       |
 +----------------+-----------------+-----------------+-----------------------------+
+| ARM            | Cortex-X1       | #2004055        | ARM64_ERRATUM_2004055       |
++----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-X1C      | #3324346        | ARM64_ERRATUM_3194386       |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-X1C      | #4193792        | ARM64_ERRATUM_4118414       |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-X1C      | #3031176        | ARM64_ERRATUM_3023823       |
 +----------------+-----------------+-----------------+-----------------------------+
+| ARM            | Cortex-X1C      | #2004056        | ARM64_ERRATUM_2004055       |
++----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-X2       | #2119858        | ARM64_ERRATUM_2119858       |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-X2       | #2224489        | ARM64_ERRATUM_2224489       |
@@ -233,6 +243,8 @@ stable kernels.
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Neoverse-N1     | #3023823        | ARM64_ERRATUM_3023823       |
 +----------------+-----------------+-----------------+-----------------------------+
+| ARM            | Neoverse-N1     | #1978083        | ARM64_ERRATUM_2004055       |
++----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Neoverse-N2     | #2139208        | ARM64_ERRATUM_2139208       |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Neoverse-N2     | #2067961        | ARM64_ERRATUM_2067961       |
@@ -255,6 +267,8 @@ stable kernels.
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Neoverse-V1     | #3028884        | ARM64_ERRATUM_3023823       |
 +----------------+-----------------+-----------------+-----------------------------+
+| ARM            | Neoverse-V1     | #1978082        | ARM64_ERRATUM_2004055       |
++----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Neoverse-V2     | #3324336        | ARM64_ERRATUM_3194386       |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Neoverse-V2     | #4193787        | ARM64_ERRATUM_4118414       |
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index ca6759d16d27..d9b27ee90ab5 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1494,6 +1494,31 @@ config ARM64_ERRATUM_3023823
 
 	  If unsure, say Y.
 
+config ARM64_ERRATUM_2004055
+	bool "Incorrect programming of PMBPTR_EL1 might result in a deadlock"
+	depends on KVM_ARM_SPE
+	default y
+	help
+	  This option adds a workaround for the following errata:
+
+	  * ARM Neoverse-N1 erratum 1978083
+	  * ARM Neoverse-V1 erratum 1978082
+	  * ARM Cortex-X1 erratum 2004055
+	  * ARM Cortex-X1C erratum 2004056
+	  * ARM Cortex-A78 erratum 2004055
+	  * ARM Cortex-A78AE erratum 2004059
+	  * ARM Cortex-A78C errata 2004056
+
+	  When PMBPTR_EL1 is programmed to be equal to or greater than
+	  PMBLIMITR_EL1.Limit, SPE is enabled and PMBSR_EL1.S is zero, the CPU
+	  might deadlock.
+
+	  The work around is for KVM to behave as if profiling is disabled when
+	  the buffer is enabled, PMSBSR_EL1.S is zero and
+	  PMBPTR_EL1.PTR >= PMBLIMITR_EL1.Limit.
+
+	  If unsure, say Y.
+
 endmenu # "ARM errata workarounds via the alternatives framework"
 
 choice
diff --git a/arch/arm64/include/asm/kvm_spe.h b/arch/arm64/include/asm/kvm_spe.h
index 385b1157e578..88f8b1bb6864 100644
--- a/arch/arm64/include/asm/kvm_spe.h
+++ b/arch/arm64/include/asm/kvm_spe.h
@@ -23,6 +23,7 @@ struct kvm_vcpu_spe {
 	int irq_num;		/* Buffer management interrupt number */
 	bool initialized;	/* SPE initialized for the VCPU */
 	bool irq_level;		/* Virtual buffer management interrupt level */
+	bool buffer_misprogrammed;
 };
 
 bool kvm_supports_spe(void);
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index ab0558fbd5b2..0b9620893763 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -648,6 +648,25 @@ static const struct midr_range erratum_3023823_list[] = {
 };
 #endif
 
+#ifdef CONFIG_ARM64_ERRATUM_2004055
+static const struct midr_range erratum_2004055_list[] = {
+	MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1),
+	/* Neoverse-V1 r0p0 to r1p1 */
+	MIDR_RANGE(MIDR_NEOVERSE_V1, 0, 0, 1, 1),
+	/* Cortex-X1 r0p0 to r1p1 */
+	MIDR_RANGE(MIDR_CORTEX_X1, 0, 0, 1, 1),
+	/* Cortex-X1 r0p0 */
+	MIDR_REV(MIDR_CORTEX_X1C, 0, 0),
+	/* Cortex-A78 r0p0 to r1p1 */
+	MIDR_RANGE(MIDR_CORTEX_A78, 0, 0, 1, 1),
+	/* Cortex-A78 r0p0 to r1p1 */
+	MIDR_RANGE(MIDR_CORTEX_A78AE, 0, 0, 0, 1),
+	/* Cortex-A78C r0p0 */
+	MIDR_REV(MIDR_CORTEX_A78C, 0, 0),
+	{},
+};
+#endif
+
 #ifdef CONFIG_AMPERE_ERRATUM_AC03_CPU_38
 static const struct midr_range erratum_ac03_cpu_38_list[] = {
 	MIDR_ALL_VERSIONS(MIDR_AMPERE1),
@@ -1012,6 +1031,13 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
 		ERRATA_MIDR_RANGE_LIST(erratum_3023823_list),
 	},
 #endif
+#ifdef CONFIG_ARM64_ERRATUM_2004055
+	{
+		.desc = "Incorrect programming of PMBPTR_EL1 leads to deadlock",
+		.capability = ARM64_WORKAROUND_2004055,
+		ERRATA_MIDR_RANGE_LIST(erratum_2004055_list),
+	},
+#endif
 #ifdef CONFIG_ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD
 	{
 		.desc = "ARM errata 2966298, 3117295",
diff --git a/arch/arm64/kvm/hyp/vhe/spe-sr.c b/arch/arm64/kvm/hyp/vhe/spe-sr.c
index df35025db19a..ec991f4cc348 100644
--- a/arch/arm64/kvm/hyp/vhe/spe-sr.c
+++ b/arch/arm64/kvm/hyp/vhe/spe-sr.c
@@ -112,7 +112,8 @@ void __kvm_spe_restore_guest_state_vhe(struct kvm_vcpu *vcpu, struct kvm_cpu_con
 
 	pmbsr = ctxt_sys_reg(guest_ctxt, PMBSR_EL1);
 	pmblimitr = ctxt_sys_reg(guest_ctxt, PMBLIMITR_EL1);
-	if (__kvm_spe_profiling_buffer_enabled(pmblimitr, pmbsr)) {
+	if (__kvm_spe_profiling_buffer_enabled(pmblimitr, pmbsr) &&
+	    !vcpu->arch.vcpu_spe.buffer_misprogrammed) {
 		write_sysreg_s(ctxt_sys_reg(guest_ctxt, PMBPTR_EL1), SYS_PMBPTR_EL1);
 		isb();
 		write_sysreg_s(pmblimitr, SYS_PMBLIMITR_EL1);
@@ -150,7 +151,8 @@ void __kvm_spe_save_guest_state_vhe(struct kvm_vcpu *vcpu, struct kvm_cpu_contex
 	 * Update PMBPTR_EL1 and PMBSR_EL1 only if profiling was enabled
 	 * when the guest state was resumed.
 	 */
-	if (__kvm_spe_profiling_buffer_enabled(pmblimitr, pmbsr)) {
+	if (__kvm_spe_profiling_buffer_enabled(pmblimitr, pmbsr) &&
+	    !vcpu->arch.vcpu_spe.buffer_misprogrammed) {
 		psb_csync();
 		dsb(nsh);
 		write_sysreg_s(0, SYS_PMBLIMITR_EL1);
diff --git a/arch/arm64/kvm/spe.c b/arch/arm64/kvm/spe.c
index 3e82edc8be85..ef138562f939 100644
--- a/arch/arm64/kvm/spe.c
+++ b/arch/arm64/kvm/spe.c
@@ -170,6 +170,25 @@ u64 kvm_spe_reset_sysreg(struct kvm_vcpu *vcpu, int reg)
 	return 0;
 }
 
+static void kvm_spe_apply_workaround_2004055(struct kvm_vcpu *vcpu)
+{
+	u64 pmblimitr_el1 = __vcpu_sys_reg(vcpu, PMBLIMITR_EL1);
+	u64 pmbsr_el1 = __vcpu_sys_reg(vcpu, PMBSR_EL1);
+	u64 ptr, limit;
+
+	if (FIELD_GET(PMBSR_EL1_S, pmbsr_el1) ||
+	    !FIELD_GET(PMBLIMITR_EL1_E, pmblimitr_el1))
+		return;
+
+	ptr = FIELD_GET(PMBPTR_EL1_PTR, __vcpu_sys_reg(vcpu, PMBPTR_EL1));
+	limit = FIELD_GET(PMBLIMITR_EL1_LIMIT, pmblimitr_el1) << 12;
+
+	if (ptr >= limit)
+		vcpu->arch.vcpu_spe.buffer_misprogrammed = true;
+	else
+		vcpu->arch.vcpu_spe.buffer_misprogrammed = false;
+}
+
 bool kvm_spe_write_sysreg(struct kvm_vcpu *vcpu, int reg, u64 val)
 {
 	struct kvm_spe *kvm_spe = &vcpu->kvm->arch.kvm_spe;
@@ -186,6 +205,9 @@ bool kvm_spe_write_sysreg(struct kvm_vcpu *vcpu, int reg, u64 val)
 		WARN_ON_ONCE("unexpected trap");
 	}
 
+	if (unlikely(kvm_spe->spe_pmu->has_workaround_2004055))
+		kvm_spe_apply_workaround_2004055(vcpu);
+
 	if (reg == PMBSR_EL1) {
 		val = __vcpu_sys_reg(vcpu, PMBSR_EL1);
 		kvm_spe_update_irq_level(vcpu, FIELD_GET(PMBSR_EL1_S, val));
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 5f997851b6d7..e001a6ea5564 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -133,3 +133,4 @@ WORKAROUND_SPECULATIVE_AT
 WORKAROUND_SPECULATIVE_SSBS
 WORKAROUND_SPECULATIVE_UNPRIV_LOAD
 WORKAROUND_3023823
+WORKAROUND_2004055
diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c
index 928ea94f2ed6..bca8cd3bca16 100644
--- a/drivers/perf/arm_spe_pmu.c
+++ b/drivers/perf/arm_spe_pmu.c
@@ -1201,6 +1201,9 @@ static void __arm_spe_pmu_dev_probe(void *info)
 	if (this_cpu_has_cap(ARM64_WORKAROUND_3023823))
 		spe_pmu->has_workaround_3023823 = true;
 
+	if (this_cpu_has_cap(ARM64_WORKAROUND_2004055))
+		spe_pmu->has_workaround_2004055 = true;
+
 	dev_info(dev,
 		 "probed SPEv1.%d for CPUs %*pbl [max_record_sz %u, align %u, features 0x%llx]\n",
 		 spe_pmu->pmsver - 1, cpumask_pr_args(&spe_pmu->supported_cpus),
diff --git a/include/linux/perf/arm_spe_pmu.h b/include/linux/perf/arm_spe_pmu.h
index edf58c51fb94..d5c510ebf785 100644
--- a/include/linux/perf/arm_spe_pmu.h
+++ b/include/linux/perf/arm_spe_pmu.h
@@ -46,6 +46,7 @@ struct arm_spe_pmu {
 	u16					align;
 	struct perf_output_handle __percpu	*handle;
 	bool					has_workaround_3023823;
+	bool					has_workaround_2004055;
 };
 
 #define to_spe_pmu(p) (container_of(p, struct arm_spe_pmu, pmu))
-- 
2.43.0




More information about the linux-arm-kernel mailing list