* Daniel Henrique Barboza (dbarb...@ventanamicro.com) wrote:
> 
> 
> On 6/30/25 9:07 PM, Dr. David Alan Gilbert wrote:
> > * Daniel Henrique Barboza (dbarb...@ventanamicro.com) wrote:
> > 
> > Hi Daniel,
> > 
> > > The RISC-V target has *a lot* of CPU registers, with more registers
> > > being added along the way when new extensions are added. In this world,
> > > 'info registers' will throw a wall of text that can be annoying to deal
> > > with when the user wants to verify the value of just a couple of
> > > registers.
> > > 
> > > Add a new 'info register' HMP command that prints a specific register.
> > > The semantics, and implementation, is similar to what 'info registers'
> > > already does, i.e. '-a' will print a register for all VCPUs and it's
> > > possible to print a reg for a specific VCPU.
> > > 
> > > A RISC-V implementation is included via riscv_cpu_dump_register().
> > > 
> > > Here's an example:
> > > 
> > > Welcome to Buildroot
> > > buildroot login: QEMU 10.0.50 monitor - type 'help' for more information
> > > (qemu) info register mstatus
> > > 
> > > CPU#0
> > >   mstatus  0000000a000000a0
> > > (qemu) info register mstatus -a
> > > 
> > > CPU#0
> > >   mstatus  0000000a000000a0
> > > 
> > > CPU#1
> > >   mstatus  0000000a000000a0
> > > (qemu)
> > 
> > OK, that makes some sense; some comments:
> > 
> >     a) I'd make that a list of register names so you can print a handful out
> > for whatever you're debugging.
> >         info register gpr0,gpr1,pc
> >     b) (But then you start wondering if having some predefined like 'gprs'
> > would work)
> >     c) It turns out there's an old, but limited similar thing called 
> > MonitorDef's
> > where a target can define a list of registers; so you might want to define
> > target_monitor_defs() OR target_get_monitor_def() for your architecture 
> > anyway,
> > on x86 you can do things like
> >            p/x $pc
> >            x/10i $pc
> >            p/x $eax
> > 
> >        It doesn't seem very well maintained in the architectures though; 
> > the x86
> >    one is prehistoric for example.
> 
> But it's a cool API to have it seems. If we couple this with 'cpu N' commands 
> we can
> print registers from multiple CPUs.
> 
> Perhaps we still want an 'info registers' or an 'info registers -r 
> reg1,reg2...' to
> print multiple registers anyway, but I don't see a reason to not support the 
> MonitorDef
> API in RISC-V too. I'll take a look.

If you were doing MonitorDef, I think of the two interfaces, 
target_get_monitor_def
looks easier to me, but I might be wrong.

> >    d) Another way would be to modify info registers to take an optional
> >       -r register-list
> > 
> > Anyway, those are _suggestions_ only.
> 
> 
> I don't mind adding new options in 'info registers' to do what this new API 
> I'm
> proposing. In fact that was my original idea, and the reason I added a new 
> API was
> more in a fear of adding too much stuff into it and the potential pushback.
> 
> IIUC from the feedbacks we have so far, we could augment 'info registers' as 
> follows:
> 
> - add a '-h' option to print the names of all available registers
> - add a '-r' option followed by a list of registers to be dumped

Yeh.

> In parallel I'll take a look in MonitorDef too. Thanks,

Nice.

Dave

