diff --git a/qemu/glib_compat.c b/qemu/glib_compat.c index 2f465182..3f11020c 100644 --- a/qemu/glib_compat.c +++ b/qemu/glib_compat.c @@ -1481,18 +1481,18 @@ static inline void g_hash_table_maybe_resize (GHashTable *hash_table) * @value (and perhaps the new @key). If it is not found, create a * new node. */ -static void g_hash_table_insert_internal (GHashTable *hash_table, - gpointer key, - gpointer value, - gboolean keep_new_key) +static gboolean g_hash_table_insert_internal (GHashTable *hash_table, + gpointer key, + gpointer value, + gboolean keep_new_key) { GHashNode *node; guint node_index; guint key_hash; guint old_hash; - if (hash_table == NULL) return; - if (hash_table->ref_count == 0) return; + if (hash_table == NULL) return false; + if (hash_table->ref_count == 0) return false; node_index = g_hash_table_lookup_node_for_insertion (hash_table, key, &key_hash); node = &hash_table->nodes [node_index]; @@ -1500,39 +1500,41 @@ static void g_hash_table_insert_internal (GHashTable *hash_table, old_hash = node->key_hash; if (old_hash > 1) - { + { if (keep_new_key) - { - if (hash_table->key_destroy_func) - hash_table->key_destroy_func (node->key); - node->key = key; - } + { + if (hash_table->key_destroy_func) + hash_table->key_destroy_func (node->key); + node->key = key; + } else - { - if (hash_table->key_destroy_func) - hash_table->key_destroy_func (key); - } + { + if (hash_table->key_destroy_func) + hash_table->key_destroy_func (key); + } if (hash_table->value_destroy_func) hash_table->value_destroy_func (node->value); node->value = value; - } + return false; + } else + { + node->key = key; + node->value = value; + node->key_hash = key_hash; + + hash_table->nnodes++; + + if (old_hash == 0) { - node->key = key; - node->value = value; - node->key_hash = key_hash; - - hash_table->nnodes++; - - if (old_hash == 0) - { - /* We replaced an empty node, and not a tombstone */ - hash_table->noccupied++; - g_hash_table_maybe_resize (hash_table); - } + /* We replaced an empty node, and not a tombstone */ + hash_table->noccupied++; + g_hash_table_maybe_resize (hash_table); } + return true; + } } GList * @@ -1571,11 +1573,11 @@ g_hash_table_get_keys (GHashTable *hash_table) * a @key_destroy_func when creating the #GHashTable, the passed key is freed * using that function. **/ -void g_hash_table_insert (GHashTable *hash_table, - gpointer key, - gpointer value) +gboolean g_hash_table_insert (GHashTable *hash_table, + gpointer key, + gpointer value) { - g_hash_table_insert_internal (hash_table, key, value, FALSE); + return g_hash_table_insert_internal (hash_table, key, value, FALSE); } /** diff --git a/qemu/include/glib_compat.h b/qemu/include/glib_compat.h index 532f60b9..dd8a1a73 100644 --- a/qemu/include/glib_compat.h +++ b/qemu/include/glib_compat.h @@ -143,7 +143,7 @@ void g_hash_table_destroy(GHashTable *hash_table); gpointer g_hash_table_find(GHashTable *hash_table, GHRFunc predicate, gpointer user_data); void g_hash_table_foreach(GHashTable *hash_table, GHFunc func, gpointer user_data); GList *g_hash_table_get_keys(GHashTable *hash_table); -void g_hash_table_insert(GHashTable *hash_table, gpointer key, gpointer value); +gboolean g_hash_table_insert(GHashTable *hash_table, gpointer key, gpointer value); void g_hash_table_replace(GHashTable *hash_table, gpointer key, gpointer value); gpointer g_hash_table_lookup(GHashTable *hash_table, gconstpointer key); GHashTable *g_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func); diff --git a/qemu/target/arm/cpu.h b/qemu/target/arm/cpu.h index 98ff1dc5..bbd9f272 100644 --- a/qemu/target/arm/cpu.h +++ b/qemu/target/arm/cpu.h @@ -2475,6 +2475,19 @@ struct ARMCPRegInfo { * fieldoffset is 0 then no reset will be done. */ CPResetFn *resetfn; + + /* + * "Original" writefn and readfn. + * For ARMv8.1-VHE register aliases, we overwrite the read/write + * accessor functions of various EL1/EL0 to perform the runtime + * check for which sysreg should actually be modified, and then + * forwards the operation. Before overwriting the accessors, + * the original function is copied here, so that accesses that + * really do go to the EL1/EL0 version proceed normally. + * (The corresponding EL2 register is linked via opaque.) + */ + CPReadFn *orig_readfn; + CPWriteFn *orig_writefn; }; /* Macros which are lvalues for the field in CPUARMState for the diff --git a/qemu/target/arm/helper.c b/qemu/target/arm/helper.c index 86303894..40ed2c15 100644 --- a/qemu/target/arm/helper.c +++ b/qemu/target/arm/helper.c @@ -5152,6 +5152,158 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { 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) { @@ -7063,6 +7215,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) : 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)