* [PATCH 0/4] target/arm: Fix handling of HCR.TID* on v7A CPUs
@ 2025-12-31 17:08 Peter Maydell
2025-12-31 17:08 ` [PATCH 1/4] target/arm: Don't specify ID_PFR1 accessfn twice Peter Maydell
` (3 more replies)
0 siblings, 4 replies; 17+ messages in thread
From: Peter Maydell @ 2025-12-31 17:08 UTC (permalink / raw)
To: qemu-arm, qemu-devel; +Cc: qemu-stable
The HCR.TID* bits are intended to cause trapping of various
register accesses to EL2. Their behaviour changed slightly
between v7A and v8A. In particular for HCR.TID3:
* v7A has a set of ID regs that definitely must trap:
- ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3},
ID_ISAR{0,1,2,3,4,5}, MVFR{0,1}
and somewhat vaguely says that "there is no requirement"
to trap for registers that are reserved in the ID reg space
(i.e. which RAZ and might be used for new ID regs in future)
* v8A adds to this list:
- ID_PFR2 and MVFR2 must trap
- ID_MMFR4, ID_MMFR5, ID_ISAR6, ID_DFR1 and reserved registers
in the ID reg space must trap if FEAT_FGT is implemented,
and it is IMPDEF if they trap if FEAT_FGT is not implemented
In QEMU we attempted to implement the v7A/v8A distinction via
accessfns like access_aa64_tid3() and access_aa32_tid3(). However,
we didn't use the right accessfns on the right registers, with
the effect that for a v7A CPU we don't trap on a lot of the
registers we should trap on, and we do trap on various things
that the v7A Arm ARM says there is no requirement to trap on.
This bug came to light because a recent change to Xen to trap
guest accesses to ID_PFR0 broke on QEMU's Cortex-A15 model.
This series sorts things out so that for v7A we trap only on the
things the architecture says we must trap on, and cleans up the
naming pattern and documentation of the accessfns so that
hopefully they don't get accidentally applied to the wrong
registers for any future additions.
thanks
-- PMM
Peter Maydell (4):
target/arm: Don't specify ID_PFR1 accessfn twice
target/arm: Correctly honour HCR.TID3 for v7A cores
target/arm: Correctly trap HCR.TID1 registers in v7A
target/arm: Rename access_aa64_tid5() to access_tid5()
target/arm/helper.c | 177 +++++++++++++++++++++++---------------------
1 file changed, 91 insertions(+), 86 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 1/4] target/arm: Don't specify ID_PFR1 accessfn twice
2025-12-31 17:08 [PATCH 0/4] target/arm: Fix handling of HCR.TID* on v7A CPUs Peter Maydell
@ 2025-12-31 17:08 ` Peter Maydell
2026-01-02 10:56 ` Alex Bennée
2026-01-05 3:57 ` Richard Henderson
2025-12-31 17:08 ` [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores Peter Maydell
` (2 subsequent siblings)
3 siblings, 2 replies; 17+ messages in thread
From: Peter Maydell @ 2025-12-31 17:08 UTC (permalink / raw)
To: qemu-arm, qemu-devel; +Cc: qemu-stable
In the definition of ID_PFR1 we have an ifdef block; we specify the
accessfn once in the common part of the ifdef and once in the
not-user-only part, which is redundant but harmless.
The accessfn will always return success in user-only mode (because
we won't trap to EL2), so specify it only in the not-user-only
half of the ifdef, as was probably the intention.
This is only cc'd to stable to avoid a textual conflict with
the following patch, which is a bug fix.
Cc: qemu-stable@nongnu.org
Fixes: 0f150c8499e970bd ("target/arm: Constify ID_PFR1 on user emulation")
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/helper.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 27ebc6f29b..ec82ea6203 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6296,7 +6296,6 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "ID_PFR1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_NO_RAW,
- .accessfn = access_aa32_tid3,
#ifdef CONFIG_USER_ONLY
.type = ARM_CP_CONST,
.resetvalue = GET_IDREG(isar, ID_PFR1),
--
2.47.3
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores
2025-12-31 17:08 [PATCH 0/4] target/arm: Fix handling of HCR.TID* on v7A CPUs Peter Maydell
2025-12-31 17:08 ` [PATCH 1/4] target/arm: Don't specify ID_PFR1 accessfn twice Peter Maydell
@ 2025-12-31 17:08 ` Peter Maydell
2026-01-02 11:17 ` Alex Bennée
2026-01-05 4:27 ` Richard Henderson
2025-12-31 17:08 ` [PATCH 3/4] target/arm: Correctly trap HCR.TID1 registers in v7A Peter Maydell
2025-12-31 17:08 ` [PATCH 4/4] target/arm: Rename access_aa64_tid5() to access_tid5() Peter Maydell
3 siblings, 2 replies; 17+ messages in thread
From: Peter Maydell @ 2025-12-31 17:08 UTC (permalink / raw)
To: qemu-arm, qemu-devel; +Cc: qemu-stable
The HCR.TID3 bit defines that we should trap to the hypervisor for
reads to a collection of ID registers. Different architecture versions
have defined this differently:
* v7A has a set of ID regs that definitely must trap:
- ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3},
ID_ISAR{0,1,2,3,4,5}, MVFR{0,1}
and somewhat vaguely says that "there is no requirement"
to trap for registers that are reserved in the ID reg space
(i.e. which RAZ and might be used for new ID regs in future)
* v8A adds to this list:
- ID_PFR2 and MVFR2 must trap
- ID_MMFR4, ID_MMFR5, ID_ISAR6, ID_DFR1 and reserved registers
in the ID reg space must trap if FEAT_FGT is implemented,
and it is IMPDEF if they trap if FEAT_FGT is not implemented
In QEMU we seem to have attempted to implement this distinction
(taking the "we do trap" IMPDEF choice if no FEAT_FGT), with
access_aa64_tid3() always trapping on TID3 and access_aa32_tid3()
trapping only if ARM_FEATURE_V8 is set. However, we didn't apply
these to the right set of registers: we use access_aa32_tid3() on all
the 32-bit ID registers *except* ID_PFR2, ID_DFR1, ID_MMFR5 and the
RES0 space, which means that for a v7 CPU we don't trap on a lot of
registers that we should trap on, and we do trap on various things
that the v7A Arm ARM says there is "no requirement" to trap on.
Straighten this out by naming the access functions more clearly for
their purpose, and documenting this: access_v7_tid3() is only for the
fixed set of ID registers that v7A traps on HCR.TID3, and
access_tid3() is for any others, including the reserved encoding
spaces and any new registers we add in future.
AArch32 MVFR2 access is handled differently, in check_hcr_el2_trap;
there we already do not trap on TID3 on v7A cores (where MVFR2
doesn't exist), because we in the code-generation function we UNDEF
if ARM_FEATURE_V8 is not set, without generating code to call
check_hcr_el2_trap.
This bug was causing a problem for Xen which (after a recent change
to Xen) expects to be able to trap ID_PFR0 on a Cortex-A15.
The result of these changes is that our v8A behaviour remains
the same, and on v7A we now trap the registers the Arm ARM definitely
requires us to trap, and don't trap the reserved space that "there is
no requirement" to trap.
Cc: qemu-stable@nongnu.org
Fixes: 6a4ef4e5d1084c ("target/arm: Honor HCR_EL2.TID3 trapping requirements")
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/helper.c | 146 ++++++++++++++++++++++++--------------------
1 file changed, 81 insertions(+), 65 deletions(-)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index ec82ea6203..c4f73eb3f3 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -5835,9 +5835,18 @@ static const ARMCPRegInfo ccsidr2_reginfo[] = {
.readfn = ccsidr2_read, .type = ARM_CP_NO_RAW },
};
-static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
- bool isread)
+static CPAccessResult access_v7_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
{
+ /*
+ * Trap on TID3 always. This should be used only for the fixed set of
+ * registers which are defined to trap on HCR.TID3 in v7A, which is:
+ * ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3}, ID_ISAR{0,1,2,3,4,5}
+ * (MVFR0 and MVFR1 also trap in v7A, but this is not handled via
+ * this accessfn but in check_hcr_el2_trap.)
+ * Any other registers in the TID3 trap space should use access_tid3(),
+ * so that they trap on v8 and above, but not on v7.
+ */
if ((arm_current_el(env) < 2) && (arm_hcr_el2_eff(env) & HCR_TID3)) {
return CP_ACCESS_TRAP_EL2;
}
@@ -5845,11 +5854,18 @@ static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK;
}
-static CPAccessResult access_aa32_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
- bool isread)
+static CPAccessResult access_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
{
+ /*
+ * Trap on TID3, if we implement at least v8. For v8 and above
+ * the ID register space is at least IMPDEF permitted to trap,
+ * and must trap if FEAT_FGT is implemented. We choose to trap
+ * always. Use this function for any new registers that should
+ * trap on TID3.
+ */
if (arm_feature(env, ARM_FEATURE_V8)) {
- return access_aa64_tid3(env, ri, isread);
+ return access_v7_tid3(env, ri, isread);
}
return CP_ACCESS_OK;
@@ -6287,7 +6303,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "ID_PFR0", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_PFR0)},
/*
* ID_PFR1 is not a plain ARM_CP_CONST because we don't know
@@ -6301,7 +6317,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.resetvalue = GET_IDREG(isar, ID_PFR1),
#else
.type = ARM_CP_NO_RAW,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.readfn = id_pfr1_read,
.writefn = arm_cp_write_ignore
#endif
@@ -6309,72 +6325,72 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "ID_DFR0", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_DFR0)},
{ .name = "ID_AFR0", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_AFR0)},
{ .name = "ID_MMFR0", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_MMFR0)},
{ .name = "ID_MMFR1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_MMFR1)},
{ .name = "ID_MMFR2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_MMFR2)},
{ .name = "ID_MMFR3", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_MMFR3)},
{ .name = "ID_ISAR0", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_ISAR0)},
{ .name = "ID_ISAR1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_ISAR1)},
{ .name = "ID_ISAR2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_ISAR2)},
{ .name = "ID_ISAR3", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_ISAR3) },
{ .name = "ID_ISAR4", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_ISAR4) },
{ .name = "ID_ISAR5", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_v7_tid3,
.resetvalue = GET_IDREG(isar, ID_ISAR5) },
{ .name = "ID_MMFR4", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_MMFR4)},
{ .name = "ID_ISAR6", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa32_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_ISAR6) },
};
define_arm_cp_regs(cpu, v6_idregs);
@@ -6425,7 +6441,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.resetvalue = GET_IDREG(isar, ID_AA64PFR0)
#else
.type = ARM_CP_NO_RAW,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.readfn = id_aa64pfr0_read,
.writefn = arm_cp_write_ignore
#endif
@@ -6433,172 +6449,172 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "ID_AA64PFR1_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64PFR1)},
{ .name = "ID_AA64PFR2_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64PFR2)},
{ .name = "ID_AA64PFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64ZFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64ZFR0)},
{ .name = "ID_AA64SMFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64SMFR0)},
{ .name = "ID_AA64PFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64PFR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64DFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64DFR0) },
{ .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64DFR1) },
{ .name = "ID_AA64DFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64DFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64AFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64AFR0) },
{ .name = "ID_AA64AFR1_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64AFR1) },
{ .name = "ID_AA64AFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64AFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64ISAR0)},
{ .name = "ID_AA64ISAR1_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64ISAR1)},
{ .name = "ID_AA64ISAR2_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64ISAR2)},
{ .name = "ID_AA64ISAR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64MMFR0)},
{ .name = "ID_AA64MMFR1_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64MMFR1) },
{ .name = "ID_AA64MMFR2_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64MMFR2) },
{ .name = "ID_AA64MMFR3_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_AA64MMFR3) },
{ .name = "ID_AA64MMFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "MVFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = cpu->isar.mvfr0 },
{ .name = "MVFR1_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = cpu->isar.mvfr1 },
{ .name = "MVFR2_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = cpu->isar.mvfr2 },
/*
* "0, c0, c3, {0,1,2}" are the encodings corresponding to
@@ -6609,17 +6625,17 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "RES_0_C0_C3_0", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "RES_0_C0_C3_1", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "RES_0_C0_C3_2", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
/*
* Other encodings in "0, c0, c3, ..." are STATE_BOTH because
@@ -6630,27 +6646,27 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "RES_0_C0_C3_3", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
{ .name = "ID_PFR2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_PFR2)},
{ .name = "ID_DFR1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_DFR1)},
{ .name = "ID_MMFR5", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = GET_IDREG(isar, ID_MMFR5)},
{ .name = "RES_0_C0_C3_7", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 },
};
#ifdef CONFIG_USER_ONLY
@@ -6800,7 +6816,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 0, .crn = 0, .crm = i, .opc2 = CP_ANY,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid3,
+ .accessfn = access_tid3,
.resetvalue = 0 };
define_one_arm_cp_reg(cpu, &v8_aa32_raz_idregs);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 3/4] target/arm: Correctly trap HCR.TID1 registers in v7A
2025-12-31 17:08 [PATCH 0/4] target/arm: Fix handling of HCR.TID* on v7A CPUs Peter Maydell
2025-12-31 17:08 ` [PATCH 1/4] target/arm: Don't specify ID_PFR1 accessfn twice Peter Maydell
2025-12-31 17:08 ` [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores Peter Maydell
@ 2025-12-31 17:08 ` Peter Maydell
2026-01-02 11:21 ` Alex Bennée
2026-01-05 4:29 ` Richard Henderson
2025-12-31 17:08 ` [PATCH 4/4] target/arm: Rename access_aa64_tid5() to access_tid5() Peter Maydell
3 siblings, 2 replies; 17+ messages in thread
From: Peter Maydell @ 2025-12-31 17:08 UTC (permalink / raw)
To: qemu-arm, qemu-devel; +Cc: qemu-stable
In v7A HCR.TID1 is defined to trap for TCMTR, TLBTR, REVIDR and AIDR.
We incorrectly use an accessfn for REVIDR and AIDR that only traps on
v8A cores. Fix this by collapsing access_aa64_tid1() and
access_aa32_tid1() together and never doing a check for v8 vs v7.
The accessfn is also used for SMIDR_EL1, which is fine as this
register is AArch64 only.
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/helper.c | 24 +++++++-----------------
1 file changed, 7 insertions(+), 17 deletions(-)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index c4f73eb3f3..0896e90965 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -924,8 +924,8 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri)
return ret;
}
-static CPAccessResult access_aa64_tid1(CPUARMState *env, const ARMCPRegInfo *ri,
- bool isread)
+static CPAccessResult access_tid1(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
{
if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TID1)) {
return CP_ACCESS_TRAP_EL2;
@@ -934,16 +934,6 @@ static CPAccessResult access_aa64_tid1(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK;
}
-static CPAccessResult access_aa32_tid1(CPUARMState *env, const ARMCPRegInfo *ri,
- bool isread)
-{
- if (arm_feature(env, ARM_FEATURE_V8)) {
- return access_aa64_tid1(env, ri, isread);
- }
-
- return CP_ACCESS_OK;
-}
-
static const ARMCPRegInfo v7_cp_reginfo[] = {
/* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */
{ .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4,
@@ -969,7 +959,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
{ .name = "AIDR", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
- .accessfn = access_aa64_tid1,
+ .accessfn = access_tid1,
.fgt = FGT_AIDR_EL1,
.resetvalue = 0 },
/*
@@ -4997,7 +4987,7 @@ static const ARMCPRegInfo sme_reginfo[] = {
.writefn = smcr_write, .raw_writefn = raw_write },
{ .name = "SMIDR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 6,
- .access = PL1_R, .accessfn = access_aa64_tid1,
+ .access = PL1_R, .accessfn = access_tid1,
/*
* IMPLEMENTOR = 0 (software)
* REVISION = 0 (implementation defined)
@@ -7094,7 +7084,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "REVIDR_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 6,
.access = PL1_R,
- .accessfn = access_aa64_tid1,
+ .accessfn = access_tid1,
.fgt = FGT_REVIDR_EL1,
.type = ARM_CP_CONST, .resetvalue = cpu->revidr },
};
@@ -7118,7 +7108,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "TCMTR",
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2,
.access = PL1_R,
- .accessfn = access_aa32_tid1,
+ .accessfn = access_tid1,
.type = ARM_CP_CONST, .resetvalue = 0 },
};
/* TLBTR is specific to VMSA */
@@ -7126,7 +7116,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.name = "TLBTR",
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3,
.access = PL1_R,
- .accessfn = access_aa32_tid1,
+ .accessfn = access_tid1,
.type = ARM_CP_CONST, .resetvalue = 0,
};
/* MPUIR is specific to PMSA V6+ */
--
2.47.3
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 4/4] target/arm: Rename access_aa64_tid5() to access_tid5()
2025-12-31 17:08 [PATCH 0/4] target/arm: Fix handling of HCR.TID* on v7A CPUs Peter Maydell
` (2 preceding siblings ...)
2025-12-31 17:08 ` [PATCH 3/4] target/arm: Correctly trap HCR.TID1 registers in v7A Peter Maydell
@ 2025-12-31 17:08 ` Peter Maydell
2026-01-02 11:21 ` Alex Bennée
2026-01-05 4:30 ` Richard Henderson
3 siblings, 2 replies; 17+ messages in thread
From: Peter Maydell @ 2025-12-31 17:08 UTC (permalink / raw)
To: qemu-arm, qemu-devel; +Cc: qemu-stable
There is no equivalent access_aa32_tid5() (HCR_EL2.TID5 only exists
starting from v8); rename access_aa64_tid5() to access_tid5() to line
up with the naming we now have for the TID1 and TID3 check functions.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/helper.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 0896e90965..b914988ac9 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -5425,8 +5425,8 @@ static const ARMCPRegInfo dcpodp_reg[] = {
.accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn },
};
-static CPAccessResult access_aa64_tid5(CPUARMState *env, const ARMCPRegInfo *ri,
- bool isread)
+static CPAccessResult access_tid5(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
{
if ((arm_current_el(env) < 2) && (arm_hcr_el2_eff(env) & HCR_TID5)) {
return CP_ACCESS_TRAP_EL2;
@@ -7449,7 +7449,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
ARMCPRegInfo gmid_reginfo = {
.name = "GMID_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 4,
- .access = PL1_R, .accessfn = access_aa64_tid5,
+ .access = PL1_R, .accessfn = access_tid5,
.type = ARM_CP_CONST, .resetvalue = cpu->gm_blocksize,
};
define_one_arm_cp_reg(cpu, &gmid_reginfo);
--
2.47.3
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH 1/4] target/arm: Don't specify ID_PFR1 accessfn twice
2025-12-31 17:08 ` [PATCH 1/4] target/arm: Don't specify ID_PFR1 accessfn twice Peter Maydell
@ 2026-01-02 10:56 ` Alex Bennée
2026-01-05 3:57 ` Richard Henderson
1 sibling, 0 replies; 17+ messages in thread
From: Alex Bennée @ 2026-01-02 10:56 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-arm, qemu-devel, qemu-stable
Peter Maydell <peter.maydell@linaro.org> writes:
> In the definition of ID_PFR1 we have an ifdef block; we specify the
> accessfn once in the common part of the ifdef and once in the
> not-user-only part, which is redundant but harmless.
>
> The accessfn will always return success in user-only mode (because
> we won't trap to EL2), so specify it only in the not-user-only
> half of the ifdef, as was probably the intention.
>
> This is only cc'd to stable to avoid a textual conflict with
> the following patch, which is a bug fix.
>
> Cc: qemu-stable@nongnu.org
> Fixes: 0f150c8499e970bd ("target/arm: Constify ID_PFR1 on user emulation")
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores
2025-12-31 17:08 ` [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores Peter Maydell
@ 2026-01-02 11:17 ` Alex Bennée
2026-01-12 18:51 ` Peter Maydell
2026-01-05 4:27 ` Richard Henderson
1 sibling, 1 reply; 17+ messages in thread
From: Alex Bennée @ 2026-01-02 11:17 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-arm, qemu-devel, qemu-stable
Peter Maydell <peter.maydell@linaro.org> writes:
> The HCR.TID3 bit defines that we should trap to the hypervisor for
> reads to a collection of ID registers. Different architecture versions
> have defined this differently:
>
> * v7A has a set of ID regs that definitely must trap:
> - ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3},
> ID_ISAR{0,1,2,3,4,5}, MVFR{0,1}
> and somewhat vaguely says that "there is no requirement"
> to trap for registers that are reserved in the ID reg space
> (i.e. which RAZ and might be used for new ID regs in future)
> * v8A adds to this list:
> - ID_PFR2 and MVFR2 must trap
> - ID_MMFR4, ID_MMFR5, ID_ISAR6, ID_DFR1 and reserved registers
> in the ID reg space must trap if FEAT_FGT is implemented,
> and it is IMPDEF if they trap if FEAT_FGT is not implemented
>
> In QEMU we seem to have attempted to implement this distinction
> (taking the "we do trap" IMPDEF choice if no FEAT_FGT), with
> access_aa64_tid3() always trapping on TID3 and access_aa32_tid3()
> trapping only if ARM_FEATURE_V8 is set. However, we didn't apply
> these to the right set of registers: we use access_aa32_tid3() on all
> the 32-bit ID registers *except* ID_PFR2, ID_DFR1, ID_MMFR5 and the
> RES0 space, which means that for a v7 CPU we don't trap on a lot of
> registers that we should trap on, and we do trap on various things
> that the v7A Arm ARM says there is "no requirement" to trap on.
>
> Straighten this out by naming the access functions more clearly for
> their purpose, and documenting this: access_v7_tid3() is only for the
> fixed set of ID registers that v7A traps on HCR.TID3, and
> access_tid3() is for any others, including the reserved encoding
> spaces and any new registers we add in future.
I'm confused by the naming - especially as searching the Arm doc site
with the Armv7-A filter didn't show up an HCR register (although it does
show up in the PDF).
I guess what you are saying is these registers trap from v7a onwards?
v8/v9 don't change the trapping on this subset of registers.
>
> AArch32 MVFR2 access is handled differently, in check_hcr_el2_trap;
> there we already do not trap on TID3 on v7A cores (where MVFR2
> doesn't exist), because we in the code-generation function we UNDEF
> if ARM_FEATURE_V8 is not set, without generating code to call
> check_hcr_el2_trap.
>
> This bug was causing a problem for Xen which (after a recent change
> to Xen) expects to be able to trap ID_PFR0 on a Cortex-A15.
>
> The result of these changes is that our v8A behaviour remains
> the same, and on v7A we now trap the registers the Arm ARM definitely
> requires us to trap, and don't trap the reserved space that "there is
> no requirement" to trap.
>
> Cc: qemu-stable@nongnu.org
> Fixes: 6a4ef4e5d1084c ("target/arm: Honor HCR_EL2.TID3 trapping requirements")
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> target/arm/helper.c | 146 ++++++++++++++++++++++++--------------------
> 1 file changed, 81 insertions(+), 65 deletions(-)
>
> diff --git a/target/arm/helper.c b/target/arm/helper.c
> index ec82ea6203..c4f73eb3f3 100644
> --- a/target/arm/helper.c
> +++ b/target/arm/helper.c
> @@ -5835,9 +5835,18 @@ static const ARMCPRegInfo ccsidr2_reginfo[] = {
> .readfn = ccsidr2_read, .type = ARM_CP_NO_RAW },
> };
>
> -static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> - bool isread)
> +static CPAccessResult access_v7_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> + bool isread)
> {
> + /*
> + * Trap on TID3 always. This should be used only for the fixed set of
> + * registers which are defined to trap on HCR.TID3 in v7A, which is:
> + * ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3}, ID_ISAR{0,1,2,3,4,5}
> + * (MVFR0 and MVFR1 also trap in v7A, but this is not handled via
> + * this accessfn but in check_hcr_el2_trap.)
> + * Any other registers in the TID3 trap space should use access_tid3(),
> + * so that they trap on v8 and above, but not on v7.
> + */
> if ((arm_current_el(env) < 2) && (arm_hcr_el2_eff(env) & HCR_TID3)) {
> return CP_ACCESS_TRAP_EL2;
> }
> @@ -5845,11 +5854,18 @@ static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> return CP_ACCESS_OK;
> }
>
> -static CPAccessResult access_aa32_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> - bool isread)
> +static CPAccessResult access_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> + bool isread)
> {
> + /*
> + * Trap on TID3, if we implement at least v8. For v8 and above
> + * the ID register space is at least IMPDEF permitted to trap,
> + * and must trap if FEAT_FGT is implemented. We choose to trap
> + * always. Use this function for any new registers that should
> + * trap on TID3.
> + */
> if (arm_feature(env, ARM_FEATURE_V8)) {
> - return access_aa64_tid3(env, ri, isread);
> + return access_v7_tid3(env, ri, isread);
This seems even more incongruous - we test for v8 but use the v7 helper.
<snip>
I think I understand whats happening but it is confusing to follow.
Naming things is hard.
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 3/4] target/arm: Correctly trap HCR.TID1 registers in v7A
2025-12-31 17:08 ` [PATCH 3/4] target/arm: Correctly trap HCR.TID1 registers in v7A Peter Maydell
@ 2026-01-02 11:21 ` Alex Bennée
2026-01-05 4:29 ` Richard Henderson
1 sibling, 0 replies; 17+ messages in thread
From: Alex Bennée @ 2026-01-02 11:21 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-arm, qemu-devel, qemu-stable
Peter Maydell <peter.maydell@linaro.org> writes:
> In v7A HCR.TID1 is defined to trap for TCMTR, TLBTR, REVIDR and AIDR.
> We incorrectly use an accessfn for REVIDR and AIDR that only traps on
> v8A cores. Fix this by collapsing access_aa64_tid1() and
> access_aa32_tid1() together and never doing a check for v8 vs v7.
>
> The accessfn is also used for SMIDR_EL1, which is fine as this
> register is AArch64 only.
>
> Cc: qemu-stable@nongnu.org
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> target/arm/helper.c | 24 +++++++-----------------
> 1 file changed, 7 insertions(+), 17 deletions(-)
>
> diff --git a/target/arm/helper.c b/target/arm/helper.c
> index c4f73eb3f3..0896e90965 100644
> --- a/target/arm/helper.c
> +++ b/target/arm/helper.c
> @@ -924,8 +924,8 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri)
> return ret;
> }
>
> -static CPAccessResult access_aa64_tid1(CPUARMState *env, const ARMCPRegInfo *ri,
> - bool isread)
> +static CPAccessResult access_tid1(CPUARMState *env, const ARMCPRegInfo *ri,
> + bool isread)
> {
> if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TID1)) {
> return CP_ACCESS_TRAP_EL2;
> @@ -934,16 +934,6 @@ static CPAccessResult access_aa64_tid1(CPUARMState *env, const ARMCPRegInfo *ri,
> return CP_ACCESS_OK;
> }
>
> -static CPAccessResult access_aa32_tid1(CPUARMState *env, const ARMCPRegInfo *ri,
> - bool isread)
> -{
> - if (arm_feature(env, ARM_FEATURE_V8)) {
> - return access_aa64_tid1(env, ri, isread);
> - }
> -
> - return CP_ACCESS_OK;
> -}
> -
This logic makes more sense from the descriptions compared to 2/4.
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 4/4] target/arm: Rename access_aa64_tid5() to access_tid5()
2025-12-31 17:08 ` [PATCH 4/4] target/arm: Rename access_aa64_tid5() to access_tid5() Peter Maydell
@ 2026-01-02 11:21 ` Alex Bennée
2026-01-05 4:30 ` Richard Henderson
1 sibling, 0 replies; 17+ messages in thread
From: Alex Bennée @ 2026-01-02 11:21 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-arm, qemu-devel, qemu-stable
Peter Maydell <peter.maydell@linaro.org> writes:
> There is no equivalent access_aa32_tid5() (HCR_EL2.TID5 only exists
> starting from v8); rename access_aa64_tid5() to access_tid5() to line
> up with the naming we now have for the TID1 and TID3 check functions.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 1/4] target/arm: Don't specify ID_PFR1 accessfn twice
2025-12-31 17:08 ` [PATCH 1/4] target/arm: Don't specify ID_PFR1 accessfn twice Peter Maydell
2026-01-02 10:56 ` Alex Bennée
@ 2026-01-05 3:57 ` Richard Henderson
1 sibling, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2026-01-05 3:57 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel; +Cc: qemu-stable
On 1/1/26 04:08, Peter Maydell wrote:
> In the definition of ID_PFR1 we have an ifdef block; we specify the
> accessfn once in the common part of the ifdef and once in the
> not-user-only part, which is redundant but harmless.
>
> The accessfn will always return success in user-only mode (because
> we won't trap to EL2), so specify it only in the not-user-only
> half of the ifdef, as was probably the intention.
>
> This is only cc'd to stable to avoid a textual conflict with
> the following patch, which is a bug fix.
>
> Cc:qemu-stable@nongnu.org
> Fixes: 0f150c8499e970bd ("target/arm: Constify ID_PFR1 on user emulation")
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> target/arm/helper.c | 1 -
> 1 file changed, 1 deletion(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores
2025-12-31 17:08 ` [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores Peter Maydell
2026-01-02 11:17 ` Alex Bennée
@ 2026-01-05 4:27 ` Richard Henderson
1 sibling, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2026-01-05 4:27 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel; +Cc: qemu-stable
On 1/1/26 04:08, Peter Maydell wrote:
> The HCR.TID3 bit defines that we should trap to the hypervisor for
> reads to a collection of ID registers. Different architecture versions
> have defined this differently:
>
> * v7A has a set of ID regs that definitely must trap:
> - ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3},
> ID_ISAR{0,1,2,3,4,5}, MVFR{0,1}
> and somewhat vaguely says that "there is no requirement"
> to trap for registers that are reserved in the ID reg space
> (i.e. which RAZ and might be used for new ID regs in future)
> * v8A adds to this list:
> - ID_PFR2 and MVFR2 must trap
> - ID_MMFR4, ID_MMFR5, ID_ISAR6, ID_DFR1 and reserved registers
> in the ID reg space must trap if FEAT_FGT is implemented,
> and it is IMPDEF if they trap if FEAT_FGT is not implemented
>
> In QEMU we seem to have attempted to implement this distinction
> (taking the "we do trap" IMPDEF choice if no FEAT_FGT), with
> access_aa64_tid3() always trapping on TID3 and access_aa32_tid3()
> trapping only if ARM_FEATURE_V8 is set. However, we didn't apply
> these to the right set of registers: we use access_aa32_tid3() on all
> the 32-bit ID registers*except* ID_PFR2, ID_DFR1, ID_MMFR5 and the
> RES0 space, which means that for a v7 CPU we don't trap on a lot of
> registers that we should trap on, and we do trap on various things
> that the v7A Arm ARM says there is "no requirement" to trap on.
>
> Straighten this out by naming the access functions more clearly for
> their purpose, and documenting this: access_v7_tid3() is only for the
> fixed set of ID registers that v7A traps on HCR.TID3, and
> access_tid3() is for any others, including the reserved encoding
> spaces and any new registers we add in future.
>
> AArch32 MVFR2 access is handled differently, in check_hcr_el2_trap;
> there we already do not trap on TID3 on v7A cores (where MVFR2
> doesn't exist), because we in the code-generation function we UNDEF
> if ARM_FEATURE_V8 is not set, without generating code to call
> check_hcr_el2_trap.
>
> This bug was causing a problem for Xen which (after a recent change
> to Xen) expects to be able to trap ID_PFR0 on a Cortex-A15.
>
> The result of these changes is that our v8A behaviour remains
> the same, and on v7A we now trap the registers the Arm ARM definitely
> requires us to trap, and don't trap the reserved space that "there is
> no requirement" to trap.
>
> Cc:qemu-stable@nongnu.org
> Fixes: 6a4ef4e5d1084c ("target/arm: Honor HCR_EL2.TID3 trapping requirements")
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> target/arm/helper.c | 146 ++++++++++++++++++++++++--------------------
> 1 file changed, 81 insertions(+), 65 deletions(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 3/4] target/arm: Correctly trap HCR.TID1 registers in v7A
2025-12-31 17:08 ` [PATCH 3/4] target/arm: Correctly trap HCR.TID1 registers in v7A Peter Maydell
2026-01-02 11:21 ` Alex Bennée
@ 2026-01-05 4:29 ` Richard Henderson
1 sibling, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2026-01-05 4:29 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel; +Cc: qemu-stable
On 1/1/26 04:08, Peter Maydell wrote:
> In v7A HCR.TID1 is defined to trap for TCMTR, TLBTR, REVIDR and AIDR.
> We incorrectly use an accessfn for REVIDR and AIDR that only traps on
> v8A cores. Fix this by collapsing access_aa64_tid1() and
> access_aa32_tid1() together and never doing a check for v8 vs v7.
>
> The accessfn is also used for SMIDR_EL1, which is fine as this
> register is AArch64 only.
>
> Cc:qemu-stable@nongnu.org
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> target/arm/helper.c | 24 +++++++-----------------
> 1 file changed, 7 insertions(+), 17 deletions(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 4/4] target/arm: Rename access_aa64_tid5() to access_tid5()
2025-12-31 17:08 ` [PATCH 4/4] target/arm: Rename access_aa64_tid5() to access_tid5() Peter Maydell
2026-01-02 11:21 ` Alex Bennée
@ 2026-01-05 4:30 ` Richard Henderson
1 sibling, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2026-01-05 4:30 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel; +Cc: qemu-stable
On 1/1/26 04:08, Peter Maydell wrote:
> There is no equivalent access_aa32_tid5() (HCR_EL2.TID5 only exists
> starting from v8); rename access_aa64_tid5() to access_tid5() to line
> up with the naming we now have for the TID1 and TID3 check functions.
>
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> target/arm/helper.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores
2026-01-02 11:17 ` Alex Bennée
@ 2026-01-12 18:51 ` Peter Maydell
2026-01-15 15:20 ` Alex Bennée
0 siblings, 1 reply; 17+ messages in thread
From: Peter Maydell @ 2026-01-12 18:51 UTC (permalink / raw)
To: Alex Bennée; +Cc: qemu-arm, qemu-devel, qemu-stable
On Fri, 2 Jan 2026 at 11:17, Alex Bennée <alex.bennee@linaro.org> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
>
> > The HCR.TID3 bit defines that we should trap to the hypervisor for
> > reads to a collection of ID registers. Different architecture versions
> > have defined this differently:
> >
> > * v7A has a set of ID regs that definitely must trap:
> > - ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3},
> > ID_ISAR{0,1,2,3,4,5}, MVFR{0,1}
> > and somewhat vaguely says that "there is no requirement"
> > to trap for registers that are reserved in the ID reg space
> > (i.e. which RAZ and might be used for new ID regs in future)
> > * v8A adds to this list:
> > - ID_PFR2 and MVFR2 must trap
> > - ID_MMFR4, ID_MMFR5, ID_ISAR6, ID_DFR1 and reserved registers
> > in the ID reg space must trap if FEAT_FGT is implemented,
> > and it is IMPDEF if they trap if FEAT_FGT is not implemented
> >
> > In QEMU we seem to have attempted to implement this distinction
> > (taking the "we do trap" IMPDEF choice if no FEAT_FGT), with
> > access_aa64_tid3() always trapping on TID3 and access_aa32_tid3()
> > trapping only if ARM_FEATURE_V8 is set. However, we didn't apply
> > these to the right set of registers: we use access_aa32_tid3() on all
> > the 32-bit ID registers *except* ID_PFR2, ID_DFR1, ID_MMFR5 and the
> > RES0 space, which means that for a v7 CPU we don't trap on a lot of
> > registers that we should trap on, and we do trap on various things
> > that the v7A Arm ARM says there is "no requirement" to trap on.
> >
> > Straighten this out by naming the access functions more clearly for
> > their purpose, and documenting this: access_v7_tid3() is only for the
> > fixed set of ID registers that v7A traps on HCR.TID3, and
> > access_tid3() is for any others, including the reserved encoding
> > spaces and any new registers we add in future.
>
> I'm confused by the naming - especially as searching the Arm doc site
> with the Armv7-A filter didn't show up an HCR register (although it does
> show up in the PDF).
Not sure why it wouldn't show up -- this is the main hypervisor
trap configuration register, and it's always been HCR for AArch32
and HCR_EL2 for AArch64.
> I guess what you are saying is these registers trap from v7a onwards?
> v8/v9 don't change the trapping on this subset of registers.
I tried to lay this out in the commit message, but basically what
we have is that this trap bit is trapping a set of the ID registers.
In v7A it was specified to trap the ID registers that were defined
at that time, but not the encodings that were reserved in the
ID register space. (As ID register space, 'reserved' means RAZ,
not UNDEF as it would elsewhere in the system register space.)
In v8A some new ID registers were defined in what was previously
the reserved space, and so v8A says that TID3 must trap those also
(and that it IMPDEF is allowed to trap the rest of the reserved space).
Finally, FEAT_FGT fixes up the unhelpful IMPDEF variability by mandating
trapping on the whole space, reserved or not.
(Before v7A HCR didn't exist at all and these ID registers never
trap anywhere: since HCR.TID3 in our implementation will always
be 0 on CPUs without EL2, we don't need to special case "doesn't
actually have Hyp mode" in the access functions.)
> > -static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> > - bool isread)
> > +static CPAccessResult access_v7_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> > + bool isread)
> > {
> > + /*
> > + * Trap on TID3 always. This should be used only for the fixed set of
> > + * registers which are defined to trap on HCR.TID3 in v7A, which is:
> > + * ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3}, ID_ISAR{0,1,2,3,4,5}
> > + * (MVFR0 and MVFR1 also trap in v7A, but this is not handled via
> > + * this accessfn but in check_hcr_el2_trap.)
> > + * Any other registers in the TID3 trap space should use access_tid3(),
> > + * so that they trap on v8 and above, but not on v7.
> > + */
> > if ((arm_current_el(env) < 2) && (arm_hcr_el2_eff(env) & HCR_TID3)) {
> > return CP_ACCESS_TRAP_EL2;
> > }
> > @@ -5845,11 +5854,18 @@ static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> > return CP_ACCESS_OK;
> > }
> >
> > -static CPAccessResult access_aa32_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> > - bool isread)
> > +static CPAccessResult access_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
> > + bool isread)
> > {
> > + /*
> > + * Trap on TID3, if we implement at least v8. For v8 and above
> > + * the ID register space is at least IMPDEF permitted to trap,
> > + * and must trap if FEAT_FGT is implemented. We choose to trap
> > + * always. Use this function for any new registers that should
> > + * trap on TID3.
> > + */
> > if (arm_feature(env, ARM_FEATURE_V8)) {
> > - return access_aa64_tid3(env, ri, isread);
> > + return access_v7_tid3(env, ri, isread);
>
> This seems even more incongruous - we test for v8 but use the v7 helper.
We have two different things we want to do:
(1) always trap if TID3 is set
(2) trap if we are v8 or better and TID3 is set
We want to use function 1 for the set of ID registers that
existed back in v7A -- these are the ones that trap for any
implementation.
We want function 2 for the ID registers that were only defined
in v8A, and for the reserved-ID-register space. These must not
trap on v7A, and either must or are IMPDEF-permitted to trap
on v8A and later.
I have tried to pick function names that make sense for "what
is this doing", and for "if somebody adds a new register here,
make the function they want be the one with a name that seems
most inviting, so they pick the right one, not the wrong one".
So I have access_v7_tid3 for ID registers defined in
v7, and access_tid3 for the rest, including any new ones.
This seemed to me better than picking a function name that
describes the internal implementation of functions 1 and 2,
on the basis that people are a lot more likely to need to
use the functions than look inside them.
If you have suggested names that you think make more sense,
I'm open to them -- since I started by knowing the behaviour
to me the names I ended up with seem more "obvious" to me than
they would to somebody else, and it's the "somebody else" that
I'm trying to help with the naming...
Separately, the implementation of function (2) in this patch
is "if ARM_FEATURE_V8 is set, call function (1) to do the test-TID3,
otherwise return ACCESS_OK to indicate don't-trap". (Inherited
from the existing implementation choice.)
Given that function (1) is only a simple test of
"((arm_current_el(env) < 2) && (arm_hcr_el2_eff(env) & HCR_TID3))"
we could alternatively open-code that check in function (2)
if you think that would be clearer.
thanks
-- PMM
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores
2026-01-12 18:51 ` Peter Maydell
@ 2026-01-15 15:20 ` Alex Bennée
2026-01-15 15:35 ` Peter Maydell
0 siblings, 1 reply; 17+ messages in thread
From: Alex Bennée @ 2026-01-15 15:20 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-arm, qemu-devel, qemu-stable
Peter Maydell <peter.maydell@linaro.org> writes:
> On Fri, 2 Jan 2026 at 11:17, Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> Peter Maydell <peter.maydell@linaro.org> writes:
>>
>> > The HCR.TID3 bit defines that we should trap to the hypervisor for
>> > reads to a collection of ID registers. Different architecture versions
>> > have defined this differently:
>> >
>> > * v7A has a set of ID regs that definitely must trap:
>> > - ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3},
>> > ID_ISAR{0,1,2,3,4,5}, MVFR{0,1}
>> > and somewhat vaguely says that "there is no requirement"
>> > to trap for registers that are reserved in the ID reg space
>> > (i.e. which RAZ and might be used for new ID regs in future)
>> > * v8A adds to this list:
>> > - ID_PFR2 and MVFR2 must trap
>> > - ID_MMFR4, ID_MMFR5, ID_ISAR6, ID_DFR1 and reserved registers
>> > in the ID reg space must trap if FEAT_FGT is implemented,
>> > and it is IMPDEF if they trap if FEAT_FGT is not implemented
>> >
>> > In QEMU we seem to have attempted to implement this distinction
>> > (taking the "we do trap" IMPDEF choice if no FEAT_FGT), with
>> > access_aa64_tid3() always trapping on TID3 and access_aa32_tid3()
>> > trapping only if ARM_FEATURE_V8 is set. However, we didn't apply
>> > these to the right set of registers: we use access_aa32_tid3() on all
>> > the 32-bit ID registers *except* ID_PFR2, ID_DFR1, ID_MMFR5 and the
>> > RES0 space, which means that for a v7 CPU we don't trap on a lot of
>> > registers that we should trap on, and we do trap on various things
>> > that the v7A Arm ARM says there is "no requirement" to trap on.
>> >
>> > Straighten this out by naming the access functions more clearly for
>> > their purpose, and documenting this: access_v7_tid3() is only for the
>> > fixed set of ID registers that v7A traps on HCR.TID3, and
>> > access_tid3() is for any others, including the reserved encoding
>> > spaces and any new registers we add in future.
>>
>> I'm confused by the naming - especially as searching the Arm doc site
>> with the Armv7-A filter didn't show up an HCR register (although it does
>> show up in the PDF).
>
> Not sure why it wouldn't show up -- this is the main hypervisor
> trap configuration register, and it's always been HCR for AArch32
> and HCR_EL2 for AArch64.
>
>> I guess what you are saying is these registers trap from v7a onwards?
>> v8/v9 don't change the trapping on this subset of registers.
>
> I tried to lay this out in the commit message, but basically what
> we have is that this trap bit is trapping a set of the ID registers.
> In v7A it was specified to trap the ID registers that were defined
> at that time, but not the encodings that were reserved in the
> ID register space. (As ID register space, 'reserved' means RAZ,
> not UNDEF as it would elsewhere in the system register space.)
> In v8A some new ID registers were defined in what was previously
> the reserved space, and so v8A says that TID3 must trap those also
> (and that it IMPDEF is allowed to trap the rest of the reserved space).
> Finally, FEAT_FGT fixes up the unhelpful IMPDEF variability by mandating
> trapping on the whole space, reserved or not.
>
> (Before v7A HCR didn't exist at all and these ID registers never
> trap anywhere: since HCR.TID3 in our implementation will always
> be 0 on CPUs without EL2, we don't need to special case "doesn't
> actually have Hyp mode" in the access functions.)
>
>> > -static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
>> > - bool isread)
>> > +static CPAccessResult access_v7_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
>> > + bool isread)
>> > {
>> > + /*
>> > + * Trap on TID3 always. This should be used only for the fixed set of
>> > + * registers which are defined to trap on HCR.TID3 in v7A, which is:
>> > + * ID_PFR{0,1}, ID_DFR0, ID_AFR0, ID_MMFR{0,1,2,3}, ID_ISAR{0,1,2,3,4,5}
>> > + * (MVFR0 and MVFR1 also trap in v7A, but this is not handled via
>> > + * this accessfn but in check_hcr_el2_trap.)
>> > + * Any other registers in the TID3 trap space should use access_tid3(),
>> > + * so that they trap on v8 and above, but not on v7.
>> > + */
>> > if ((arm_current_el(env) < 2) && (arm_hcr_el2_eff(env) & HCR_TID3)) {
>> > return CP_ACCESS_TRAP_EL2;
>> > }
>> > @@ -5845,11 +5854,18 @@ static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
>> > return CP_ACCESS_OK;
>> > }
>> >
>> > -static CPAccessResult access_aa32_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
>> > - bool isread)
>> > +static CPAccessResult access_tid3(CPUARMState *env, const ARMCPRegInfo *ri,
>> > + bool isread)
>> > {
>> > + /*
>> > + * Trap on TID3, if we implement at least v8. For v8 and above
>> > + * the ID register space is at least IMPDEF permitted to trap,
>> > + * and must trap if FEAT_FGT is implemented. We choose to trap
>> > + * always. Use this function for any new registers that should
>> > + * trap on TID3.
>> > + */
>> > if (arm_feature(env, ARM_FEATURE_V8)) {
>> > - return access_aa64_tid3(env, ri, isread);
>> > + return access_v7_tid3(env, ri, isread);
>>
>> This seems even more incongruous - we test for v8 but use the v7 helper.
>
> We have two different things we want to do:
>
> (1) always trap if TID3 is set
> (2) trap if we are v8 or better and TID3 is set
>
> We want to use function 1 for the set of ID registers that
> existed back in v7A -- these are the ones that trap for any
> implementation.
> We want function 2 for the ID registers that were only defined
> in v8A, and for the reserved-ID-register space. These must not
> trap on v7A, and either must or are IMPDEF-permitted to trap
> on v8A and later.
>
> I have tried to pick function names that make sense for "what
> is this doing", and for "if somebody adds a new register here,
> make the function they want be the one with a name that seems
> most inviting, so they pick the right one, not the wrong one".
> So I have access_v7_tid3 for ID registers defined in
> v7, and access_tid3 for the rest, including any new ones.
> This seemed to me better than picking a function name that
> describes the internal implementation of functions 1 and 2,
> on the basis that people are a lot more likely to need to
> use the functions than look inside them.
>
> If you have suggested names that you think make more sense,
> I'm open to them -- since I started by knowing the behaviour
> to me the names I ended up with seem more "obvious" to me than
> they would to somebody else, and it's the "somebody else" that
> I'm trying to help with the naming...
I think I follow now. My only real suggestion would be to make the name
_v7a to be distinct from the v7m profile. Although HCR.TID3 seems to
exist for v7r as well.
Anyway:
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
>
> Separately, the implementation of function (2) in this patch
> is "if ARM_FEATURE_V8 is set, call function (1) to do the test-TID3,
> otherwise return ACCESS_OK to indicate don't-trap". (Inherited
> from the existing implementation choice.)
>
> Given that function (1) is only a simple test of
> "((arm_current_el(env) < 2) && (arm_hcr_el2_eff(env) & HCR_TID3))"
> we could alternatively open-code that check in function (2)
> if you think that would be clearer.
>
> thanks
> -- PMM
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores
2026-01-15 15:20 ` Alex Bennée
@ 2026-01-15 15:35 ` Peter Maydell
2026-01-15 15:54 ` Alex Bennée
0 siblings, 1 reply; 17+ messages in thread
From: Peter Maydell @ 2026-01-15 15:35 UTC (permalink / raw)
To: Alex Bennée; +Cc: qemu-arm, qemu-devel, qemu-stable
On Thu, 15 Jan 2026 at 15:20, Alex Bennée <alex.bennee@linaro.org> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
> > If you have suggested names that you think make more sense,
> > I'm open to them -- since I started by knowing the behaviour
> > to me the names I ended up with seem more "obvious" to me than
> > they would to somebody else, and it's the "somebody else" that
> > I'm trying to help with the naming...
>
> I think I follow now. My only real suggestion would be to make the name
> _v7a to be distinct from the v7m profile. Although HCR.TID3 seems to
> exist for v7r as well.
>
> Anyway:
>
> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Thanks; I'll make that change and apply these to
target-arm.next.
FWIW, v7R can't have the virtualization extension, so
it doesn't have HCR.TID3. Virtualization for R-profile
only came in with v8R.
thanks
-- PMM
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores
2026-01-15 15:35 ` Peter Maydell
@ 2026-01-15 15:54 ` Alex Bennée
0 siblings, 0 replies; 17+ messages in thread
From: Alex Bennée @ 2026-01-15 15:54 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-arm, qemu-devel, qemu-stable
Peter Maydell <peter.maydell@linaro.org> writes:
> On Thu, 15 Jan 2026 at 15:20, Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> Peter Maydell <peter.maydell@linaro.org> writes:
>> > If you have suggested names that you think make more sense,
>> > I'm open to them -- since I started by knowing the behaviour
>> > to me the names I ended up with seem more "obvious" to me than
>> > they would to somebody else, and it's the "somebody else" that
>> > I'm trying to help with the naming...
>>
>> I think I follow now. My only real suggestion would be to make the name
>> _v7a to be distinct from the v7m profile. Although HCR.TID3 seems to
>> exist for v7r as well.
>>
>> Anyway:
>>
>> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
>
> Thanks; I'll make that change and apply these to
> target-arm.next.
>
> FWIW, v7R can't have the virtualization extension, so
> it doesn't have HCR.TID3. Virtualization for R-profile
> only came in with v8R.
Ahh found it described as "OPTIONAL set of extensions to VMSAv7" - so as
a subset of the memory model architecture. I guess because v7r is
PMSAv7. So many acronyms ;-)
>
> thanks
> -- PMM
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-01-15 15:54 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-31 17:08 [PATCH 0/4] target/arm: Fix handling of HCR.TID* on v7A CPUs Peter Maydell
2025-12-31 17:08 ` [PATCH 1/4] target/arm: Don't specify ID_PFR1 accessfn twice Peter Maydell
2026-01-02 10:56 ` Alex Bennée
2026-01-05 3:57 ` Richard Henderson
2025-12-31 17:08 ` [PATCH 2/4] target/arm: Correctly honour HCR.TID3 for v7A cores Peter Maydell
2026-01-02 11:17 ` Alex Bennée
2026-01-12 18:51 ` Peter Maydell
2026-01-15 15:20 ` Alex Bennée
2026-01-15 15:35 ` Peter Maydell
2026-01-15 15:54 ` Alex Bennée
2026-01-05 4:27 ` Richard Henderson
2025-12-31 17:08 ` [PATCH 3/4] target/arm: Correctly trap HCR.TID1 registers in v7A Peter Maydell
2026-01-02 11:21 ` Alex Bennée
2026-01-05 4:29 ` Richard Henderson
2025-12-31 17:08 ` [PATCH 4/4] target/arm: Rename access_aa64_tid5() to access_tid5() Peter Maydell
2026-01-02 11:21 ` Alex Bennée
2026-01-05 4:30 ` Richard Henderson
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.