[PATCH v2 01/11] hw/hexagon: Add globalreg model

Brian Cain posted 11 patches 3 weeks, 5 days ago
[PATCH v2 01/11] hw/hexagon: Add globalreg model
Posted by Brian Cain 3 weeks, 5 days ago
Some of the system registers are shared among all threads
in the core.  This object contains the representation and
interface to the system registers.

Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 include/hw/hexagon/hexagon_globalreg.h |  60 ++++++
 target/hexagon/cpu.h                   |   1 +
 hw/hexagon/hexagon_globalreg.c         | 269 +++++++++++++++++++++++++
 target/hexagon/cpu.c                   |   2 +
 4 files changed, 332 insertions(+)
 create mode 100644 include/hw/hexagon/hexagon_globalreg.h
 create mode 100644 hw/hexagon/hexagon_globalreg.c

diff --git a/include/hw/hexagon/hexagon_globalreg.h b/include/hw/hexagon/hexagon_globalreg.h
new file mode 100644
index 0000000000..01eac06056
--- /dev/null
+++ b/include/hw/hexagon/hexagon_globalreg.h
@@ -0,0 +1,60 @@
+/*
+ * Hexagon Global Registers QOM Object
+ *
+ * Copyright(c) 2025 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HEXAGON_GLOBALREG_H
+#define HEXAGON_GLOBALREG_H
+
+#include "hw/qdev-core.h"
+#include "hw/sysbus.h"
+#include "qom/object.h"
+#include "target/hexagon/cpu.h"
+
+#define TYPE_HEXAGON_GLOBALREG "hexagon-globalreg"
+OBJECT_DECLARE_SIMPLE_TYPE(HexagonGlobalRegState, HEXAGON_GLOBALREG)
+
+struct HexagonGlobalRegState {
+    SysBusDevice parent_obj;
+
+    /* Array of system registers */
+    target_ulong regs[NUM_SREGS];
+
+    /* Global performance cycle counter base */
+    uint64_t g_pcycle_base;
+
+    /* Properties for global register reset values */
+    uint32_t boot_evb;           /* Boot Exception Vector Base (HEX_SREG_EVB) */
+    uint64_t config_table_addr;  /* Configuration table base */
+    uint32_t dsp_rev;           /* DSP revision register (HEX_SREG_REV) */
+
+    /* ISDB properties */
+    bool isdben_etm_enable;     /* ISDB ETM enable bit */
+    bool isdben_dfd_enable;     /* ISDB DFD enable bit */
+    bool isdben_trusted;        /* ISDB trusted mode bit */
+    bool isdben_secure;         /* ISDB secure mode bit */
+
+    /* Hardware base addresses */
+    uint32_t qtimer_base_addr;  /* QTimer hardware base address */
+};
+
+/* Public interface functions */
+uint32_t hexagon_globalreg_read(HexagonCPU *cpu, uint32_t reg);
+void hexagon_globalreg_write(HexagonCPU *cpu, uint32_t reg,
+                             uint32_t value);
+uint32_t hexagon_globalreg_masked_value(HexagonCPU *cpu, uint32_t reg,
+                                        uint32_t value);
+void hexagon_globalreg_write_masked(HexagonCPU *cpu, uint32_t reg,
+                                    uint32_t value);
+void hexagon_globalreg_reset(HexagonCPU *cpu);
+
+/* Global performance cycle counter access */
+uint64_t hexagon_globalreg_get_pcycle_base(HexagonCPU *cpu);
+void hexagon_globalreg_set_pcycle_base(HexagonCPU *cpu, uint64_t value);
+
+/* Hardware base address access */
+uint32_t hexagon_globalreg_get_qtimer_base_addr(HexagonCPU *cpu);
+
+#endif /* HEXAGON_GLOBALREG_H */
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index 39d6983263..89c634a09c 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -197,6 +197,7 @@ struct ArchCPU {
     uint32_t num_tlbs;
     uint32_t l2vic_base_addr;
     uint32_t boot_addr;
+    struct HexagonGlobalRegState *globalregs;
 #endif
 };
 
diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c
new file mode 100644
index 0000000000..da943f7261
--- /dev/null
+++ b/hw/hexagon/hexagon_globalreg.c
@@ -0,0 +1,269 @@
+/*
+ * Hexagon Global Registers
+ *
+ * Copyright(c) 2025 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hexagon/hexagon.h"
+#include "hw/hexagon/hexagon_globalreg.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "hw/resettable.h"
+#include "migration/vmstate.h"
+#include "qom/object.h"
+#include "target/hexagon/cpu.h"
+#include "target/hexagon/hex_regs.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+
+#define IMMUTABLE (~0)
+#define INVALID_REG_VAL 0xdeadbeef
+
+/* Global system register mutability masks */
+static const uint32_t global_sreg_immut_masks[NUM_SREGS] = {
+    [HEX_SREG_EVB] = 0x000000ff,
+    [HEX_SREG_MODECTL] = IMMUTABLE,
+    [HEX_SREG_SYSCFG] = 0x80001c00,
+    [HEX_SREG_IPENDAD] = IMMUTABLE,
+    [HEX_SREG_VID] = 0xfc00fc00,
+    [HEX_SREG_VID1] = 0xfc00fc00,
+    [HEX_SREG_BESTWAIT] = 0xfffffe00,
+    [HEX_SREG_IAHL] = 0x00000000,
+    [HEX_SREG_SCHEDCFG] = 0xfffffee0,
+    [HEX_SREG_CFGBASE] = IMMUTABLE,
+    [HEX_SREG_DIAG] = 0x00000000,
+    [HEX_SREG_REV] = IMMUTABLE,
+    [HEX_SREG_ISDBST] = IMMUTABLE,
+    [HEX_SREG_ISDBCFG0] = 0xe0000000,
+    [HEX_SREG_BRKPTPC0] = 0x00000003,
+    [HEX_SREG_BRKPTCFG0] = 0xfc007000,
+    [HEX_SREG_BRKPTPC1] = 0x00000003,
+    [HEX_SREG_BRKPTCFG1] = 0xfc007000,
+    [HEX_SREG_ISDBMBXIN] = IMMUTABLE,
+    [HEX_SREG_ISDBMBXOUT] = 0x00000000,
+    [HEX_SREG_ISDBEN] = 0xfffffffe,
+    [HEX_SREG_TIMERLO] = IMMUTABLE,
+    [HEX_SREG_TIMERHI] = IMMUTABLE,
+};
+
+static void hexagon_globalreg_init(Object *obj)
+{
+    HexagonGlobalRegState *s = HEXAGON_GLOBALREG(obj);
+
+    memset(s->regs, 0, sizeof(target_ulong) * NUM_SREGS);
+}
+
+static inline uint32_t apply_write_mask(uint32_t new_val, uint32_t cur_val,
+                                        uint32_t reg_mask)
+{
+    if (reg_mask) {
+        return (new_val & ~reg_mask) | (cur_val & reg_mask);
+    }
+    return new_val;
+}
+
+static void read_timer(HexagonGlobalRegState *s, uint32_t *low, uint32_t *high)
+{
+    /* Not yet implemented */
+    *low = 0;
+    *high = 0;
+}
+
+uint32_t hexagon_globalreg_read(HexagonCPU *cpu, uint32_t reg)
+{
+    g_assert(reg < NUM_SREGS);
+    g_assert(reg >= HEX_SREG_GLB_START);
+    HexagonGlobalRegState *s = cpu->globalregs;
+    g_assert(s);
+
+    uint32_t value;
+    uint32_t low;
+    uint32_t high;
+    if ((reg == HEX_SREG_TIMERLO) || (reg == HEX_SREG_TIMERHI)) {
+        read_timer(s, &low, &high);
+        value = (reg == HEX_SREG_TIMERLO) ? low : high;
+    } else {
+        value = s->regs[reg];
+    }
+
+    return value;
+}
+
+void hexagon_globalreg_write(HexagonCPU *cpu, uint32_t reg,
+                             uint32_t value)
+{
+    HexagonGlobalRegState *s = cpu->globalregs;
+    g_assert(s);
+    g_assert(reg < NUM_SREGS);
+    g_assert(reg >= HEX_SREG_GLB_START);
+    s->regs[reg] = value;
+}
+
+uint32_t hexagon_globalreg_masked_value(HexagonCPU *cpu, uint32_t reg,
+                                        uint32_t value)
+{
+    HexagonGlobalRegState *s = cpu->globalregs;
+    g_assert(s);
+    g_assert(reg < NUM_SREGS);
+    g_assert(reg >= HEX_SREG_GLB_START);
+    const uint32_t reg_mask = global_sreg_immut_masks[reg];
+    return reg_mask == IMMUTABLE ?
+            s->regs[reg] :
+            apply_write_mask(value, s->regs[reg], reg_mask);
+}
+
+void hexagon_globalreg_write_masked(HexagonCPU *cpu, uint32_t reg,
+                                    uint32_t value)
+{
+    HexagonGlobalRegState *s = cpu->globalregs;
+    g_assert(s);
+    s->regs[reg] = hexagon_globalreg_masked_value(cpu, reg, value);
+}
+
+uint64_t hexagon_globalreg_get_pcycle_base(HexagonCPU *cpu)
+{
+    HexagonGlobalRegState *s = cpu->globalregs;
+    g_assert(s);
+    return s->g_pcycle_base;
+}
+
+void hexagon_globalreg_set_pcycle_base(HexagonCPU *cpu, uint64_t value)
+{
+    HexagonGlobalRegState *s = cpu->globalregs;
+    g_assert(s);
+    s->g_pcycle_base = value;
+}
+
+uint32_t hexagon_globalreg_get_qtimer_base_addr(HexagonCPU *cpu)
+{
+    HexagonGlobalRegState *s = cpu->globalregs;
+    g_assert(s);
+    return s->qtimer_base_addr;
+}
+
+static void do_hexagon_globalreg_reset(HexagonGlobalRegState *s)
+{
+    g_assert(s);
+    memset(s->regs, 0, sizeof(target_ulong) * NUM_SREGS);
+
+    s->g_pcycle_base = 0;
+
+    s->regs[HEX_SREG_EVB] = s->boot_evb;
+    s->regs[HEX_SREG_CFGBASE] = HEXAGON_CFG_ADDR_BASE(s->config_table_addr);
+    s->regs[HEX_SREG_REV] = s->dsp_rev;
+
+    target_ulong isdben_val = 0;
+    if (s->isdben_etm_enable) {
+        isdben_val |= (1 << 0);  /* ETM enable bit */
+    }
+    if (s->isdben_dfd_enable) {
+        isdben_val |= (1 << 1);  /* DFD enable bit */
+    }
+    if (s->isdben_trusted) {
+        isdben_val |= (1 << 2);  /* Trusted bit */
+    }
+    if (s->isdben_secure) {
+        isdben_val |= (1 << 3);  /* Secure bit */
+    }
+    s->regs[HEX_SREG_ISDBEN] = isdben_val;
+    s->regs[HEX_SREG_MODECTL] = 0x1;
+
+    /*
+     * These register indices are placeholders in these arrays
+     * and their actual values are synthesized from state elsewhere.
+     * We can initialize these with invalid values so that if we
+     * mistakenly generate reads, they will look obviously wrong.
+     */
+    s->regs[HEX_SREG_PCYCLELO] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_PCYCLEHI] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_TIMERLO] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_TIMERHI] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_PMUCNT0] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_PMUCNT1] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_PMUCNT2] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_PMUCNT3] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_PMUCNT4] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_PMUCNT5] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_PMUCNT6] = INVALID_REG_VAL;
+    s->regs[HEX_SREG_PMUCNT7] = INVALID_REG_VAL;
+}
+
+static void hexagon_globalreg_realize(DeviceState *dev, Error **errp)
+{
+}
+
+void hexagon_globalreg_reset(HexagonCPU *cpu)
+{
+    do_hexagon_globalreg_reset(cpu->globalregs);
+}
+
+static void hexagon_globalreg_reset_hold(Object *obj, ResetType type)
+{
+    HexagonGlobalRegState *s = HEXAGON_GLOBALREG(obj);
+    do_hexagon_globalreg_reset(s);
+}
+
+static const VMStateDescription vmstate_hexagon_globalreg = {
+    .name = "hexagon_globalreg",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]){
+        VMSTATE_UINT32_ARRAY(regs, HexagonGlobalRegState, NUM_SREGS),
+        VMSTATE_UINT64(g_pcycle_base, HexagonGlobalRegState),
+        VMSTATE_UINT32(boot_evb, HexagonGlobalRegState),
+        VMSTATE_UINT64(config_table_addr, HexagonGlobalRegState),
+        VMSTATE_UINT32(dsp_rev, HexagonGlobalRegState),
+        VMSTATE_BOOL(isdben_etm_enable, HexagonGlobalRegState),
+        VMSTATE_BOOL(isdben_dfd_enable, HexagonGlobalRegState),
+        VMSTATE_BOOL(isdben_trusted, HexagonGlobalRegState),
+        VMSTATE_BOOL(isdben_secure, HexagonGlobalRegState),
+        VMSTATE_UINT32(qtimer_base_addr, HexagonGlobalRegState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const Property hexagon_globalreg_properties[] = {
+    DEFINE_PROP_UINT32("boot-evb", HexagonGlobalRegState, boot_evb, 0x0),
+    DEFINE_PROP_UINT64("config-table-addr", HexagonGlobalRegState,
+                       config_table_addr, 0xffffffffULL),
+    DEFINE_PROP_UINT32("dsp-rev", HexagonGlobalRegState, dsp_rev, 0),
+    DEFINE_PROP_BOOL("isdben-etm-enable", HexagonGlobalRegState,
+                     isdben_etm_enable, false),
+    DEFINE_PROP_BOOL("isdben-dfd-enable", HexagonGlobalRegState,
+                     isdben_dfd_enable, false),
+    DEFINE_PROP_BOOL("isdben-trusted", HexagonGlobalRegState,
+                     isdben_trusted, false),
+    DEFINE_PROP_BOOL("isdben-secure", HexagonGlobalRegState,
+                     isdben_secure, false),
+    DEFINE_PROP_UINT32("qtimer-base-addr", HexagonGlobalRegState,
+                       qtimer_base_addr, 0),
+};
+
+static void hexagon_globalreg_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    dc->realize = hexagon_globalreg_realize;
+    rc->phases.hold = hexagon_globalreg_reset_hold;
+    dc->vmsd = &vmstate_hexagon_globalreg;
+    dc->user_creatable = false;
+    device_class_set_props(dc, hexagon_globalreg_properties);
+}
+
+static const TypeInfo hexagon_globalreg_info = {
+    .name = TYPE_HEXAGON_GLOBALREG,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(HexagonGlobalRegState),
+    .instance_init = hexagon_globalreg_init,
+    .class_init = hexagon_globalreg_class_init,
+};
+
+static void hexagon_globalreg_register_types(void)
+{
+    type_register_static(&hexagon_globalreg_info);
+}
+
+type_init(hexagon_globalreg_register_types)
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 751ba613cc..256fdc177e 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -70,6 +70,8 @@ static const Property hexagon_cpu_properties[] = {
     DEFINE_PROP_UINT32("l2vic-base-addr", HexagonCPU, l2vic_base_addr,
         0xffffffffULL),
     DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffffULL),
+    DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs,
+                     TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *),
 #endif
     DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false),
     DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0,
-- 
2.34.1