qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] hmp-cmds-target, target/riscv: add 'info register'
@ 2025-06-30 13:22 Daniel Henrique Barboza
  2025-06-30 16:00 ` Philippe Mathieu-Daudé
  2025-07-01  0:07 ` Dr. David Alan Gilbert
  0 siblings, 2 replies; 6+ messages in thread
From: Daniel Henrique Barboza @ 2025-06-30 13:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-riscv, alistair.francis, liwei1518, zhiwei_liu, palmer,
	Daniel Henrique Barboza, Dr. David Alan Gilbert, Marcel Apfelbaum,
	Philippe Mathieu-Daudé

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)

The API is introduced as TARGET_RISCV only.

Cc: Dr. David Alan Gilbert <dave@treblig.org>
Cc: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
Cc: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Daniel Henrique Barboza <dbarboza@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



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] hmp-cmds-target, target/riscv: add 'info register'
  2025-06-30 13:22 [PATCH] hmp-cmds-target, target/riscv: add 'info register' Daniel Henrique Barboza
@ 2025-06-30 16:00 ` Philippe Mathieu-Daudé
  2025-06-30 17:46   ` Daniel Henrique Barboza
  2025-07-01  0:07 ` Dr. David Alan Gilbert
  1 sibling, 1 reply; 6+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-06-30 16:00 UTC (permalink / raw)
  To: Daniel Henrique Barboza, qemu-devel
  Cc: qemu-riscv, alistair.francis, liwei1518, zhiwei_liu, palmer,
	Dr. David Alan Gilbert, Marcel Apfelbaum

Hi Daniel,

On 30/6/25 15:22, Daniel Henrique Barboza wrote:
> 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)
> 
> The API is introduced as TARGET_RISCV only.
> 
> Cc: Dr. David Alan Gilbert <dave@treblig.org>
> Cc: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
> Cc: Philippe Mathieu-Daudé <philmd@linaro.org>
> Signed-off-by: Daniel Henrique Barboza <dbarboza@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)

Just make this command available for all targets, displaying
"No such register" or better when no handler registered.

> +    {
> +        .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)",

I'd invert the default behavior: dump for all vcpus except if a specific
one is specified.

I wonder about a 'info register -h' do list all register names available.

> +        .cmd        = hmp_info_register,
> +    },
> +
> +SRST
> +  ``info register``
> +    Show a cpu register.
> +ERST
> +#endif

Regards,

Phil.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] hmp-cmds-target, target/riscv: add 'info register'
  2025-06-30 16:00 ` Philippe Mathieu-Daudé
@ 2025-06-30 17:46   ` Daniel Henrique Barboza
  0 siblings, 0 replies; 6+ messages in thread
From: Daniel Henrique Barboza @ 2025-06-30 17:46 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, qemu-devel
  Cc: qemu-riscv, alistair.francis, liwei1518, zhiwei_liu, palmer,
	Dr. David Alan Gilbert, Marcel Apfelbaum



On 6/30/25 1:00 PM, Philippe Mathieu-Daudé wrote:
> Hi Daniel,
> 
> On 30/6/25 15:22, Daniel Henrique Barboza wrote:
>> 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)
>>
>> The API is introduced as TARGET_RISCV only.
>>
>> Cc: Dr. David Alan Gilbert <dave@treblig.org>
>> Cc: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
>> Cc: Philippe Mathieu-Daudé <philmd@linaro.org>
>> Signed-off-by: Daniel Henrique Barboza <dbarboza@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)
> 
> Just make this command available for all targets, displaying
> "No such register" or better when no handler registered.

What about "<targe-name> does not support this command" ?


> 
>> +    {
>> +        .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)",
> 
> I'd invert the default behavior: dump for all vcpus except if a specific
> one is specified.

Fair enough.

> 
> I wonder about a 'info register -h' do list all register names available.

That's a neat idea. I'll see what I can do.


Thanks,

Daniel

> 
>> +        .cmd        = hmp_info_register,
>> +    },
>> +
>> +SRST
>> +  ``info register``
>> +    Show a cpu register.
>> +ERST
>> +#endif
> 
> Regards,
> 
> Phil.



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] hmp-cmds-target, target/riscv: add 'info register'
  2025-06-30 13:22 [PATCH] hmp-cmds-target, target/riscv: add 'info register' Daniel Henrique Barboza
  2025-06-30 16:00 ` Philippe Mathieu-Daudé
