Track a pending board-level Debug Module halt request in the CPU
state so machine code can queue haltreq and reset-haltreq before
the hart reaches the normal debug entry path.

Add the CPU-side helper, the GPIO input, and the migration state
needed to preserve the pending request and its debug cause across
machine resets and VM state transfers. Later patches will route
this state through the regular interrupt and Debug Mode entry
flow.

Signed-off-by: Chao Liu <[email protected]>
---
 target/riscv/cpu.c      | 68 +++++++++++++++++++++++++++++++++++++++++
 target/riscv/cpu.h      |  5 +++
 target/riscv/cpu_bits.h |  1 +
 target/riscv/machine.c  |  2 ++
 4 files changed, 76 insertions(+)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 470c2d5b39..fce778a67d 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -784,6 +784,8 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType 
type)
 
 #ifndef CONFIG_USER_ONLY
     env->debug_mode = false;
+    env->dm_halt_request = false;
+    env->dm_halt_cause = DCSR_CAUSE_HALTREQ;
     env->dcsr = DCSR_DEBUGVER(4);
     env->dpc = 0;
     env->dscratch[0] = 0;
@@ -1086,6 +1088,70 @@ static void riscv_cpu_set_nmi(void *opaque, int irq, int 
level)
 {
     riscv_cpu_set_rnmi(RISCV_CPU(opaque), irq, level);
 }
+
+/*
+ * Debug cause priority (higher to lower), per Debug Spec v1.0 Table 4.2:
+ * reset-haltreq, halt-group, haltreq, trigger, ebreak, step.
+ */
+static int riscv_debug_cause_priority(uint32_t cause)
+{
+    switch (cause & 0x7) {
+    case DCSR_CAUSE_RESET:
+        return 0;
+    case DCSR_CAUSE_GROUP:
+        return 1;
+    case DCSR_CAUSE_HALTREQ:
+        return 2;
+    case DCSR_CAUSE_TRIGGER:
+        return 3;
+    case DCSR_CAUSE_EBREAK:
+        return 4;
+    case DCSR_CAUSE_STEP:
+        return 5;
+    case DCSR_CAUSE_OTHER:
+        return 6;
+    default:
+        return 7;
+    }
+}
+
+static bool riscv_debug_cause_is_higher(uint32_t new_cause,
+                                        uint32_t current_cause)
+{
+    return riscv_debug_cause_priority(new_cause) <
+           riscv_debug_cause_priority(current_cause);
+}
+
+void riscv_cpu_request_dm_halt(RISCVCPU *cpu, uint32_t cause)
+{
+    CPURISCVState *env = &cpu->env;
+    CPUState *cs = CPU(cpu);
+
+    if (!riscv_cpu_cfg(env)->ext_sdext) {
+        return;
+    }
+
+    if (env->dm_halt_request &&
+        !riscv_debug_cause_is_higher(cause, env->dm_halt_cause)) {
+        return;
+    }
+
+    env->dm_halt_request = true;
+    env->dm_halt_cause = cause & 0x7;
+    cpu_interrupt(cs, CPU_INTERRUPT_DM_HALT);
+}
+
+static void riscv_cpu_dm_halt_req(void *opaque, int irq, int level)
+{
+    RISCVCPU *cpu = RISCV_CPU(opaque);
+    CPURISCVState *env = &cpu->env;
+
+    qemu_log_mask(CPU_LOG_INT, "dm_halt_req: level=%d ext_sdext=%d\n",
+                  level, riscv_cpu_cfg(env)->ext_sdext);
+    if (level) {
+        riscv_cpu_request_dm_halt(cpu, DCSR_CAUSE_HALTREQ);
+    }
+}
 #endif /* CONFIG_USER_ONLY */
 
 static bool riscv_cpu_is_dynamic(Object *cpu_obj)
@@ -1106,6 +1172,8 @@ static void riscv_cpu_init(Object *obj)
                       IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX);
     qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_nmi,
                             "riscv.cpu.rnmi", RNMI_MAX);
+    qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_dm_halt_req,
+                            "dm-halt-req", 1);
 #endif /* CONFIG_USER_ONLY */
 
     general_user_opts = g_hash_table_new(g_str_hash, g_str_equal);
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 0d6b70c9f0..6fb255c7c3 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -481,6 +481,10 @@ struct CPUArchState {
     target_ulong dpc;
     target_ulong dscratch[2];
 
+    /* Pending Debug Module halt request from the board-level controller. */
+    bool dm_halt_request;
+    uint8_t dm_halt_cause;
+
     uint64_t mstateen[SMSTATEEN_MAX_COUNT];
     uint64_t hstateen[SMSTATEEN_MAX_COUNT];
     uint64_t sstateen[SMSTATEEN_MAX_COUNT];
@@ -642,6 +646,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
 int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts);
 uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask,
                               uint64_t value);
+void riscv_cpu_request_dm_halt(RISCVCPU *cpu, uint32_t cause);
 void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level);
 void riscv_cpu_interrupt(CPURISCVState *env);
 #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index bb59f7ff56..01ec2a69c4 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -1177,6 +1177,7 @@ typedef enum CTRType {
 
 /* RISC-V-specific interrupt pending bits. */
 #define CPU_INTERRUPT_RNMI                 CPU_INTERRUPT_TGT_EXT_0
+#define CPU_INTERRUPT_DM_HALT              CPU_INTERRUPT_TGT_INT_0
 
 /* JVT CSR bits */
 #define JVT_MODE                           0x3F
diff --git a/target/riscv/machine.c b/target/riscv/machine.c
index ddd0569d9e..e811abc868 100644
--- a/target/riscv/machine.c
+++ b/target/riscv/machine.c
@@ -263,6 +263,8 @@ static const VMStateDescription vmstate_sdext = {
         VMSTATE_UINTTL(env.dcsr, RISCVCPU),
         VMSTATE_UINTTL(env.dpc, RISCVCPU),
         VMSTATE_UINTTL_ARRAY(env.dscratch, RISCVCPU, 2),
+        VMSTATE_BOOL(env.dm_halt_request, RISCVCPU),
+        VMSTATE_UINT8(env.dm_halt_cause, RISCVCPU),
         VMSTATE_END_OF_LIST()
     }
 };
-- 
2.53.0


Reply via email to