Add per-hart arrays for tracking halt, resume-acknowledge,
have-reset and reset-halt-request state. The initial DM reset
handler clears all arrays consistently using a for loop.
Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
---
hw/riscv/dm.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 183 insertions(+), 10 deletions(-)
diff --git a/hw/riscv/dm.c b/hw/riscv/dm.c
index 9ac5f2cfac..fad8e2b2ce 100644
--- a/hw/riscv/dm.c
+++ b/hw/riscv/dm.c
@@ -16,32 +16,148 @@
REG32(DMCONTROL, 0x40)
FIELD(DMCONTROL, DMACTIVE, 0, 1)
+ FIELD(DMCONTROL, HARTSELLO, 6, 10)
+ FIELD(DMCONTROL, HARTSELHI, 16, 10)
+ FIELD(DMCONTROL, HASEL, 26, 1)
REG32(DMSTATUS, 0x44)
FIELD(DMSTATUS, VERSION, 0, 4)
+ FIELD(DMSTATUS, HASRESETHALTREQ, 5, 1)
FIELD(DMSTATUS, AUTHENTICATED, 7, 1)
+ FIELD(DMSTATUS, ANYHALTED, 8, 1)
+ FIELD(DMSTATUS, ALLHALTED, 9, 1)
+ FIELD(DMSTATUS, ANYRUNNING, 10, 1)
+ FIELD(DMSTATUS, ALLRUNNING, 11, 1)
+ FIELD(DMSTATUS, ANYNONEXISTENT, 14, 1)
+ FIELD(DMSTATUS, ALLNONEXISTENT, 15, 1)
+ FIELD(DMSTATUS, ANYRESUMEACK, 16, 1)
+ FIELD(DMSTATUS, ALLRESUMEACK, 17, 1)
+ FIELD(DMSTATUS, ANYHAVERESET, 18, 1)
+ FIELD(DMSTATUS, ALLHAVERESET, 19, 1)
+ FIELD(DMSTATUS, IMPEBREAK, 22, 1)
REG32(HARTINFO, 0x48)
+ FIELD(HARTINFO, DATAADDR, 0, 12)
+ FIELD(HARTINFO, DATASIZE, 12, 4)
+ FIELD(HARTINFO, DATAACCESS, 16, 1)
+ FIELD(HARTINFO, NSCRATCH, 20, 4)
REG32(ABSTRACTCS, 0x58)
FIELD(ABSTRACTCS, DATACOUNT, 0, 4)
+ FIELD(ABSTRACTCS, CMDERR, 8, 3)
+ FIELD(ABSTRACTCS, BUSY, 12, 1)
FIELD(ABSTRACTCS, PROGBUFSIZE, 24, 5)
+static inline uint32_t dm_get_hartsel(RISCVDMState *s)
+{
+ uint32_t hi = ARRAY_FIELD_EX32(s->regs, DMCONTROL, HARTSELHI);
+ uint32_t lo = ARRAY_FIELD_EX32(s->regs, DMCONTROL, HARTSELLO);
+
+ return (hi << 10) | lo;
+}
+
+static inline bool dm_hart_valid(RISCVDMState *s, uint32_t hartsel)
+{
+ return hartsel < s->num_harts;
+}
+
+static void dm_status_refresh(RISCVDMState *s)
+{
+ uint32_t hartsel = dm_get_hartsel(s);
+ bool valid = dm_hart_valid(s, hartsel);
+ bool halted = valid && s->hart_halted[hartsel];
+ bool running = valid && !s->hart_halted[hartsel];
+ bool resumeack = valid && s->hart_resumeack[hartsel];
+ bool havereset = valid && s->hart_havereset[hartsel];
+
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ANYNONEXISTENT, !valid);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ALLNONEXISTENT, !valid);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ANYHALTED, halted);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ALLHALTED, halted);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ANYRUNNING, running);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ALLRUNNING, running);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ANYRESUMEACK, resumeack);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ALLRESUMEACK, resumeack);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ANYHAVERESET, havereset);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, ALLHAVERESET, havereset);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, HASRESETHALTREQ, 1);
+}
+
+static void dm_debug_reset(RISCVDMState *s);
+
+static uint64_t dm_dmcontrol_pre_write(RegisterInfo *reg, uint64_t val64)
+{
+ RISCVDMState *s = RISCV_DM(reg->opaque);
+ uint32_t val = val64;
+
+ if (!FIELD_EX32(val, DMCONTROL, DMACTIVE)) {
+ dm_debug_reset(s);
+ return 0;
+ }
+
+ s->dm_active = true;
+
+ val = FIELD_DP32(val, DMCONTROL, DMACTIVE, 1);
+ s->regs[R_DMCONTROL] = val;
+ dm_status_refresh(s);
+ return val;
+}
+
+static uint64_t dm_dmstatus_post_read(RegisterInfo *reg, uint64_t val)
+{
+ RISCVDMState *s = RISCV_DM(reg->opaque);
+
+ dm_status_refresh(s);
+ return val;
+}
+
+static uint64_t dm_hartinfo_post_read(RegisterInfo *reg, uint64_t val)
+{
+ RISCVDMState *s = RISCV_DM(reg->opaque);
+ uint32_t v = val;
+
+ v = FIELD_DP32(v, HARTINFO, DATAADDR, RISCV_DM_ROM_DATA);
+ v = FIELD_DP32(v, HARTINFO, DATASIZE, s->num_abstract_data);
+ v = FIELD_DP32(v, HARTINFO, DATAACCESS, 1);
+ v = FIELD_DP32(v, HARTINFO, NSCRATCH, s->nscratch);
+ return v;
+}
+
static RegisterAccessInfo riscv_dm_regs_info[] = {
- { .name = "DMCONTROL", .addr = A_DMCONTROL, },
- { .name = "DMSTATUS", .addr = A_DMSTATUS, .ro = 0xffffffff, },
- { .name = "HARTINFO", .addr = A_HARTINFO, .ro = 0xffffffff, },
- { .name = "ABSTRACTCS", .addr = A_ABSTRACTCS, .ro = 0xffffffff, },
+ { .name = "DMCONTROL", .addr = A_DMCONTROL,
+ .pre_write = dm_dmcontrol_pre_write, },
+ { .name = "DMSTATUS", .addr = A_DMSTATUS,
+ .ro = 0xffffffff,
+ .post_read = dm_dmstatus_post_read, },
+ { .name = "HARTINFO", .addr = A_HARTINFO,
+ .ro = 0xffffffff,
+ .post_read = dm_hartinfo_post_read, },
+ { .name = "ABSTRACTCS", .addr = A_ABSTRACTCS,
+ .ro = 0xffffffff, },
};
static uint64_t riscv_dm_read(void *opaque, hwaddr addr, unsigned size)
{
+ RegisterInfoArray *reg_array = opaque;
+ RISCVDMState *s = RISCV_DM(reg_array->r[0]->opaque);
+
+ if (!s->dm_active && addr != A_DMCONTROL) {
+ return 0;
+ }
+
return register_read_memory(opaque, addr, size);
}
static void riscv_dm_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
+ RegisterInfoArray *reg_array = opaque;
+ RISCVDMState *s = RISCV_DM(reg_array->r[0]->opaque);
+
+ if (!s->dm_active && addr != A_DMCONTROL) {
+ return;
+ }
+
register_write_memory(opaque, addr, value, size);
}
@@ -75,6 +191,20 @@ static void dm_rom_write(void *opaque, hwaddr offset,
RISCVDMState *s = opaque;
stn_le_p(s->rom_ptr + offset, size, value);
+
+ if (offset == RISCV_DM_ROM_HARTID && size == 4) {
+ riscv_dm_hart_halted(s, value);
+ return;
+ }
+
+ if (offset == RISCV_DM_ROM_RESUME && size == 4) {
+ riscv_dm_hart_resumed(s, value);
+ return;
+ }
+
+ if (offset == RISCV_DM_ROM_EXCP && size == 4) {
+ riscv_dm_abstracts_exception(s, dm_get_hartsel(s));
+ }
}
static const MemoryRegionOps dm_rom_ops = {
@@ -113,26 +243,38 @@ static bool dm_rom_realize(RISCVDMState *s, Error **errp)
void riscv_dm_hart_halted(RISCVDMState *s, uint32_t hartsel)
{
- (void)s;
- (void)hartsel;
+ if (!dm_hart_valid(s, hartsel)) {
+ return;
+ }
+
+ s->hart_halted[hartsel] = true;
+ riscv_dm_abstracts_done(s, hartsel);
+ dm_status_refresh(s);
}
void riscv_dm_hart_resumed(RISCVDMState *s, uint32_t hartsel)
{
- (void)s;
- (void)hartsel;
+ if (!dm_hart_valid(s, hartsel)) {
+ return;
+ }
+
+ s->hart_halted[hartsel] = false;
+ s->hart_resumeack[hartsel] = true;
+ dm_status_refresh(s);
}
void riscv_dm_abstracts_done(RISCVDMState *s, uint32_t hartsel)
{
- (void)s;
(void)hartsel;
+ ARRAY_FIELD_DP32(s->regs, ABSTRACTCS, BUSY, 0);
}
void riscv_dm_abstracts_exception(RISCVDMState *s, uint32_t hartsel)
{
- (void)s;
(void)hartsel;
+ ARRAY_FIELD_DP32(s->regs, ABSTRACTCS, BUSY, 0);
+ ARRAY_FIELD_DP32(s->regs, ABSTRACTCS, CMDERR,
+ RISCV_DM_CMDERR_EXCEPTION);
}
static void dm_debug_reset(RISCVDMState *s)
@@ -140,12 +282,23 @@ static void dm_debug_reset(RISCVDMState *s)
memset(s->regs, 0, sizeof(s->regs));
ARRAY_FIELD_DP32(s->regs, DMSTATUS, VERSION, 3);
ARRAY_FIELD_DP32(s->regs, DMSTATUS, AUTHENTICATED, 1);
+ ARRAY_FIELD_DP32(s->regs, DMSTATUS, IMPEBREAK, s->impebreak);
ARRAY_FIELD_DP32(s->regs, ABSTRACTCS, DATACOUNT, s->num_abstract_data);
ARRAY_FIELD_DP32(s->regs, ABSTRACTCS, PROGBUFSIZE, s->progbuf_size);
+ if (s->hart_halted) {
+ memset(s->hart_halted, 0, s->num_harts * sizeof(bool));
+ memset(s->hart_resumeack, 0, s->num_harts * sizeof(bool));
+ memset(s->hart_havereset, 1, s->num_harts * sizeof(bool));
+ }
+
+ s->dm_active = false;
+
if (s->rom_ptr) {
memset(s->rom_ptr, 0, RISCV_DM_SIZE);
}
+
+ dm_status_refresh(s);
}
static void riscv_dm_init(Object *obj)
@@ -171,6 +324,10 @@ static void riscv_dm_realize(DeviceState *dev, Error **errp)
return;
}
+ s->hart_halted = g_new0(bool, s->num_harts);
+ s->hart_resumeack = g_new0(bool, s->num_harts);
+ s->hart_havereset = g_new0(bool, s->num_harts);
+
if (!dm_rom_realize(s, errp)) {
return;
}
@@ -184,6 +341,15 @@ static void riscv_dm_reset_hold(Object *obj, ResetType type)
dm_debug_reset(RISCV_DM(obj));
}
+static void riscv_dm_unrealize(DeviceState *dev)
+{
+ RISCVDMState *s = RISCV_DM(dev);
+
+ g_free(s->hart_halted);
+ g_free(s->hart_resumeack);
+ g_free(s->hart_havereset);
+}
+
static const Property riscv_dm_props[] = {
DEFINE_PROP_UINT32("num-harts", RISCVDMState, num_harts, 1),
DEFINE_PROP_UINT32("datacount", RISCVDMState, num_abstract_data, 2),
@@ -200,6 +366,12 @@ static const VMStateDescription vmstate_riscv_dm = {
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, RISCVDMState, RISCV_DM_R_MAX),
VMSTATE_BOOL(dm_active, RISCVDMState),
+ VMSTATE_VARRAY_UINT32(hart_halted, RISCVDMState, num_harts,
+ 0, vmstate_info_bool, bool),
+ VMSTATE_VARRAY_UINT32(hart_resumeack, RISCVDMState, num_harts,
+ 0, vmstate_info_bool, bool),
+ VMSTATE_VARRAY_UINT32(hart_havereset, RISCVDMState, num_harts,
+ 0, vmstate_info_bool, bool),
VMSTATE_END_OF_LIST(),
}
};
@@ -210,6 +382,7 @@ static void riscv_dm_class_init(ObjectClass *klass, const void *data)
ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->realize = riscv_dm_realize;
+ dc->unrealize = riscv_dm_unrealize;
dc->vmsd = &vmstate_riscv_dm;
device_class_set_props(dc, riscv_dm_props);
rc->phases.hold = riscv_dm_reset_hold;
--
2.53.0