@ 2025-07-01  0:07 ` Dr. David Alan Gilbert
  2025-07-01 22:05   ` Daniel Henrique Barboza
  1 sibling, 1 reply; 6+ messages in thread
From: Dr. David Alan Gilbert @ 2025-07-01  0:07 UTC (permalink / raw)
  To: Daniel Henrique Barboza
  Cc: qemu-devel, qemu-riscv, alistair.francis, liwei1518, zhiwei_liu,
	palmer, Marcel Apfelbaum, Philippe Mathieu-Daudé

* Daniel Henrique Barboza (dbarboza@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.

  d) Another way would be to modify info registers to take an optional
     -r register-list

Anyway, those are _suggestions_ only.

Dave

> The API is introduced as TARGET_RISCV only.
> 
> Cc: Dr. David Alan Gilbert <dave@treblig.org>
> Cc: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
> Cc: Philippe Mathieu-Daudé <philmd@linaro.org>
> Signed-off-by: Daniel Henrique Barboza <dbarboza@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   |_______/


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] hmp-cmds-target, target/riscv: add 'info register'
  2025-07-01  0:07 ` Dr. David Alan Gilbert
@ 2025-07-01 22:05   ` Daniel Henrique Barboza
  2025-07-02  0:24     ` Dr. David Alan Gilbert
  0 siblings, 1 reply; 6+ messages in thread
From: Daniel Henrique Barboza @ 2025-07-01 22:05 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: qemu-devel, qemu-riscv, alistair.francis, liwei1518, zhiwei_liu,
	palmer, Marcel Apfelbaum, Philippe Mathieu-Daudé



On 6/30/25 9:07 PM, Dr. David Alan Gilbert wrote:
> * Daniel Henrique Barboza (dbarboza@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.


> 
>    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


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

Daniel





> 
> Dave
> 
>> The API is introduced as TARGET_RISCV only.
>>
>> Cc: Dr. David Alan Gilbert <dave@treblig.org>
>> Cc: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
>> Cc: Philippe Mathieu-Daudé <philmd@linaro.org>
>> Signed-off-by: Daniel Henrique Barboza <dbarboza@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
>>



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] hmp-cmds-target, target/riscv: add 'info register'
  2025-07-01 22:05   ` Daniel Henrique Barboza
@ 2025-07-02  0:24     ` Dr. David Alan Gilbert
  0 siblings, 0 replies; 6+ messages in thread
From: Dr. David Alan Gilbert @ 2025-07-02  0:24 UTC (permalink / raw)
  To: Daniel Henrique Barboza
  Cc: qemu-devel, qemu-riscv, alistair.francis, liwei1518, zhiwei_liu,
	palmer, Marcel Apfelbaum, Philippe Mathieu-Daudé

* Daniel Henrique Barboza (dbarboza@ventanamicro.com) wrote:
> 
> 
> On 6/30/25 9:07 PM, Dr. David Alan Gilbert wrote:
> > * Daniel Henrique Barboza (dbarboza@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 <dave@treblig.org>
> > > Cc: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
> > > Cc: Philippe Mathieu-Daudé <philmd@linaro.org>
> > > Signed-off-by: Daniel Henrique Barboza <dbarboza@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   |_______/


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-07-02  0:26 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-30 13:22 [PATCH] hmp-cmds-target, target/riscv: add 'info register' Daniel Henrique Barboza
2025-06-30 16:00 ` Philippe Mathieu-Daudé
2025-06-30 17:46   ` Daniel Henrique Barboza
2025-07-01  0:07 ` Dr. David Alan Gilbert
2025-07-01 22:05   ` Daniel Henrique Barboza
2025-07-02  0:24     ` Dr. David Alan Gilbert

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).