Add FEAT_SCTLR2, which introduces the SCTLR2_EL1, SCTLR2_EL2, and
SCTLR2_EL3 registers. These registers are extension of the SCTLR_ELx
ones.
Because the bits in these registers depend on other CPU features, and
only FEAT_MEC is supported at the moment, this commit only implements
the EMEC bits related to FEAT_MEC in CTLR2_EL2 and SCTLR2_EL3.
Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org>
---
docs/system/arm/emulation.rst | 1 +
target/arm/cpu-features.h | 5 ++++
target/arm/cpu.h | 15 +++++++++++
target/arm/helper.c | 51 +++++++++++++++++++++++++++++++++++
target/arm/tcg/cpu64.c | 1 +
5 files changed, 73 insertions(+)
diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst
index 78c2fd2113..5a82c602f2 100644
--- a/docs/system/arm/emulation.rst
+++ b/docs/system/arm/emulation.rst
@@ -121,6 +121,7 @@ the following architecture extensions:
- FEAT_RPRES (Increased precision of FRECPE and FRSQRTE)
- FEAT_S2FWB (Stage 2 forced Write-Back)
- FEAT_SB (Speculation Barrier)
+- FEAT_SCTLR2 (Extension to SCTLR_ELx)
- FEAT_SEL2 (Secure EL2)
- FEAT_SHA1 (SHA1 instructions)
- FEAT_SHA256 (SHA256 instructions)
diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h
index 4452e7c21e..a42d1133c2 100644
--- a/target/arm/cpu-features.h
+++ b/target/arm/cpu-features.h
@@ -296,6 +296,11 @@ static inline bool isar_feature_aa32_ats1e1(const ARMISARegisters *id)
return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) >= 2;
}
+static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id)
+{
+ return FIELD_EX64(id->id_mmfr3, ID_AA64MMFR3, SCTLRX) == 1;
+}
+
static inline bool isar_feature_aa32_pmuv3p1(const ARMISARegisters *id)
{
/* 0xf means "non-standard IMPDEF PMU" */
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 9509217486..ac38306873 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -326,6 +326,7 @@ typedef struct CPUArchState {
};
uint64_t sctlr_el[4];
};
+ uint64_t sctlr2_el[4]; /* Extension to System control register. */
uint64_t vsctlr; /* Virtualization System control register. */
uint64_t cpacr_el1; /* Architectural feature access control register */
uint64_t cptr_el[4]; /* ARMv8 feature trap registers */
@@ -1401,6 +1402,19 @@ void pmu_init(ARMCPU *cpu);
#define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */
#define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */
+#define SCTLR2_EMEC (1ULL << 1) /* FEAT_MEC */
+#define SCTLR2_NMEA (1ULL << 2) /* FEAT_DoubleFault2 */
+#define SCTLR2_ENADERR (1ULL << 3) /* FEAT_ADERR */
+#define SCTLR2_ENANERR (1ULL << 4) /* FEAT_ANERR */
+#define SCTLR2_EASE (1ULL << 5) /* FEAT_DoubleFault2 */
+#define SCTLR2_ENIDCP128 (1ULL << 6) /* FEAT_SYSREG128 */
+#define SCTLR2_ENPACM (1ULL << 7) /* FEAT_PAuth_LR */
+#define SCTLR2_ENPACM0 (1ULL << 8 /* FEAT_PAuth_LR */
+#define SCTLR2_CPTA (1ULL << 9) /* FEAT_CPA2 */
+#define SCTLR2_CPTA0 (1ULL << 10) /* FEAT_CPA2 */
+#define SCTLR2_CPTM (1ULL << 11) /* FEAT_CPA2 */
+#define SCTLR2_CPTM0 (1ULL << 12) /* FEAT_CAP2 */
+
#define CPSR_M (0x1fU)
#define CPSR_T (1U << 5)
#define CPSR_F (1U << 6)
@@ -1692,6 +1706,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_SCTLR2EN (1ULL << 42)
#define SCR_GPF (1ULL << 48)
#define SCR_MECEN (1ULL << 49)
#define SCR_NSE (1ULL << 62)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 9f8a284261..413672174b 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -7840,6 +7840,53 @@ static const ARMCPRegInfo actlr2_hactlr2_reginfo[] = {
.resetvalue = 0 },
};
+static CPAccessResult sctlr2_access(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
+{
+ int el = arm_current_el(env);
+
+ if (!cpu_isar_feature(aa64_sctlr2, env_archcpu(env))) {
+ return CP_ACCESS_UNDEFINED;
+ }
+
+ if ((el < 3) && !(env->cp15.scr_el3 & SCR_SCTLR2EN)) {
+ return CP_ACCESS_TRAP_EL3;
+ }
+ return CP_ACCESS_OK;
+};
+
+static void sctlr2_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ int el = arm_current_el(env);
+ uint64_t valid_mask = 0ULL;
+
+ if (el > 1 && cpu_isar_feature(aa64_mec, env_archcpu(env))) {
+ /* SCTLR2_EL1 does not implement the EMEC bit */
+ valid_mask |= SCTLR2_EMEC;
+ }
+ value &= valid_mask;
+ raw_write(env, ri, value);
+};
+
+static const ARMCPRegInfo sctlr2_reginfo[] = {
+ { .name = "SCTLR2_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 1, .crm = 0,
+ .access = PL1_RW, .accessfn = sctlr2_access,
+ .writefn = sctlr2_write,
+ .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[1]) },
+ { .name = "SCTLR2_EL2", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 1, .crm = 0,
+ .access = PL2_RW, .accessfn = sctlr2_access,
+ .writefn = sctlr2_write,
+ .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[2]) },
+ { .name = "SCTLR2_EL3", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 6, .opc2 = 3, .crn = 1, .crm = 0,
+ .access = PL3_RW, .accessfn = sctlr2_access,
+ .writefn = sctlr2_write,
+ .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[3]) },
+};
+
void register_cp_regs_for_features(ARMCPU *cpu)
{
/* Register all the coprocessor registers based on feature bits */
@@ -9106,6 +9153,10 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, mec_reginfo);
}
+ if (cpu_isar_feature(aa64_sctlr2, cpu)) {
+ define_arm_cp_regs(cpu, sctlr2_reginfo);
+ }
+
if (cpu_isar_feature(any_predinv, cpu)) {
define_arm_cp_regs(cpu, predinv_reginfo);
}
diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c
index 5d8ed2794d..5f77d320ea 100644
--- a/target/arm/tcg/cpu64.c
+++ b/target/arm/tcg/cpu64.c
@@ -1242,6 +1242,7 @@ void aarch64_max_tcg_initfn(Object *obj)
t = cpu->isar.id_aa64mmfr3;
t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */
+ t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */
cpu->isar.id_aa64mmfr3 = t;
t = cpu->isar.id_aa64zfr0;
--
2.34.1
On 7/4/25 09:14, Gustavo Romero wrote:
> diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h
> index 4452e7c21e..a42d1133c2 100644
> --- a/target/arm/cpu-features.h
> +++ b/target/arm/cpu-features.h
> @@ -296,6 +296,11 @@ static inline bool isar_feature_aa32_ats1e1(const ARMISARegisters *id)
> return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) >= 2;
> }
>
> +static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id)
> +{
> + return FIELD_EX64(id->id_mmfr3, ID_AA64MMFR3, SCTLRX) == 1;
> +}
!= 0. Value 2 will mean "value 1, plus ..."
This also needs updating for the IDREG reorg that landed this week.
> @@ -1692,6 +1706,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_SCTLR2EN (1ULL << 42)
44. Bit 42 is RCWMASKEn.
> +static CPAccessResult sctlr2_access(CPUARMState *env, const ARMCPRegInfo *ri,
> + bool isread)
> +{
> + int el = arm_current_el(env);
> +
> + if (!cpu_isar_feature(aa64_sctlr2, env_archcpu(env))) {
> + return CP_ACCESS_UNDEFINED;
> + }
Not needed, because the cpregs will not be registered otherwise.
> + if ((el < 3) && !(env->cp15.scr_el3 & SCR_SCTLR2EN)) {
Remove extra () and extra space.
For SCTLR2_EL1, this is missing the TVM/TRVM check (use access_tvm_trvm first), and the
HCRX_EL2.SCTLR2En check.
> +static void sctlr2_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> +{
> + int el = arm_current_el(env);
> + uint64_t valid_mask = 0ULL;
ULL is unnecessary.
> +
> + if (el > 1 && cpu_isar_feature(aa64_mec, env_archcpu(env))) {
> + /* SCTLR2_EL1 does not implement the EMEC bit */
Incorrect test for SCTLR2_EL1: EL is the current cpu state, nothing to do with the
register. You might notice that all three registers have different sets of supported
bits. If you want to properly validate, you might as well you three different functions.
> +static const ARMCPRegInfo sctlr2_reginfo[] = {
> + { .name = "SCTLR2_EL1", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 1, .crm = 0,
> + .access = PL1_RW, .accessfn = sctlr2_access,
> + .writefn = sctlr2_write,
> + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[1]) },
Missing .fgt = FGT_SCTLR_EL1, .nv2_redirect_offset.
> + { .name = "SCTLR2_EL2", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 1, .crm = 0,
> + .access = PL2_RW, .accessfn = sctlr2_access,
> + .writefn = sctlr2_write,
> + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[2]) },
> + { .name = "SCTLR2_EL3", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 6, .opc2 = 3, .crn = 1, .crm = 0,
> + .access = PL3_RW, .accessfn = sctlr2_access,
No accessfn.
r~
© 2016 - 2025 Red Hat, Inc.