REGINFO_SENTINEL
};
+#ifndef CONFIG_USER_ONLY
+/* Test if system register redirection is to occur in the current state. */
+static bool redirect_for_e2h(CPUARMState *env)
+{
+ return arm_current_el(env) == 2 && (arm_hcr_el2_eff(env) & HCR_E2H);
+}
+
+static uint64_t el2_e2h_read(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ CPReadFn *readfn;
+
+ if (redirect_for_e2h(env)) {
+ /* Switch to the saved EL2 version of the register. */
+ ri = ri->opaque;
+ readfn = ri->readfn;
+ } else {
+ readfn = ri->orig_readfn;
+ }
+ if (readfn == NULL) {
+ readfn = raw_read;
+ }
+ return readfn(env, ri);
+}
+
+static void el2_e2h_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ CPWriteFn *writefn;
+
+ if (redirect_for_e2h(env)) {
+ /* Switch to the saved EL2 version of the register. */
+ ri = ri->opaque;
+ writefn = ri->writefn;
+ } else {
+ writefn = ri->orig_writefn;
+ }
+ if (writefn == NULL) {
+ writefn = raw_write;
+ }
+ writefn(env, ri, value);
+}
+
+static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu)
+{
+ struct E2HAlias {
+ uint32_t src_key, dst_key, new_key;
+ const char *src_name, *dst_name, *new_name;
+ bool (*feature)(const ARMISARegisters *id);
+ };
+
+#define K(op0, op1, crn, crm, op2) \
+ ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2)
+
+ static const struct E2HAlias aliases[] = {
+ { K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0),
+ "SCTLR", "SCTLR_EL2", "SCTLR_EL12" },
+ { K(3, 0, 1, 0, 2), K(3, 4, 1, 1, 2), K(3, 5, 1, 0, 2),
+ "CPACR", "CPTR_EL2", "CPACR_EL12" },
+ { K(3, 0, 2, 0, 0), K(3, 4, 2, 0, 0), K(3, 5, 2, 0, 0),
+ "TTBR0_EL1", "TTBR0_EL2", "TTBR0_EL12" },
+ { K(3, 0, 2, 0, 1), K(3, 4, 2, 0, 1), K(3, 5, 2, 0, 1),
+ "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, 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),
+ "ELR_EL1", "ELR_EL2", "ELR_EL12" },
+ { K(3, 0, 5, 1, 0), K(3, 4, 5, 1, 0), K(3, 5, 5, 1, 0),
+ "AFSR0_EL1", "AFSR0_EL2", "AFSR0_EL12" },
+ { K(3, 0, 5, 1, 1), K(3, 4, 5, 1, 1), K(3, 5, 5, 1, 1),
+ "AFSR1_EL1", "AFSR1_EL2", "AFSR1_EL12" },
+ { K(3, 0, 5, 2, 0), K(3, 4, 5, 2, 0), K(3, 5, 5, 2, 0),
+ "ESR_EL1", "ESR_EL2", "ESR_EL12" },
+ { K(3, 0, 6, 0, 0), K(3, 4, 6, 0, 0), K(3, 5, 6, 0, 0),
+ "FAR_EL1", "FAR_EL2", "FAR_EL12" },
+ { K(3, 0, 10, 2, 0), K(3, 4, 10, 2, 0), K(3, 5, 10, 2, 0),
+ "MAIR_EL1", "MAIR_EL2", "MAIR_EL12" },
+ { K(3, 0, 10, 3, 0), K(3, 4, 10, 3, 0), K(3, 5, 10, 3, 0),
+ "AMAIR0", "AMAIR_EL2", "AMAIR_EL12" },
+ { K(3, 0, 12, 0, 0), K(3, 4, 12, 0, 0), K(3, 5, 12, 0, 0),
+ "VBAR", "VBAR_EL2", "VBAR_EL12" },
+ { K(3, 0, 13, 0, 1), K(3, 4, 13, 0, 1), K(3, 5, 13, 0, 1),
+ "CONTEXTIDR_EL1", "CONTEXTIDR_EL2", "CONTEXTIDR_EL12" },
+ { K(3, 0, 14, 1, 0), K(3, 4, 14, 1, 0), K(3, 5, 14, 1, 0),
+ "CNTKCTL", "CNTHCTL_EL2", "CNTKCTL_EL12" },
+
+ /*
+ * Note that redirection of ZCR is mentioned in the description
+ * of ZCR_EL2, and aliasing in the description of ZCR_EL1, but
+ * not in the summary table.
+ */
+ { K(3, 0, 1, 2, 0), K(3, 4, 1, 2, 0), K(3, 5, 1, 2, 0),
+ "ZCR_EL1", "ZCR_EL2", "ZCR_EL12", isar_feature_aa64_sve },
+
+ /* TODO: ARMv8.2-SPE -- PMSCR_EL2 */
+ /* TODO: ARMv8.4-Trace -- TRFCR_EL2 */
+ };
+#undef K
+
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(aliases); i++) {
+ const struct E2HAlias *a = &aliases[i];
+ ARMCPRegInfo *src_reg, *dst_reg;
+
+ if (a->feature && !a->feature(&cpu->isar)) {
+ continue;
+ }
+
+ src_reg = g_hash_table_lookup(cpu->cp_regs, &a->src_key);
+ dst_reg = g_hash_table_lookup(cpu->cp_regs, &a->dst_key);
+ g_assert(src_reg != NULL);
+ g_assert(dst_reg != NULL);
+
+ /* Cross-compare names to detect typos in the keys. */
+ g_assert(strcmp(src_reg->name, a->src_name) == 0);
+ g_assert(strcmp(dst_reg->name, a->dst_name) == 0);
+
+ /* None of the core system registers use opaque; we will. */
+ g_assert(src_reg->opaque == NULL);
+
+ /* Create alias before redirection so we dup the right data. */
+ if (a->new_key) {
+ ARMCPRegInfo *new_reg = g_memdup(src_reg, sizeof(ARMCPRegInfo));
+ uint32_t *new_key = g_memdup(&a->new_key, sizeof(uint32_t));
+ bool ok;
+
+ new_reg->name = a->new_name;
+ new_reg->type |= ARM_CP_ALIAS;
+ /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */
+ new_reg->access &= PL2_RW | PL3_RW;
+
+ ok = g_hash_table_insert(cpu->cp_regs, new_key, new_reg);
+ g_assert(ok);
+ }
+
+ src_reg->opaque = dst_reg;
+ src_reg->orig_readfn = src_reg->readfn ?: raw_read;
+ src_reg->orig_writefn = src_reg->writefn ?: raw_write;
+ if (!src_reg->raw_readfn) {
+ src_reg->raw_readfn = raw_read;
+ }
+ if (!src_reg->raw_writefn) {
+ src_reg->raw_writefn = raw_write;
+ }
+ src_reg->readfn = el2_e2h_read;
+ src_reg->writefn = el2_e2h_write;
+ }
+}
+#endif
+
static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
: cpu_isar_feature(aa32_predinv, cpu)) {
define_arm_cp_regs(cpu, predinv_reginfo);
}
+
+#ifndef CONFIG_USER_ONLY
+ /*
+ * Register redirections and aliases must be done last,
+ * after the registers from the other extensions have been defined.
+ */
+ if (arm_feature(env, ARM_FEATURE_EL2) && cpu_isar_feature(aa64_vh, cpu)) {
+ define_arm_vh_e2h_redirects_aliases(cpu);
+ }
+#endif
}
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)