Hi Daniel,

On 1/29/2026 3:42 AM, Daniel Henrique Barboza wrote:
> 
> 
> On 1/27/2026 10:15 AM, Chao Liu wrote:
>> RISC-V Debug Specification:
>> https://github.com/riscv/riscv-debug-spec/releases/tag/1.0
>>
>> Add architectural state for Sdext Debug Mode: debug_mode, dcsr, dpc
>> and dscratch0/1. Wire up CSR access for dcsr/dpc/dscratch and gate
>> them to Debug Mode (or host debugger access).
>>
>> The Sdext is not fully implemented, so it is disabled by default.
>>
>> Signed-off-by: Chao Liu <[email protected]>
>> ---
>>   target/riscv/cpu.c                |  10 +++
>>   target/riscv/cpu.h                |   4 +
>>   target/riscv/cpu_bits.h           |  33 ++++++++
>>   target/riscv/cpu_cfg_fields.h.inc |   1 +
>>   target/riscv/csr.c                | 126 ++++++++++++++++++++++++++++++
>>   target/riscv/machine.c            |  20 +++++
>>   6 files changed, 194 insertions(+)
>>
>> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
>> index 0ba98a62e4..ba8fd1557a 100644
>> --- a/target/riscv/cpu.c
>> +++ b/target/riscv/cpu.c
>> @@ -209,6 +209,7 @@ const RISCVIsaExtData isa_edata_arr[] = {
>>       ISA_EXT_DATA_ENTRY(zvkt, PRIV_VERSION_1_12_0, ext_zvkt),
>>       ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx),
>>       ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin),
>> +    ISA_EXT_DATA_ENTRY(sdext, PRIV_VERSION_1_12_0, ext_sdext),
>>       ISA_EXT_DATA_ENTRY(sdtrig, PRIV_VERSION_1_12_0, ext_sdtrig),
>>       ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12),
>>       ISA_EXT_DATA_ENTRY(sha, PRIV_VERSION_1_12_0, ext_sha),
>> @@ -779,6 +780,11 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType
>> type)
>>       /* Default NaN value: sign bit clear, frac msb set */
>>       set_float_default_nan_pattern(0b01000000, &env->fp_status);
>>       env->vill = true;
>> +    env->debug_mode = false;
>> +    env->dcsr = DCSR_DEBUGVER(4);
>> +    env->dpc = 0;
>> +    env->dscratch[0] = 0;
>> +    env->dscratch[1] = 0;
>>     #ifndef CONFIG_USER_ONLY
>>       if (cpu->cfg.ext_sdtrig) {
>> @@ -1131,6 +1137,9 @@ static void riscv_cpu_init(Object *obj)
>>        */
>>       cpu->cfg.ext_sdtrig = true;
>>   +    /* sdext is not fully implemented, so it is disabled by default. */
>> +    cpu->cfg.ext_sdext = false;
>> +
>>       if (mcc->def->profile) {
>>           mcc->def->profile->enabled = true;
>>       }
>> @@ -1245,6 +1254,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
>>       MULTI_EXT_CFG_BOOL("smcdeleg", ext_smcdeleg, false),
>>       MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false),
>>       MULTI_EXT_CFG_BOOL("ssccfg", ext_ssccfg, false),
>> +    MULTI_EXT_CFG_BOOL("sdext", ext_sdext, false),
>>       MULTI_EXT_CFG_BOOL("sdtrig", ext_sdtrig, true),
>>       MULTI_EXT_CFG_BOOL("smctr", ext_smctr, false),
>>       MULTI_EXT_CFG_BOOL("ssctr", ext_ssctr, false),
>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>> index 5c6824f2d3..a474494dff 100644
>> --- a/target/riscv/cpu.h
>> +++ b/target/riscv/cpu.h
>> @@ -476,6 +476,10 @@ struct CPUArchState {
>>         /* True if in debugger mode.  */
>>       bool debugger;
>> +    bool debug_mode;
>> +    target_ulong dcsr;
>> +    target_ulong dpc;
>> +    target_ulong dscratch[2];
> 
> This patch (and I believe almost every single other patch that follows) won't
> build in the linux-user target. It will throw errors like this:
> 
> ../target/riscv/cpu_helper.c: In function ‘riscv_cpu_enter_debug_mode’:  
> ../target/riscv/cpu_helper.c:154:8: error: ‘CPURISCVState’ {aka ‘struct
> CPUArchState’} has no member named ‘debug_mode’       154 | env->debug_mode =
> true;                                              |        ^~  
>                    ../target/riscv/cpu_helper.c:155:10: error:
> ‘CPURISCVState’ {aka ‘struct CPUArchState’} has no member named ‘dpc’; did you
> mean ‘pc’?                                                               155 |
> env->dpc = pc & get_xepc_mask(env);                                           
>   
> |          ^~~
> 
> 
> The reason is that the flags you're declaring up above exist only in the 
> system
> emulation mode. If you double check target/riscv/cpu.h you'll notice that the
> flags you're adding are inside a big ifdef around line 270:
> 
> #ifndef CONFIG_USER_ONLY
>     /* This contains QEMU specific information about the virt state. */
> 
> 
> So debug_mode, dscr, dpc and dscratch aren't available for user mode. Note 
> that
> this is correct - user mode can't deal with sdext or any other debug trigger
> extension. But you'll have to gate all logic that uses those flags in #ifndef
> CONFIG_USER_ONLY blocks.
> 
> 
> For example, in patch 3:
> 
> 
> +void riscv_cpu_enter_debug_mode(CPURISCVState *env, target_ulong pc,
> +                                uint32_t cause)
> +{
> +#ifndef CONFIG_USER_ONLY
> +    if (!riscv_sdext_enabled(env)) {
> +        return;
> +    }
> +#endif
> +    env->debug_mode = true;
> +    env->dpc = pc & get_xepc_mask(env);
> +    env->dcsr &= ~(DCSR_CAUSE_MASK | DCSR_PRV_MASK | DCSR_V);
> +    env->dcsr |= ((target_ulong)(cause & 0x7)) << DCSR_CAUSE_SHIFT;
> +    env->dcsr |= env->priv & DCSR_PRV_MASK;
> +    if (env->virt_enabled && riscv_has_ext(env, RVH)) {
> +        env->dcsr |= DCSR_V;
> +    }
> +#ifndef CONFIG_USER_ONLY
> +    if (env_archcpu(env)->cfg.ext_zicfilp) {
> +        if (env->elp) {
> +            env->dcsr |= DCSR_PELP;
> +        } else {
> +            env->dcsr &= ~DCSR_PELP;
> +        }
> +        env->elp = false;
> +    }
> +#endif
> 
> 
> We want the whole function body inside an "#ifndef CONFIG_USER_ONLY" because
> everything being done is unavailable in that mode.
> 
> You can use the build target 'riscv64-linux-user' to build qemu-riscv64 (i.e.
> user mode) to check if the patches will break it by accident. I usually build
> all riscv targets to catch this type of build errors:
> 
> 
> ./configure --target-list=riscv64-softmmu,riscv64-linux-user,riscv32-
> softmmu,riscv32-linux-user  (...)
> 
> 
Thanks for catching this! You're right - I missed the linux-user build
target entirely.

I'll fix this in v4 by moving debug_mode/dcsr/dpc/dscratch fields inside
the #ifndef CONFIG_USER_ONLY block in cpu.h, and wrapping all code using
these fields with proper guards.

Thanks,
Chao
> 
> Thanks,
> Daniel
> 
> 
> 
> 
>>         uint64_t mstateen[SMSTATEEN_MAX_COUNT];
>>       uint64_t hstateen[SMSTATEEN_MAX_COUNT];
>> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>> index b62dd82fe7..bb59f7ff56 100644
>> --- a/target/riscv/cpu_bits.h
>> +++ b/target/riscv/cpu_bits.h
>> @@ -467,6 +467,39 @@
>>   #define CSR_DCSR            0x7b0
>>   #define CSR_DPC             0x7b1
>>   #define CSR_DSCRATCH        0x7b2
>> +#define CSR_DSCRATCH1       0x7b3
>> +
>> +/* DCSR fields */
>> +#define DCSR_XDEBUGVER_SHIFT    28
>> +#define DCSR_XDEBUGVER_MASK     (0xfu << DCSR_XDEBUGVER_SHIFT)
>> +#define DCSR_DEBUGVER(val)      ((target_ulong)(val) << 
>> DCSR_XDEBUGVER_SHIFT)
>> +#define DCSR_EXTCAUSE_SHIFT     24
>> +#define DCSR_EXTCAUSE_MASK      (0x7u << DCSR_EXTCAUSE_SHIFT)
>> +#define DCSR_CETRIG             BIT(19)
>> +#define DCSR_PELP               BIT(18)
>> +#define DCSR_EBREAKVS           BIT(17)
>> +#define DCSR_EBREAKVU           BIT(16)
>> +#define DCSR_EBREAKM            BIT(15)
>> +#define DCSR_EBREAKS            BIT(13)
>> +#define DCSR_EBREAKU            BIT(12)
>> +#define DCSR_STEPIE             BIT(11)
>> +#define DCSR_STOPCOUNT          BIT(10)
>> +#define DCSR_STOPTIME           BIT(9)
>> +#define DCSR_CAUSE_SHIFT        6
>> +#define DCSR_CAUSE_MASK         (0x7u << DCSR_CAUSE_SHIFT)
>> +#define DCSR_V                  BIT(5)
>> +#define DCSR_MPRVEN             BIT(4)
>> +#define DCSR_NMIP               BIT(3)
>> +#define DCSR_STEP               BIT(2)
>> +#define DCSR_PRV_MASK           0x3u
>> +
>> +#define DCSR_CAUSE_EBREAK       1
>> +#define DCSR_CAUSE_TRIGGER      2
>> +#define DCSR_CAUSE_HALTREQ      3
>> +#define DCSR_CAUSE_STEP         4
>> +#define DCSR_CAUSE_RESET        5
>> +#define DCSR_CAUSE_GROUP        6
>> +#define DCSR_CAUSE_OTHER        7
>>     /* Performance Counters */
>>   #define CSR_MHPMCOUNTER3    0xb03
>> diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/
>> cpu_cfg_fields.h.inc
>> index 492fdd1553..4b157ac920 100644
>> --- a/target/riscv/cpu_cfg_fields.h.inc
>> +++ b/target/riscv/cpu_cfg_fields.h.inc
>> @@ -46,6 +46,7 @@ BOOL_FIELD(ext_zilsd)
>>   BOOL_FIELD(ext_zimop)
>>   BOOL_FIELD(ext_zcmop)
>>   BOOL_FIELD(ext_ztso)
>> +BOOL_FIELD(ext_sdext)
>>   BOOL_FIELD(ext_sdtrig)
>>   BOOL_FIELD(ext_smstateen)
>>   BOOL_FIELD(ext_sstc)
>> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
>> index 870fad87ac..3e38c943e0 100644
>> --- a/target/riscv/csr.c
>> +++ b/target/riscv/csr.c
>> @@ -3136,6 +3136,126 @@ static RISCVException write_mtval(CPURISCVState *env,
>> int csrno,
>>       return RISCV_EXCP_NONE;
>>   }
>>   +#if !defined(CONFIG_USER_ONLY)
>> +static RISCVException sdext(CPURISCVState *env, int csrno)
>> +{
>> +    if (!riscv_cpu_cfg(env)->ext_sdext) {
>> +        return RISCV_EXCP_ILLEGAL_INST;
>> +    }
>> +
>> +    if (!env->debug_mode && !env->debugger) {
>> +        return RISCV_EXCP_ILLEGAL_INST;
>> +    }
>> +
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>> +static target_ulong dcsr_visible_mask(CPURISCVState *env)
>> +{
>> +    target_ulong mask = (target_ulong)-1;
>> +    RISCVCPU *cpu = env_archcpu(env);
>> +
>> +    if (!riscv_has_ext(env, RVH)) {
>> +        mask &= ~(DCSR_EBREAKVS | DCSR_EBREAKVU | DCSR_V);
>> +    }
>> +    if (!riscv_has_ext(env, RVS)) {
>> +        mask &= ~DCSR_EBREAKS;
>> +    }
>> +    if (!riscv_has_ext(env, RVU)) {
>> +        mask &= ~DCSR_EBREAKU;
>> +    }
>> +    if (!cpu->cfg.ext_zicfilp) {
>> +        mask &= ~DCSR_PELP;
>> +    }
>> +    if (!cpu->cfg.ext_smdbltrp) {
>> +        mask &= ~DCSR_CETRIG;
>> +    }
>> +
>> +    return mask;
>> +}
>> +
>> +static RISCVException read_dcsr(CPURISCVState *env, int csrno,
>> +                                target_ulong *val)
>> +{
>> +    *val = env->dcsr & dcsr_visible_mask(env);
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>> +static target_ulong dcsr_writable_mask(CPURISCVState *env)
>> +{
>> +    target_ulong mask = DCSR_EBREAKM | DCSR_EBREAKS | DCSR_EBREAKU |
>> +                        DCSR_STEPIE | DCSR_STOPCOUNT | DCSR_STOPTIME |
>> +                        DCSR_STEP | DCSR_PRV_MASK;
>> +    RISCVCPU *cpu = env_archcpu(env);
>> +
>> +    mask |= DCSR_MPRVEN;
>> +
>> +    if (riscv_has_ext(env, RVH)) {
>> +        mask |= DCSR_EBREAKVS | DCSR_EBREAKVU | DCSR_V;
>> +    }
>> +    if (riscv_has_ext(env, RVS)) {
>> +        mask |= DCSR_EBREAKS;
>> +    }
>> +    if (riscv_has_ext(env, RVU)) {
>> +        mask |= DCSR_EBREAKU;
>> +    }
>> +    if (cpu->cfg.ext_zicfilp) {
>> +        mask |= DCSR_PELP;
>> +    }
>> +    if (cpu->cfg.ext_smdbltrp) {
>> +        mask |= DCSR_CETRIG;
>> +    }
>> +
>> +    return mask;
>> +}
>> +
>> +static RISCVException write_dcsr(CPURISCVState *env, int csrno,
>> +                                 target_ulong val, uintptr_t ra)
>> +{
>> +    target_ulong mask = dcsr_writable_mask(env);
>> +    target_ulong new_val = env->dcsr;
>> +
>> +    new_val &= ~mask;
>> +    new_val |= val & mask;
>> +    new_val &= ~DCSR_XDEBUGVER_MASK;
>> +    new_val |= DCSR_DEBUGVER(4);
>> +    env->dcsr = new_val;
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>> +static RISCVException read_dpc(CPURISCVState *env, int csrno,
>> +                               target_ulong *val)
>> +{
>> +    *val = env->dpc & get_xepc_mask(env);
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>> +static RISCVException write_dpc(CPURISCVState *env, int csrno,
>> +                                target_ulong val, uintptr_t ra)
>> +{
>> +    env->dpc = val & get_xepc_mask(env);
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>> +static RISCVException read_dscratch(CPURISCVState *env, int csrno,
>> +                                    target_ulong *val)
>> +{
>> +    int index = (csrno == CSR_DSCRATCH1) ? 1 : 0;
>> +
>> +    *val = env->dscratch[index];
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>> +static RISCVException write_dscratch(CPURISCVState *env, int csrno,
>> +                                     target_ulong val, uintptr_t ra)
>> +{
>> +    int index = (csrno == CSR_DSCRATCH1) ? 1 : 0;
>> +
>> +    env->dscratch[index] = val;
>> +    return RISCV_EXCP_NONE;
>> +}
>> +#endif /* !CONFIG_USER_ONLY */
>> +
>>   /* Execution environment configuration setup */
>>   static RISCVException read_menvcfg(CPURISCVState *env, int csrno,
>>                                      target_ulong *val)
>> @@ -6297,6 +6417,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>>       [CSR_TDATA3]    =  { "tdata3",   debug, read_tdata,    write_tdata    
>> },
>>       [CSR_TINFO]     =  { "tinfo",    debug, read_tinfo,    write_ignore   
>> },
>>       [CSR_MCONTEXT]  =  { "mcontext", debug, read_mcontext, write_mcontext 
>> },
>> +#if !defined(CONFIG_USER_ONLY)
>> +    [CSR_DCSR]      =  { "dcsr",      sdext, read_dcsr,     write_dcsr },
>> +    [CSR_DPC]       =  { "dpc",       sdext, read_dpc,      write_dpc },
>> +    [CSR_DSCRATCH]  =  { "dscratch0", sdext, read_dscratch, write_dscratch 
>> },
>> +    [CSR_DSCRATCH1] =  { "dscratch1", sdext, read_dscratch, write_dscratch 
>> },
>> +#endif
>>         [CSR_MCTRCTL]    = { "mctrctl",    ctr_mmode,  NULL, NULL,
>> rmw_xctrctl    },
>>       [CSR_SCTRCTL]    = { "sctrctl",    ctr_smode,  NULL, NULL,
>> rmw_xctrctl    },
>> diff --git a/target/riscv/machine.c b/target/riscv/machine.c
>> index 62c51c8033..52264cf047 100644
>> --- a/target/riscv/machine.c
>> +++ b/target/riscv/machine.c
>> @@ -248,6 +248,25 @@ static const VMStateDescription vmstate_sdtrig = {
>>           VMSTATE_UINTTL_ARRAY(env.tdata1, RISCVCPU, RV_MAX_TRIGGERS),
>>           VMSTATE_UINTTL_ARRAY(env.tdata2, RISCVCPU, RV_MAX_TRIGGERS),
>>           VMSTATE_UINTTL_ARRAY(env.tdata3, RISCVCPU, RV_MAX_TRIGGERS),
>> +        VMSTATE_BOOL_V(env.debug_mode, RISCVCPU, 3),
>> +        VMSTATE_UINTTL_V(env.dcsr, RISCVCPU, 3),
>> +        VMSTATE_UINTTL_V(env.dpc, RISCVCPU, 3),
>> +        VMSTATE_UINTTL_ARRAY_V(env.dscratch, RISCVCPU, 2, 3),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static const VMStateDescription vmstate_sdext = {
>> +    .name = "cpu/sdext",
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .needed = sdtrig_needed,
>> +    .post_load = sdtrig_post_load,
>> +    .fields = (const VMStateField[]) {
>> +        VMSTATE_BOOL_V(env.debug_mode, RISCVCPU, 3),
>> +        VMSTATE_UINTTL_V(env.dcsr, RISCVCPU, 3),
>> +        VMSTATE_UINTTL_V(env.dpc, RISCVCPU, 3),
>> +        VMSTATE_UINTTL_ARRAY_V(env.dscratch, RISCVCPU, 2, 3),
>>           VMSTATE_END_OF_LIST()
>>       }
>>   };
>> @@ -499,6 +518,7 @@ const VMStateDescription vmstate_riscv_cpu = {
>>           &vmstate_ctr,
>>           &vmstate_sstc,
>>           &vmstate_sdtrig,
>> +        &vmstate_sdext,
>>           NULL
>>       }
>>   };
> 


Reply via email to