> 
> Daniel
> 
> 
> 
> 
> 
> > 
> > Dave
> > 
> > > The API is introduced as TARGET_RISCV only.
> > > 
> > > Cc: Dr. David Alan Gilbert <d...@treblig.org>
> > > Cc: Marcel Apfelbaum <marcel.apfelb...@gmail.com>
> > > Cc: Philippe Mathieu-Daudé <phi...@linaro.org>
> > > Signed-off-by: Daniel Henrique Barboza <dbarb...@ventanamicro.com>
> > > ---
> > >   hmp-commands-info.hx         | 17 +++++++++++++
> > >   hw/core/cpu-common.c         |  8 ++++++
> > >   include/hw/core/cpu.h        | 11 +++++++++
> > >   include/monitor/hmp-target.h |  1 +
> > >   monitor/hmp-cmds-target.c    | 30 ++++++++++++++++++++++
> > >   target/riscv/cpu.c           | 48 ++++++++++++++++++++++++++++++++++++
> > >   6 files changed, 115 insertions(+)
> > > 
> > > diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> > > index 639a450ee5..f3561e4a02 100644
> > > --- a/hmp-commands-info.hx
> > > +++ b/hmp-commands-info.hx
> > > @@ -113,6 +113,23 @@ SRST
> > >       Show the cpu registers.
> > >   ERST
> > > +#if defined(TARGET_RISCV)
> > > +    {
> > > +        .name       = "register",
> > > +        .args_type  = "register:s,cpustate_all:-a,vcpu:i?",
> > > +        .params     = "[register|-a|vcpu]",
> > > +        .help       = "show a cpu register (-a: show the register value 
> > > for all cpus;"
> > > +                      " vcpu: specific vCPU to query; show the current 
> > > CPU's register if"
> > > +                      " no vcpu is specified)",
> > > +        .cmd        = hmp_info_register,
> > > +    },
> > > +
> > > +SRST
> > > +  ``info register``
> > > +    Show a cpu register.
> > > +ERST
> > > +#endif
> > > +
> > >   #if defined(TARGET_I386)
> > >       {
> > >           .name       = "lapic",
> > > diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
> > > index 39e674aca2..9c65ce1537 100644
> > > --- a/hw/core/cpu-common.c
> > > +++ b/hw/core/cpu-common.c
> > > @@ -108,6 +108,14 @@ void cpu_dump_state(CPUState *cpu, FILE *f, int 
> > > flags)
> > >       }
> > >   }
> > > +void cpu_dump_register(CPUState *cpu, const char *reg, FILE *f)
> > > +{
> > > +    if (cpu->cc->dump_register) {
> > > +        cpu_synchronize_state(cpu);
> > > +        cpu->cc->dump_register(cpu, reg, f);
> > > +    }
> > > +}
> > > +
> > >   void cpu_reset(CPUState *cpu)
> > >   {
> > >       device_cold_reset(DEVICE(cpu));
> > > diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
> > > index 33296a1c08..b9ddce22bd 100644
> > > --- a/include/hw/core/cpu.h
> > > +++ b/include/hw/core/cpu.h
> > > @@ -160,6 +160,7 @@ struct CPUClass {
> > >       int (*memory_rw_debug)(CPUState *cpu, vaddr addr,
> > >                              uint8_t *buf, size_t len, bool is_write);
> > >       void (*dump_state)(CPUState *cpu, FILE *, int flags);
> > > +    void (*dump_register)(CPUState *cpu, const char *reg, FILE *);
> > >       void (*query_cpu_fast)(CPUState *cpu, CpuInfoFast *value);
> > >       int64_t (*get_arch_id)(CPUState *cpu);
> > >       void (*set_pc)(CPUState *cpu, vaddr value);
> > > @@ -693,6 +694,16 @@ enum CPUDumpFlags {
> > >    */
> > >   void cpu_dump_state(CPUState *cpu, FILE *f, int flags);
> > > +/**
> > > + * cpu_dump_register:
> > > + * @cpu: The CPU whose register state is to be dumped.
> > > + * @reg: CPU register name to be dumped.
> > > + * @f: If non-null, dump to this stream, else to current print sink.
> > > + *
> > > + * Dumps CPU register state.
> > > + */
> > > +void cpu_dump_register(CPUState *cpu, const char *reg, FILE *f);
> > > +
> > >   /**
> > >    * cpu_get_phys_page_attrs_debug:
> > >    * @cpu: The CPU to obtain the physical page address for.
> > > diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h
> > > index b679aaebbf..da9d690f89 100644
> > > --- a/include/monitor/hmp-target.h
> > > +++ b/include/monitor/hmp-target.h
> > > @@ -57,6 +57,7 @@ void hmp_info_via(Monitor *mon, const QDict *qdict);
> > >   void hmp_memory_dump(Monitor *mon, const QDict *qdict);
> > >   void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict);
> > >   void hmp_info_registers(Monitor *mon, const QDict *qdict);
> > > +void hmp_info_register(Monitor *mon, const QDict *qdict);
> > >   void hmp_gva2gpa(Monitor *mon, const QDict *qdict);
> > >   void hmp_gpa2hva(Monitor *mon, const QDict *qdict);
> > >   void hmp_gpa2hpa(Monitor *mon, const QDict *qdict);
> > > diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c
> > > index 8eaf70d9c9..43f509aa60 100644
> > > --- a/monitor/hmp-cmds-target.c
> > > +++ b/monitor/hmp-cmds-target.c
> > > @@ -121,6 +121,36 @@ void hmp_info_registers(Monitor *mon, const QDict 
> > > *qdict)
> > >       }
> > >   }
> > > +/*
> > > + * Based on hmp_info_registers().
> > > + */
> > > +void hmp_info_register(Monitor *mon, const QDict *qdict)
> > > +{
> > > +    const char *reg = qdict_get_try_str(qdict, "register");
> > > +    bool all_cpus = qdict_get_try_bool(qdict, "cpustate_all", false);
> > > +    int vcpu = qdict_get_try_int(qdict, "vcpu", -1);
> > > +    CPUState *cs;
> > > +
> > > +    if (all_cpus) {
> > > +        CPU_FOREACH(cs) {
> > > +            cpu_dump_register(cs, reg, NULL);
> > > +        }
> > > +    } else {
> > > +        cs = vcpu >= 0 ? qemu_get_cpu(vcpu) : mon_get_cpu(mon);
> > > +
> > > +        if (!cs) {
> > > +            if (vcpu >= 0) {
> > > +                monitor_printf(mon, "CPU#%d not available\n", vcpu);
> > > +            } else {
> > > +                monitor_printf(mon, "No CPU available\n");
> > > +            }
> > > +            return;
> > > +        }
> > > +
> > > +        cpu_dump_register(cs, reg, NULL);
> > > +    }
> > > +}
> > > +
> > >   static void memory_dump(Monitor *mon, int count, int format, int wsize,
> > >                           hwaddr addr, int is_physical)
> > >   {
> > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > > index e3f8ecef68..8b3edf7b23 100644
> > > --- a/target/riscv/cpu.c
> > > +++ b/target/riscv/cpu.c
> > > @@ -640,6 +640,53 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE 
> > > *f, int flags)
> > >       }
> > >   }
> > > +static void riscv_cpu_dump_register(CPUState *cs, const char *reg, FILE 
> > > *f)
> > > +{
> > > +    RISCVCPU *cpu = RISCV_CPU(cs);
> > > +    CPURISCVState *env = &cpu->env;
> > > +    bool match_found = false;
> > > +    int i;
> > > +
> > > +    for (i = 0; i < ARRAY_SIZE(csr_ops); i++) {
> > > +        RISCVException res;
> > > +        target_ulong val = 0;
> > > +        int csrno = i;
> > > +
> > > +        /*
> > > +         * Early skip when possible since we're going
> > > +         * through a lot of NULL entries.
> > > +         */
> > > +        if (csr_ops[csrno].predicate == NULL) {
> > > +            continue;
> > > +        }
> > > +
> > > +        /*
> > > +         * We're doing partial register name matching,
> > > +         * e.g. 'mhpm' will match all registers that
> > > +         * starts with 'mhpm'.
> > > +         */
> > > +        if (strncasecmp(csr_ops[csrno].name, reg, strlen(reg)) != 0) {
> > > +            continue;
> > > +        }
> > > +
> > > +        res = riscv_csrrw_debug(env, csrno, &val, 0, 0);
> > > +
> > > +        /*
> > > +         * Rely on the smode, hmode, etc, predicates within csr.c
> > > +         * to do the filtering of the registers that are present.
> > > +         */
> > > +        if (res == RISCV_EXCP_NONE) {
> > > +            if (!match_found) {
> > > +                match_found = true;
> > > +                qemu_fprintf(f, "\nCPU#%d\n", cs->cpu_index);
> > > +            }
> > > +
> > > +            qemu_fprintf(f, " %-8s " TARGET_FMT_lx "\n",
> > > +                         csr_ops[csrno].name, val);
> > > +        }
> > > +    }
> > > +}
> > > +
> > >   static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
> > >   {
> > >       RISCVCPU *cpu = RISCV_CPU(cs);
> > > @@ -2690,6 +2737,7 @@ static void riscv_cpu_common_class_init(ObjectClass 
> > > *c, const void *data)
> > >       cc->class_by_name = riscv_cpu_class_by_name;
> > >       cc->dump_state = riscv_cpu_dump_state;
> > > +    cc->dump_register = riscv_cpu_dump_register;
> > >       cc->set_pc = riscv_cpu_set_pc;
> > >       cc->get_pc = riscv_cpu_get_pc;
> > >       cc->gdb_read_register = riscv_cpu_gdb_read_register;
> > > -- 
> > > 2.49.0
> > > 
> 
-- 
 -----Open up your eyes, open up your mind, open up your code -------   
/ Dr. David Alan Gilbert    |       Running GNU/Linux       | Happy  \ 
\        dave @ treblig.org |                               | In Hex /
 \ _________________________|_____ http://www.treblig.org   |_______/

Reply via email to