Add FEAT_TCR2, which introduces the TCR2_EL1 and TCR2_EL2 registers.
These registers are extensions of the TCR_ELx registers and provide
top-level control of the EL10 and EL20 translation regimes.
Since the bits in these registers depend on other CPU features, and only
FEAT_MEC is supported at the moment, the FEAT_TCR2 only implements the
AMEC bits for now.
Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org>
---
docs/system/arm/emulation.rst | 1 +
target/arm/cpu-features.h | 5 +++
target/arm/cpu.c | 3 ++
target/arm/cpu.h | 2 ++
target/arm/helper.c | 62 +++++++++++++++++++++++++++++++++++
target/arm/internals.h | 19 +++++++++++
target/arm/tcg/cpu64.c | 1 +
7 files changed, 93 insertions(+)
diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst
index 66043b0747..1c597d8673 100644
--- a/docs/system/arm/emulation.rst
+++ b/docs/system/arm/emulation.rst
@@ -149,6 +149,7 @@ the following architecture extensions:
- FEAT_SPECRES (Speculation restriction instructions)
- FEAT_SSBS (Speculative Store Bypass Safe)
- FEAT_SSBS2 (MRS and MSR instructions for SSBS version 2)
+- FEAT_TCR2 (Support for TCR2_ELx)
- FEAT_TGran16K (Support for 16KB memory translation granule size at stage 1)
- FEAT_TGran4K (Support for 4KB memory translation granule size at stage 1)
- FEAT_TGran64K (Support for 64KB memory translation granule size at stage 1)
diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h
index a5fc2ca572..9579d93cec 100644
--- a/target/arm/cpu-features.h
+++ b/target/arm/cpu-features.h
@@ -904,6 +904,11 @@ static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64MMFR2, NV) >= 2;
}
+static inline bool isar_feature_aa64_tcr2(const ARMISARegisters *id)
+{
+ return FIELD_EX64_IDREG(id, ID_AA64MMFR3, TCRX) != 0;
+}
+
static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0;
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 2a89dc90c0..34638ea100 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -645,6 +645,9 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el)
if (cpu_isar_feature(aa64_fgt, cpu)) {
env->cp15.scr_el3 |= SCR_FGTEN;
}
+ if (cpu_isar_feature(aa64_tcr2, cpu)) {
+ env->cp15.scr_el3 |= SCR_TCR2EN;
+ }
if (cpu_isar_feature(aa64_sctlr2, cpu)) {
env->cp15.scr_el3 |= SCR_SCTLR2EN;
}
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index af83a16b7e..5156120b50 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -366,6 +366,7 @@ typedef struct CPUArchState {
uint64_t vsttbr_el2; /* Secure Virtualization Translation Table. */
/* MMU translation table base control. */
uint64_t tcr_el[4];
+ uint64_t tcr2_el[3];
uint64_t vtcr_el2; /* Virtualization Translation Control. */
uint64_t vstcr_el2; /* Secure Virtualization Translation Control. */
uint32_t c2_data; /* MPU data cacheable bits. */
@@ -1735,6 +1736,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
#define SCR_HXEN (1ULL << 38)
#define SCR_TRNDR (1ULL << 40)
#define SCR_ENTP2 (1ULL << 41)
+#define SCR_TCR2EN (1ULL << 43)
#define SCR_SCTLR2EN (1ULL << 44)
#define SCR_GPF (1ULL << 48)
#define SCR_MECEN (1ULL << 49)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 6c32bfcae5..973b276d90 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -4523,6 +4523,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu)
"TTBR1_EL1", "TTBR1_EL2", "TTBR1_EL12" },
{ K(3, 0, 2, 0, 2), K(3, 4, 2, 0, 2), K(3, 5, 2, 0, 2),
"TCR_EL1", "TCR_EL2", "TCR_EL12" },
+ { K(3, 0, 2, 0, 3), K(3, 4, 2, 0, 3), K(3, 5, 2, 0, 3),
+ "TCR2_EL1", "TCR2_EL2", "TCR2_EL12", isar_feature_aa64_tcr2 },
{ K(3, 0, 4, 0, 0), K(3, 4, 4, 0, 0), K(3, 5, 4, 0, 0),
"SPSR_EL1", "SPSR_EL2", "SPSR_EL12" },
{ K(3, 0, 4, 0, 1), K(3, 4, 4, 0, 1), K(3, 5, 4, 0, 1),
@@ -6136,6 +6138,62 @@ static const ARMCPRegInfo sctlr2_reginfo[] = {
.fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[3]) },
};
+static CPAccessResult tcr2_el2_access(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
+{
+ if (arm_current_el(env) < 3
+ && arm_feature(env, ARM_FEATURE_EL3)
+ && !(env->cp15.scr_el3 & SCR_TCR2EN)) {
+ return CP_ACCESS_TRAP_EL3;
+ }
+ return CP_ACCESS_OK;
+}
+
+static CPAccessResult tcr2_el1_access(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
+{
+ CPAccessResult ret = access_tvm_trvm(env, ri, isread);
+ if (ret != CP_ACCESS_OK) {
+ return ret;
+ }
+ if (arm_current_el(env) < 2 && !(arm_hcrx_el2_eff(env) & HCRX_TCR2EN)) {
+ return CP_ACCESS_TRAP_EL2;
+ }
+ return tcr2_el2_access(env, ri, isread);
+}
+
+static void tcr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ /* This register does not control any feature yet. */
+}
+
+static void tcr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ uint64_t valid_mask = 0;
+
+ if (cpu_isar_feature(aa64_mec, env_archcpu(env))) {
+ valid_mask |= TCR2_AMEC0 | TCR2_AMEC1;
+ }
+ value &= valid_mask;
+ raw_write(env, ri, value);
+}
+
+static const ARMCPRegInfo tcr2_reginfo[] = {
+ { .name = "TCR2_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 2, .crm = 0,
+ .access = PL1_RW, .accessfn = tcr2_el1_access,
+ .writefn = tcr2_el1_write, .fgt = FGT_TCR_EL1,
+ .nv2_redirect_offset = 0x270 | NV2_REDIR_NV1,
+ .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[1]) },
+ { .name = "TCR2_EL2", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 2, .crm = 0,
+ .access = PL2_RW, .accessfn = tcr2_el2_access,
+ .writefn = tcr2_el2_write,
+ .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[2]) },
+};
+
void register_cp_regs_for_features(ARMCPU *cpu)
{
/* Register all the coprocessor registers based on feature bits */
@@ -7373,6 +7431,10 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, sctlr2_reginfo);
}
+ if (cpu_isar_feature(aa64_tcr2, cpu)) {
+ define_arm_cp_regs(cpu, tcr2_reginfo);
+ }
+
if (cpu_isar_feature(any_predinv, cpu)) {
define_arm_cp_regs(cpu, predinv_reginfo);
}
diff --git a/target/arm/internals.h b/target/arm/internals.h
index d60d235b19..6c1112e641 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -201,6 +201,24 @@ FIELD(CPTR_EL3, TCPAC, 31, 1)
#define TTBCR_SH1 (1U << 28)
#define TTBCR_EAE (1U << 31)
+#define TCR2_PNCH (1ULL << 0)
+#define TCR2_PIE (1ULL << 1)
+#define TCR2_E0POE (1ULL << 2)
+#define TCR2_POE (1ULL << 3)
+#define TCR2_AIE (1ULL << 4)
+#define TCR2_D128 (1ULL << 5)
+#define TCR2_PTTWI (1ULL << 10)
+#define TCR2_HAFT (1ULL << 11)
+#define TCR2_AMEC0 (1ULL << 12)
+#define TCR2_AMEC1 (1ULL << 13)
+#define TCR2_DISCH0 (1ULL << 14)
+#define TCR2_DISCH1 (1ULL << 15)
+#define TCR2_A2 (1ULL << 16)
+#define TCR2_FNG0 (1ULL << 17)
+#define TCR2_FNG1 (1ULL << 18)
+#define TCR2_FNGNA0 (1ULL << 20)
+#define TCR2_FNGNA1 (1ULL << 21)
+
FIELD(VTCR, T0SZ, 0, 6)
FIELD(VTCR, SL0, 6, 2)
FIELD(VTCR, IRGN0, 8, 2)
@@ -232,6 +250,7 @@ FIELD(VTCR, SL2, 33, 1)
#define HCRX_CMOW (1ULL << 9)
#define HCRX_MCE2 (1ULL << 10)
#define HCRX_MSCEN (1ULL << 11)
+#define HCRX_TCR2EN (1ULL << 14)
#define HCRX_SCTLR2EN (1ULL << 15)
#define HPFAR_NS (1ULL << 63)
diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c
index f4efff03a5..4eb51420ef 100644
--- a/target/arm/tcg/cpu64.c
+++ b/target/arm/tcg/cpu64.c
@@ -1248,6 +1248,7 @@ void aarch64_max_tcg_initfn(Object *obj)
SET_IDREG(isar, ID_AA64MMFR2, t);
t = GET_IDREG(isar, ID_AA64MMFR3);
+ t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* FEAT_TCR2 */
t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */
t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */
SET_IDREG(isar, ID_AA64MMFR3, t);
--
2.34.1
On 7/11/25 08:08, Gustavo Romero wrote: > Add FEAT_TCR2, which introduces the TCR2_EL1 and TCR2_EL2 registers. > These registers are extensions of the TCR_ELx registers and provide > top-level control of the EL10 and EL20 translation regimes. > > Since the bits in these registers depend on other CPU features, and only > FEAT_MEC is supported at the moment, the FEAT_TCR2 only implements the > AMEC bits for now. > > Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> This causes a regression in tests/functional/test_aarch64_device_passthrough.py, by continually trapping on an access to TCR2_EL1 while the HCRX_EL2 enable bit is not set. Unlike the similar SCTRL2 failure, it's not 100% clear to me how the guest and nested guest kernels are related. But it is clear that the outer kernel does not does not support TCR2_EL1 (and also doesn't manipulate ID_AA64MMFR3_EL1 to hide FEAT_TCR2), but the nested guest kernel does support TCR2_EL1. r~
On 7/13/25 2:59 PM, Richard Henderson wrote: > On 7/11/25 08:08, Gustavo Romero wrote: >> Add FEAT_TCR2, which introduces the TCR2_EL1 and TCR2_EL2 registers. >> These registers are extensions of the TCR_ELx registers and provide >> top-level control of the EL10 and EL20 translation regimes. >> >> Since the bits in these registers depend on other CPU features, and only >> FEAT_MEC is supported at the moment, the FEAT_TCR2 only implements the >> AMEC bits for now. >> >> Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> > This causes a regression in tests/functional/test_aarch64_device_passthrough.py, by > continually trapping on an access to TCR2_EL1 while the HCRX_EL2 enable bit is not set. > > Unlike the similar SCTRL2 failure, it's not 100% clear to me how the guest and nested > guest kernels are related. But it is clear that the outer kernel does not does not > support TCR2_EL1 (and also doesn't manipulate ID_AA64MMFR3_EL1 to hide FEAT_TCR2), but the > nested guest kernel does support TCR2_EL1. > The same kernel is used for host and guest. Maybe it's related to kvm support? > > r~
On 7/14/25 00:21, Pierrick Bouvier wrote: > On 7/13/25 2:59 PM, Richard Henderson wrote: >> On 7/11/25 08:08, Gustavo Romero wrote: >>> Add FEAT_TCR2, which introduces the TCR2_EL1 and TCR2_EL2 registers. >>> These registers are extensions of the TCR_ELx registers and provide >>> top-level control of the EL10 and EL20 translation regimes. >>> >>> Since the bits in these registers depend on other CPU features, and only >>> FEAT_MEC is supported at the moment, the FEAT_TCR2 only implements the >>> AMEC bits for now. >>> >>> Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> >> This causes a regression in tests/functional/test_aarch64_device_passthrough.py, by >> continually trapping on an access to TCR2_EL1 while the HCRX_EL2 enable bit is not set. >> >> Unlike the similar SCTRL2 failure, it's not 100% clear to me how the guest and nested >> guest kernels are related. But it is clear that the outer kernel does not does not >> support TCR2_EL1 (and also doesn't manipulate ID_AA64MMFR3_EL1 to hide FEAT_TCR2), but the >> nested guest kernel does support TCR2_EL1. >> > > The same kernel is used for host and guest. > Maybe it's related to kvm support? Oops, no, the patch fails to enable HCRX_TCR2EN in hcrx_write or SCR_TCR2EN in scr_write. The same is true for the previous patch with HCRX_SCTLR2EN and SCR_SCTLR2EN. r~
On 7/14/25 5:58 AM, Richard Henderson wrote: > On 7/14/25 00:21, Pierrick Bouvier wrote: >> On 7/13/25 2:59 PM, Richard Henderson wrote: >>> On 7/11/25 08:08, Gustavo Romero wrote: >>>> Add FEAT_TCR2, which introduces the TCR2_EL1 and TCR2_EL2 registers. >>>> These registers are extensions of the TCR_ELx registers and provide >>>> top-level control of the EL10 and EL20 translation regimes. >>>> >>>> Since the bits in these registers depend on other CPU features, and only >>>> FEAT_MEC is supported at the moment, the FEAT_TCR2 only implements the >>>> AMEC bits for now. >>>> >>>> Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> >>> This causes a regression in tests/functional/test_aarch64_device_passthrough.py, by >>> continually trapping on an access to TCR2_EL1 while the HCRX_EL2 enable bit is not set. >>> >>> Unlike the similar SCTRL2 failure, it's not 100% clear to me how the guest and nested >>> guest kernels are related. But it is clear that the outer kernel does not does not >>> support TCR2_EL1 (and also doesn't manipulate ID_AA64MMFR3_EL1 to hide FEAT_TCR2), but the >>> nested guest kernel does support TCR2_EL1. >>> >> >> The same kernel is used for host and guest. >> Maybe it's related to kvm support? > > Oops, no, the patch fails to enable HCRX_TCR2EN in hcrx_write or SCR_TCR2EN in scr_write. > The same is true for the previous patch with HCRX_SCTLR2EN and SCR_SCTLR2EN. > Thanks for the investigation. Indeed, building TF-A with ENABLE_FEAT_TCR2 and ENABLE_FEAT_SCTRL2 didn't change anything. It's possible that it's still needed to update test images though, so I'll try when Gustavo will post v8. Thanks, Pierrick > > r~
Hi Richard, On 7/14/25 09:58, Richard Henderson wrote: > On 7/14/25 00:21, Pierrick Bouvier wrote: >> On 7/13/25 2:59 PM, Richard Henderson wrote: >>> On 7/11/25 08:08, Gustavo Romero wrote: >>>> Add FEAT_TCR2, which introduces the TCR2_EL1 and TCR2_EL2 registers. >>>> These registers are extensions of the TCR_ELx registers and provide >>>> top-level control of the EL10 and EL20 translation regimes. >>>> >>>> Since the bits in these registers depend on other CPU features, and only >>>> FEAT_MEC is supported at the moment, the FEAT_TCR2 only implements the >>>> AMEC bits for now. >>>> >>>> Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> >>> This causes a regression in tests/functional/test_aarch64_device_passthrough.py, by >>> continually trapping on an access to TCR2_EL1 while the HCRX_EL2 enable bit is not set. >>> >>> Unlike the similar SCTRL2 failure, it's not 100% clear to me how the guest and nested >>> guest kernels are related. But it is clear that the outer kernel does not does not >>> support TCR2_EL1 (and also doesn't manipulate ID_AA64MMFR3_EL1 to hide FEAT_TCR2), but the >>> nested guest kernel does support TCR2_EL1. >>> >> >> The same kernel is used for host and guest. >> Maybe it's related to kvm support? > > Oops, no, the patch fails to enable HCRX_TCR2EN in hcrx_write or SCR_TCR2EN in scr_write. > The same is true for the previous patch with HCRX_SCTLR2EN and SCR_SCTLR2EN. Thanks for the investigation. I'm just taking a look at it right now. I'll send a v8 fixing it. Cheers, Gustavo
© 2016 - 2025 Red Hat, Inc.