* [PATCH v2 00/11] hexagon system emulation v2, part 3/3
@ 2025-09-02 3:49 Brian Cain
2025-09-02 3:49 ` [PATCH v2 01/11] hw/hexagon: Add globalreg model Brian Cain
` (11 more replies)
0 siblings, 12 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning
As with parts 1, 2 - some of the changes requested have been made, but not
all.
New features for part 3:
- global registers device model
- boot-serial-test qtest case
Issues not addressed with v2:
* "Add l2vic interrupt controller" - DECLARE_SIMPLE_TYPE, bitops
- I might also push this and/or QTimer out of this series to simplify
things a bit.
Brian Cain (8):
hw/hexagon: Add globalreg model
hw/hexagon: Add global register tracing
hw/hexagon: Add machine configs for sysemu
hw/hexagon: Add v68, sa8775-cdsp0 defs
hw/hexagon: Modify "Standalone" symbols
target/hexagon: add build config for softmmu
hw/hexagon: Define hexagon "virt" machine
tests/qtest: Add hexagon boot-serial-test
Sid Manning (3):
hw/hexagon: Add support for cfgbase
hw/timer: Add QTimer device
hw/intc: Add l2vic interrupt controller
MAINTAINERS | 7 +
docs/devel/hexagon-l2vic.rst | 59 +++
docs/devel/index-internals.rst | 1 +
configs/devices/hexagon-softmmu/default.mak | 8 +
configs/targets/hexagon-softmmu.mak | 7 +
meson.build | 1 +
qapi/machine.json | 4 +-
include/hw/hexagon/hexagon.h | 150 ++++++
include/hw/hexagon/hexagon_globalreg.h | 60 +++
include/hw/hexagon/virt.h | 43 ++
include/hw/intc/l2vic.h | 38 ++
include/hw/timer/qct-qtimer.h | 85 ++++
target/hexagon/cpu.h | 6 +-
hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc | 63 +++
hw/hexagon/machine_cfg_v66g_1024.h.inc | 63 +++
hw/hexagon/machine_cfg_v68n_1024.h.inc | 64 +++
hw/hexagon/hexagon_dsp.c | 211 ++++++++
hw/hexagon/hexagon_globalreg.c | 345 +++++++++++++
hw/hexagon/virt.c | 457 +++++++++++++++++
hw/intc/l2vic.c | 421 ++++++++++++++++
hw/timer/qct-qtimer.c | 520 ++++++++++++++++++++
system/qdev-monitor.c | 2 +-
target/hexagon/cpu.c | 5 +
target/hexagon/op_helper.c | 2 +-
target/hexagon/translate.c | 1 +
tests/qtest/boot-serial-test.c | 8 +
hw/Kconfig | 1 +
hw/hexagon/Kconfig | 16 +
hw/hexagon/meson.build | 7 +
hw/hexagon/trace-events | 3 +
hw/intc/Kconfig | 3 +
hw/intc/meson.build | 2 +
hw/intc/trace-events | 4 +
hw/meson.build | 1 +
hw/timer/meson.build | 2 +
target/Kconfig | 1 +
target/hexagon/Kconfig | 2 +
target/hexagon/meson.build | 12 +-
tests/qemu-iotests/testenv.py | 1 +
tests/qtest/meson.build | 2 +
40 files changed, 2680 insertions(+), 8 deletions(-)
create mode 100644 docs/devel/hexagon-l2vic.rst
create mode 100644 configs/devices/hexagon-softmmu/default.mak
create mode 100644 configs/targets/hexagon-softmmu.mak
create mode 100644 include/hw/hexagon/hexagon.h
create mode 100644 include/hw/hexagon/hexagon_globalreg.h
create mode 100644 include/hw/hexagon/virt.h
create mode 100644 include/hw/intc/l2vic.h
create mode 100644 include/hw/timer/qct-qtimer.h
create mode 100644 hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc
create mode 100644 hw/hexagon/machine_cfg_v66g_1024.h.inc
create mode 100644 hw/hexagon/machine_cfg_v68n_1024.h.inc
create mode 100644 hw/hexagon/hexagon_dsp.c
create mode 100644 hw/hexagon/hexagon_globalreg.c
create mode 100644 hw/hexagon/virt.c
create mode 100644 hw/intc/l2vic.c
create mode 100644 hw/timer/qct-qtimer.c
create mode 100644 hw/hexagon/Kconfig
create mode 100644 hw/hexagon/meson.build
create mode 100644 hw/hexagon/trace-events
create mode 100644 target/hexagon/Kconfig
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 01/11] hw/hexagon: Add globalreg model
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 3:49 ` [PATCH v2 02/11] hw/hexagon: Add global register tracing Brian Cain
` (10 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning
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
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 02/11] hw/hexagon: Add global register tracing
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
2025-09-02 3:49 ` [PATCH v2 01/11] hw/hexagon: Add globalreg model Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-10-02 5:42 ` Philippe Mathieu-Daudé
2025-09-02 3:49 ` [PATCH v2 03/11] hw/hexagon: Add machine configs for sysemu Brian Cain
` (9 subsequent siblings)
11 siblings, 1 reply; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
meson.build | 1 +
hw/hexagon/hexagon_globalreg.c | 73 ++++++++++++++++++++++++++++++++++
hw/hexagon/trace-events | 3 ++
3 files changed, 77 insertions(+)
create mode 100644 hw/hexagon/trace-events
diff --git a/meson.build b/meson.build
index 0d42de61ae..d7e6149ab5 100644
--- a/meson.build
+++ b/meson.build
@@ -3669,6 +3669,7 @@ if have_system
'hw/display',
'hw/dma',
'hw/fsi',
+ 'hw/hexagon',
'hw/hyperv',
'hw/i2c',
'hw/i386',
diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c
index da943f7261..fcbf2ae4b2 100644
--- a/hw/hexagon/hexagon_globalreg.c
+++ b/hw/hexagon/hexagon_globalreg.c
@@ -16,11 +16,82 @@
#include "target/hexagon/cpu.h"
#include "target/hexagon/hex_regs.h"
#include "qemu/log.h"
+#include "trace/trace-hw_hexagon.h"
#include "qapi/error.h"
#define IMMUTABLE (~0)
#define INVALID_REG_VAL 0xdeadbeef
+static const char *hex_sreg_names[] = {
+ [HEX_SREG_SGP0] = "sgp0",
+ [HEX_SREG_SGP1] = "sgp1",
+ [HEX_SREG_STID] = "stid",
+ [HEX_SREG_ELR] = "elr",
+ [HEX_SREG_BADVA0] = "badva0",
+ [HEX_SREG_BADVA1] = "badva1",
+ [HEX_SREG_SSR] = "ssr",
+ [HEX_SREG_CCR] = "ccr",
+ [HEX_SREG_HTID] = "htid",
+ [HEX_SREG_BADVA] = "badva",
+ [HEX_SREG_IMASK] = "imask",
+ [HEX_SREG_GEVB] = "gevb",
+ [HEX_SREG_EVB] = "evb",
+ [HEX_SREG_MODECTL] = "modectl",
+ [HEX_SREG_SYSCFG] = "syscfg",
+ [HEX_SREG_IPENDAD] = "ipendad",
+ [HEX_SREG_VID] = "vid",
+ [HEX_SREG_VID1] = "vid1",
+ [HEX_SREG_BESTWAIT] = "bestwait",
+ [HEX_SREG_IEL] = "iel",
+ [HEX_SREG_SCHEDCFG] = "schedcfg",
+ [HEX_SREG_IAHL] = "iahl",
+ [HEX_SREG_CFGBASE] = "cfgbase",
+ [HEX_SREG_DIAG] = "diag",
+ [HEX_SREG_REV] = "rev",
+ [HEX_SREG_PCYCLELO] = "pcyclelo",
+ [HEX_SREG_PCYCLEHI] = "pcyclehi",
+ [HEX_SREG_ISDBST] = "isdbst",
+ [HEX_SREG_ISDBCFG0] = "isdbcfg0",
+ [HEX_SREG_ISDBCFG1] = "isdbcfg1",
+ [HEX_SREG_LIVELOCK] = "livelock",
+ [HEX_SREG_BRKPTPC0] = "brkptpc0",
+ [HEX_SREG_BRKPTCFG0] = "brkptcfg0",
+ [HEX_SREG_BRKPTPC1] = "brkptpc1",
+ [HEX_SREG_BRKPTCFG1] = "brkptcfg1",
+ [HEX_SREG_ISDBMBXIN] = "isdbmbxin",
+ [HEX_SREG_ISDBMBXOUT] = "isdbmbxout",
+ [HEX_SREG_ISDBEN] = "isdben",
+ [HEX_SREG_ISDBGPR] = "isdbgpr",
+ [HEX_SREG_PMUCNT4] = "pmucnt4",
+ [HEX_SREG_PMUCNT5] = "pmucnt5",
+ [HEX_SREG_PMUCNT6] = "pmucnt6",
+ [HEX_SREG_PMUCNT7] = "pmucnt7",
+ [HEX_SREG_PMUCNT0] = "pmucnt0",
+ [HEX_SREG_PMUCNT1] = "pmucnt1",
+ [HEX_SREG_PMUCNT2] = "pmucnt2",
+ [HEX_SREG_PMUCNT3] = "pmucnt3",
+ [HEX_SREG_PMUEVTCFG] = "pmuevtcfg",
+ [HEX_SREG_PMUSTID0] = "pmustid0",
+ [HEX_SREG_PMUEVTCFG1] = "pmuevtcfg1",
+ [HEX_SREG_PMUSTID1] = "pmustid1",
+ [HEX_SREG_TIMERLO] = "timerlo",
+ [HEX_SREG_TIMERHI] = "timerhi",
+ [HEX_SREG_PMUCFG] = "pmucfg",
+ [HEX_SREG_S59] = "s59",
+ [HEX_SREG_S60] = "s60",
+ [HEX_SREG_S61] = "s61",
+ [HEX_SREG_S62] = "s62",
+ [HEX_SREG_S63] = "s63",
+};
+
+static const char *get_sreg_name(uint32_t reg)
+{
+ if (reg < ARRAY_SIZE(hex_sreg_names) && hex_sreg_names[reg]) {
+ return hex_sreg_names[reg];
+ }
+ return "UNKNOWN";
+}
+
/* Global system register mutability masks */
static const uint32_t global_sreg_immut_masks[NUM_SREGS] = {
[HEX_SREG_EVB] = 0x000000ff,
@@ -88,6 +159,7 @@ uint32_t hexagon_globalreg_read(HexagonCPU *cpu, uint32_t reg)
value = s->regs[reg];
}
+ trace_hexagon_globalreg_read(get_sreg_name(reg), value);
return value;
}
@@ -99,6 +171,7 @@ void hexagon_globalreg_write(HexagonCPU *cpu, uint32_t reg,
g_assert(reg < NUM_SREGS);
g_assert(reg >= HEX_SREG_GLB_START);
s->regs[reg] = value;
+ trace_hexagon_globalreg_write(get_sreg_name(reg), value);
}
uint32_t hexagon_globalreg_masked_value(HexagonCPU *cpu, uint32_t reg,
diff --git a/hw/hexagon/trace-events b/hw/hexagon/trace-events
new file mode 100644
index 0000000000..6ef88d9e05
--- /dev/null
+++ b/hw/hexagon/trace-events
@@ -0,0 +1,3 @@
+# Hexagon global register access
+hexagon_globalreg_read(const char *reg_name, uint32_t value) "reg=%s value=0x%x"
+hexagon_globalreg_write(const char *reg_name, uint32_t value) "reg=%s value=0x%x"
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 03/11] hw/hexagon: Add machine configs for sysemu
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
2025-09-02 3:49 ` [PATCH v2 01/11] hw/hexagon: Add globalreg model Brian Cain
2025-09-02 3:49 ` [PATCH v2 02/11] hw/hexagon: Add global register tracing Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 3:49 ` [PATCH v2 04/11] hw/hexagon: Add v68, sa8775-cdsp0 defs Brian Cain
` (8 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning, Brian Cain, Markus Armbruster, Mike Lambert,
Sid Manning
From: Brian Cain <bcain@quicinc.com>
Some header includes are modified here: these are uniquely required for
basic system emulation functionality and had not been required for linux-user.
Acked-by: Markus Armbruster <armbru@redhat.com>
Co-authored-by: Mike Lambert <mlambert@quicinc.com>
Co-authored-by: Sid Manning <sidneym@quicinc.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
MAINTAINERS | 2 +
qapi/machine.json | 4 +-
include/hw/hexagon/hexagon.h | 150 ++++++++++++++++++++
target/hexagon/cpu.h | 1 +
hw/hexagon/machine_cfg_v66g_1024.h.inc | 63 +++++++++
hw/hexagon/hexagon_dsp.c | 183 +++++++++++++++++++++++++
system/qdev-monitor.c | 2 +-
target/hexagon/cpu.c | 1 +
target/hexagon/op_helper.c | 2 +-
target/hexagon/translate.c | 1 +
hw/Kconfig | 1 +
hw/hexagon/Kconfig | 5 +
hw/hexagon/meson.build | 5 +
hw/meson.build | 1 +
14 files changed, 418 insertions(+), 3 deletions(-)
create mode 100644 include/hw/hexagon/hexagon.h
create mode 100644 hw/hexagon/machine_cfg_v66g_1024.h.inc
create mode 100644 hw/hexagon/hexagon_dsp.c
create mode 100644 hw/hexagon/Kconfig
create mode 100644 hw/hexagon/meson.build
diff --git a/MAINTAINERS b/MAINTAINERS
index bf7695658a..efc237b27b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -229,6 +229,8 @@ Hexagon TCG CPUs
M: Brian Cain <brian.cain@oss.qualcomm.com>
S: Supported
F: target/hexagon/
+F: hw/hexagon/
+F: include/hw/hexagon/
X: target/hexagon/idef-parser/
X: target/hexagon/gen_idef_parser_funcs.py
F: linux-user/hexagon/
diff --git a/qapi/machine.json b/qapi/machine.json
index 038eab281c..67f1929a98 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -26,6 +26,8 @@
#
# @avr: since 5.1
#
+# @hexagon: since 10.2
+#
# @loongarch64: since 7.1
#
# .. note:: The resulting QMP strings can be appended to the
@@ -35,7 +37,7 @@
# Since: 3.0
##
{ 'enum' : 'SysEmuTarget',
- 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'hppa', 'i386',
+ 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'hexagon', 'hppa', 'i386',
'loongarch64', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
'mips64el', 'mipsel', 'or1k', 'ppc',
'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4',
diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h
new file mode 100644
index 0000000000..eb0c20276f
--- /dev/null
+++ b/include/hw/hexagon/hexagon.h
@@ -0,0 +1,150 @@
+/*
+ * Hexagon Baseboard System emulation.
+ *
+ * Copyright (c) 2020-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+#ifndef HW_HEXAGON_H
+#define HW_HEXAGON_H
+
+#include "system/memory.h"
+
+struct hexagon_board_boot_info {
+ uint64_t ram_size;
+ const char *kernel_filename;
+ uint32_t kernel_elf_flags;
+};
+
+typedef enum {
+ unknown_rev = 0,
+ v66_rev = 0xa666,
+ v67_rev = 0x2667,
+ v68_rev = 0x8d68,
+ v69_rev = 0x8c69,
+ v71_rev = 0x8c71,
+ v73_rev = 0x8c73,
+ v73m_rev = 0xcc73,
+} Rev_t;
+#define HEXAGON_LATEST_REV v73
+#define HEXAGON_LATEST_REV_UPPER V73
+
+/*
+ * Config table address bases represent bits [35:16].
+ */
+#define HEXAGON_CFG_ADDR_BASE(addr) (((addr) >> 16) & 0x0fffff)
+
+#define HEXAGON_CFGSPACE_ENTRIES (128)
+
+typedef union {
+ struct {
+ /* Base address of L2TCM space */
+ uint32_t l2tcm_base;
+ uint32_t reserved0;
+ /* Base address of subsystem space */
+ uint32_t subsystem_base;
+ /* Base address of ETM space */
+ uint32_t etm_base;
+ /* Base address of L2 configuration space */
+ uint32_t l2cfg_base;
+ uint32_t reserved1;
+ /* Base address of L1S */
+ uint32_t l1s0_base;
+ /* Base address of AXI2 */
+ uint32_t axi2_lowaddr;
+ /* Base address of streamer base */
+ uint32_t streamer_base;
+ uint32_t reserved2;
+ /* Base address of fast L2VIC */
+ uint32_t fastl2vic_base;
+ /* Number of entries in JTLB */
+ uint32_t jtlb_size_entries;
+ /* Coprocessor type */
+ uint32_t coproc_present;
+ /* Number of extension execution contexts available */
+ uint32_t ext_contexts;
+ /* Base address of Hexagon Vector Tightly Coupled Memory (VTCM) */
+ uint32_t vtcm_base;
+ /* Size of VTCM (in KB) */
+ uint32_t vtcm_size_kb;
+ /* L2 tag size */
+ uint32_t l2tag_size;
+ /* Amount of physical L2 memory in released version */
+ uint32_t l2ecomem_size;
+ /* Hardware threads available on the core */
+ uint32_t thread_enable_mask;
+ /* Base address of the ECC registers */
+ uint32_t eccreg_base;
+ /* L2 line size */
+ uint32_t l2line_size;
+ /* Small Core processor (also implies audio extension) */
+ uint32_t tiny_core;
+ /* Size of L2TCM */
+ uint32_t l2itcm_size;
+ /* Base address of L2-ITCM */
+ uint32_t l2itcm_base;
+ uint32_t reserved3;
+ /* DTM is present */
+ uint32_t dtm_present;
+ /* Version of the DMA */
+ uint32_t dma_version;
+ /* Native HVX vector length in log of bytes */
+ uint32_t hvx_vec_log_length;
+ /* Core ID of the multi-core */
+ uint32_t core_id;
+ /* Number of multi-core cores */
+ uint32_t core_count;
+ uint32_t coproc2_reg0;
+ uint32_t coproc2_reg1;
+ /* Supported HVX vector length */
+ uint32_t v2x_mode;
+ uint32_t coproc2_reg2;
+ uint32_t coproc2_reg3;
+ uint32_t coproc2_reg4;
+ uint32_t coproc2_reg5;
+ uint32_t coproc2_reg6;
+ uint32_t coproc2_reg7;
+ /* Voltage droop mitigation technique parameter */
+ uint32_t acd_preset;
+ /* Voltage droop mitigation technique parameter */
+ uint32_t mnd_preset;
+ /* L1 data cache size (in KB) */
+ uint32_t l1d_size_kb;
+ /* L1 instruction cache size in (KB) */
+ uint32_t l1i_size_kb;
+ /* L1 data cache write policy: see HexagonL1WritePolicy */
+ uint32_t l1d_write_policy;
+ /* VTCM bank width */
+ uint32_t vtcm_bank_width;
+ uint32_t reserved4;
+ uint32_t reserved5;
+ uint32_t reserved6;
+ uint32_t coproc2_cvt_mpy_size;
+ uint32_t consistency_domain;
+ uint32_t capacity_domain;
+ uint32_t axi3_lowaddr;
+ uint32_t coproc2_int8_subcolumns;
+ uint32_t corecfg_present;
+ uint32_t coproc2_fp16_acc_exp;
+ uint32_t AXIM2_secondary_base;
+ };
+ uint32_t raw[HEXAGON_CFGSPACE_ENTRIES];
+} hexagon_config_table;
+
+typedef struct {
+ /* Base address of config table */
+ uint32_t cfgbase;
+ /* Size of L2 TCM */
+ uint32_t l2tcm_size;
+ /* Base address of L2VIC */
+ uint32_t l2vic_base;
+ /* Size of L2VIC region */
+ uint32_t l2vic_size;
+ /* QTimer csr base */
+ uint32_t csr_base;
+ uint32_t qtmr_region;
+ hexagon_config_table cfgtable;
+} hexagon_machine_config;
+
+#endif
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index 89c634a09c..86fbe30dbb 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -196,6 +196,7 @@ struct ArchCPU {
#ifndef CONFIG_USER_ONLY
uint32_t num_tlbs;
uint32_t l2vic_base_addr;
+ uint32_t hvx_contexts;
uint32_t boot_addr;
struct HexagonGlobalRegState *globalregs;
#endif
diff --git a/hw/hexagon/machine_cfg_v66g_1024.h.inc b/hw/hexagon/machine_cfg_v66g_1024.h.inc
new file mode 100644
index 0000000000..8f2a593bb8
--- /dev/null
+++ b/hw/hexagon/machine_cfg_v66g_1024.h.inc
@@ -0,0 +1,63 @@
+
+static hexagon_machine_config v66g_1024 = {
+ .cfgbase = 0xd8180000,
+ .l2tcm_size = 0x00000000,
+ .l2vic_base = 0xfc910000,
+ .l2vic_size = 0x00001000,
+ .csr_base = 0xfc900000,
+ .qtmr_region = 0xfc921000,
+ .cfgtable = {
+ .l2tcm_base = 0x0000d800,
+ .reserved0 = 0x0000d400,
+ .subsystem_base = 0x0000fc90,
+ .etm_base = 0x0000d805,
+ .l2cfg_base = 0x0000d81a,
+ .reserved1 = 0x00000000,
+ .l1s0_base = 0x0000d820,
+ .axi2_lowaddr = 0x00003000,
+ .streamer_base = 0x00000000,
+ .reserved2 = 0x0000d819,
+ .fastl2vic_base = 0x0000d81e,
+ .jtlb_size_entries = 0x00000080,
+ .coproc_present = 0x00000001,
+ .ext_contexts = 0x00000004,
+ .vtcm_base = 0x0000d820,
+ .vtcm_size_kb = 0x00000100,
+ .l2tag_size = 0x00000400,
+ .l2ecomem_size = 0x00000400,
+ .thread_enable_mask = 0x0000000f,
+ .eccreg_base = 0x0000d81f,
+ .l2line_size = 0x00000080,
+ .tiny_core = 0x00000000,
+ .l2itcm_size = 0x00000000,
+ .l2itcm_base = 0x0000d820,
+ .reserved3 = 0x00000000,
+ .dtm_present = 0x00000000,
+ .dma_version = 0x00000000,
+ .hvx_vec_log_length = 0x00000080,
+ .core_id = 0x00000000,
+ .core_count = 0x00000000,
+ .coproc2_reg0 = 0x00000000,
+ .coproc2_reg1 = 0x00000000,
+ .v2x_mode = 0x00000000,
+ .coproc2_reg2 = 0x00000000,
+ .coproc2_reg3 = 0x00000000,
+ .coproc2_reg4 = 0x00000000,
+ .coproc2_reg5 = 0x00000000,
+ .coproc2_reg6 = 0x00000000,
+ .coproc2_reg7 = 0x00000000,
+ .acd_preset = 0x00000000,
+ .mnd_preset = 0x00000000,
+ .l1d_size_kb = 0x00000000,
+ .l1i_size_kb = 0x00000000,
+ .l1d_write_policy = 0x00000000,
+ .vtcm_bank_width = 0x00000000,
+ .reserved3 = 0x00000000,
+ .reserved4 = 0x00000000,
+ .reserved5 = 0x00000000,
+ .coproc2_cvt_mpy_size = 0x00000000,
+ .consistency_domain = 0x00000000,
+ .capacity_domain = 0x00000000,
+ .axi3_lowaddr = 0x00000000,
+ },
+};
diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c
new file mode 100644
index 0000000000..d491a21a76
--- /dev/null
+++ b/hw/hexagon/hexagon_dsp.c
@@ -0,0 +1,183 @@
+/*
+ * Hexagon DSP Subsystem emulation. This represents a generic DSP
+ * subsystem with few peripherals, like the Compute DSP.
+ *
+ * Copyright (c) 2020-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "system/address-spaces.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/qdev-properties.h"
+#include "hw/hexagon/hexagon.h"
+#include "hw/hexagon/hexagon_globalreg.h"
+#include "hw/loader.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "elf.h"
+#include "cpu.h"
+#include "include/migration/cpu.h"
+#include "include/system/system.h"
+#include "target/hexagon/internal.h"
+#include "system/reset.h"
+
+#include "machine_cfg_v66g_1024.h.inc"
+
+static void hex_symbol_callback(const char *st_name, int st_info,
+ uint64_t st_value, uint64_t st_size)
+{
+}
+
+/* Board init. */
+static struct hexagon_board_boot_info hexagon_binfo;
+
+static void hexagon_load_kernel(HexagonCPU *cpu)
+{
+ uint64_t pentry;
+ long kernel_size;
+
+ kernel_size = load_elf_ram_sym(hexagon_binfo.kernel_filename, NULL, NULL,
+ NULL, &pentry, NULL, NULL,
+ &hexagon_binfo.kernel_elf_flags, 0, EM_HEXAGON, 0, 0,
+ &address_space_memory, false, hex_symbol_callback);
+
+ if (kernel_size <= 0) {
+ error_report("no kernel file '%s'",
+ hexagon_binfo.kernel_filename);
+ exit(1);
+ }
+
+ qdev_prop_set_uint32(DEVICE(cpu), "exec-start-addr", pentry);
+}
+
+static void hexagon_init_bootstrap(MachineState *machine, HexagonCPU *cpu)
+{
+ if (machine->kernel_filename) {
+ hexagon_load_kernel(cpu);
+ }
+}
+
+static void do_cpu_reset(void *opaque)
+{
+ HexagonCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+ cpu_reset(cs);
+}
+
+static void hexagon_common_init(MachineState *machine, Rev_t rev,
+ hexagon_machine_config *m_cfg)
+{
+ memset(&hexagon_binfo, 0, sizeof(hexagon_binfo));
+ if (machine->kernel_filename) {
+ hexagon_binfo.ram_size = machine->ram_size;
+ hexagon_binfo.kernel_filename = machine->kernel_filename;
+ }
+
+ machine->enable_graphics = 0;
+
+ MemoryRegion *address_space = get_system_memory();
+
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+ memory_region_init_ram(sram, NULL, "ddr.ram",
+ machine->ram_size, &error_fatal);
+ memory_region_add_subregion(address_space, 0x0, sram);
+
+ Error **errp = NULL;
+
+ DeviceState *glob_regs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG);
+ qdev_prop_set_uint64(glob_regs_dev, "config-table-addr", m_cfg->cfgbase);
+ qdev_prop_set_uint32(glob_regs_dev, "qtimer-base-addr", m_cfg->qtmr_region);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs_dev), errp);
+
+ for (int i = 0; i < machine->smp.cpus; i++) {
+ HexagonCPU *cpu = HEXAGON_CPU(object_new(machine->cpu_type));
+ qemu_register_reset(do_cpu_reset, cpu);
+
+ /*
+ * CPU #0 is the only CPU running at boot, others must be
+ * explicitly enabled via start instruction.
+ */
+ qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0));
+ qdev_prop_set_uint32(DEVICE(cpu), "l2vic-base-addr", m_cfg->l2vic_base);
+ qdev_prop_set_uint32(DEVICE(cpu), "config-table-addr", m_cfg->cfgbase);
+ qdev_prop_set_uint32(DEVICE(cpu), "hvx-contexts",
+ m_cfg->cfgtable.ext_contexts);
+ qdev_prop_set_uint32(DEVICE(cpu), "jtlb-entries",
+ m_cfg->cfgtable.jtlb_size_entries);
+
+
+ if (i == 0) {
+ hexagon_init_bootstrap(machine, cpu);
+ if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) {
+ return;
+ }
+ DeviceState *l2vic_dev;
+ l2vic_dev = sysbus_create_varargs("l2vic", m_cfg->l2vic_base,
+ /* IRQ#, Evnt#,CauseCode */
+ qdev_get_gpio_in(DEVICE(cpu), 0),
+ qdev_get_gpio_in(DEVICE(cpu), 1),
+ qdev_get_gpio_in(DEVICE(cpu), 2),
+ qdev_get_gpio_in(DEVICE(cpu), 3),
+ qdev_get_gpio_in(DEVICE(cpu), 4),
+ qdev_get_gpio_in(DEVICE(cpu), 5),
+ qdev_get_gpio_in(DEVICE(cpu), 6),
+ qdev_get_gpio_in(DEVICE(cpu), 7),
+ NULL);
+ sysbus_mmio_map(SYS_BUS_DEVICE(l2vic_dev), 1,
+ m_cfg->cfgtable.fastl2vic_base << 16);
+ } else if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) {
+ return;
+ }
+
+ }
+}
+
+static void init_mc(MachineClass *mc)
+{
+ mc->block_default_type = IF_SD;
+ mc->default_ram_size = 4 * GiB;
+ mc->no_parallel = 1;
+ mc->no_floppy = 1;
+ mc->no_cdrom = 1;
+ mc->no_serial = 1;
+ mc->is_default = false;
+ mc->max_cpus = 8;
+}
+
+/* ----------------------------------------------------------------- */
+/* Core-specific configuration settings are defined below this line. */
+/* Config table values defined in machine_configs.h.inc */
+/* ----------------------------------------------------------------- */
+
+static void v66g_1024_config_init(MachineState *machine)
+{
+ hexagon_common_init(machine, v66_rev, &v66g_1024);
+}
+
+static void v66g_1024_init(ObjectClass *oc, const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Hexagon V66G_1024";
+ mc->init = v66g_1024_config_init;
+ init_mc(mc);
+ mc->is_default = true;
+ mc->default_cpu_type = TYPE_HEXAGON_CPU_V66;
+ mc->default_cpus = 4;
+}
+
+static const TypeInfo hexagon_machine_types[] = {
+ {
+ .name = MACHINE_TYPE_NAME("V66G_1024"),
+ .parent = TYPE_MACHINE,
+ .class_init = v66g_1024_init,
+ },
+};
+
+DEFINE_TYPES(hexagon_machine_types)
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c
index 2ac92d0a07..4acc2d3280 100644
--- a/system/qdev-monitor.c
+++ b/system/qdev-monitor.c
@@ -69,7 +69,7 @@ typedef struct QDevAlias
QEMU_ARCH_SPARC | \
QEMU_ARCH_XTENSA)
#define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X)
-#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K)
+#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K | QEMU_ARCH_HEXAGON)
/* Please keep this table sorted by typename. */
static const QDevAlias qdev_alias_table[] = {
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 256fdc177e..ba78a04bdc 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -69,6 +69,7 @@ static const Property hexagon_cpu_properties[] = {
DEFINE_PROP_UINT32("jtlb-entries", HexagonCPU, num_tlbs, MAX_TLB_ENTRIES),
DEFINE_PROP_UINT32("l2vic-base-addr", HexagonCPU, l2vic_base_addr,
0xffffffffULL),
+ DEFINE_PROP_UINT32("hvx-contexts", HexagonCPU, hvx_contexts, 0),
DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffffULL),
DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs,
TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *),
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 1bc32769c6..fa893e480b 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -20,9 +20,9 @@
#include "accel/tcg/cpu-ldst.h"
#include "accel/tcg/probe.h"
#include "qemu/main-loop.h"
+#include "cpu.h"
#include "exec/helper-proto.h"
#include "fpu/softfloat.h"
-#include "cpu.h"
#include "exec/cpu-interrupt.h"
#include "internal.h"
#include "macros.h"
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index 38c2037c47..3e70fa29bc 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -32,6 +32,7 @@
#include "translate.h"
#include "genptr.h"
#include "printinsn.h"
+#include "exec/target_page.h"
#define HELPER_H "helper.h"
#include "exec/helper-info.c.inc"
diff --git a/hw/Kconfig b/hw/Kconfig
index 9e6c789ae7..1c3684055a 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -52,6 +52,7 @@ source arm/Kconfig
source cpu/Kconfig
source alpha/Kconfig
source avr/Kconfig
+source hexagon/Kconfig
source hppa/Kconfig
source i386/Kconfig
source loongarch/Kconfig
diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig
new file mode 100644
index 0000000000..7b9577f68f
--- /dev/null
+++ b/hw/hexagon/Kconfig
@@ -0,0 +1,5 @@
+config HEX_DSP
+ bool
+ default y
+ depends on HEXAGON && TCG
+ imply PTIMER
diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build
new file mode 100644
index 0000000000..11e71a67d5
--- /dev/null
+++ b/hw/hexagon/meson.build
@@ -0,0 +1,5 @@
+hexagon_ss = ss.source_set()
+hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c', 'hexagon_globalreg.c'))
+
+hw_arch += {'hexagon': hexagon_ss}
+
diff --git a/hw/meson.build b/hw/meson.build
index 791ce21ab4..7530742e69 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -50,6 +50,7 @@ subdir('fsi')
subdir('alpha')
subdir('arm')
subdir('avr')
+subdir('hexagon')
subdir('hppa')
subdir('i386')
subdir('loongarch')
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 04/11] hw/hexagon: Add v68, sa8775-cdsp0 defs
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
` (2 preceding siblings ...)
2025-09-02 3:49 ` [PATCH v2 03/11] hw/hexagon: Add machine configs for sysemu Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 3:49 ` [PATCH v2 05/11] hw/hexagon: Add support for cfgbase Brian Cain
` (7 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning, Brian Cain
From: Brian Cain <bcain@quicinc.com>
Acked-by: Taylor Simpson <ltaylorsimpson@gmail.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc | 63 ++++++++++++++++++++++
hw/hexagon/machine_cfg_v68n_1024.h.inc | 64 +++++++++++++++++++++++
2 files changed, 127 insertions(+)
create mode 100644 hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc
create mode 100644 hw/hexagon/machine_cfg_v68n_1024.h.inc
diff --git a/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc b/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc
new file mode 100644
index 0000000000..70b1eabfe9
--- /dev/null
+++ b/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc
@@ -0,0 +1,63 @@
+
+static hexagon_machine_config SA8775P_cdsp0 = {
+ .cfgbase = 0x24000000 + 0x180000,
+ .l2tcm_size = 0x00000000,
+ .l2vic_base = 0x26300000 + 0x90000,
+ .l2vic_size = 0x00001000,
+ .csr_base = 0x26300000,
+ .qtmr_region = 0x26300000 + 0xA1000,
+ .cfgtable = {
+ .l2tcm_base = 0x00002400,
+ .reserved0 = 0x00000000,
+ .subsystem_base = 0x00002638,
+ .etm_base = 0x00002419,
+ .l2cfg_base = 0x0000241a,
+ .reserved1 = 0x0000241b,
+ .l1s0_base = 0x00002500,
+ .axi2_lowaddr = 0x00000000,
+ .streamer_base = 0x00000000,
+ .reserved2 = 0x00000000,
+ .fastl2vic_base = 0x0000241e,
+ .jtlb_size_entries = 0x00000080,
+ .coproc_present = 0x00000001,
+ .ext_contexts = 0x00000004,
+ .vtcm_base = 0x00002500,
+ .vtcm_size_kb = 0x00002000,
+ .l2tag_size = 0x00000400,
+ .l2ecomem_size = 0x00000000,
+ .thread_enable_mask = 0x0000003f,
+ .eccreg_base = 0x0000241f,
+ .l2line_size = 0x00000080,
+ .tiny_core = 0x00000000,
+ .l2itcm_size = 0x00000000,
+ .l2itcm_base = 0x00002400,
+ .reserved3 = 0x00000000,
+ .dtm_present = 0x00000000,
+ .dma_version = 0x00000003,
+ .hvx_vec_log_length = 0x00000007,
+ .core_id = 0x00000000,
+ .core_count = 0x00000000,
+ .coproc2_reg0 = 0x00000040,
+ .coproc2_reg1 = 0x00000020,
+ .v2x_mode = 0x00000001,
+ .coproc2_reg2 = 0x00000008,
+ .coproc2_reg3 = 0x00000020,
+ .coproc2_reg4 = 0x00000000,
+ .coproc2_reg5 = 0x00000002,
+ .coproc2_reg6 = 0x00000016,
+ .coproc2_reg7 = 0x00000006,
+ .acd_preset = 0x00000001,
+ .mnd_preset = 0x00000000,
+ .l1d_size_kb = 0x00000010,
+ .l1i_size_kb = 0x00000020,
+ .l1d_write_policy = 0x00000002,
+ .vtcm_bank_width = 0x00000080,
+ .reserved3 = 0x00000001,
+ .reserved4 = 0x00000000,
+ .reserved5 = 0x00000003,
+ .coproc2_cvt_mpy_size = 0x0000000a,
+ .consistency_domain = 0x000000e0,
+ .capacity_domain = 0x00000080,
+ .axi3_lowaddr = 0x00000000,
+ },
+};
diff --git a/hw/hexagon/machine_cfg_v68n_1024.h.inc b/hw/hexagon/machine_cfg_v68n_1024.h.inc
new file mode 100644
index 0000000000..257c133df8
--- /dev/null
+++ b/hw/hexagon/machine_cfg_v68n_1024.h.inc
@@ -0,0 +1,64 @@
+
+static hexagon_machine_config v68n_1024 = {
+ .cfgbase = 0xde000000,
+ .l2tcm_size = 0x00000000,
+ .l2vic_base = 0xfc910000,
+ .l2vic_size = 0x00001000,
+ .csr_base = 0xfc900000,
+ .qtmr_region = 0xfc921000,
+ .cfgtable = {
+ .l2tcm_base = 0x0000d800,
+ .reserved0 = 0x00000000,
+ .subsystem_base = 0x0000fc90,
+ .etm_base = 0x0000d819,
+ .l2cfg_base = 0x0000d81a,
+ .reserved1 = 0x00000000,
+ .l1s0_base = 0x0000d840,
+ .axi2_lowaddr = 0x00003000,
+ .streamer_base = 0x0000d81c,
+ .reserved2 = 0x0000d81d,
+ .fastl2vic_base = 0x0000d81e,
+ .jtlb_size_entries = 0x00000080,
+ .coproc_present = 0x00000001,
+ .ext_contexts = 0x00000004,
+ .vtcm_base = 0x0000d840,
+ .vtcm_size_kb = 0x00001000,
+ .l2tag_size = 0x00000400,
+ .l2ecomem_size = 0x00000400,
+ .thread_enable_mask = 0x0000003f,
+ .eccreg_base = 0x0000d81f,
+ .l2line_size = 0x00000080,
+ .tiny_core = 0x00000000,
+ .l2itcm_size = 0x00000000,
+ .l2itcm_base = 0x0000d820,
+ .reserved3 = 0x00000000,
+ .dtm_present = 0x00000000,
+ .dma_version = 0x00000001,
+ .hvx_vec_log_length = 0x00000007,
+ .core_id = 0x00000000,
+ .core_count = 0x00000000,
+ .coproc2_reg0 = 0x00000040,
+ .coproc2_reg1 = 0x00000020,
+ .v2x_mode = 0x1f1f1f1f,
+ .coproc2_reg2 = 0x1f1f1f1f,
+ .coproc2_reg3 = 0x1f1f1f1f,
+ .coproc2_reg4 = 0x1f1f1f1f,
+ .coproc2_reg5 = 0x1f1f1f1f,
+ .coproc2_reg6 = 0x1f1f1f1f,
+ .coproc2_reg7 = 0x1f1f1f1f,
+ .acd_preset = 0x1f1f1f1f,
+ .mnd_preset = 0x1f1f1f1f,
+ .l1d_size_kb = 0x1f1f1f1f,
+ .l1i_size_kb = 0x1f1f1f1f,
+ .l1d_write_policy = 0x1f1f1f1f,
+ .vtcm_bank_width = 0x1f1f1f1f,
+ .reserved3 = 0x1f1f1f1f,
+ .reserved4 = 0x1f1f1f1f,
+ .reserved5 = 0x1f1f1f1f,
+ .coproc2_cvt_mpy_size = 0x1f1f1f1f,
+ .consistency_domain = 0x1f1f1f1f,
+ .capacity_domain = 0x1f1f1f1f,
+ .axi3_lowaddr = 0x1f1f1f1f,
+ },
+};
+
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 05/11] hw/hexagon: Add support for cfgbase
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
` (3 preceding siblings ...)
2025-09-02 3:49 ` [PATCH v2 04/11] hw/hexagon: Add v68, sa8775-cdsp0 defs Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 3:49 ` [PATCH v2 06/11] hw/hexagon: Modify "Standalone" symbols Brian Cain
` (6 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning, Sid Manning
From: Sid Manning <sidneym@quicinc.com>
Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com>
Signed-off-by: Sid Manning <sidneym@quicinc.com>
---
hw/hexagon/hexagon_dsp.c | 10 ++++++++++
target/hexagon/cpu.c | 2 ++
2 files changed, 12 insertions(+)
diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c
index d491a21a76..486dc41f5d 100644
--- a/hw/hexagon/hexagon_dsp.c
+++ b/hw/hexagon/hexagon_dsp.c
@@ -83,6 +83,12 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev,
MemoryRegion *address_space = get_system_memory();
+ MemoryRegion *config_table_rom = g_new(MemoryRegion, 1);
+ memory_region_init_rom(config_table_rom, NULL, "config_table.rom",
+ sizeof(m_cfg->cfgtable), &error_fatal);
+ memory_region_add_subregion(address_space, m_cfg->cfgbase,
+ config_table_rom);
+
MemoryRegion *sram = g_new(MemoryRegion, 1);
memory_region_init_ram(sram, NULL, "ddr.ram",
machine->ram_size, &error_fatal);
@@ -136,6 +142,10 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev,
}
}
+
+ rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable,
+ sizeof(m_cfg->cfgtable), m_cfg->cfgbase,
+ &address_space_memory);
}
static void init_mc(MachineClass *mc)
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index ba78a04bdc..6cf9250b99 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -405,6 +405,8 @@ void hexagon_cpu_soft_reset(CPUHexagonState *env)
}
#endif
+
+#define HEXAGON_CFG_ADDR_BASE(addr) (((addr) >> 16) & 0x0fffff)
static void hexagon_cpu_reset_hold(Object *obj, ResetType type)
{
CPUState *cs = CPU(obj);
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 06/11] hw/hexagon: Modify "Standalone" symbols
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
` (4 preceding siblings ...)
2025-09-02 3:49 ` [PATCH v2 05/11] hw/hexagon: Add support for cfgbase Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 3:49 ` [PATCH v2 07/11] target/hexagon: add build config for softmmu Brian Cain
` (5 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning, Brian Cain
From: Brian Cain <bcain@quicinc.com>
These symbols are used by Hexagon Standalone OS to indicate whether
the program should halt and wait for interrupts at startup. For QEMU,
we want these programs to just continue crt0 startup through to the user
program's main().
Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
hw/hexagon/hexagon_dsp.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c
index 486dc41f5d..510378280e 100644
--- a/hw/hexagon/hexagon_dsp.c
+++ b/hw/hexagon/hexagon_dsp.c
@@ -29,9 +29,17 @@
#include "machine_cfg_v66g_1024.h.inc"
+static hwaddr isdb_secure_flag;
+static hwaddr isdb_trusted_flag;
static void hex_symbol_callback(const char *st_name, int st_info,
uint64_t st_value, uint64_t st_size)
{
+ if (!g_strcmp0("isdb_secure_flag", st_name)) {
+ isdb_secure_flag = st_value;
+ }
+ if (!g_strcmp0("isdb_trusted_flag", st_name)) {
+ isdb_trusted_flag = st_value;
+ }
}
/* Board init. */
@@ -60,6 +68,13 @@ static void hexagon_init_bootstrap(MachineState *machine, HexagonCPU *cpu)
{
if (machine->kernel_filename) {
hexagon_load_kernel(cpu);
+ uint32_t mem = 1;
+ if (isdb_secure_flag) {
+ cpu_physical_memory_write(isdb_secure_flag, &mem, sizeof(mem));
+ }
+ if (isdb_trusted_flag) {
+ cpu_physical_memory_write(isdb_trusted_flag, &mem, sizeof(mem));
+ }
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 07/11] target/hexagon: add build config for softmmu
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
` (5 preceding siblings ...)
2025-09-02 3:49 ` [PATCH v2 06/11] hw/hexagon: Modify "Standalone" symbols Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 3:49 ` [PATCH v2 08/11] hw/hexagon: Define hexagon "virt" machine Brian Cain
` (4 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning
Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
MAINTAINERS | 1 +
configs/devices/hexagon-softmmu/default.mak | 7 +++++++
configs/targets/hexagon-softmmu.mak | 6 ++++++
target/hexagon/cpu.h | 4 ----
target/Kconfig | 1 +
target/hexagon/Kconfig | 2 ++
target/hexagon/meson.build | 12 +++++++++++-
7 files changed, 28 insertions(+), 5 deletions(-)
create mode 100644 configs/devices/hexagon-softmmu/default.mak
create mode 100644 configs/targets/hexagon-softmmu.mak
create mode 100644 target/hexagon/Kconfig
diff --git a/MAINTAINERS b/MAINTAINERS
index efc237b27b..e19fcf9e69 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -237,6 +237,7 @@ F: linux-user/hexagon/
F: tests/tcg/hexagon/
F: disas/hexagon.c
F: configs/targets/hexagon-linux-user/default.mak
+F: configs/devices/hexagon-softmmu/default.mak
F: docker/dockerfiles/debian-hexagon-cross.docker
F: gdb-xml/hexagon*.xml
F: docs/system/target-hexagon.rst
diff --git a/configs/devices/hexagon-softmmu/default.mak b/configs/devices/hexagon-softmmu/default.mak
new file mode 100644
index 0000000000..08e709aea7
--- /dev/null
+++ b/configs/devices/hexagon-softmmu/default.mak
@@ -0,0 +1,7 @@
+# Default configuration for hexagon-softmmu
+
+# Uncomment the following lines to disable these optional devices:
+
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_HEX_DSP=y
+# CONFIG_L2VIC=y
diff --git a/configs/targets/hexagon-softmmu.mak b/configs/targets/hexagon-softmmu.mak
new file mode 100644
index 0000000000..8c208bf468
--- /dev/null
+++ b/configs/targets/hexagon-softmmu.mak
@@ -0,0 +1,6 @@
+# Default configuration for hexagon-softmmu
+
+TARGET_ARCH=hexagon
+TARGET_SUPPORTS_MTTCG=y
+TARGET_XML_FILES=gdb-xml/hexagon-core.xml gdb-xml/hexagon-hvx.xml gdb-xml/hexagon-sys.xml
+TARGET_LONG_BITS=32
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index 86fbe30dbb..57840d2100 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -33,10 +33,6 @@
#include "mmvec/mmvec.h"
#include "hw/registerfields.h"
-#ifndef CONFIG_USER_ONLY
-#error "Hexagon does not support system emulation"
-#endif
-
#ifndef CONFIG_USER_ONLY
#include "reg_fields.h"
typedef struct CPUHexagonTLBContext CPUHexagonTLBContext;
diff --git a/target/Kconfig b/target/Kconfig
index d0c7b59d9c..37781146b9 100644
--- a/target/Kconfig
+++ b/target/Kconfig
@@ -16,6 +16,7 @@ source sh4/Kconfig
source sparc/Kconfig
source tricore/Kconfig
source xtensa/Kconfig
+source hexagon/Kconfig
config TARGET_BIG_ENDIAN
bool
diff --git a/target/hexagon/Kconfig b/target/hexagon/Kconfig
new file mode 100644
index 0000000000..7e556f3506
--- /dev/null
+++ b/target/hexagon/Kconfig
@@ -0,0 +1,2 @@
+config HEXAGON
+ bool
diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build
index 3ec53010fa..d2b56b9e65 100644
--- a/target/hexagon/meson.build
+++ b/target/hexagon/meson.build
@@ -245,6 +245,7 @@ decodetree_trans_funcs_generated = custom_target(
command: [python, files('gen_trans_funcs.py'), semantics_generated, '@OUTPUT@'],
)
hexagon_ss.add(decodetree_trans_funcs_generated)
+hexagon_softmmu_ss = ss.source_set()
hexagon_ss.add(files(
'cpu.c',
@@ -264,6 +265,13 @@ hexagon_ss.add(files(
'mmvec/system_ext_mmvec.c',
))
+hexagon_softmmu_ss.add(files(
+ 'hex_mmu.c',
+ 'hex_interrupts.c',
+ 'hexswi.c',
+ 'machine.c',
+))
+
#
# Step 4.5
# We use flex/bison based idef-parser to generate TCG code for a lot
@@ -273,7 +281,8 @@ hexagon_ss.add(files(
# idef-generated-enabled-instructions
#
idef_parser_enabled = get_option('hexagon_idef_parser')
-if idef_parser_enabled and 'hexagon-linux-user' in target_dirs
+if idef_parser_enabled and ('hexagon-linux-user' in target_dirs or
+ 'hexagon-softmmu' in target_dirs)
idef_parser_input_generated = custom_target(
'idef_parser_input.h.inc',
output: 'idef_parser_input.h.inc',
@@ -400,3 +409,4 @@ analyze_funcs_generated = custom_target(
hexagon_ss.add(analyze_funcs_generated)
target_arch += {'hexagon': hexagon_ss}
+target_system_arch += {'hexagon': hexagon_softmmu_ss}
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 08/11] hw/hexagon: Define hexagon "virt" machine
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
` (6 preceding siblings ...)
2025-09-02 3:49 ` [PATCH v2 07/11] target/hexagon: add build config for softmmu Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 3:49 ` [PATCH v2 09/11] tests/qtest: Add hexagon boot-serial-test Brian Cain
` (3 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning, Brian Cain
From: Brian Cain <bcain@quicinc.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
configs/devices/hexagon-softmmu/default.mak | 1 +
configs/targets/hexagon-softmmu.mak | 1 +
include/hw/hexagon/virt.h | 43 ++
hw/hexagon/virt.c | 435 ++++++++++++++++++++
hw/hexagon/Kconfig | 10 +
hw/hexagon/meson.build | 2 +
tests/qemu-iotests/testenv.py | 1 +
7 files changed, 493 insertions(+)
create mode 100644 include/hw/hexagon/virt.h
create mode 100644 hw/hexagon/virt.c
diff --git a/configs/devices/hexagon-softmmu/default.mak b/configs/devices/hexagon-softmmu/default.mak
index 08e709aea7..37b4f9f323 100644
--- a/configs/devices/hexagon-softmmu/default.mak
+++ b/configs/devices/hexagon-softmmu/default.mak
@@ -3,5 +3,6 @@
# Uncomment the following lines to disable these optional devices:
# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_HEX_VIRT=y
# CONFIG_HEX_DSP=y
# CONFIG_L2VIC=y
diff --git a/configs/targets/hexagon-softmmu.mak b/configs/targets/hexagon-softmmu.mak
index 8c208bf468..9f8fca1dc1 100644
--- a/configs/targets/hexagon-softmmu.mak
+++ b/configs/targets/hexagon-softmmu.mak
@@ -4,3 +4,4 @@ TARGET_ARCH=hexagon
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES=gdb-xml/hexagon-core.xml gdb-xml/hexagon-hvx.xml gdb-xml/hexagon-sys.xml
TARGET_LONG_BITS=32
+TARGET_NEED_FDT=y
diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h
new file mode 100644
index 0000000000..0c92fb3bb8
--- /dev/null
+++ b/include/hw/hexagon/virt.h
@@ -0,0 +1,43 @@
+/*
+ * Definitions for hexagon virt board.
+ *
+ * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_HEXAGONVIRT_H
+#define HW_HEXAGONVIRT_H
+
+#include "hw/boards.h"
+#include "target/hexagon/cpu.h"
+
+struct HexagonVirtMachineState {
+ /*< private >*/
+ MachineState parent_obj;
+
+ int fdt_size;
+ MemoryRegion *sys;
+ MemoryRegion cfgtable;
+ MemoryRegion ram;
+ MemoryRegion tcm;
+ MemoryRegion vtcm;
+ MemoryRegion bios;
+ DeviceState *l2vic;
+ Clock *apb_clk;
+};
+
+void hexagon_load_fdt(const struct HexagonVirtMachineState *vms);
+
+enum {
+ VIRT_UART0,
+ VIRT_QTMR0,
+ VIRT_QTMR1,
+ VIRT_GPT,
+ VIRT_MMIO,
+ VIRT_FDT,
+};
+
+#define TYPE_HEXAGON_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
+OBJECT_DECLARE_SIMPLE_TYPE(HexagonVirtMachineState, HEXAGON_VIRT_MACHINE)
+
+#endif /* HW_HEXAGONVIRT_H */
diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c
new file mode 100644
index 0000000000..615fde773d
--- /dev/null
+++ b/hw/hexagon/virt.c
@@ -0,0 +1,435 @@
+/*
+ * Hexagon virt emulation
+ *
+ * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hexagon/virt.h"
+#include "elf.h"
+#include "hw/char/pl011.h"
+#include "hw/clock.h"
+#include "hw/core/sysbus-fdt.h"
+#include "hw/hexagon/hexagon.h"
+#include "hw/hexagon/hexagon_globalreg.h"
+#include "hw/loader.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-clock.h"
+#include "hw/register.h"
+#include "qemu/error-report.h"
+#include "qemu/guest-random.h"
+#include "qemu/units.h"
+#include "elf.h"
+#include "machine_cfg_v68n_1024.h.inc"
+#include "system/address-spaces.h"
+#include "system/device_tree.h"
+#include "system/reset.h"
+#include "system/system.h"
+#include <libfdt.h>
+
+static const int VIRTIO_DEV_COUNT = 8;
+
+static const MemMapEntry base_memmap[] = {
+ [VIRT_UART0] = { 0x10000000, 0x00000200 },
+ [VIRT_MMIO] = { 0x11000000, 0x1000000, },
+ [VIRT_GPT] = { 0xab000000, 0x00001000 },
+ [VIRT_FDT] = { 0x99800000, 0x00400000 },
+};
+
+static const int irqmap[] = {
+ [VIRT_MMIO] = 18, /* ...to 18 + VIRTIO_DEV_COUNT - 1 */
+ [VIRT_GPT] = 12,
+ [VIRT_UART0] = 15,
+ [VIRT_QTMR0] = 2,
+ [VIRT_QTMR1] = 4,
+};
+
+
+static void create_fdt(HexagonVirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ void *fdt = create_device_tree(&vms->fdt_size);
+
+ if (!fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+
+ ms->fdt = fdt;
+
+ qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,hexagon-virt");
+ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1);
+ qemu_fdt_setprop_string(fdt, "/", "model", "linux,hexagon-virt");
+
+ qemu_fdt_setprop_string(fdt, "/", "model", "hexagon-virt,qemu");
+ qemu_fdt_setprop_string(fdt, "/", "compatible", "qcom,sm8150");
+
+ qemu_fdt_add_subnode(fdt, "/soc");
+ qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1);
+ qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+
+ qemu_fdt_add_subnode(fdt, "/chosen");
+
+ uint8_t rng_seed[32];
+ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
+ qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
+}
+
+static void fdt_add_hvx(HexagonVirtMachineState *vms,
+ const hexagon_machine_config *m_cfg, Error **errp)
+{
+ const MachineState *ms = MACHINE(vms);
+ uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024;
+ if (vtcm_size_bytes > 0) {
+ memory_region_init_ram(&vms->vtcm, NULL, "vtcm.ram", vtcm_size_bytes,
+ errp);
+ memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base << 16,
+ &vms->vtcm);
+
+ qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm");
+ qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible",
+ "qcom,hexagon_vtcm");
+
+ assert(sizeof(m_cfg->cfgtable.vtcm_base) == sizeof(uint32_t));
+ qemu_fdt_setprop_cells(ms->fdt, "/soc/vtcm", "reg", 0,
+ m_cfg->cfgtable.vtcm_base << 16,
+ vtcm_size_bytes);
+ }
+
+ if (m_cfg->cfgtable.ext_contexts > 0) {
+ qemu_fdt_add_subnode(ms->fdt, "/soc/hvx");
+ qemu_fdt_setprop_string(ms->fdt, "/soc/hvx", "compatible",
+ "qcom,hexagon-hvx");
+ qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-max-ctxts",
+ m_cfg->cfgtable.ext_contexts);
+ qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-vlength",
+ m_cfg->cfgtable.hvx_vec_log_length);
+ }
+}
+
+static int32_t irq_hvm_ic_phandle = -1;
+static void fdt_add_hvm_pic_node(HexagonVirtMachineState *vms,
+ const hexagon_machine_config *m_cfg)
+{
+ MachineState *ms = MACHINE(vms);
+ irq_hvm_ic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+
+ qemu_fdt_setprop_cell(ms->fdt, "/soc", "interrupt-parent",
+ irq_hvm_ic_phandle);
+
+ qemu_fdt_add_subnode(ms->fdt, "/soc/interrupt-controller");
+ qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller",
+ "#address-cells", 2);
+ qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller",
+ "#interrupt-cells", 2);
+ qemu_fdt_setprop_string(ms->fdt, "/soc/interrupt-controller", "compatible",
+ "qcom,h2-pic,hvm-pic");
+ qemu_fdt_setprop(ms->fdt, "/soc/interrupt-controller",
+ "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller", "phandle",
+ irq_hvm_ic_phandle);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(vms->l2vic), 1,
+ m_cfg->cfgtable.fastl2vic_base << 16);
+}
+
+
+static void fdt_add_gpt_node(HexagonVirtMachineState *vms)
+{
+ g_autofree char *name = NULL;
+ MachineState *ms = MACHINE(vms);
+
+ name = g_strdup_printf("/soc/gpt@%" PRIx64,
+ (int64_t)base_memmap[VIRT_GPT].base);
+ qemu_fdt_add_subnode(ms->fdt, name);
+ qemu_fdt_setprop_string(ms->fdt, name, "compatible",
+ "qcom,h2-timer,hvm-timer");
+ qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", irqmap[VIRT_GPT], 0);
+ qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0,
+ base_memmap[VIRT_GPT].base,
+ base_memmap[VIRT_GPT].size);
+}
+
+static int32_t clock_phandle = -1;
+static void fdt_add_clocks(const HexagonVirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ clock_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ qemu_fdt_add_subnode(ms->fdt, "/apb-pclk");
+ qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "compatible", "fixed-clock");
+ qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "#clock-cells", 0x0);
+ qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "clock-frequency", 24000000);
+ qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "clock-output-names",
+ "clk24mhz");
+ qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "phandle", clock_phandle);
+}
+
+static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart)
+{
+ char *nodename;
+ hwaddr base = base_memmap[uart].base;
+ hwaddr size = base_memmap[uart].size;
+ assert(uart == 0);
+ int irq = irqmap[VIRT_UART0 + uart];
+ const char compat[] = "arm,pl011\0arm,primecell";
+ const char clocknames[] = "uartclk\0apb_pclk";
+ MachineState *ms = MACHINE(vms);
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_new(TYPE_PL011);
+ s = SYS_BUS_DEVICE(dev);
+ qdev_prop_set_chr(dev, "chardev", serial_hd(0));
+ qdev_connect_clock_in(dev, "clk", vms->apb_clk);
+ sysbus_realize_and_unref(s, &error_fatal);
+ sysbus_mmio_map(s, 0, base);
+ sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->l2vic, irq));
+
+ nodename = g_strdup_printf("/pl011@%" PRIx64, base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+
+ /* Note that we can't use setprop_string because of the embedded NUL */
+ qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat));
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", 32 + irq, 0);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clock_phandle,
+ clock_phandle);
+ qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames,
+ sizeof(clocknames));
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ irq_hvm_ic_phandle);
+
+ qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
+ qemu_fdt_add_subnode(ms->fdt, "/aliases");
+ qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename);
+
+ g_free(nodename);
+}
+
+static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ qemu_fdt_add_subnode(ms->fdt, "/cpus");
+ qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1);
+ qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0);
+
+ /* cpu nodes */
+ for (int num = ms->smp.cpus - 1; num >= 0; num--) {
+ char *nodename = g_strdup_printf("/cpus/cpu@%d", num);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu");
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle",
+ qemu_fdt_alloc_phandle(ms->fdt));
+ g_free(nodename);
+ }
+}
+
+
+static void fdt_add_virtio_devices(const HexagonVirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ /* VirtIO MMIO devices */
+ for (int i = 0; i < VIRTIO_DEV_COUNT; i++) {
+ char *nodename;
+ int irq = irqmap[VIRT_MMIO] + i;
+ size_t size = base_memmap[VIRT_MMIO].size;
+ hwaddr base = base_memmap[VIRT_MMIO].base + i * size;
+
+ nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "virtio,mmio");
+ qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 1,
+ size);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ irq_hvm_ic_phandle);
+
+ sysbus_create_simple(
+ "virtio-mmio", base,
+ qdev_get_gpio_in(vms->l2vic, irqmap[VIRT_MMIO] + i));
+
+ g_free(nodename);
+ }
+}
+
+static void virt_instance_init(Object *obj)
+{
+ HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj);
+
+ create_fdt(vms);
+}
+
+void hexagon_load_fdt(const HexagonVirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ hwaddr fdt_addr = base_memmap[VIRT_FDT].base;
+ uint32_t fdtsize = vms->fdt_size;
+
+ g_assert(fdtsize <= base_memmap[VIRT_FDT].size);
+ /* copy in the device tree */
+ rom_add_blob_fixed_as("fdt", ms->fdt, fdtsize, fdt_addr,
+ &address_space_memory);
+ qemu_register_reset_nosnapshotload(
+ qemu_fdt_randomize_seeds,
+ rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize));
+}
+
+static uint64_t load_kernel(const HexagonVirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ uint64_t entry = 0;
+ if (load_elf_ram_sym(ms->kernel_filename, NULL, NULL, NULL, &entry, NULL,
+ NULL, NULL, 0, EM_HEXAGON, 0, 0, &address_space_memory,
+ false, NULL) > 0) {
+ return entry;
+ }
+ error_report("error loading '%s'", ms->kernel_filename);
+ exit(1);
+}
+
+static uint64_t load_bios(HexagonVirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ uint64_t bios_addr = 0x0; /* Load BIOS at reset vector address 0x0 */
+ int bios_size;
+
+ bios_size = load_image_targphys(ms->firmware ?: "", bios_addr, 64 * 1024);
+ if (bios_size < 0) {
+ error_report("Could not load BIOS '%s'", ms->firmware ?: "");
+ exit(1);
+ }
+
+ return bios_addr; /* Return entry point at address 0x0 */
+}
+
+static void do_cpu_reset(void *opaque)
+{
+ HexagonCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+ cpu_reset(cs);
+}
+
+static void virt_init(MachineState *ms)
+{
+ HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms);
+ Error **errp = NULL;
+ const hexagon_machine_config *m_cfg = &v68n_1024;
+
+ qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline);
+
+ vms->sys = get_system_memory();
+
+ /* Create APB clock for peripherals */
+ vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk");
+ clock_set_hz(vms->apb_clk, 24000000);
+
+ memory_region_init_ram(&vms->ram, NULL, "ddr.ram", ms->ram_size, errp);
+ memory_region_add_subregion(vms->sys, 0x0, &vms->ram);
+
+ if (m_cfg->l2tcm_size) {
+ memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size,
+ errp);
+ memory_region_add_subregion(vms->sys, m_cfg->cfgtable.l2tcm_base << 16,
+ &vms->tcm);
+ }
+
+ memory_region_init_rom(&vms->cfgtable, NULL, "config_table.rom",
+ sizeof(m_cfg->cfgtable), errp);
+ memory_region_add_subregion(vms->sys, m_cfg->cfgbase, &vms->cfgtable);
+ fdt_add_hvx(vms, m_cfg, errp);
+ const char *cpu_model = ms->cpu_type;
+
+ if (!cpu_model) {
+ cpu_model = HEXAGON_CPU_TYPE_NAME("v73");
+ }
+
+ DeviceState *gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG);
+ object_property_add_child(OBJECT(ms), "global-regs", OBJECT(gsregs_dev));
+ qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase);
+ qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev);
+ qdev_prop_set_uint32(gsregs_dev, "qtimer-base-addr", m_cfg->qtmr_region);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), errp);
+
+ HexagonCPU *cpu_0 = NULL;
+ for (int i = 0; i < ms->smp.cpus; i++) {
+ HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type));
+ qemu_register_reset(do_cpu_reset, cpu);
+
+ if (i == 0) {
+ cpu_0 = cpu;
+ if (ms->kernel_filename) {
+ uint64_t entry = load_kernel(vms);
+ qdev_prop_set_uint32(DEVICE(cpu_0), "exec-start-addr", entry);
+ } else if (ms->firmware) {
+ uint64_t entry = load_bios(vms);
+ qdev_prop_set_uint32(DEVICE(cpu_0), "exec-start-addr", entry);
+ }
+ }
+ qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0));
+ qdev_prop_set_uint32(DEVICE(cpu), "hvx-contexts",
+ m_cfg->cfgtable.ext_contexts);
+ object_property_set_link(OBJECT(cpu), "global-regs",
+ OBJECT(gsregs_dev), errp);
+ qdev_prop_set_uint32(DEVICE(cpu), "l2vic-base-addr", m_cfg->l2vic_base);
+ qdev_prop_set_uint32(DEVICE(cpu), "jtlb-entries",
+ m_cfg->cfgtable.jtlb_size_entries);
+
+ if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) {
+ return;
+ }
+ }
+ vms->l2vic = sysbus_create_varargs(
+ "l2vic", m_cfg->l2vic_base, qdev_get_gpio_in(DEVICE(cpu_0), 0),
+ qdev_get_gpio_in(DEVICE(cpu_0), 1), qdev_get_gpio_in(DEVICE(cpu_0), 2),
+ qdev_get_gpio_in(DEVICE(cpu_0), 3), qdev_get_gpio_in(DEVICE(cpu_0), 4),
+ qdev_get_gpio_in(DEVICE(cpu_0), 5), qdev_get_gpio_in(DEVICE(cpu_0), 6),
+ qdev_get_gpio_in(DEVICE(cpu_0), 7), NULL);
+
+ fdt_add_hvm_pic_node(vms, m_cfg);
+ fdt_add_virtio_devices(vms);
+ fdt_add_cpu_nodes(vms);
+ fdt_add_clocks(vms);
+ fdt_add_uart(vms, VIRT_UART0);
+ fdt_add_gpt_node(vms);
+
+ rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable,
+ sizeof(m_cfg->cfgtable), m_cfg->cfgbase,
+ &address_space_memory);
+
+
+ hexagon_load_fdt(vms);
+}
+
+
+static void virt_class_init(ObjectClass *oc, const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = virt_init;
+ mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v73");
+ mc->default_ram_size = 4 * GiB;
+ mc->max_cpus = 8;
+ mc->default_cpus = 8;
+ mc->is_default = false;
+ mc->default_kernel_irqchip_split = false;
+ mc->block_default_type = IF_VIRTIO;
+ mc->default_boot_order = NULL;
+ mc->no_cdrom = 1;
+ mc->numa_mem_supported = false;
+ mc->default_nic = "virtio-mmio-bus";
+}
+
+
+static const TypeInfo virt_machine_types[] = { {
+ .name = TYPE_HEXAGON_VIRT_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(HexagonVirtMachineState),
+ .class_init = virt_class_init,
+ .instance_init = virt_instance_init,
+} };
+
+DEFINE_TYPES(virt_machine_types)
diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig
index 7b9577f68f..dc74751d21 100644
--- a/hw/hexagon/Kconfig
+++ b/hw/hexagon/Kconfig
@@ -3,3 +3,13 @@ config HEX_DSP
default y
depends on HEXAGON && TCG
imply PTIMER
+
+config HEX_VIRT
+ bool
+ default y
+ depends on HEX_DSP && FDT
+ select DEVICE_TREE
+ select VIRTIO_MMIO
+ select PL011
+ select VIRTIO_BLK
+ select VIRTIO_SCSI
diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build
index 11e71a67d5..4b856b9583 100644
--- a/hw/hexagon/meson.build
+++ b/hw/hexagon/meson.build
@@ -3,3 +3,5 @@ hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c', 'hexagon_
hw_arch += {'hexagon': hexagon_ss}
+hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c', 'hexagon_globalreg.c'))
+
diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
index 6326e46b7b..b8eeed703a 100644
--- a/tests/qemu-iotests/testenv.py
+++ b/tests/qemu-iotests/testenv.py
@@ -239,6 +239,7 @@ def __init__(self, source_dir: str, build_dir: str,
('arm', 'virt'),
('aarch64', 'virt'),
('avr', 'mega2560'),
+ ('hexagon', 'virt'),
('m68k', 'virt'),
('or1k', 'virt'),
('riscv32', 'virt'),
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 09/11] tests/qtest: Add hexagon boot-serial-test
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
` (7 preceding siblings ...)
2025-09-02 3:49 ` [PATCH v2 08/11] hw/hexagon: Define hexagon "virt" machine Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 3:49 ` [PATCH v2 10/11] hw/timer: Add QTimer device Brian Cain
` (2 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning
Add boot-serial-test support for Hexagon architecture using the virt
machine.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/qtest/boot-serial-test.c | 8 ++++++++
tests/qtest/meson.build | 2 ++
2 files changed, 10 insertions(+)
diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c
index a05d26ee99..733e0f855c 100644
--- a/tests/qtest/boot-serial-test.c
+++ b/tests/qtest/boot-serial-test.c
@@ -142,6 +142,13 @@ static const uint8_t kernel_stm32vldiscovery[] = {
0x04, 0x38, 0x01, 0x40 /* 0x40013804 = USART1 TXD */
};
+static const uint8_t bios_hexagon[] = {
+ 0x00, 0x40, 0x00, 0x01, /* immext(#0x10000000) */
+ 0x00, 0xc0, 0x00, 0x78, /* r0 = ##0x10000000 */
+ 0x54, 0xc0, 0x00, 0x3c, /* memb(r0+#0) = #0x54 Write 'T' */
+ 0xf8, 0xff, 0xff, 0x59 /* jump 0x0 ; Loop back to start */
+};
+
typedef struct testdef {
const char *arch; /* Target architecture */
const char *machine; /* Name of the machine */
@@ -194,6 +201,7 @@ static const testdef_t tests[] = {
{ "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 },
{ "arm", "stm32vldiscovery", "", "T",
sizeof(kernel_stm32vldiscovery), kernel_stm32vldiscovery },
+ { "hexagon", "virt", "", "TT", sizeof(bios_hexagon), NULL, bios_hexagon },
{ NULL }
};
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 669d07c06b..4e79cd0a8f 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -284,6 +284,8 @@ qtests_riscv32 = \
qtests_riscv64 = ['riscv-csr-test'] + \
(unpack_edk2_blobs ? ['bios-tables-test'] : [])
+qtests_hexagon = ['boot-serial-test']
+
qos_test_ss = ss.source_set()
qos_test_ss.add(
'ac97-test.c',
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 10/11] hw/timer: Add QTimer device
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
` (8 preceding siblings ...)
2025-09-02 3:49 ` [PATCH v2 09/11] tests/qtest: Add hexagon boot-serial-test Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 3:49 ` [PATCH v2 11/11] hw/intc: Add l2vic interrupt controller Brian Cain
2025-09-02 8:11 ` [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Philippe Mathieu-Daudé
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning, Sid Manning, Damien Hedde, Tobias Röhmel
From: Sid Manning <sidneym@quicinc.com>
Note: QTimer was implemented before ARM SSE Timer was upstreamed, there may
be opportunity to use that device instead.
Co-authored-by: Damien Hedde <damien.hedde@dahe.fr>
Co-authored-by: Tobias Röhmel <quic_trohmel@quicinc.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
MAINTAINERS | 2 +
include/hw/timer/qct-qtimer.h | 85 ++++++
hw/hexagon/hexagon_dsp.c | 7 +-
hw/hexagon/hexagon_globalreg.c | 9 +-
hw/hexagon/virt.c | 22 ++
hw/timer/qct-qtimer.c | 520 +++++++++++++++++++++++++++++++++
hw/timer/meson.build | 2 +
7 files changed, 642 insertions(+), 5 deletions(-)
create mode 100644 include/hw/timer/qct-qtimer.h
create mode 100644 hw/timer/qct-qtimer.c
diff --git a/MAINTAINERS b/MAINTAINERS
index e19fcf9e69..4f7748679b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -230,7 +230,9 @@ M: Brian Cain <brian.cain@oss.qualcomm.com>
S: Supported
F: target/hexagon/
F: hw/hexagon/
+F: hw/timer/qct-qtimer.c
F: include/hw/hexagon/
+F: include/hw/timer/qct-qtimer.h
X: target/hexagon/idef-parser/
X: target/hexagon/gen_idef_parser_funcs.py
F: linux-user/hexagon/
diff --git a/include/hw/timer/qct-qtimer.h b/include/hw/timer/qct-qtimer.h
new file mode 100644
index 0000000000..90f7981ccf
--- /dev/null
+++ b/include/hw/timer/qct-qtimer.h
@@ -0,0 +1,85 @@
+/*
+ * Qualcomm QCT QTimer
+ *
+ * Copyright(c) 2019-2025 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef TIMER_QCT_QTIMER_H
+#define TIMER_QCT_QTIMER_H
+
+#include "hw/ptimer.h"
+#include "hw/sysbus.h"
+
+#define TYPE_QCT_QTIMER "qct-qtimer"
+#define TYPE_QCT_HEXTIMER "qct-hextimer"
+OBJECT_DECLARE_SIMPLE_TYPE(QCTQtimerState, QCT_QTIMER)
+OBJECT_DECLARE_SIMPLE_TYPE(QCTHextimerState, QCT_HEXTIMER)
+
+struct QCTHextimerState {
+ QCTQtimerState *qtimer;
+ ptimer_state *timer;
+ uint64_t cntval; /*
+ * Physical timer compare value interrupt when cntpct >
+ * cntval
+ */
+ uint64_t cntpct; /* Physical counter */
+ uint32_t control;
+ uint32_t cnt_ctrl;
+ uint32_t cntpl0acr;
+ uint64_t limit;
+ uint32_t freq;
+ uint32_t int_level;
+ qemu_irq irq;
+};
+
+#define QCT_QTIMER_TIMER_FRAME_ELTS (8)
+#define QCT_QTIMER_TIMER_VIEW_ELTS (2)
+struct QCTQtimerState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ MemoryRegion view_iomem;
+ uint32_t secure;
+ struct QCTHextimerState timer[QCT_QTIMER_TIMER_FRAME_ELTS];
+ uint32_t frame_id;
+ uint32_t freq;
+ uint32_t nr_frames;
+ uint32_t nr_views;
+ uint32_t cnttid;
+};
+
+#define QCT_QTIMER_AC_CNTFRQ (0x000)
+#define QCT_QTIMER_AC_CNTSR (0x004)
+#define QCT_QTIMER_AC_CNTSR_NSN_1 (1 << 0)
+#define QCT_QTIMER_AC_CNTSR_NSN_2 (1 << 1)
+#define QCT_QTIMER_AC_CNTSR_NSN_3 (1 << 2)
+#define QCT_QTIMER_AC_CNTTID (0x08)
+#define QCT_QTIMER_AC_CNTACR_0 (0x40)
+#define QCT_QTIMER_AC_CNTACR_1 (0x44)
+#define QCT_QTIMER_AC_CNTACR_2 (0x48)
+#define QCT_QTIMER_AC_CNTACR_RWPT (1 << 5) /* R/W of CNTP_* regs */
+#define QCT_QTIMER_AC_CNTACR_RWVT (1 << 4) /* R/W of CNTV_* regs */
+#define QCT_QTIMER_AC_CNTACR_RVOFF (1 << 3) /* R/W of CNTVOFF register */
+#define QCT_QTIMER_AC_CNTACR_RFRQ (1 << 2) /* R/W of CNTFRQ register */
+#define QCT_QTIMER_AC_CNTACR_RPVCT (1 << 1) /* R/W of CNTVCT register */
+#define QCT_QTIMER_AC_CNTACR_RPCT (1 << 0) /* R/W of CNTPCT register */
+#define QCT_QTIMER_VERSION (0x0fd0)
+#define QCT_QTIMER_CNTPCT_LO (0x000)
+#define QCT_QTIMER_CNTPCT_HI (0x004)
+#define QCT_QTIMER_CNT_FREQ (0x010)
+#define QCT_QTIMER_CNTPL0ACR (0x014)
+#define QCT_QTIMER_CNTPL0ACR_PL0CTEN (1 << 9)
+#define QCT_QTIMER_CNTPL0ACR_PL0TVEN (1 << 8)
+#define QCT_QTIMER_CNTPL0ACR_PL0VCTEN (1 << 1)
+#define QCT_QTIMER_CNTPL0ACR_PL0PCTEN (1 << 0)
+#define QCT_QTIMER_CNTP_CVAL_LO (0x020)
+#define QCT_QTIMER_CNTP_CVAL_HI (0x024)
+#define QCT_QTIMER_CNTP_TVAL (0x028)
+#define QCT_QTIMER_CNTP_CTL (0x02c)
+#define QCT_QTIMER_CNTP_CTL_ISTAT (1 << 2)
+#define QCT_QTIMER_CNTP_CTL_INTEN (1 << 1)
+#define QCT_QTIMER_CNTP_CTL_ENABLE (1 << 0)
+#define QCT_QTIMER_AC_CNTACR_START 0x40
+#define QCT_QTIMER_AC_CNTACR_END 0x5C
+
+#endif /* TIMER_QCT_QTIMER_H */
diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c
index 510378280e..e0e39fca90 100644
--- a/hw/hexagon/hexagon_dsp.c
+++ b/hw/hexagon/hexagon_dsp.c
@@ -3,7 +3,6 @@
* subsystem with few peripherals, like the Compute DSP.
*
* Copyright (c) 2020-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
- *
* SPDX-License-Identifier: GPL-2.0-or-later
*/
@@ -126,7 +125,11 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev,
*/
qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0));
qdev_prop_set_uint32(DEVICE(cpu), "l2vic-base-addr", m_cfg->l2vic_base);
- qdev_prop_set_uint32(DEVICE(cpu), "config-table-addr", m_cfg->cfgbase);
+ if (!object_property_set_link(OBJECT(cpu), "global-regs",
+ OBJECT(glob_regs_dev), errp)) {
+ error_report("Failed to link global system registers to CPU %d", i);
+ return;
+ }
qdev_prop_set_uint32(DEVICE(cpu), "hvx-contexts",
m_cfg->cfgtable.ext_contexts);
qdev_prop_set_uint32(DEVICE(cpu), "jtlb-entries",
diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c
index fcbf2ae4b2..c94d2c4c7f 100644
--- a/hw/hexagon/hexagon_globalreg.c
+++ b/hw/hexagon/hexagon_globalreg.c
@@ -14,6 +14,7 @@
#include "migration/vmstate.h"
#include "qom/object.h"
#include "target/hexagon/cpu.h"
+#include "hw/timer/qct-qtimer.h"
#include "target/hexagon/hex_regs.h"
#include "qemu/log.h"
#include "trace/trace-hw_hexagon.h"
@@ -137,9 +138,11 @@ static inline uint32_t apply_write_mask(uint32_t new_val, uint32_t cur_val,
static void read_timer(HexagonGlobalRegState *s, uint32_t *low, uint32_t *high)
{
- /* Not yet implemented */
- *low = 0;
- *high = 0;
+ const hwaddr low_addr = s->qtimer_base_addr + QCT_QTIMER_CNTPCT_LO;
+ const hwaddr high_addr = s->qtimer_base_addr + QCT_QTIMER_CNTPCT_HI;
+
+ cpu_physical_memory_read(low_addr, low, sizeof(*low));
+ cpu_physical_memory_read(high_addr, high, sizeof(*high));
}
uint32_t hexagon_globalreg_read(HexagonCPU *cpu, uint32_t reg)
diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c
index 615fde773d..c93d19ff89 100644
--- a/hw/hexagon/virt.c
+++ b/hw/hexagon/virt.c
@@ -17,6 +17,8 @@
#include "hw/qdev-properties.h"
#include "hw/qdev-clock.h"
#include "hw/register.h"
+#include "hw/timer/qct-qtimer.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/guest-random.h"
#include "qemu/units.h"
@@ -256,6 +258,25 @@ static void fdt_add_virtio_devices(const HexagonVirtMachineState *vms)
}
}
+static void create_qtimer(HexagonVirtMachineState *vms,
+ const hexagon_machine_config *m_cfg)
+{
+ Error **errp = NULL;
+ QCTQtimerState *qtimer = QCT_QTIMER(qdev_new(TYPE_QCT_QTIMER));
+
+ object_property_set_uint(OBJECT(qtimer), "nr_frames", 2, errp);
+ object_property_set_uint(OBJECT(qtimer), "nr_views", 1, errp);
+ object_property_set_uint(OBJECT(qtimer), "cnttid", 0x111, errp);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(qtimer), errp);
+
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 1, m_cfg->qtmr_region);
+ sysbus_connect_irq(SYS_BUS_DEVICE(qtimer), 0,
+ qdev_get_gpio_in(vms->l2vic, irqmap[VIRT_QTMR0]));
+ sysbus_connect_irq(SYS_BUS_DEVICE(qtimer), 1,
+ qdev_get_gpio_in(vms->l2vic, irqmap[VIRT_QTMR1]));
+}
+
static void virt_instance_init(Object *obj)
{
HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj);
@@ -395,6 +416,7 @@ static void virt_init(MachineState *ms)
fdt_add_clocks(vms);
fdt_add_uart(vms, VIRT_UART0);
fdt_add_gpt_node(vms);
+ create_qtimer(vms, m_cfg);
rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable,
sizeof(m_cfg->cfgtable), m_cfg->cfgbase,
diff --git a/hw/timer/qct-qtimer.c b/hw/timer/qct-qtimer.c
new file mode 100644
index 0000000000..bd7123264c
--- /dev/null
+++ b/hw/timer/qct-qtimer.c
@@ -0,0 +1,520 @@
+/*
+ * Qualcomm QCT QTimer
+ *
+ * Copyright(c) 2019-2025 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/timer/qct-qtimer.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+
+/* Common timer implementation. */
+
+#define QTIMER_MEM_SIZE_BYTES 0x1000
+#define QTIMER_MEM_REGION_SIZE_BYTES 0x1000
+#define QTIMER_DEFAULT_FREQ_HZ 19200000ULL
+#define QTMR_TIMER_INDEX_MASK (0xf000)
+#define HIGH_32(val) (0x0ffffffffULL & (val >> 32))
+#define LOW_32(val) (0x0ffffffffULL & val)
+
+/*
+ * QTimer version reg:
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Major | Minor | Step |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static unsigned int TIMER_VERSION = 0x20020000;
+
+/*
+ * qct_qtimer_read/write:
+ * if offset < 0x1000 read restricted registers:
+ * QCT_QTIMER_AC_CNTFREQ/CNTSR/CNTTID/CNTACR/CNTOFF_(LO/HI)/QCT_QTIMER_VERSION
+ */
+static uint64_t qct_qtimer_read(void *opaque, hwaddr offset, unsigned size)
+{
+ QCTQtimerState *s = (QCTQtimerState *)opaque;
+ uint32_t frame = 0;
+
+ switch (offset) {
+ case QCT_QTIMER_AC_CNTFRQ:
+ return s->freq;
+ case QCT_QTIMER_AC_CNTSR:
+ return s->secure;
+ case QCT_QTIMER_AC_CNTTID:
+ return s->cnttid;
+ case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END:
+ frame = (offset - 0x40) / 0x4;
+ if (frame >= s->nr_frames) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: QCT_QTIMER_AC_CNT: Bad offset %x\n", __func__,
+ (int)offset);
+ return 0x0;
+ }
+ return s->timer[frame].cnt_ctrl;
+ case QCT_QTIMER_VERSION:
+ return TIMER_VERSION;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: QCT_QTIMER_AC_CNT: Bad offset %" PRIx32 "\n",
+ __func__, (int)offset);
+ return 0x0;
+ }
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" PRIx32 "\n", __func__,
+ (int)offset);
+ return 0;
+}
+
+static void qct_qtimer_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ QCTQtimerState *s = (QCTQtimerState *)opaque;
+ uint32_t frame = 0;
+
+ if (offset < 0x1000) {
+ switch (offset) {
+ case QCT_QTIMER_AC_CNTFRQ:
+ s->freq = value;
+ return;
+ case QCT_QTIMER_AC_CNTSR:
+ if (value > 0xFF)
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: QCT_QTIMER_AC_CNTSR: Bad value %x\n",
+ __func__, (int)value);
+ else
+ s->secure = value;
+ return;
+ case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END:
+ frame = (offset - QCT_QTIMER_AC_CNTACR_START) / 0x4;
+ if (frame >= s->nr_frames) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: QCT_QTIMER_AC_CNT: Bad offset %x\n",
+ __func__, (int)offset);
+ return;
+ }
+ s->timer[frame].cnt_ctrl = value;
+ return;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: QCT_QTIMER_AC_CNT: Bad offset %x\n", __func__,
+ (int)offset);
+ return;
+ }
+ } else
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" PRIx32 "\n", __func__,
+ (int)offset);
+}
+
+static const MemoryRegionOps qct_qtimer_ops = {
+ .read = qct_qtimer_read,
+ .write = qct_qtimer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_qct_qtimer = {
+ .name = "qct-qtimer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]){ VMSTATE_END_OF_LIST() }
+};
+
+static void qct_qtimer_init(Object *obj)
+{
+ QCTQtimerState *s = QCT_QTIMER(obj);
+
+ object_property_add_uint32_ptr(obj, "secure", &s->secure,
+ OBJ_PROP_FLAG_READ);
+ object_property_add_uint32_ptr(obj, "frame_id", &s->frame_id,
+ OBJ_PROP_FLAG_READ);
+}
+
+static void hex_timer_update(QCTHextimerState *s)
+{
+ /* Update interrupts. */
+ int level = s->int_level && (s->control & QCT_QTIMER_CNTP_CTL_ENABLE);
+ qemu_set_irq(s->irq, level);
+}
+
+static MemTxResult hex_timer_read(void *opaque, hwaddr offset, uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
+{
+ QCTQtimerState *qct_s = (QCTQtimerState *)opaque;
+ uint32_t slot_nr = (offset & 0xF000) >> 12;
+ uint32_t reg_offset = offset & 0xFFF;
+ uint32_t view = slot_nr % qct_s->nr_views;
+ uint32_t frame = slot_nr / qct_s->nr_views;
+
+ if (frame >= qct_s->nr_frames) {
+ *data = 0;
+ return MEMTX_ACCESS_ERROR;
+ }
+ QCTHextimerState *s = &qct_s->timer[frame];
+
+
+ /*
+ * This is the case where we have 2 views, but the second one is not
+ * implemented.
+ */
+ if (view && !(qct_s->cnttid & (0x4 << (frame * 4)))) {
+ *data = 0;
+ return MEMTX_OK;
+ }
+
+ switch (reg_offset) {
+ case (QCT_QTIMER_CNT_FREQ): /* Ticks/Second */
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RFRQ)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !((s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0PCTEN) ||
+ (s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0VCTEN))) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ *data = s->freq;
+ return MEMTX_OK;
+ case (QCT_QTIMER_CNTP_CVAL_LO): /* TimerLoad */
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ *data = LOW_32((s->cntval));
+ return MEMTX_OK;
+ case (QCT_QTIMER_CNTP_CVAL_HI): /* TimerLoad */
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ *data = HIGH_32((s->cntval));
+ return MEMTX_OK;
+ case QCT_QTIMER_CNTPCT_LO:
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RPCT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0PCTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ *data = LOW_32((s->cntpct + (ptimer_get_count(s->timer))));
+ return MEMTX_OK;
+ case QCT_QTIMER_CNTPCT_HI:
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RPCT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0PCTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ *data = HIGH_32((s->cntpct + (ptimer_get_count(s->timer))));
+ return MEMTX_OK;
+ case (QCT_QTIMER_CNTP_TVAL): /* CVAL - CNTP */
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ *data =
+ (s->cntval - (HIGH_32((s->cntpct + (ptimer_get_count(s->timer)))) +
+ LOW_32((s->cntpct + (ptimer_get_count(s->timer))))));
+ return MEMTX_OK;
+ case (QCT_QTIMER_CNTP_CTL): /* TimerMIS */
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ *data = s->int_level;
+ return MEMTX_OK;
+ case QCT_QTIMER_CNTPL0ACR:
+ if (view) {
+ *data = 0;
+ } else {
+ *data = s->cntpl0acr;
+ }
+ return MEMTX_OK;
+
+ case QCT_QTIMER_VERSION:
+ *data = TIMER_VERSION;
+ return MEMTX_OK;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" PRIx32 "\n", __func__,
+ (int)offset);
+ *data = 0;
+ return MEMTX_ACCESS_ERROR;
+ }
+}
+
+/*
+ * Reset the timer limit after settings have changed.
+ * May only be called from inside a ptimer transaction block.
+ */
+static void hex_timer_recalibrate(QCTHextimerState *s, int reload)
+{
+ uint64_t limit;
+ /* Periodic. */
+ limit = s->limit;
+ ptimer_set_limit(s->timer, limit, reload);
+}
+
+static MemTxResult hex_timer_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size, MemTxAttrs attrs)
+{
+ QCTQtimerState *qct_s = (QCTQtimerState *)opaque;
+ uint32_t slot_nr = (offset & 0xF000) >> 12;
+ uint32_t reg_offset = offset & 0xFFF;
+ uint32_t view = slot_nr % qct_s->nr_views;
+ uint32_t frame = slot_nr / qct_s->nr_views;
+
+ if (frame >= qct_s->nr_frames) {
+ return MEMTX_ACCESS_ERROR;
+ }
+ QCTHextimerState *s = &qct_s->timer[frame];
+
+ /*
+ * This is the case where we have 2 views, but the second one is not
+ * implemented.
+ */
+ if (view && !(qct_s->cnttid & (0x4 << (frame * 4)))) {
+ return MEMTX_OK;
+ }
+
+ switch (reg_offset) {
+ case (QCT_QTIMER_CNTP_CVAL_LO): /* TimerLoad */
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+
+ s->int_level = 0;
+ s->cntval = value;
+ ptimer_transaction_begin(s->timer);
+ if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) {
+ /*
+ * Pause the timer if it is running. This may cause some
+ * inaccuracy due to rounding, but avoids other issues.
+ */
+ ptimer_stop(s->timer);
+ }
+ hex_timer_recalibrate(s, 1);
+ if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) {
+ ptimer_run(s->timer, 0);
+ }
+ ptimer_transaction_commit(s->timer);
+ break;
+ case (QCT_QTIMER_CNTP_CVAL_HI):
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ break;
+ case (QCT_QTIMER_CNTP_CTL): /* Timer control register */
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ ptimer_transaction_begin(s->timer);
+ if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) {
+ /*
+ * Pause the timer if it is running. This may cause some
+ * inaccuracy due to rounding, but avoids other issues.
+ */
+ ptimer_stop(s->timer);
+ }
+ s->control = value;
+ hex_timer_recalibrate(s, s->control & QCT_QTIMER_CNTP_CTL_ENABLE);
+ ptimer_set_freq(s->timer, s->freq);
+ ptimer_set_period(s->timer, 1);
+ if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) {
+ ptimer_run(s->timer, 0);
+ }
+ ptimer_transaction_commit(s->timer);
+ break;
+ case (QCT_QTIMER_CNTP_TVAL): /* CVAL - CNTP */
+ if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ ptimer_transaction_begin(s->timer);
+ if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) {
+ /*
+ * Pause the timer if it is running. This may cause some
+ * inaccuracy due to rounding, but avoids other issues.
+ */
+ ptimer_stop(s->timer);
+ }
+ s->cntval = s->cntpct + value;
+ ptimer_set_freq(s->timer, s->freq);
+ ptimer_set_period(s->timer, 1);
+ if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) {
+ ptimer_run(s->timer, 0);
+ }
+ ptimer_transaction_commit(s->timer);
+ break;
+ case QCT_QTIMER_CNTPL0ACR:
+ if (view) {
+ break;
+ }
+
+ s->cntpl0acr = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" PRIx32 "\n", __func__,
+ (int)offset);
+ return MEMTX_ACCESS_ERROR;
+ }
+ hex_timer_update(s);
+ return MEMTX_OK;
+}
+
+static void hex_timer_tick(void *opaque)
+{
+ QCTHextimerState *s = (QCTHextimerState *)opaque;
+ if ((s->cntpct >= s->cntval) && (s->int_level != 1)) {
+ s->int_level = 1;
+ hex_timer_update(s);
+ return;
+ }
+ s->cntpct += s->limit;
+}
+
+static const MemoryRegionOps hex_timer_ops = {
+ .read_with_attrs = hex_timer_read,
+ .write_with_attrs = hex_timer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_hex_timer = {
+ .name = "hex_timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]){ VMSTATE_UINT32(control, QCTHextimerState),
+ VMSTATE_UINT32(cnt_ctrl, QCTHextimerState),
+ VMSTATE_UINT64(cntpct, QCTHextimerState),
+ VMSTATE_UINT64(cntval, QCTHextimerState),
+ VMSTATE_UINT64(limit, QCTHextimerState),
+ VMSTATE_UINT32(int_level, QCTHextimerState),
+ VMSTATE_PTIMER(timer, QCTHextimerState),
+ VMSTATE_END_OF_LIST() }
+};
+
+static void qct_qtimer_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ QCTQtimerState *s = QCT_QTIMER(dev);
+ unsigned int i;
+
+ if (s->nr_frames > QCT_QTIMER_TIMER_FRAME_ELTS) {
+ error_setg(errp, "nr_frames too high");
+ return;
+ }
+
+ if (s->nr_views > QCT_QTIMER_TIMER_VIEW_ELTS) {
+ error_setg(errp, "nr_views too high");
+ return;
+ }
+
+ memory_region_init_io(&s->iomem, OBJECT(sbd), &qct_qtimer_ops, s, "qutimer",
+ QTIMER_MEM_SIZE_BYTES);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ memory_region_init_io(&s->view_iomem, OBJECT(sbd), &hex_timer_ops, s,
+ "qutimer_views",
+ QTIMER_MEM_SIZE_BYTES * s->nr_frames * s->nr_views);
+ sysbus_init_mmio(sbd, &s->view_iomem);
+
+ for (i = 0; i < s->nr_frames; i++) {
+ s->timer[i].limit = 1;
+ s->timer[i].control = QCT_QTIMER_CNTP_CTL_ENABLE;
+ s->timer[i].cnt_ctrl =
+ (QCT_QTIMER_AC_CNTACR_RWPT | QCT_QTIMER_AC_CNTACR_RWVT |
+ QCT_QTIMER_AC_CNTACR_RVOFF | QCT_QTIMER_AC_CNTACR_RFRQ |
+ QCT_QTIMER_AC_CNTACR_RPVCT | QCT_QTIMER_AC_CNTACR_RPCT);
+ s->timer[i].qtimer = s;
+ s->timer[i].freq = QTIMER_DEFAULT_FREQ_HZ;
+
+ s->secure |= (1 << i);
+
+ sysbus_init_irq(sbd, &(s->timer[i].irq));
+
+ (s->timer[i]).timer =
+ ptimer_init(hex_timer_tick, &s->timer[i], PTIMER_POLICY_LEGACY);
+ vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_hex_timer,
+ &s->timer[i]);
+ }
+}
+
+static const Property qct_qtimer_properties[] = {
+ DEFINE_PROP_UINT32("freq", QCTQtimerState, freq, QTIMER_DEFAULT_FREQ_HZ),
+ DEFINE_PROP_UINT32("nr_frames", QCTQtimerState, nr_frames, 2),
+ DEFINE_PROP_UINT32("nr_views", QCTQtimerState, nr_views, 1),
+ DEFINE_PROP_UINT32("cnttid", QCTQtimerState, cnttid, 0x11),
+};
+
+static void qct_qtimer_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *k = DEVICE_CLASS(klass);
+
+ device_class_set_props(k, qct_qtimer_properties);
+ k->realize = qct_qtimer_realize;
+ k->vmsd = &vmstate_qct_qtimer;
+}
+
+static const TypeInfo qct_qtimer_info = {
+ .name = TYPE_QCT_QTIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(QCTQtimerState),
+ .instance_init = qct_qtimer_init,
+ .class_init = qct_qtimer_class_init,
+};
+
+static void qct_qtimer_register_types(void)
+{
+ type_register_static(&qct_qtimer_info);
+}
+
+type_init(qct_qtimer_register_types)
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index 178321c029..69468672bc 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -34,3 +34,5 @@ specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c'))
system_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c'))
specific_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c'))
+
+specific_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('qct-qtimer.c'))
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 11/11] hw/intc: Add l2vic interrupt controller
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
` (9 preceding siblings ...)
2025-09-02 3:49 ` [PATCH v2 10/11] hw/timer: Add QTimer device Brian Cain
@ 2025-09-02 3:49 ` Brian Cain
2025-09-02 8:11 ` [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Philippe Mathieu-Daudé
11 siblings, 0 replies; 14+ messages in thread
From: Brian Cain @ 2025-09-02 3:49 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, richard.henderson, philmd, matheus.bernardino, ale,
anjo, marco.liebel, ltaylorsimpson, alex.bennee, quic_mburton,
sid.manning, Sid Manning, Matheus Tavares Bernardino,
Damien Hedde
From: Sid Manning <sidneym@quicinc.com>
Co-authored-by: Matheus Tavares Bernardino <quic_mathbern@quicinc.com>
Co-authored-by: Damien Hedde <damien.hedde@dahe.fr>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
MAINTAINERS | 2 +
docs/devel/hexagon-l2vic.rst | 59 +++++
docs/devel/index-internals.rst | 1 +
include/hw/intc/l2vic.h | 38 +++
hw/intc/l2vic.c | 421 +++++++++++++++++++++++++++++++++
hw/hexagon/Kconfig | 1 +
hw/intc/Kconfig | 3 +
hw/intc/meson.build | 2 +
hw/intc/trace-events | 4 +
9 files changed, 531 insertions(+)
create mode 100644 docs/devel/hexagon-l2vic.rst
create mode 100644 include/hw/intc/l2vic.h
create mode 100644 hw/intc/l2vic.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 4f7748679b..228b5b672f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -233,6 +233,7 @@ F: hw/hexagon/
F: hw/timer/qct-qtimer.c
F: include/hw/hexagon/
F: include/hw/timer/qct-qtimer.h
+F: hw/intc/l2vic.[ch]
X: target/hexagon/idef-parser/
X: target/hexagon/gen_idef_parser_funcs.py
F: linux-user/hexagon/
@@ -245,6 +246,7 @@ F: gdb-xml/hexagon*.xml
F: docs/system/target-hexagon.rst
F: docs/system/hexagon/cdsp.rst
F: docs/devel/hexagon-sys.rst
+F: docs/devel/hexagon-l2vic.rst
T: git https://github.com/quic/qemu.git hex-next
Hexagon idef-parser
diff --git a/docs/devel/hexagon-l2vic.rst b/docs/devel/hexagon-l2vic.rst
new file mode 100644
index 0000000000..0885636274
--- /dev/null
+++ b/docs/devel/hexagon-l2vic.rst
@@ -0,0 +1,59 @@
+Hexagon L2 Vectored Interrupt Controller
+========================================
+
+
+.. code-block:: none
+
+ +-------+
+ | | +----------------+
+ | l2vic | | hexagon core |
+ | | | |
+ | +-----| | |
+ ------> |VID0 >------------->irq2 -\ |
+ ------> | | | | |
+ ... > | | | | |
+ ------> | | | <int steering> |
+ | +-----| | / | | \ |
+ | ... | | | | | | |
+ | +-----| | t0 t1 t2 t3 ...|
+ ------> |VIDN | | |
+ ------> | | | |
+ ------> | | | |
+ ------> | | | |
+ | +-----| | |
+ | | |Global SREG File|
+ | State | | |
+ | [ ]|<============|=>[VID ] |
+ | [ ]|<============|=>[VID1] |
+ | [ ]| | |
+ | [ ]| | |
+ | | | |
+ +-------+ +----------------+
+
+L2VIC/Core Integration
+----------------------
+
+* hexagon core supports 8 external interrupt sources
+* l2vic supports 1024 input interrupts mapped among 4 output interrupts
+* l2vic has four output signals: { VID0, VID1, VID2, VID3 }
+* l2vic device has a bank of registers per-VID that can be used to query
+ the status or assert new interrupts.
+* Interrupts are 'steered' to threads based on { thread priority, 'EX' state,
+ thread interrupt mask, thread interrupt enable, global interrupt enable,
+ etc. }.
+* Any hardware thread could conceivably handle any input interrupt, dependent
+ on state.
+* The system register transfer instruction can read the VID0-VID3 values from
+ the l2vic when reading from hexagon core system registers "VID" and "VID1".
+* When l2vic VID0 has multiple active interrupts, it pulses the VID0 output
+ IRQ and stores the IRQ number for the VID0 register field. Only after this
+ interrupt is cleared can the l2vic pulse the VID0 output IRQ again and provide
+ the next interrupt number on the VID0 register.
+* The ``ciad`` instruction clears the l2vic input interrupt and un-disables the
+ core interrupt. If some/an l2vic VID0 interrupt is pending when this occurs,
+ the next interrupt should fire and any subseqeunt reads of the VID register
+ should reflect the newly raised interrupt.
+* In QEMU, on an external interrupt or an unmasked-pending interrupt,
+ all vCPUs are triggered (has_work==true) and each will grab the IO lock
+ while considering the steering logic to determine whether they're the thread
+ that must handle the interrupt.
diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst
index 0471db8064..6620497595 100644
--- a/docs/devel/index-internals.rst
+++ b/docs/devel/index-internals.rst
@@ -15,6 +15,7 @@ Details about QEMU's various subsystems including how to add features to them.
clocks
ebpf_rss
hexagon-sys
+ hexagon-l2vic
migration/index
multi-process
reset
diff --git a/include/hw/intc/l2vic.h b/include/hw/intc/l2vic.h
new file mode 100644
index 0000000000..be845e360c
--- /dev/null
+++ b/include/hw/intc/l2vic.h
@@ -0,0 +1,38 @@
+/*
+ * QEMU L2VIC Interrupt Controller
+ *
+ * Copyright(c) 2020-2025 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define L2VIC_VID_GRP_0 0x0 /* Read */
+#define L2VIC_VID_GRP_1 0x4 /* Read */
+#define L2VIC_VID_GRP_2 0x8 /* Read */
+#define L2VIC_VID_GRP_3 0xC /* Read */
+#define L2VIC_VID_0 0x10 /* Read SOFTWARE DEFINED */
+#define L2VIC_VID_1 0x14 /* Read SOFTWARE DEFINED NOT YET USED */
+#define L2VIC_INT_ENABLEn 0x100 /* Read/Write */
+#define L2VIC_INT_ENABLE_CLEARn 0x180 /* Write */
+#define L2VIC_INT_ENABLE_SETn 0x200 /* Write */
+#define L2VIC_INT_TYPEn 0x280 /* Read/Write */
+#define L2VIC_INT_STATUSn 0x380 /* Read */
+#define L2VIC_INT_CLEARn 0x400 /* Write */
+#define L2VIC_SOFT_INTn 0x480 /* Write */
+#define L2VIC_INT_PENDINGn 0x500 /* Read */
+#define L2VIC_INT_GRPn_0 0x600 /* Read/Write */
+#define L2VIC_INT_GRPn_1 0x680 /* Read/Write */
+#define L2VIC_INT_GRPn_2 0x700 /* Read/Write */
+#define L2VIC_INT_GRPn_3 0x780 /* Read/Write */
+
+#define L2VIC_INTERRUPT_MAX 1024
+#define L2VIC_CIAD_INSTRUCTION -1
+#define L2VIC_NO_PENDING 0xffffffff
+/*
+ * Note about l2vic groups:
+ * Each interrupt to L2VIC can be configured to associate with one of
+ * four groups.
+ * Group 0 interrupts go to IRQ2 via VID 0 (SSR: 0xC2, the default)
+ * Group 1 interrupts go to IRQ3 via VID 1 (SSR: 0xC3)
+ * Group 2 interrupts go to IRQ4 via VID 2 (SSR: 0xC4)
+ * Group 3 interrupts go to IRQ5 via VID 3 (SSR: 0xC5)
+ */
diff --git a/hw/intc/l2vic.c b/hw/intc/l2vic.c
new file mode 100644
index 0000000000..9753ba2d25
--- /dev/null
+++ b/hw/intc/l2vic.c
@@ -0,0 +1,421 @@
+/*
+ * QEMU L2VIC Interrupt Controller
+ *
+ * Arm PrimeCell PL190 Vector Interrupt Controller was used as a reference.
+ * Copyright(c) 2020-2025 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/intc/l2vic.h"
+#include "trace.h"
+
+#define L2VICA(s, n) (s[(n) >> 2])
+
+#define TYPE_L2VIC "l2vic"
+OBJECT_DECLARE_SIMPLE_TYPE(L2VICState, L2VIC)
+
+#define SLICE_MAX (L2VIC_INTERRUPT_MAX / 32)
+
+typedef struct L2VICState {
+ SysBusDevice parent_obj;
+
+ QemuMutex active;
+ MemoryRegion iomem;
+ MemoryRegion fast_iomem;
+ uint32_t level;
+ /*
+ * offset 0:vid group 0 etc, 10 bits in each group
+ * are used:
+ */
+ uint32_t vid_group[4];
+ uint32_t vid0;
+ /* Clear Status of Active Edge interrupt, not used: */
+ uint32_t int_clear[SLICE_MAX] QEMU_ALIGNED(16);
+ /* Enable interrupt source */
+ uint32_t int_enable[SLICE_MAX] QEMU_ALIGNED(16);
+ /* Clear (set to 0) corresponding bit in int_enable */
+ uint32_t int_enable_clear;
+ /* Set (to 1) corresponding bit in int_enable */
+ uint32_t int_enable_set;
+ /* Present for debugging, not used */
+ uint32_t int_pending[SLICE_MAX] QEMU_ALIGNED(16);
+ /* Generate an interrupt */
+ uint32_t int_soft;
+ /* Which enabled interrupt is active */
+ uint32_t int_status[SLICE_MAX] QEMU_ALIGNED(16);
+ /* Edge or Level interrupt */
+ uint32_t int_type[SLICE_MAX] QEMU_ALIGNED(16);
+ /* L2 interrupt group 0-3 0x600-0x7FF */
+ uint32_t int_group_n0[SLICE_MAX] QEMU_ALIGNED(16);
+ uint32_t int_group_n1[SLICE_MAX] QEMU_ALIGNED(16);
+ uint32_t int_group_n2[SLICE_MAX] QEMU_ALIGNED(16);
+ uint32_t int_group_n3[SLICE_MAX] QEMU_ALIGNED(16);
+ qemu_irq irq[8];
+} L2VICState;
+
+
+/*
+ * Find out if this irq is associated with a group other than
+ * the default group
+ */
+static uint32_t *get_int_group(L2VICState *s, int irq)
+{
+ int n = irq & 0x1f;
+ if (n < 8) {
+ return s->int_group_n0;
+ }
+ if (n < 16) {
+ return s->int_group_n1;
+ }
+ if (n < 24) {
+ return s->int_group_n2;
+ }
+ return s->int_group_n3;
+}
+
+static int find_slice(int irq)
+{
+ return irq / 32;
+}
+
+static int get_vid(L2VICState *s, int irq)
+{
+ uint32_t *group = get_int_group(s, irq);
+ uint32_t slice = group[find_slice(irq)];
+ /* Mask with 0x7 to remove the GRP:EN bit */
+ uint32_t val = slice >> ((irq & 0x7) * 4);
+ if (val & 0x8) {
+ return val & 0x7;
+ } else {
+ return 0;
+ }
+}
+
+static inline bool vid_active(L2VICState *s)
+
+{
+ /* scan all 1024 bits in int_status arrary */
+ const int size = sizeof(s->int_status) * CHAR_BIT;
+ const int active_irq = find_first_bit((unsigned long *)s->int_status, size);
+ return ((active_irq != size)) ? true : false;
+}
+
+static bool l2vic_update(L2VICState *s, int irq)
+{
+ if (vid_active(s)) {
+ return true;
+ }
+
+ bool pending = test_bit(irq, (unsigned long *)s->int_pending);
+ bool enable = test_bit(irq, (unsigned long *)s->int_enable);
+ if (pending && enable) {
+ int vid = get_vid(s, irq);
+ set_bit(irq, (unsigned long *)s->int_status);
+ clear_bit(irq, (unsigned long *)s->int_pending);
+ clear_bit(irq, (unsigned long *)s->int_enable);
+ /* ensure the irq line goes low after going high */
+ s->vid0 = irq;
+ s->vid_group[get_vid(s, irq)] = irq;
+
+ /* already low: now call pulse */
+ /* pulse: calls qemu_upper() and then qemu_lower()) */
+ qemu_irq_pulse(s->irq[vid + 2]);
+ trace_l2vic_delivered(irq, vid);
+ return true;
+ }
+ return false;
+}
+
+static void l2vic_update_all(L2VICState *s)
+{
+ for (int i = 0; i < L2VIC_INTERRUPT_MAX; i++) {
+ if (l2vic_update(s, i) == true) {
+ /* once vid is active, no-one else can set it until ciad */
+ return;
+ }
+ }
+}
+
+static void l2vic_set_irq(void *opaque, int irq, int level)
+{
+ L2VICState *s = (L2VICState *)opaque;
+ if (level) {
+ qemu_mutex_lock(&s->active);
+ set_bit(irq, (unsigned long *)s->int_pending);
+ qemu_mutex_unlock(&s->active);
+ }
+ l2vic_update(s, irq);
+}
+
+static void l2vic_write(void *opaque, hwaddr offset, uint64_t val,
+ unsigned size)
+{
+ L2VICState *s = (L2VICState *)opaque;
+ qemu_mutex_lock(&s->active);
+ trace_l2vic_reg_write((unsigned)offset, (uint32_t)val);
+
+ if (offset == L2VIC_VID_0) {
+ if ((int)val != L2VIC_CIAD_INSTRUCTION) {
+ s->vid0 = val;
+ } else {
+ /* ciad issued: clear int_status */
+ clear_bit(s->vid0, (unsigned long *)s->int_status);
+ }
+ } else if (offset >= L2VIC_INT_ENABLEn &&
+ offset < (L2VIC_INT_ENABLE_CLEARn)) {
+ L2VICA(s->int_enable, offset - L2VIC_INT_ENABLEn) = val;
+ } else if (offset >= L2VIC_INT_ENABLE_CLEARn &&
+ offset < L2VIC_INT_ENABLE_SETn) {
+ L2VICA(s->int_enable, offset - L2VIC_INT_ENABLE_CLEARn) &= ~val;
+ } else if (offset >= L2VIC_INT_ENABLE_SETn && offset < L2VIC_INT_TYPEn) {
+ L2VICA(s->int_enable, offset - L2VIC_INT_ENABLE_SETn) |= val;
+ } else if (offset >= L2VIC_INT_TYPEn && offset < L2VIC_INT_TYPEn + 0x80) {
+ L2VICA(s->int_type, offset - L2VIC_INT_TYPEn) = val;
+ } else if (offset >= L2VIC_INT_STATUSn && offset < L2VIC_INT_CLEARn) {
+ L2VICA(s->int_status, offset - L2VIC_INT_STATUSn) = val;
+ } else if (offset >= L2VIC_INT_CLEARn && offset < L2VIC_SOFT_INTn) {
+ L2VICA(s->int_clear, offset - L2VIC_INT_CLEARn) = val;
+ } else if (offset >= L2VIC_INT_PENDINGn &&
+ offset < L2VIC_INT_PENDINGn + 0x80) {
+ L2VICA(s->int_pending, offset - L2VIC_INT_PENDINGn) = val;
+ } else if (offset >= L2VIC_SOFT_INTn && offset < L2VIC_INT_PENDINGn) {
+ L2VICA(s->int_enable, offset - L2VIC_SOFT_INTn) |= val;
+ /*
+ * Need to reverse engineer the actual irq number.
+ */
+ int irq = find_first_bit((unsigned long *)&val,
+ sizeof(s->int_enable[0]) * CHAR_BIT);
+ hwaddr byteoffset = offset - L2VIC_SOFT_INTn;
+ g_assert(irq != sizeof(s->int_enable[0]) * CHAR_BIT);
+ irq += byteoffset * 8;
+
+ /* The soft-int interface only works with edge-triggered interrupts */
+ if (test_bit(irq, (unsigned long *)s->int_type)) {
+ qemu_mutex_unlock(&s->active);
+ l2vic_set_irq(opaque, irq, 1);
+ qemu_mutex_lock(&s->active);
+ }
+ } else if (offset >= L2VIC_INT_GRPn_0 && offset < L2VIC_INT_GRPn_1) {
+ L2VICA(s->int_group_n0, offset - L2VIC_INT_GRPn_0) = val;
+ } else if (offset >= L2VIC_INT_GRPn_1 && offset < L2VIC_INT_GRPn_2) {
+ L2VICA(s->int_group_n1, offset - L2VIC_INT_GRPn_1) = val;
+ } else if (offset >= L2VIC_INT_GRPn_2 && offset < L2VIC_INT_GRPn_3) {
+ L2VICA(s->int_group_n2, offset - L2VIC_INT_GRPn_2) = val;
+ } else if (offset >= L2VIC_INT_GRPn_3 && offset < L2VIC_INT_GRPn_3 + 0x80) {
+ L2VICA(s->int_group_n3, offset - L2VIC_INT_GRPn_3) = val;
+ } else {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: offset %" PRIx32 " unimplemented\n", __func__,
+ (int)offset);
+ }
+ l2vic_update_all(s);
+ qemu_mutex_unlock(&s->active);
+ return;
+}
+
+static uint64_t l2vic_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint64_t value;
+ L2VICState *s = (L2VICState *)opaque;
+ qemu_mutex_lock(&s->active);
+
+ if (offset == L2VIC_VID_GRP_0) {
+ value = s->vid_group[0];
+ } else if (offset == L2VIC_VID_GRP_1) {
+ value = s->vid_group[1];
+ } else if (offset == L2VIC_VID_GRP_2) {
+ value = s->vid_group[2];
+ } else if (offset == L2VIC_VID_GRP_3) {
+ value = s->vid_group[3];
+ } else if (offset == L2VIC_VID_0) {
+ value = s->vid0;
+ } else if (offset >= L2VIC_INT_ENABLEn &&
+ offset < L2VIC_INT_ENABLE_CLEARn) {
+ value = L2VICA(s->int_enable, offset - L2VIC_INT_ENABLEn);
+ } else if (offset >= L2VIC_INT_ENABLE_CLEARn &&
+ offset < L2VIC_INT_ENABLE_SETn) {
+ value = 0;
+ } else if (offset >= L2VIC_INT_ENABLE_SETn && offset < L2VIC_INT_TYPEn) {
+ value = 0;
+ } else if (offset >= L2VIC_INT_TYPEn && offset < L2VIC_INT_TYPEn + 0x80) {
+ value = L2VICA(s->int_type, offset - L2VIC_INT_TYPEn);
+ } else if (offset >= L2VIC_INT_STATUSn && offset < L2VIC_INT_CLEARn) {
+ value = L2VICA(s->int_status, offset - L2VIC_INT_STATUSn);
+ } else if (offset >= L2VIC_INT_CLEARn && offset < L2VIC_SOFT_INTn) {
+ value = L2VICA(s->int_clear, offset - L2VIC_INT_CLEARn);
+ } else if (offset >= L2VIC_SOFT_INTn && offset < L2VIC_INT_PENDINGn) {
+ value = 0;
+ } else if (offset >= L2VIC_INT_PENDINGn &&
+ offset < L2VIC_INT_PENDINGn + 0x80) {
+ value = L2VICA(s->int_pending, offset - L2VIC_INT_PENDINGn);
+ } else if (offset >= L2VIC_INT_GRPn_0 && offset < L2VIC_INT_GRPn_1) {
+ value = L2VICA(s->int_group_n0, offset - L2VIC_INT_GRPn_0);
+ } else if (offset >= L2VIC_INT_GRPn_1 && offset < L2VIC_INT_GRPn_2) {
+ value = L2VICA(s->int_group_n1, offset - L2VIC_INT_GRPn_1);
+ } else if (offset >= L2VIC_INT_GRPn_2 && offset < L2VIC_INT_GRPn_3) {
+ value = L2VICA(s->int_group_n2, offset - L2VIC_INT_GRPn_2);
+ } else if (offset >= L2VIC_INT_GRPn_3 && offset < L2VIC_INT_GRPn_3 + 0x80) {
+ value = L2VICA(s->int_group_n3, offset - L2VIC_INT_GRPn_3);
+ } else {
+ value = 0;
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "L2VIC: %s: offset 0x%" PRIx32 "\n", __func__,
+ (int)offset);
+ }
+
+ trace_l2vic_reg_read((unsigned)offset, value);
+ qemu_mutex_unlock(&s->active);
+
+ return value;
+}
+
+static const MemoryRegionOps l2vic_ops = {
+ .read = l2vic_read,
+ .write = l2vic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+#define FASTL2VIC_ENABLE 0x0
+#define FASTL2VIC_DISABLE 0x1
+#define FASTL2VIC_INT 0x2
+
+static void fastl2vic_write(void *opaque, hwaddr offset, uint64_t val,
+ unsigned size)
+{
+ if (offset == 0) {
+ uint32_t cmd = (val >> 16) & 0x3;
+ uint32_t irq = val & 0x3ff;
+ uint32_t slice = (irq / 32) * 4;
+ val = 1 << (irq % 32);
+
+ if (cmd == FASTL2VIC_ENABLE) {
+ l2vic_write(opaque, L2VIC_INT_ENABLE_SETn + slice, val, size);
+ } else if (cmd == FASTL2VIC_DISABLE) {
+ l2vic_write(opaque, L2VIC_INT_ENABLE_CLEARn + slice, val, size);
+ } else if (cmd == FASTL2VIC_INT) {
+ l2vic_write(opaque, L2VIC_SOFT_INTn + slice, val, size);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid write cmd %" PRId32 "\n",
+ __func__, cmd);
+ }
+ return;
+ }
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid write offset 0x%08" HWADDR_PRIx
+ "\n", __func__, offset);
+}
+
+static const MemoryRegionOps fastl2vic_ops = {
+ .write = fastl2vic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static void l2vic_reset_hold(Object *obj, G_GNUC_UNUSED ResetType res_type)
+{
+ L2VICState *s = L2VIC(obj);
+ memset(s->int_clear, 0, sizeof(s->int_clear));
+ memset(s->int_enable, 0, sizeof(s->int_enable));
+ memset(s->int_pending, 0, sizeof(s->int_pending));
+ memset(s->int_status, 0, sizeof(s->int_status));
+ memset(s->int_type, 0, sizeof(s->int_type));
+ memset(s->int_group_n0, 0, sizeof(s->int_group_n0));
+ memset(s->int_group_n1, 0, sizeof(s->int_group_n1));
+ memset(s->int_group_n2, 0, sizeof(s->int_group_n2));
+ memset(s->int_group_n3, 0, sizeof(s->int_group_n3));
+ s->int_soft = 0;
+ s->vid0 = 0;
+
+ l2vic_update_all(s);
+}
+
+
+static void reset_irq_handler(void *opaque, int irq, int level)
+{
+ L2VICState *s = (L2VICState *)opaque;
+ Object *obj = OBJECT(opaque);
+ if (level) {
+ l2vic_reset_hold(obj, RESET_TYPE_COLD);
+ }
+ l2vic_update_all(s);
+}
+
+static void l2vic_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ L2VICState *s = L2VIC(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ int i;
+
+ memory_region_init_io(&s->iomem, obj, &l2vic_ops, s, "l2vic", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ memory_region_init_io(&s->fast_iomem, obj, &fastl2vic_ops, s, "fast",
+ 0x10000);
+ sysbus_init_mmio(sbd, &s->fast_iomem);
+
+ qdev_init_gpio_in(dev, l2vic_set_irq, L2VIC_INTERRUPT_MAX);
+ qdev_init_gpio_in_named(dev, reset_irq_handler, "reset", 1);
+ for (i = 0; i < 8; i++) {
+ sysbus_init_irq(sbd, &s->irq[i]);
+ }
+ qemu_mutex_init(&s->active); /* TODO: Remove this is an experiment */
+}
+
+static const VMStateDescription vmstate_l2vic = {
+ .name = "l2vic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields =
+ (VMStateField[]){
+ VMSTATE_UINT32(level, L2VICState),
+ VMSTATE_UINT32_ARRAY(vid_group, L2VICState, 4),
+ VMSTATE_UINT32(vid0, L2VICState),
+ VMSTATE_UINT32_ARRAY(int_enable, L2VICState, SLICE_MAX),
+ VMSTATE_UINT32(int_enable_clear, L2VICState),
+ VMSTATE_UINT32(int_enable_set, L2VICState),
+ VMSTATE_UINT32_ARRAY(int_type, L2VICState, SLICE_MAX),
+ VMSTATE_UINT32_ARRAY(int_status, L2VICState, SLICE_MAX),
+ VMSTATE_UINT32_ARRAY(int_clear, L2VICState, SLICE_MAX),
+ VMSTATE_UINT32(int_soft, L2VICState),
+ VMSTATE_UINT32_ARRAY(int_pending, L2VICState, SLICE_MAX),
+ VMSTATE_UINT32_ARRAY(int_group_n0, L2VICState, SLICE_MAX),
+ VMSTATE_UINT32_ARRAY(int_group_n1, L2VICState, SLICE_MAX),
+ VMSTATE_UINT32_ARRAY(int_group_n2, L2VICState, SLICE_MAX),
+ VMSTATE_UINT32_ARRAY(int_group_n3, L2VICState, SLICE_MAX),
+ VMSTATE_END_OF_LIST() }
+};
+
+static void l2vic_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->vmsd = &vmstate_l2vic;
+ rc->phases.hold = l2vic_reset_hold;
+}
+
+static const TypeInfo l2vic_info = {
+ .name = TYPE_L2VIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(L2VICState),
+ .instance_init = l2vic_init,
+ .class_init = l2vic_class_init,
+};
+
+static void l2vic_register_types(void)
+{
+ type_register_static(&l2vic_info);
+}
+
+type_init(l2vic_register_types)
diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig
index dc74751d21..f51c381a12 100644
--- a/hw/hexagon/Kconfig
+++ b/hw/hexagon/Kconfig
@@ -3,6 +3,7 @@ config HEX_DSP
default y
depends on HEXAGON && TCG
imply PTIMER
+ select L2VIC
config HEX_VIRT
bool
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index 7547528f2c..a5b136e2fa 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -8,6 +8,9 @@ config I8259
config PL190
bool
+config L2VIC
+ bool
+
config IOAPIC
bool
select I8259
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 3137521a4a..9a5e0b3a73 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -67,6 +67,8 @@ specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xi
specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c'))
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
if_true: files('spapr_xive_kvm.c'))
+
+specific_ss.add(when: 'CONFIG_L2VIC', if_true: files('l2vic.c'))
specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
specific_ss.add(when: 'CONFIG_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c'))
specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c'))
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 018c609ca5..327514f498 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -314,6 +314,10 @@ sh_intc_register(const char *s, int id, unsigned short v, int c, int m) "%s %u -
sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " -> 0x%lx"
sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx"
sh_intc_set(int id, int enable) "setting interrupt group %d to %d"
+# l2vic.c
+l2vic_reg_write(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32
+l2vic_reg_read(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32
+l2vic_delivered(int irq, int vid) "l2vic: delivered %d (vid %d)"
# loongson_ipi.c
loongson_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v2 00/11] hexagon system emulation v2, part 3/3
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
` (10 preceding siblings ...)
2025-09-02 3:49 ` [PATCH v2 11/11] hw/intc: Add l2vic interrupt controller Brian Cain
@ 2025-09-02 8:11 ` Philippe Mathieu-Daudé
11 siblings, 0 replies; 14+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-09-02 8:11 UTC (permalink / raw)
To: Brian Cain, qemu-devel
Cc: richard.henderson, matheus.bernardino, ale, anjo, marco.liebel,
ltaylorsimpson, alex.bennee, quic_mburton, sid.manning
Hi,
On 2/9/25 05:49, Brian Cain wrote:
> As with parts 1, 2 - some of the changes requested have been made, but not
> all.
>
> New features for part 3:
> - global registers device model
> - boot-serial-test qtest case
>
> Issues not addressed with v2:
> * "Add l2vic interrupt controller" - DECLARE_SIMPLE_TYPE, bitops
> - I might also push this and/or QTimer out of this series to simplify
> things a bit.
>
> Brian Cain (8):
> hw/hexagon: Add globalreg model
> hw/hexagon: Add global register tracing
> hw/hexagon: Add machine configs for sysemu
> hw/hexagon: Add v68, sa8775-cdsp0 defs
> hw/hexagon: Modify "Standalone" symbols
> target/hexagon: add build config for softmmu
> hw/hexagon: Define hexagon "virt" machine
> tests/qtest: Add hexagon boot-serial-test
>
> Sid Manning (3):
> hw/hexagon: Add support for cfgbase
> hw/timer: Add QTimer device
> hw/intc: Add l2vic interrupt controller
FYI I have tagged this series to review, but I might not have time the
following 3 weeks.
In doubt, please ping during last week of September to remind me ;)
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 02/11] hw/hexagon: Add global register tracing
2025-09-02 3:49 ` [PATCH v2 02/11] hw/hexagon: Add global register tracing Brian Cain
@ 2025-10-02 5:42 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 14+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-10-02 5:42 UTC (permalink / raw)
To: Brian Cain, qemu-devel
Cc: richard.henderson, matheus.bernardino, ale, anjo, marco.liebel,
ltaylorsimpson, alex.bennee, quic_mburton, sid.manning
On 2/9/25 05:49, Brian Cain wrote:
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> meson.build | 1 +
> hw/hexagon/hexagon_globalreg.c | 73 ++++++++++++++++++++++++++++++++++
> hw/hexagon/trace-events | 3 ++
> 3 files changed, 77 insertions(+)
> create mode 100644 hw/hexagon/trace-events
> @@ -99,6 +171,7 @@ void hexagon_globalreg_write(HexagonCPU *cpu, uint32_t reg,
> g_assert(reg < NUM_SREGS);
> g_assert(reg >= HEX_SREG_GLB_START);
> s->regs[reg] = value;
> + trace_hexagon_globalreg_write(get_sreg_name(reg), value);
> }
>
> uint32_t hexagon_globalreg_masked_value(HexagonCPU *cpu, uint32_t reg,
> diff --git a/hw/hexagon/trace-events b/hw/hexagon/trace-events
> new file mode 100644
> index 0000000000..6ef88d9e05
> --- /dev/null
> +++ b/hw/hexagon/trace-events
> @@ -0,0 +1,3 @@
> +# Hexagon global register access
> +hexagon_globalreg_read(const char *reg_name, uint32_t value) "reg=%s value=0x%x"
> +hexagon_globalreg_write(const char *reg_name, uint32_t value) "reg=%s value=0x%x"
Maybe worth also tracing the cpuid, regardless:
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2025-10-02 5:46 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-02 3:49 [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Brian Cain
2025-09-02 3:49 ` [PATCH v2 01/11] hw/hexagon: Add globalreg model Brian Cain
2025-09-02 3:49 ` [PATCH v2 02/11] hw/hexagon: Add global register tracing Brian Cain
2025-10-02 5:42 ` Philippe Mathieu-Daudé
2025-09-02 3:49 ` [PATCH v2 03/11] hw/hexagon: Add machine configs for sysemu Brian Cain
2025-09-02 3:49 ` [PATCH v2 04/11] hw/hexagon: Add v68, sa8775-cdsp0 defs Brian Cain
2025-09-02 3:49 ` [PATCH v2 05/11] hw/hexagon: Add support for cfgbase Brian Cain
2025-09-02 3:49 ` [PATCH v2 06/11] hw/hexagon: Modify "Standalone" symbols Brian Cain
2025-09-02 3:49 ` [PATCH v2 07/11] target/hexagon: add build config for softmmu Brian Cain
2025-09-02 3:49 ` [PATCH v2 08/11] hw/hexagon: Define hexagon "virt" machine Brian Cain
2025-09-02 3:49 ` [PATCH v2 09/11] tests/qtest: Add hexagon boot-serial-test Brian Cain
2025-09-02 3:49 ` [PATCH v2 10/11] hw/timer: Add QTimer device Brian Cain
2025-09-02 3:49 ` [PATCH v2 11/11] hw/intc: Add l2vic interrupt controller Brian Cain
2025-09-02 8:11 ` [PATCH v2 00/11] hexagon system emulation v2, part 3/3 Philippe Mathieu-Daudé
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).