:p
atchew
Login
This series adds support for all FEAT_MEC registers to the arm64 max CPU. It includes the FEAT_MEC registers but does not modify the translation regimes to support the MECIDs. It also does not yet implement the two cache management instructions introduced with FEAT_MEC. Despite these limitations, most software stacks that rely on FEAT_MEC should work properly with this minimal support at the moment. I'm currently exploring possibilities to support FEAT_MEC encryption (or obfuscation, for testing purposes) in QEMU for the various translation regimes used on arm64. Gustavo Romero (5): target/arm: Add the MECEn SCR_EL3 bit target/arm: Add FEAT_MEC registers target/arm: Add FEAT_SCTLR2 target/arm: Add FEAT_TCR2 target/arm: Advertise FEAT_MEC in cpu max docs/system/arm/emulation.rst | 3 + target/arm/cpu-features.h | 15 +++ target/arm/cpu.h | 31 ++++++ target/arm/helper.c | 189 ++++++++++++++++++++++++++++++++++ target/arm/internals.h | 18 ++++ target/arm/tcg/cpu64.c | 2 + 6 files changed, 258 insertions(+) -- 2.34.1
The MECEn bit in SCR_EL3 enables access to the EL2 MECID registers from EL2, so add it to the SCR mask list to use it later on. Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> --- target/arm/cpu.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_TRNDR (1ULL << 40) #define SCR_ENTP2 (1ULL << 41) #define SCR_GPF (1ULL << 48) +#define SCR_MECEN (1ULL << 49) #define SCR_NSE (1ULL << 62) /* Return the current FPSCR value. */ -- 2.34.1
Add all FEAT_MEC registers. To work properly, FEAT_MEC also depends on FEAT_SCTLR2 and FEAT_TCR2, which are not implemented in this commit. The bits in SCTLR2 and TCR2 control which translation regimes use MECIDs, and determine which MECID is selected. FEAT_MEC also requires two new cache management instructions, not included in this commit, that will be implemented in subsequent commits. Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> --- target/arm/cpu.h | 14 +++++++ target/arm/helper.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ typedef struct CPUArchState { /* NV2 register */ uint64_t vncr_el2; + + /* MEC registers */ + uint64_t mecidr_el2; + uint64_t mecid_p0_el2; + uint64_t mecid_a0_el2; + uint64_t mecid_p1_el2; + uint64_t mecid_a1_el2; + uint64_t mecid_rl_a_el3; + uint64_t vmecid_p_el2; + uint64_t vmecid_a_el2; } cp15; struct { @@ -XXX,XX +XXX,XX @@ FIELD(MFAR, FPA, 12, 40) FIELD(MFAR, NSE, 62, 1) FIELD(MFAR, NS, 63, 1) +FIELD(MECIDR, MECIDW, 0, 4) +FIELD(MECID, MECID, 0, 16) +FIELD(VMECID, MECID, 0, 16) + QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK); /* If adding a feature bit which corresponds to a Linux ELF diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo nmi_reginfo[] = { .resetfn = arm_cp_reset_ignore }, }; +static void mecidr_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* MECIDWidthm1 = 15, i.e. 16 bits is the width of a MECID. */ + env->cp15.mecidr_el2 = FIELD_DP64(0, MECIDR, MECIDW, 15); +} + +static uint64_t mecidr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint64_t valid_mask; + + if (!arm_is_el2_enabled(env)) { + /* All bits are RES0. */ + return 0ULL; + } + + valid_mask = R_MECIDR_MECIDW_MASK; + return env->cp15.mecidr_el2 & valid_mask; +} + +static CPAccessResult mecid_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + int el; + + el = arm_current_el(env); + if (el == 2 && !(env->cp15.scr_el3 & SCR_MECEN)) { + return CP_ACCESS_TRAP_EL3; + } + + return CP_ACCESS_OK; +} + +static void mecid_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask; + + valid_mask = R_MECID_MECID_MASK; + value &= valid_mask; + raw_write(env, ri, value); +} + +static uint64_t mecid_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint64_t valid_mask; + + valid_mask = R_MECID_MECID_MASK; + return raw_read(env, ri) & valid_mask; +} + +static const ARMCPRegInfo mec_reginfo[] = { + { .name = "MECIDR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 7, .crn = 10, .crm = 8, + .resetfn = mecidr_reset, + .access = PL2_RW, .accessfn = mecid_access, .readfn = mecidr_read, + .writefn = arm_cp_write_ignore, + .fieldoffset = offsetof(CPUARMState, cp15.mecidr_el2) }, + { .name = "MECID_P0_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 0, .crn = 10, .crm = 8, + .access = PL2_RW, .accessfn = mecid_access, + .readfn = mecid_read, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_p0_el2) }, + { .name = "MECID_A0_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 1, .crn = 10, .crm = 8, + .access = PL2_RW, .accessfn = mecid_access, + .readfn = mecid_read, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_a0_el2) }, + { .name = "MECID_P1_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 2, .crn = 10, .crm = 8, + .access = PL2_RW, .accessfn = mecid_access, + .readfn = mecid_read, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_p1_el2) }, + { .name = "MECID_A1_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 10, .crm = 8, + .access = PL2_RW, .accessfn = mecid_access, + .readfn = mecid_read, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_a1_el2) }, + { .name = "MECID_RL_A_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .opc2 = 1, .crn = 10, .crm = 10, + .access = PL2_RW, .accessfn = mecid_access, + .readfn = mecid_read, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_rl_a_el3) }, + { .name = "VMECID_P_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 0, .crn = 10, .crm = 9, + .access = PL2_RW, .accessfn = mecid_access, + .readfn = mecid_read, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.vmecid_p_el2) }, + { .name = "VMECID_A_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 1, .crn = 10, .crm = 9, + .access = PL2_RW, .accessfn = mecid_access, + .readfn = mecid_read, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.vmecid_a_el2) }, +}; + static void define_pmu_regs(ARMCPU *cpu) { /* @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, nmi_reginfo); } + if (cpu_isar_feature(aa64_mec, cpu)) { + define_arm_cp_regs(cpu, mec_reginfo); + } + if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); } -- 2.34.1
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 XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ 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 */ @@ -XXX,XX +XXX,XX @@ 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) @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ 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 */ @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -XXX,XX +XXX,XX @@ 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
Add FEAT_TCR2, which introduces 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 registers only implement the bits related to FEAT_MEC 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.h | 1 + target/arm/helper.c | 40 +++++++++++++++++++++++++++++++++++ target/arm/internals.h | 18 ++++++++++++++++ 5 files changed, 65 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_xs(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, XS) != 0; } +static inline bool isar_feature_aa64_tcr2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr3, ID_AA64MMFR3, TCRX) != 0; +} + /* * These are the values from APA/API/APA3. * In general these must be compared '>=', per the normal Arm ARM diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ 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. */ diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo sctlr2_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[3]) }, }; +static CPAccessResult tcr2_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + return CP_ACCESS_OK; +}; + +static void tcr2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int el = arm_current_el(env); + uint64_t valid_mask = 0ULL; + + valid_mask |= TCR2_AMEC0; + if (el_is_in_host(env, el)) { + if (cpu_isar_feature(aa64_mec, env_archcpu(env))) { + valid_mask |= 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_access, + .writefn = tcr2_write, + .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_access, + .writefn = tcr2_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 */ @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -XXX,XX +XXX,XX @@ 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) -- 2.34.1
Advertise FEAT_MEC in AA64MMFR3 ID register for the Arm64 cpu max as a first step to fully support FEAT_MEC. The FEAT_MEC is an extension to FEAT_RME that implements multiple Memory Encryption Contexts (MEC) so the memory in a realm can be encrypted and accessing it from the wrong encryption context is not possible. An encryption context allow the selection of a memory encryption engine. At this point, no real memory encryption or obfuscation is supported, but software stacks that rely on FEAT_MEC to run should work properly, except if they use the new cache management instructions, which will be implement in a subsequent commit. Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> --- docs/system/arm/emulation.rst | 1 + target/arm/cpu-features.h | 5 +++++ target/arm/tcg/cpu64.c | 1 + 3 files changed, 7 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -XXX,XX +XXX,XX @@ the following architecture extensions: - FEAT_LSE (Large System Extensions) - FEAT_LSE2 (Large System Extensions v2) - FEAT_LVA (Large Virtual Address space) +- FEAT_MEC (Memory Encryption Contexts) - FEAT_MixedEnd (Mixed-endian support) - FEAT_MixedEndEL0 (Mixed-endian support at EL0) - FEAT_MOPS (Standardization of memory operations) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; } +static inline bool isar_feature_aa64_mec(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr3, ID_AA64MMFR3, MEC); +} + static inline bool isar_feature_aa64_mops(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, MOPS); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -XXX,XX +XXX,XX @@ 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 */ + t = FIELD_DP64(t, ID_AA64MMFR3, MEC, 1); /* FEAT_MEC */ cpu->isar.id_aa64mmfr3 = t; t = cpu->isar.id_aa64zfr0; -- 2.34.1
This series adds support for all FEAT_MEC registers and cache instructions to the Arm64 max CPU. It includes the FEAT_MEC registers and cache maintenance instructions, but does not modify the translation regimes to support the MECIDs, so no encryption is supported yet. However, software stacks that rely on FEAT_MEC should work properly at this point. Cheers, Gustavo v10: - Addressed comment from pm215 that registers with opc1 == 4 or 5 must actually not trap when nested virtualization is turned on. This resulted in one additional patch in the series: "Add a cpreg flag to indicate no trap in NV". All previous versions of this series can be found in: https://patchew.org/QEMU/20250727074202.83141-1-richard.henderson@linaro.org/ Please note that the patches related to FEAT_SCTRL2 and FEAT_TCR2, which are prerequisites for this series, have already been merged into master. Gustavo Romero (3): target/arm: Add a cpreg flag to indicate no trap in NV target/arm: Implement FEAT_MEC registers target/arm: Enable FEAT_MEC in -cpu max docs/system/arm/emulation.rst | 3 + target/arm/cpregs.h | 11 +++- target/arm/cpu-features.h | 5 ++ target/arm/cpu.c | 3 + target/arm/cpu.h | 10 ++++ target/arm/helper.c | 109 ++++++++++++++++++++++++++++++++++ target/arm/internals.h | 3 + target/arm/tcg/cpu64.c | 1 + 8 files changed, 142 insertions(+), 3 deletions(-) -- 2.34.1
Add a new flag, ARM_CP_NV_NO_TRAP, to indicate that a CP register, even though it has opc1 == 4 or 5, does not trap when nested virtualization is enabled (FEAT_NV/FEAT_NV2). Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> --- target/arm/cpregs.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ enum { * identically to the normal one, other than FGT trapping handling.) */ ARM_CP_ADD_TLBI_NXS = 1 << 21, + ARM_CP_NV_NO_TRAP = 1 << 22, }; /* @@ -XXX,XX +XXX,XX @@ static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri) * * In particular, note that the released sysreg XML defines that * the FEAT_MEC sysregs and instructions do not follow this FEAT_NV - * trapping rule, so we will need to add an ARM_CP_* flag to indicate - * "register does not trap on NV" to handle those if/when we implement - * FEAT_MEC. + * trapping rule, so a register flagged as ARM_CP_NV_NO_TRAP indicates + * the register does not trap on NV even if opc1 == 4 or 5. */ + + if (ri->type & ARM_CP_NV_NO_TRAP) { + return false; + } + return ri->opc1 == 4 || ri->opc1 == 5; } -- 2.34.1
Add all FEAT_MEC registers. Enable access to the registers via the SCTLR2 and TCR2 control bits. Add the two new cache management instructions, which are nops in QEMU because we do not model caches. Message-ID: <20250711140828.1714666-3-gustavo.romero@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> [rth: Squash 3 patches to add all registers at once.] Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> --- target/arm/cpu-features.h | 5 ++ target/arm/cpu.c | 3 ++ target/arm/cpu.h | 10 ++++ target/arm/helper.c | 109 ++++++++++++++++++++++++++++++++++++++ target/arm/internals.h | 3 ++ 5 files changed, 130 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0; } +static inline bool isar_feature_aa64_mec(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, MEC) != 0; +} + static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 4 && diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) if (cpu_isar_feature(aa64_sctlr2, cpu)) { env->cp15.scr_el3 |= SCR_SCTLR2EN; } + if (cpu_isar_feature(aa64_mec, cpu)) { + env->cp15.scr_el3 |= SCR_MECEN; + } } if (target_el == 2) { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ typedef struct CPUArchState { /* NV2 register */ uint64_t vncr_el2; + + /* MEC registers */ + uint64_t mecid_p0_el2; + uint64_t mecid_a0_el2; + uint64_t mecid_p1_el2; + uint64_t mecid_a1_el2; + uint64_t mecid_rl_a_el3; + uint64_t vmecid_p_el2; + uint64_t vmecid_a_el2; } cp15; struct { @@ -XXX,XX +XXX,XX @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_TCR2EN (1ULL << 43) #define SCR_SCTLR2EN (1ULL << 44) #define SCR_GPF (1ULL << 48) +#define SCR_MECEN (1ULL << 49) #define SCR_NSE (1ULL << 62) /* Return the current FPSCR value. */ diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_sctlr2, cpu)) { valid_mask |= SCR_SCTLR2EN; } + if (cpu_isar_feature(aa64_mec, cpu)) { + valid_mask |= SCR_MECEN; + } } else { valid_mask &= ~(SCR_RW | SCR_ST); if (cpu_isar_feature(aa32_ras, cpu)) { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo nmi_reginfo[] = { .resetfn = arm_cp_reset_ignore }, }; +static CPAccessResult mecid_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + int el = arm_current_el(env); + + if (el == 2) { + if (arm_security_space(env) != ARMSS_Realm) { + return CP_ACCESS_UNDEFINED; + } + + if (!(env->cp15.scr_el3 & SCR_MECEN)) { + return CP_ACCESS_TRAP_EL3; + } + } + + return CP_ACCESS_OK; +} + +static void mecid_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + value = extract64(value, 0, MECID_WIDTH); + raw_write(env, ri, value); +} + +static CPAccessResult cipae_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + switch (arm_security_space(env)) { + case ARMSS_Root: /* EL3 */ + case ARMSS_Realm: /* Realm EL2 */ + return CP_ACCESS_OK; + default: + return CP_ACCESS_UNDEFINED; + } +} + +static const ARMCPRegInfo mec_reginfo[] = { + { .name = "MECIDR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 7, .crn = 10, .crm = 8, + .access = PL2_R, .type = ARM_CP_CONST | ARM_CP_NV_NO_TRAP, + .resetvalue = MECID_WIDTH - 1 }, + { .name = "MECID_P0_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 0, .crn = 10, .crm = 8, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_p0_el2) }, + { .name = "MECID_A0_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 1, .crn = 10, .crm = 8, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_a0_el2) }, + { .name = "MECID_P1_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 2, .crn = 10, .crm = 8, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_p1_el2) }, + { .name = "MECID_A1_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 10, .crm = 8, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_a1_el2) }, + { .name = "MECID_RL_A_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .opc2 = 1, .crn = 10, .crm = 10, + .access = PL3_RW, .accessfn = mecid_access, + .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.mecid_rl_a_el3) }, + { .name = "VMECID_P_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 0, .crn = 10, .crm = 9, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.vmecid_p_el2) }, + { .name = "VMECID_A_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 1, .crn = 10, .crm = 9, + .access = PL2_RW, .type = ARM_CP_NV_NO_TRAP, + .accessfn = mecid_access, .writefn = mecid_write, + .fieldoffset = offsetof(CPUARMState, cp15.vmecid_a_el2) }, + { .name = "DC_CIPAE", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 14, .opc2 = 0, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_NV_NO_TRAP, + .accessfn = cipae_access }, +}; + +static const ARMCPRegInfo mec_mte_reginfo[] = { + { .name = "DC_CIGDPAE", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 14, .opc2 = 7, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_NV_NO_TRAP, + .accessfn = cipae_access }, +}; + #ifndef CONFIG_USER_ONLY /* * We don't know until after realize whether there's a GICv3 @@ -XXX,XX +XXX,XX @@ static void sctlr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint64_t valid_mask = 0; + if (cpu_isar_feature(aa64_mec, env_archcpu(env))) { + valid_mask |= SCTLR2_EMEC; + } value &= valid_mask; raw_write(env, ri, value); } @@ -XXX,XX +XXX,XX @@ static void sctlr2_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint64_t valid_mask = 0; + if (cpu_isar_feature(aa64_mec, env_archcpu(env))) { + valid_mask |= SCTLR2_EMEC; + } value &= valid_mask; raw_write(env, ri, value); } @@ -XXX,XX +XXX,XX @@ static void tcr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, { 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); } @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, tcr2_reginfo); } + if (cpu_isar_feature(aa64_mec, cpu)) { + define_arm_cp_regs(cpu, mec_reginfo); + if (cpu_isar_feature(aa64_mte, cpu)) { + define_arm_cp_regs(cpu, mec_mte_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 XXXXXXX..XXXXXXX 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -XXX,XX +XXX,XX @@ bool arm_pan_enabled(CPUARMState *env); /* Compare uint64_t for qsort and bsearch. */ int compare_u64(const void *a, const void *b); +/* Used in FEAT_MEC to set the MECIDWidthm1 field in the MECIDR_EL2 register. */ +#define MECID_WIDTH 16 + #endif -- 2.34.1
Advertise FEAT_MEC in AA64MMFR3 ID register for the Arm64 cpu max as a first step to fully support FEAT_MEC. The FEAT_MEC is an extension to FEAT_RME that implements multiple Memory Encryption Contexts (MEC) so the memory in a realm can be encrypted and accessing it from the wrong encryption context is not possible. An encryption context allow the selection of a memory encryption engine. At this point, no real memory encryption is supported, but software stacks that rely on FEAT_MEC should work properly. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20250711140828.1714666-7-gustavo.romero@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> --- docs/system/arm/emulation.rst | 3 +++ target/arm/tcg/cpu64.c | 1 + 2 files changed, 4 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -XXX,XX +XXX,XX @@ the following architecture extensions: - FEAT_LSE2 (Large System Extensions v2) - FEAT_LSE128 (128-bit Atomics) - FEAT_LVA (Large Virtual Address space) +- FEAT_MEC (Memory Encryption Contexts) + + * This is a register-only implementation without encryption. - FEAT_MixedEnd (Mixed-endian support) - FEAT_MixedEndEL0 (Mixed-endian support at EL0) - FEAT_MOPS (Standardization of memory operations) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -XXX,XX +XXX,XX @@ void aarch64_max_tcg_initfn(Object *obj) 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, MEC, 1); /* FEAT_MEC */ t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ SET_IDREG(isar, ID_AA64MMFR3, t); -- 2.34.1