The HVX register file belongs to an extension context, not a hardware thread. Model each system-emulation context as a QOM device, link the contexts to each vCPU, and use SSR:XA to select the active context.
Keep one embedded context per linux-user CPU. Carry the selected register-file base with HVX operands so translated code can address the selected context at run time. Reviewed-by: Richard Henderson <[email protected]> Signed-off-by: Brian Cain <[email protected]> --- include/hw/hexagon/hexagon.h | 1 + include/hw/hexagon/hexagon_hvx_context.h | 27 ++++++++ target/hexagon/cpu.h | 24 +++++-- target/hexagon/cpu_helper.h | 1 + target/hexagon/translate.h | 7 +- hw/hexagon/hex-subsys.c | 29 ++++++++ hw/hexagon/hexagon_hvx_context.c | 84 ++++++++++++++++++++++++ target/hexagon/cpu.c | 16 +++++ target/hexagon/cpu_helper.c | 82 +++++++++++++++++++++++ target/hexagon/machine.c | 12 ++++ target/hexagon/translate.c | 9 ++- hw/hexagon/meson.build | 1 + 12 files changed, 286 insertions(+), 7 deletions(-) create mode 100644 include/hw/hexagon/hexagon_hvx_context.h create mode 100644 hw/hexagon/hexagon_hvx_context.c diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h index 62398eeb359..9b5d33ffc3b 100644 --- a/include/hw/hexagon/hexagon.h +++ b/include/hw/hexagon/hexagon.h @@ -173,6 +173,7 @@ struct HexagonCommonMachineState { DeviceState *qtimer; DeviceState *glob_regs; DeviceState *tlb; + unsigned num_hvx_ctx; }; #endif diff --git a/include/hw/hexagon/hexagon_hvx_context.h b/include/hw/hexagon/hexagon_hvx_context.h new file mode 100644 index 00000000000..ab85816a7ff --- /dev/null +++ b/include/hw/hexagon/hexagon_hvx_context.h @@ -0,0 +1,27 @@ +/* + * Hexagon HVX Extension Context QOM Object + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_HVX_CONTEXT_H +#define HEXAGON_HVX_CONTEXT_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" +#include "target/hexagon/cpu.h" + +#define TYPE_HEXAGON_HVX_CONTEXT "hexagon-hvx-context" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonHVXContextState, HEXAGON_HVX_CONTEXT) + +struct HexagonHVXContextState { + SysBusDevice parent_obj; + + /* Physical context number, as SSR:XA maps to it. */ + uint32_t index; + + HexagonHVXContext regs; +}; + +#endif /* HEXAGON_HVX_CONTEXT_H */ diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 965abe4f6d4..df4c0182247 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -29,6 +29,7 @@ typedef struct HexagonTLBState HexagonTLBState; typedef struct HexagonGlobalRegState HexagonGlobalRegState; +typedef struct HexagonHVXContextState HexagonHVXContextState; #include "cpu-qom.h" #include "exec/cpu-common.h" @@ -49,6 +50,7 @@ typedef struct HexagonGlobalRegState HexagonGlobalRegState; #define REG_WRITES_MAX 32 #define PRED_WRITES_MAX 5 /* 4 insns + endloop */ #define VSTORES_MAX 2 +#define HVX_CONTEXTS_MAX 8 #define MAX_TLB_ENTRIES 1024 #define THREADS_MAX 16 @@ -120,6 +122,11 @@ typedef struct { /* Maximum number of vector temps in a packet */ #define VECTOR_TEMPS_MAX 4 +typedef struct HexagonHVXContext { + MMVector VRegs[NUM_VREGS]; + MMQReg QRegs[NUM_QREGS]; +} QEMU_ALIGNED(16) HexagonHVXContext; + typedef struct CPUArchState { target_ulong gpr[TOTAL_PER_THREAD_REGS]; target_ulong pred[NUM_PREGS]; @@ -159,11 +166,15 @@ typedef struct CPUArchState { target_ulong llsc_val; uint64_t llsc_val_i64; - MMVector VRegs[NUM_VREGS] QEMU_ALIGNED(16); +#ifdef CONFIG_USER_ONLY + HexagonHVXContext hvx_ctx; +#else + HexagonHVXContext *hvx; +#endif + MMVector future_VRegs[VECTOR_TEMPS_MAX] QEMU_ALIGNED(16); MMVector tmp_VRegs[VECTOR_TEMPS_MAX] QEMU_ALIGNED(16); - MMQReg QRegs[NUM_QREGS] QEMU_ALIGNED(16); MMQReg future_QRegs[NUM_QREGS] QEMU_ALIGNED(16); /* Temporaries used within instructions */ @@ -196,6 +207,7 @@ struct ArchCPU { CPUHexagonState env; HexagonCPUConfig cfg; #ifndef CONFIG_USER_ONLY + HexagonHVXContextState *hvx_ctx[HVX_CONTEXTS_MAX]; HexagonTLBState *tlb; uint32_t boot_addr; HexagonGlobalRegState *globalregs; @@ -204,9 +216,13 @@ struct ArchCPU { #endif }; -static inline CPUHexagonState *hex_hvx(CPUHexagonState *env) +static inline HexagonHVXContext *hex_hvx(CPUHexagonState *env) { - return env; +#ifdef CONFIG_USER_ONLY + return &env->hvx_ctx; +#else + return env->hvx; +#endif } FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1) diff --git a/target/hexagon/cpu_helper.h b/target/hexagon/cpu_helper.h index 12512efd74f..afb4df59f80 100644 --- a/target/hexagon/cpu_helper.h +++ b/target/hexagon/cpu_helper.h @@ -15,6 +15,7 @@ void hexagon_peek_memory_range(CPUHexagonState *env, uint32_t start_addr, uint32_t length, uintptr_t retaddr); uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index); void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old); +unsigned hexagon_hvx_select_context(CPUHexagonState *env, uint32_t ssr); int get_cpu_mode(const CPUHexagonState *env); int get_exe_mode(const CPUHexagonState *env); void clear_wait_mode(CPUHexagonState *env); diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index d5940b93763..e17261761b2 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -363,8 +363,13 @@ extern TCGv_i64 hex_llsc_val_i64; extern TCGv hex_vstore_addr[VSTORES_MAX]; extern TCGv hex_vstore_size[VSTORES_MAX]; extern TCGv hex_vstore_pending[VSTORES_MAX]; +#ifdef CONFIG_USER_ONLY #define hex_hvx_ptr tcg_env -#define HEX_HVX_OFFSET(member) offsetof(CPUHexagonState, member) +#define HEX_HVX_OFFSET(member) offsetof(CPUHexagonState, hvx_ctx.member) +#else +extern TCGv_ptr hex_hvx_ptr; +#define HEX_HVX_OFFSET(member) offsetof(HexagonHVXContext, member) +#endif #ifndef CONFIG_USER_ONLY extern TCGv_i32 hex_greg[NUM_GREGS]; extern TCGv_i32 hex_t_sreg[NUM_SREGS]; diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c index 4e3a418340e..9818cf58878 100644 --- a/hw/hexagon/hex-subsys.c +++ b/hw/hexagon/hex-subsys.c @@ -9,6 +9,7 @@ #include "qapi/error.h" #include "hw/hexagon/hex-subsys.h" #include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon_hvx_context.h" #include "hw/hexagon/hexagon_tlb.h" #include "hw/intc/hex-l2vic.h" #include "hw/timer/qct-qtimer.h" @@ -98,6 +99,27 @@ static DeviceState *tlb_create(HexagonCommonMachineState *hms, return tlb; } +/* + * Create the core's HVX extension contexts. There are fewer of them than + * there are hardware threads, and SSR:XA picks which one a thread uses, so + * they belong to the subsystem rather than to any one CPU. + */ +static void hvx_contexts_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg) +{ + unsigned n = m_cfg->cfgtable.ext_contexts; + assert(n <= HVX_CONTEXTS_MAX); + for (unsigned i = 0; i < n; i++) { + DeviceState *ctx = qdev_new(TYPE_HEXAGON_HVX_CONTEXT); + g_autofree char *name = g_strdup_printf("hvx-context[%u]", i); + + object_property_add_child(OBJECT(hms), name, OBJECT(ctx)); + qdev_prop_set_uint32(ctx, "index", i); + sysbus_realize_and_unref(SYS_BUS_DEVICE(ctx), &error_fatal); + } + hms->num_hvx_ctx = n; +} + static DeviceState *cluster_create(HexagonCommonMachineState *hms) { DeviceState *cluster = qdev_new(TYPE_CPU_CLUSTER); @@ -140,6 +162,7 @@ void hex_subsys_create(HexagonCommonMachineState *hms, hms->qtimer = qtimer_create(hms, m_cfg); hms->glob_regs = globalreg_create(hms, m_cfg, rev); hms->tlb = tlb_create(hms, m_cfg); + hvx_contexts_create(hms, m_cfg); } void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) @@ -151,6 +174,12 @@ void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) &error_fatal); object_property_set_link(OBJECT(cpu), "l2vic", OBJECT(hms->l2vic), &error_fatal); + for (unsigned i = 0; i < hms->num_hvx_ctx; i++) { + g_autofree char *name = g_strdup_printf("hvx-context[%u]", i); + Object *ctx = object_resolve_path_component(OBJECT(hms), name); + + object_property_set_link(OBJECT(cpu), name, ctx, &error_fatal); + } } void hex_subsys_realize_cluster(HexagonCommonMachineState *hms) diff --git a/hw/hexagon/hexagon_hvx_context.c b/hw/hexagon/hexagon_hvx_context.c new file mode 100644 index 00000000000..3b73498298e --- /dev/null +++ b/hw/hexagon/hexagon_hvx_context.c @@ -0,0 +1,84 @@ +/* + * Hexagon HVX Extension Context QOM Object + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/hexagon/hexagon_hvx_context.h" +#include "hw/core/qdev-properties.h" +#include "migration/vmstate.h" + +static void hexagon_hvx_context_reset_hold(Object *obj, ResetType type) +{ + HexagonHVXContextState *s = HEXAGON_HVX_CONTEXT(obj); + + memset(&s->regs, 0, sizeof(s->regs)); +} + +/* gvec needs VRegs/QRegs 16-aligned within the struct. */ +QEMU_BUILD_BUG_ON(offsetof(HexagonHVXContextState, regs) % 16 != 0); + +static const VMStateDescription vmstate_mmvector = { + .name = "hexagon_mmvector", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]){ + VMSTATE_UINT64_ARRAY(ud, MMVector, MAX_VEC_SIZE_BYTES / 8), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_mmqreg = { + .name = "hexagon_mmqreg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]){ + VMSTATE_UINT64_ARRAY(ud, MMQReg, MAX_VEC_SIZE_BYTES / 8 / 8), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_hexagon_hvx_context = { + .name = "hexagon_hvx_context", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]){ + VMSTATE_STRUCT_ARRAY(regs.VRegs, HexagonHVXContextState, NUM_VREGS, + 1, vmstate_mmvector, MMVector), + VMSTATE_STRUCT_ARRAY(regs.QRegs, HexagonHVXContextState, NUM_QREGS, + 1, vmstate_mmqreg, MMQReg), + VMSTATE_END_OF_LIST() + } +}; + +static const Property hexagon_hvx_context_properties[] = { + DEFINE_PROP_UINT32("index", HexagonHVXContextState, index, 0), +}; + +static void hexagon_hvx_context_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = hexagon_hvx_context_reset_hold; + dc->vmsd = &vmstate_hexagon_hvx_context; + dc->user_creatable = false; + device_class_set_props(dc, hexagon_hvx_context_properties); +} + +static const TypeInfo hexagon_hvx_context_info = { + .name = TYPE_HEXAGON_HVX_CONTEXT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HexagonHVXContextState), + .instance_align = __alignof(HexagonHVXContextState), + .class_init = hexagon_hvx_context_class_init, +}; + +static void hexagon_hvx_context_register_types(void) +{ + type_register_static(&hexagon_hvx_context_info); +} + +type_init(hexagon_hvx_context_register_types) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 7298cd27a0e..912bae7b6c2 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -43,6 +43,7 @@ #include "exec/page-protection.h" #include "exec/target_page.h" #include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon_hvx_context.h" #endif static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) @@ -328,6 +329,9 @@ static TCGTBCPUState hexagon_get_tb_cpu_state(CPUState *cs) CPUHexagonState *env = cpu_env(cs); vaddr pc = env->gpr[HEX_REG_PC]; uint32_t hex_flags = 0; +#ifndef CONFIG_USER_ONLY + HexagonCPU *cpu; +#endif if (pc == env->gpr[HEX_REG_SA0]) { hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); @@ -338,10 +342,12 @@ static TCGTBCPUState hexagon_get_tb_cpu_state(CPUState *cs) } #ifndef CONFIG_USER_ONLY + cpu = env_archcpu(env); hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, cpu_mmu_index(env_cpu(env), false)); hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, PCYCLE_ENABLED, 1); hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, HVX_COPROC_ENABLED, + cpu->hvx_ctx[0] && GET_SSR_FIELD(SSR_XE, env->t_sreg[HEX_SREG_SSR])); #else hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, MMU_USER_IDX); @@ -449,6 +455,7 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type) env->t_sreg[HEX_SREG_HTID] = cpu->htid; env->threadId = cpu->htid; hexagon_cpu_soft_reset(env); + hexagon_hvx_select_context(env, env->t_sreg[HEX_SREG_SSR]); env->cause_code = HEX_EVENT_NONE; env->gpr[HEX_REG_PC] = cpu->boot_addr; #endif @@ -554,7 +561,16 @@ static void hexagon_cpu_init(Object *obj) { #ifndef CONFIG_USER_ONLY HexagonCPU *cpu = HEXAGON_CPU(obj); + qdev_init_gpio_in(DEVICE(cpu), hexagon_cpu_set_irq, 8); + + for (int i = 0; i < HVX_CONTEXTS_MAX; i++) { + object_property_add_link(obj, "hvx-context[*]", + TYPE_HEXAGON_HVX_CONTEXT, + (Object **)&cpu->hvx_ctx[i], + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG); + } #endif } diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c index eea5f60b46c..cf0762ff05f 100644 --- a/target/hexagon/cpu_helper.c +++ b/target/hexagon/cpu_helper.c @@ -11,6 +11,7 @@ #include "hw/core/boards.h" #include "hw/hexagon/hexagon.h" #include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon_hvx_context.h" #include "hex_interrupts.h" #include "hex_mmu.h" #include "system/runstate.h" @@ -246,6 +247,75 @@ void hexagon_resume_threads(CPUHexagonState *current_env, uint32_t mask) } } +static unsigned hexagon_hvx_context_count(HexagonCPU *cpu) +{ + unsigned n; + + for (n = 0; n < HVX_CONTEXTS_MAX; n++) { + if (!cpu->hvx_ctx[n]) { + break; + } + } + return n; +} + +static unsigned hexagon_hvx_context_index(HexagonCPU *cpu, uint8_t xa) +{ + unsigned n = hexagon_hvx_context_count(cpu); + + if (n == 0) { + return 0; + } + if (xa >= n) { + qemu_log_mask(LOG_GUEST_ERROR, + "SSR.XA %u is out of range for %u HVX contexts\n", + xa, n); + } + /* + * The behavior here is unspecified, reference simulator uses + * mappings that effectively keep the context in-range. The + * modulus seems just as good as any. + */ + return xa % n; +} + +/* + * Diagnostic only. Called separately from hexagon_hvx_select_context() + * so migration post_load, where other CPUs' env->hvx may still be stale, + * doesn't trip a false positive. + */ +static void hexagon_hvx_check_overcommit(CPUHexagonState *env, unsigned idx) +{ + CPUState *cs; + unsigned users = 0; + + CPU_FOREACH(cs) { + CPUHexagonState *other = cpu_env(cs); + + if (other->hvx == env->hvx && + GET_SSR_FIELD(SSR_XE, other->t_sreg[HEX_SREG_SSR])) { + users++; + } + } + + if (users > 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "HVX context %u is enabled for %u hardware threads " + "at once, which is undefined\n", idx, users); + } +} + +unsigned hexagon_hvx_select_context(CPUHexagonState *env, uint32_t ssr) +{ + HexagonCPU *cpu = env_archcpu(env); + unsigned idx = hexagon_hvx_context_index(cpu, GET_SSR_FIELD(SSR_XA, ssr)); + + if (cpu->hvx_ctx[0]) { + env->hvx = &cpu->hvx_ctx[idx]->regs; + } + return idx; +} + void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old) { bool old_EX, old_UM, old_GM, old_IE; @@ -269,6 +339,18 @@ void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old) hex_mmu_mode_change(env); } + bool xa_changed = GET_SSR_FIELD(SSR_XA, new) != GET_SSR_FIELD(SSR_XA, old); + bool xe_changed = GET_SSR_FIELD(SSR_XE, new) != GET_SSR_FIELD(SSR_XE, old); + + if (xa_changed || xe_changed) { + unsigned idx = xa_changed + ? hexagon_hvx_select_context(env, new) + : hexagon_hvx_context_index(env_archcpu(env), + GET_SSR_FIELD(SSR_XA, new)); + + hexagon_hvx_check_overcommit(env, idx); + } + old_asid = GET_SSR_FIELD(SSR_ASID, old); new_asid = GET_SSR_FIELD(SSR_ASID, new); if (new_asid != old_asid) { diff --git a/target/hexagon/machine.c b/target/hexagon/machine.c index bf4646f4a8b..179b6f53a2a 100644 --- a/target/hexagon/machine.c +++ b/target/hexagon/machine.c @@ -7,11 +7,23 @@ #include "qemu/osdep.h" #include "migration/vmstate.h" #include "cpu.h" +#include "cpu_helper.h" + +static int hexagon_cpu_post_load(void *opaque, int version_id) +{ + HexagonCPU *cpu = opaque; + CPUHexagonState *env = &cpu->env; + + hexagon_hvx_select_context(env, env->t_sreg[HEX_SREG_SSR]); + + return 0; +} const VMStateDescription vmstate_hexagon_cpu = { .name = "cpu", .version_id = 2, .minimum_version_id = 2, + .post_load = hexagon_cpu_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(env.gpr, HexagonCPU, TOTAL_PER_THREAD_REGS), VMSTATE_UINT32_ARRAY(env.pred, HexagonCPU, NUM_PREGS), diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index bebfeb2daca..827859e052c 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -70,6 +70,9 @@ TCGv hex_imprecise_exception; TCGv hex_vstore_addr[VSTORES_MAX]; TCGv hex_vstore_size[VSTORES_MAX]; TCGv hex_vstore_pending[VSTORES_MAX]; +#ifndef CONFIG_USER_ONLY +TCGv_ptr hex_hvx_ptr; +#endif #ifndef CONFIG_USER_ONLY TCGv_i32 hex_greg[NUM_GREGS]; @@ -1070,7 +1073,7 @@ static void gen_commit_hvx(DisasContext *ctx) /* * for (i = 0; i < ctx->vreg_log_idx; i++) { * int rnum = ctx->vreg_log[i]; - * hex_hvx(env)->VRegs[rnum] = env->future_VRegs[rnum]; + * env->hvx->VRegs[rnum] = env->future_VRegs[rnum]; * } */ for (i = 0; i < ctx->vreg_log_idx; i++) { @@ -1087,7 +1090,7 @@ static void gen_commit_hvx(DisasContext *ctx) /* * for (i = 0; i < ctx->qreg_log_idx; i++) { * int rnum = ctx->qreg_log[i]; - * hex_hvx(env)->QRegs[rnum] = env->future_QRegs[rnum]; + * env->hvx->QRegs[rnum] = env->future_QRegs[rnum]; * } */ for (i = 0; i < ctx->qreg_log_idx; i++) { @@ -1421,6 +1424,8 @@ void hexagon_translate_init(void) opcode_init(); #ifndef CONFIG_USER_ONLY + hex_hvx_ptr = tcg_global_mem_new_ptr(tcg_env, + offsetof(CPUHexagonState, hvx), "hvx"); for (i = 0; i < NUM_GREGS; i++) { hex_greg[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUHexagonState, greg[i]), diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build index 720a5d54dcc..c4b257dfa28 100644 --- a/hw/hexagon/meson.build +++ b/hw/hexagon/meson.build @@ -1,6 +1,7 @@ hexagon_ss = ss.source_set() hexagon_ss.add(files('hexagon_tlb.c')) hexagon_ss.add(files('hexagon_globalreg.c')) +hexagon_ss.add(files('hexagon_hvx_context.c')) hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hex-subsys.c')) hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c')) hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c')) -- 2.34.1
