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
© 2016 - 2025 Red Hat, Inc.