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 <[email protected]>
---
 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


Reply via email to