aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/arm/cpregs.h12
-rw-r--r--target/arm/cpu.h4
-rw-r--r--target/arm/tcg/hflags.c6
-rw-r--r--target/arm/tcg/translate-a64.c56
-rw-r--r--target/arm/tcg/translate.h6
5 files changed, 84 insertions, 0 deletions
diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h
index cb795bed75..b6fdd0f3eb 100644
--- a/target/arm/cpregs.h
+++ b/target/arm/cpregs.h
@@ -826,6 +826,11 @@ typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque);
#define CP_ANY 0xff
+/* Flags in the high bits of nv2_redirect_offset */
+#define NV2_REDIR_NV1 0x4000 /* Only redirect when HCR_EL2.NV1 == 1 */
+#define NV2_REDIR_NO_NV1 0x8000 /* Only redirect when HCR_EL2.NV1 == 0 */
+#define NV2_REDIR_FLAG_MASK 0xc000
+
/* Definition of an ARM coprocessor register */
struct ARMCPRegInfo {
/* Name of register (useful mainly for debugging, need not be unique) */
@@ -867,6 +872,13 @@ struct ARMCPRegInfo {
* value encodes both the trap register and bit within it.
*/
FGTBit fgt;
+
+ /*
+ * Offset from VNCR_EL2 when FEAT_NV2 redirects access to memory;
+ * may include an NV2_REDIR_* flag.
+ */
+ uint32_t nv2_redirect_offset;
+
/*
* The opaque pointer passed to define_arm_cp_regs_with_opaque() when
* this register was defined: can be used to hand data through to the
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index f521219ea9..9281d74aa9 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -3241,6 +3241,10 @@ FIELD(TBFLAG_A64, ATA0, 31, 1)
FIELD(TBFLAG_A64, NV, 32, 1)
FIELD(TBFLAG_A64, NV1, 33, 1)
FIELD(TBFLAG_A64, NV2, 34, 1)
+/* Set if FEAT_NV2 RAM accesses use the EL2&0 translation regime */
+FIELD(TBFLAG_A64, NV2_MEM_E20, 35, 1)
+/* Set if FEAT_NV2 RAM accesses are big-endian */
+FIELD(TBFLAG_A64, NV2_MEM_BE, 36, 1)
/*
* Helpers for using the above. Note that only the A64 accessors use
diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c
index d2b352663e..8e5d35d922 100644
--- a/target/arm/tcg/hflags.c
+++ b/target/arm/tcg/hflags.c
@@ -307,6 +307,12 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
}
if (hcr & HCR_NV2) {
DP_TBFLAG_A64(flags, NV2, 1);
+ if (hcr & HCR_E2H) {
+ DP_TBFLAG_A64(flags, NV2_MEM_E20, 1);
+ }
+ if (env->cp15.sctlr_el[2] & SCTLR_EE) {
+ DP_TBFLAG_A64(flags, NV2_MEM_BE, 1);
+ }
}
}
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index 2ada5b7e3f..2938397d52 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -2135,6 +2135,7 @@ static void handle_sys(DisasContext *s, bool isread,
bool nv_trap_to_el2 = false;
bool nv_redirect_reg = false;
bool skip_fp_access_checks = false;
+ bool nv2_mem_redirect = false;
TCGv_ptr tcg_ri = NULL;
TCGv_i64 tcg_rt;
uint32_t syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread);
@@ -2167,6 +2168,21 @@ static void handle_sys(DisasContext *s, bool isread,
return;
}
+ if (s->nv2 && ri->nv2_redirect_offset) {
+ /*
+ * Some registers always redirect to memory; some only do so if
+ * HCR_EL2.NV1 is 0, and some only if NV1 is 1 (these come in
+ * pairs which share an offset; see the table in R_CSRPQ).
+ */
+ if (ri->nv2_redirect_offset & NV2_REDIR_NV1) {
+ nv2_mem_redirect = s->nv1;
+ } else if (ri->nv2_redirect_offset & NV2_REDIR_NO_NV1) {
+ nv2_mem_redirect = !s->nv1;
+ } else {
+ nv2_mem_redirect = true;
+ }
+ }
+
/* Check access permissions */
if (!cp_access_ok(s->current_el, ri, isread)) {
/*
@@ -2182,6 +2198,12 @@ static void handle_sys(DisasContext *s, bool isread,
* the EL2 register's accessfn.
*/
nv_redirect_reg = true;
+ assert(!nv2_mem_redirect);
+ } else if (nv2_mem_redirect) {
+ /*
+ * NV2 redirect-to-memory takes precedence over trap to EL2 or
+ * UNDEF to EL1.
+ */
} else if (s->nv && arm_cpreg_traps_in_nv(ri)) {
/*
* This register / instruction exists and is an EL2 register, so
@@ -2255,6 +2277,38 @@ static void handle_sys(DisasContext *s, bool isread,
assert(!(ri->type & ARM_CP_RAISES_EXC));
}
+ if (nv2_mem_redirect) {
+ /*
+ * This system register is being redirected into an EL2 memory access.
+ * This means it is not an IO operation, doesn't change hflags,
+ * and need not end the TB, because it has no side effects.
+ *
+ * The access is 64-bit single copy atomic, guaranteed aligned because
+ * of the definition of VCNR_EL2. Its endianness depends on
+ * SCTLR_EL2.EE, not on the data endianness of EL1.
+ * It is done under either the EL2 translation regime or the EL2&0
+ * translation regime, depending on HCR_EL2.E2H. It behaves as if
+ * PSTATE.PAN is 0.
+ */
+ TCGv_i64 ptr = tcg_temp_new_i64();
+ MemOp mop = MO_64 | MO_ALIGN | MO_ATOM_IFALIGN;
+ ARMMMUIdx armmemidx = s->nv2_mem_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2;
+ int memidx = arm_to_core_mmu_idx(armmemidx);
+
+ mop |= (s->nv2_mem_be ? MO_BE : MO_LE);
+
+ tcg_gen_ld_i64(ptr, tcg_env, offsetof(CPUARMState, cp15.vncr_el2));
+ tcg_gen_addi_i64(ptr, ptr,
+ (ri->nv2_redirect_offset & ~NV2_REDIR_FLAG_MASK));
+ tcg_rt = cpu_reg(s, rt);
+ if (isread) {
+ tcg_gen_qemu_ld_i64(tcg_rt, ptr, memidx, mop);
+ } else {
+ tcg_gen_qemu_st_i64(tcg_rt, ptr, memidx, mop);
+ }
+ return;
+ }
+
/* Handle special cases first */
switch (ri->type & ARM_CP_SPECIAL_MASK) {
case 0:
@@ -14063,6 +14117,8 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->nv = EX_TBFLAG_A64(tb_flags, NV);
dc->nv1 = EX_TBFLAG_A64(tb_flags, NV1);
dc->nv2 = EX_TBFLAG_A64(tb_flags, NV2);
+ dc->nv2_mem_e20 = EX_TBFLAG_A64(tb_flags, NV2_MEM_E20);
+ dc->nv2_mem_be = EX_TBFLAG_A64(tb_flags, NV2_MEM_BE);
dc->vec_len = 0;
dc->vec_stride = 0;
dc->cp_regs = arm_cpu->cp_regs;
diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h
index 9e13c4ef7b..93be745cf3 100644
--- a/target/arm/tcg/translate.h
+++ b/target/arm/tcg/translate.h
@@ -150,6 +150,10 @@ typedef struct DisasContext {
bool nv1;
/* True if NV enabled and HCR_EL2.NV2 is set */
bool nv2;
+ /* True if NV2 enabled and NV2 RAM accesses use EL2&0 translation regime */
+ bool nv2_mem_e20;
+ /* True if NV2 enabled and NV2 RAM accesses are big-endian */
+ bool nv2_mem_be;
/*
* >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI.
* < 0, set by the current instruction.
@@ -165,6 +169,8 @@ typedef struct DisasContext {
int c15_cpar;
/* TCG op of the current insn_start. */
TCGOp *insn_start;
+ /* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */
+ uint32_t nv2_redirect_offset;
} DisasContext;
typedef struct DisasCompare {