Commit 1099acc19a for qemu.org

commit 1099acc19a9973dabb7079242efe38eec6ab0ca4
Author: Brian Cain <brian.cain@oss.qualcomm.com>
Date:   Wed Sep 23 10:15:15 2026 -0700

    target/hexagon: share HVX contexts among CPUs

    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 <richard.henderson@linaro.org>
    Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c
index 4e3a418340..9818cf5887 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 0000000000..3b73498298
--- /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/hw/hexagon/meson.build b/hw/hexagon/meson.build
index 720a5d54dc..c4b257dfa2 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'))
diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h
index 62398eeb35..9b5d33ffc3 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 0000000000..ab85816a7f
--- /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.c b/target/hexagon/cpu.c
index 7298cd27a0..912bae7b6c 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.h b/target/hexagon/cpu.h
index 965abe4f6d..df4c018224 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.c b/target/hexagon/cpu_helper.c
index eea5f60b46..cf0762ff05 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/cpu_helper.h b/target/hexagon/cpu_helper.h
index 12512efd74..afb4df59f8 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/machine.c b/target/hexagon/machine.c
index bf4646f4a8..179b6f53a2 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 bebfeb2dac..827859e052 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/target/hexagon/translate.h b/target/hexagon/translate.h
index d5940b9376..e17261761b 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];