* [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers
@ 2026-10-12 20:34 Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 01/10] hmp: don't crash on invalid register Marc-André Lureau
` (9 more replies)
0 siblings, 10 replies; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
Hi,
HMP monitor supports printing $register expression. The lookup
was done via GDB stub registers, and supplementary MonitorDef
HMP-specific tables.
Migrate HMP registers to use the GDB infrastructure and remove the
legacy HMP MonitorDef. Internal registers aren't exposed to GDB.
This also drops HMP $pc x86 fallback (use $eip/$rip instead).
Known behavioural changes on RISC-V: CSR lookup is now case-sensitive
and vector registers now report "unkown register" instead of a
target-specific message ("Unable to print the value of vector vreg 'v0'
from this API")
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
Changes in v2:
- dropped v1 patch 5 (CPUClass::get_pc fallback for $pc)
- new patch gdb_register_internal_coprocessor() to hide registers from GDB
- SPARC64, m68k, RISC-V aliases now use internal registration
- pickup trailer tags
- Link to v1: https://lore.kernel.org/qemu-devel/20260911-query-reg-v1-0-0c04f0c02cf3@redhat.com
---
Marc-André Lureau (10):
hmp: don't crash on invalid register
gdbstub: fix next register base after register gaps
gdbstub: set feature->base_reg for dynamic features
gdbstub: add gdb_register_internal_coprocessor()
target/i386: expose segment limits via GDB
target/m68k: expose system registers via GDB
target/sparc64: expose system registers via GDB
target/riscv: bypass smstateen check in debugger mode
target/riscv: expose register aliases via GDB
monitor/hmp: remove legacy MonitorDef infrastructure
configs/targets/i386-softmmu.mak | 2 +-
configs/targets/m68k-softmmu.mak | 2 +-
configs/targets/riscv32-linux-user.mak | 2 +-
configs/targets/riscv32-softmmu.mak | 2 +-
configs/targets/riscv64-bsd-user.mak | 2 +-
configs/targets/riscv64-linux-user.mak | 2 +-
configs/targets/riscv64-softmmu.mak | 2 +-
configs/targets/sparc64-softmmu.mak | 2 +-
configs/targets/x86_64-softmmu.mak | 2 +-
gdbstub/gdb-xml/i386-segments.xml | 14 +++
gdbstub/gdb-xml/m68k-system.xml | 32 +++++++
gdbstub/gdb-xml/riscv-32bit-alias.xml | 105 ++++++++++++++++++++++
gdbstub/gdb-xml/riscv-32bit-fpu-alias.xml | 40 +++++++++
gdbstub/gdb-xml/riscv-64bit-alias.xml | 105 ++++++++++++++++++++++
gdbstub/gdb-xml/riscv-64bit-fpu-alias.xml | 46 ++++++++++
gdbstub/gdb-xml/sparc64-system.xml | 22 +++++
gdbstub/gdbstub.c | 85 ++++++++++++++----
include/exec/gdbstub.h | 21 +++++
include/hw/core/cpu.h | 2 +-
include/hw/core/sysemu-cpu-ops.h | 16 ----
include/monitor/hmp.h | 6 --
include/qemu/typedefs.h | 1 -
monitor/hmp.c | 46 +---------
target/arm/gdbstub.c | 8 +-
target/arm/gdbstub64.c | 10 ++-
target/i386/cpu.c | 32 -------
target/i386/gdbstub.c | 24 +++++
target/m68k/cpu.c | 22 -----
target/m68k/helper.c | 47 ++++++++++
target/ppc/gdbstub.c | 2 +-
target/riscv/cpu.c | 1 -
target/riscv/gdbstub.c | 56 +++++++++++-
target/riscv/internals.h | 3 -
target/riscv/monitor.c | 140 ------------------------------
target/riscv/tcg/csr.c | 3 +-
target/sparc/cpu.c | 19 ----
target/sparc/gdbstub.c | 38 ++++++++
target/xtensa/cpu.c | 2 +-
38 files changed, 645 insertions(+), 321 deletions(-)
---
base-commit: 615ece3c406b262996e569f29ea6eed0d81c4d8d
change-id: 20260910-query-reg-80cd59cf4707
Best regards,
--
Marc-André Lureau <marcandre.lureau@redhat.com>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 01/10] hmp: don't crash on invalid register
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 02/10] gdbstub: fix next register base after register gaps Marc-André Lureau
` (8 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
When printing an invalid/denied register, QEMU crashes with:
ERROR:include/qemu/bswap.h:488:ldn_le_p: code should not be reached
(ex: "p $sireg" when siselect=0 on riscv)
Return false instead.
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
monitor/hmp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/monitor/hmp.c b/monitor/hmp.c
index 488ec23937df..10e19e417aba 100644
--- a/monitor/hmp.c
+++ b/monitor/hmp.c
@@ -424,7 +424,7 @@ static bool gdb_get_register(MonitorHMP *hmp, int64_t *pval, const char *name)
buf = g_byte_array_new();
reg_size = gdb_read_register(cs, buf, reg->gdb_reg);
- if (reg_size > sizeof(*pval)) {
+ if (reg_size == 0 || reg_size > sizeof(*pval)) {
return false;
}
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 02/10] gdbstub: fix next register base after register gaps
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 01/10] hmp: don't crash on invalid register Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 03/10] gdbstub: set feature->base_reg for dynamic features Marc-André Lureau
` (7 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
Commit b3e88abb200c ("gdbstub: Consider GDBFeature::base_reg in
gdb_register_coprocessor()") identified that register numbers may
have gaps. We also need to adjust the next base_reg correctly
to avoid overlapping registers. Rename the field to be more
explicit about its usage.
Acked-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
gdbstub/gdbstub.c | 13 ++++++-------
include/hw/core/cpu.h | 2 +-
target/arm/gdbstub.c | 8 +++++---
target/arm/gdbstub64.c | 10 ++++++----
target/ppc/gdbstub.c | 2 +-
target/riscv/gdbstub.c | 9 ++++++---
target/xtensa/cpu.c | 2 +-
7 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 0a328b0dd488..0ce871d74929 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -605,13 +605,13 @@ void gdb_init_cpu(CPUState *cpu)
gdb_register_feature(cpu, 0,
cc->gdb_read_register, cc->gdb_write_register,
feature);
- cpu->gdb_num_regs = cpu->gdb_num_g_regs = feature->num_regs;
+ cpu->gdb_next_base_reg = cpu->gdb_num_g_regs = feature->num_regs;
} else {
- cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
+ cpu->gdb_next_base_reg = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
}
trace_gdbxml_init_cpu(object_get_typename(OBJECT(cpu)), cpu->cpu_index,
- cpu->gdb_num_regs, cpu->gdb_num_g_regs,
+ cpu->gdb_next_base_reg, cpu->gdb_num_g_regs,
cc->gdb_num_core_regs);
}
@@ -621,7 +621,7 @@ void gdb_register_coprocessor(CPUState *cpu,
{
GDBRegisterState *s;
guint i;
- int base_reg = cpu->gdb_num_regs;
+ int base_reg = cpu->gdb_next_base_reg;
for (i = 0; i < cpu->gdb_regs->len; i++) {
/* Check for duplicates. */
@@ -639,7 +639,7 @@ void gdb_register_coprocessor(CPUState *cpu,
gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature);
/* Add to end of list. */
- cpu->gdb_num_regs += feature->num_regs;
+ cpu->gdb_next_base_reg = base_reg + feature->num_regs;
}
void gdb_unregister_coprocessor_all(CPUState *cpu)
@@ -651,7 +651,7 @@ void gdb_unregister_coprocessor_all(CPUState *cpu)
g_array_free(cpu->gdb_regs, true);
cpu->gdb_regs = NULL;
- cpu->gdb_num_regs = 0;
+ cpu->gdb_next_base_reg = 0;
cpu->gdb_num_g_regs = 0;
}
@@ -2522,4 +2522,3 @@ void gdb_create_default_process(GDBState *s)
process->attached = false;
process->target_xml = NULL;
}
-
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index 328e30045ce1..02212f555c2e 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -521,7 +521,7 @@ struct CPUState {
struct CPUJumpCache *tb_jmp_cache;
GArray *gdb_regs;
- int gdb_num_regs;
+ int gdb_next_base_reg;
int gdb_num_g_regs;
QTAILQ_ENTRY(CPUState) node;
diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c
index d6e29c4cf467..e5cbe4d951ef 100644
--- a/target/arm/gdbstub.c
+++ b/target/arm/gdbstub.c
@@ -555,19 +555,21 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
gdb_register_coprocessor(cs, mve_gdb_get_reg, mve_gdb_set_reg,
gdb_find_static_feature("arm-m-profile-mve.xml"));
}
+ GDBFeature *feature =
+ arm_gen_dynamic_sysreg_feature(cs, cs->gdb_next_base_reg);
gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg,
- arm_gen_dynamic_sysreg_feature(cs, cs->gdb_num_regs));
+ feature);
#ifdef CONFIG_TCG
if (arm_feature(env, ARM_FEATURE_M) && tcg_enabled()) {
gdb_register_coprocessor(cs,
arm_gdb_get_m_systemreg, arm_gdb_set_m_systemreg,
- arm_gen_dynamic_m_systemreg_feature(cs, cs->gdb_num_regs));
+ arm_gen_dynamic_m_systemreg_feature(cs, cs->gdb_next_base_reg));
#ifndef CONFIG_USER_ONLY
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
gdb_register_coprocessor(cs,
arm_gdb_get_m_secextreg, arm_gdb_set_m_secextreg,
- arm_gen_dynamic_m_secextreg_feature(cs, cs->gdb_num_regs));
+ arm_gen_dynamic_m_secextreg_feature(cs, cs->gdb_next_base_reg));
}
#endif
}
diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c
index 0c3e5b30bd6a..cf5b95b54641 100644
--- a/target/arm/gdbstub64.c
+++ b/target/arm/gdbstub64.c
@@ -885,7 +885,8 @@ void aarch64_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
CPUState *cs = CPU(cpu);
if (isar_feature_aa64_sve(&cpu->isar) ||
isar_feature_aa64_sme(&cpu->isar)) {
- GDBFeature *feature = arm_gen_dynamic_svereg_feature(cs, cs->gdb_num_regs);
+ GDBFeature *feature =
+ arm_gen_dynamic_svereg_feature(cs, cs->gdb_next_base_reg);
gdb_register_coprocessor(cs, aarch64_gdb_get_sve_reg,
aarch64_gdb_set_sve_reg, feature);
} else {
@@ -896,7 +897,7 @@ void aarch64_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
if (isar_feature_aa64_sme(&cpu->isar)) {
GDBFeature *sme_feature =
- arm_gen_dynamic_smereg_feature(cs, cs->gdb_num_regs);
+ arm_gen_dynamic_smereg_feature(cs, cs->gdb_next_base_reg);
gdb_register_coprocessor(cs, aarch64_gdb_get_sme_reg,
aarch64_gdb_set_sme_reg, sme_feature);
if (isar_feature_aa64_sme2(&cpu->isar)) {
@@ -927,7 +928,8 @@ void aarch64_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
#endif
/* All AArch64 CPUs have at least TPIDR */
+ GDBFeature *tls_feature =
+ arm_gen_dynamic_tls_feature(cs, cs->gdb_next_base_reg);
gdb_register_coprocessor(cs, aarch64_gdb_get_tls_reg,
- aarch64_gdb_set_tls_reg,
- arm_gen_dynamic_tls_feature(cs, cs->gdb_num_regs));
+ aarch64_gdb_set_tls_reg, tls_feature);
}
diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c
index 4d622c5cad59..ec25c541c685 100644
--- a/target/ppc/gdbstub.c
+++ b/target/ppc/gdbstub.c
@@ -206,7 +206,7 @@ static void gdb_gen_spr_feature(CPUState *cs)
gdb_feature_builder_init(&builder, &pcc->gdb_spr,
"org.qemu.power.spr", "power-spr.xml",
- cs->gdb_num_regs);
+ cs->gdb_next_base_reg);
for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) {
ppc_spr_t *spr = &env->spr_cb[i];
diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
index 9abbf5bcdf0e..f279f4ab2461 100644
--- a/target/riscv/gdbstub.c
+++ b/target/riscv/gdbstub.c
@@ -348,9 +348,10 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs)
gdb_find_static_feature("riscv-32bit-fpu.xml"));
}
if (cpu->cfg.ext_zve32x) {
+ GDBFeature *feature =
+ ricsv_gen_dynamic_vector_feature(cs, cs->gdb_next_base_reg);
gdb_register_coprocessor(cs, riscv_gdb_get_vector,
- riscv_gdb_set_vector,
- ricsv_gen_dynamic_vector_feature(cs, cs->gdb_num_regs));
+ riscv_gdb_set_vector, feature);
}
#ifdef CONFIG_TCG
@@ -373,8 +374,10 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs)
}
if (cpu->cfg.ext_zicsr) {
+ GDBFeature *feature =
+ riscv_gen_dynamic_csr_feature(cs, cs->gdb_next_base_reg);
gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr,
- riscv_gen_dynamic_csr_feature(cs, cs->gdb_num_regs));
+ feature);
}
#endif
}
diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c
index 7c25b9ab707a..48863a1859d4 100644
--- a/target/xtensa/cpu.c
+++ b/target/xtensa/cpu.c
@@ -263,7 +263,7 @@ static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp)
return;
}
- cs->gdb_num_regs = xcc->config->gdb_regmap.num_regs;
+ cs->gdb_next_base_reg = xcc->config->gdb_regmap.num_regs;
qemu_init_vcpu(cs);
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 03/10] gdbstub: set feature->base_reg for dynamic features
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 01/10] hmp: don't crash on invalid register Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 02/10] gdbstub: fix next register base after register gaps Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 04/10] gdbstub: add gdb_register_internal_coprocessor() Marc-André Lureau
` (6 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
gdb_feature_builder_end() never propagated builder->base_reg into
feature->base_reg, leaving it at 0 for all dynamically-built features.
This made the trace log at gdb_get_register_list() report an incorrect
base_reg for dynamic features.
GDBRegisterState::base_reg was correctly set, so this doesn't fix
an actual issue.
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
gdbstub/gdbstub.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 0ce871d74929..912e31b66744 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -476,6 +476,7 @@ void gdb_feature_builder_end(const GDBFeatureBuilder *builder)
g_ptr_array_free(builder->xml, TRUE);
+ builder->feature->base_reg = builder->base_reg;
builder->feature->num_regs = builder->regs->len;
builder->feature->regs = (void *)g_ptr_array_free(builder->regs, FALSE);
trace_gdbxml_feature_builder_header(builder->feature->name,
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 04/10] gdbstub: add gdb_register_internal_coprocessor()
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
` (2 preceding siblings ...)
2026-10-12 20:34 ` [PATCH v2 03/10] gdbstub: set feature->base_reg for dynamic features Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
2026-10-13 8:42 ` Alex Bennée
2026-10-12 20:34 ` [PATCH v2 05/10] target/i386: expose segment limits via GDB Marc-André Lureau
` (5 subsequent siblings)
9 siblings, 1 reply; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
Add support for registering coprocessor features that are only visible
to QEMU's internal APIs (HMP monitor $register expressions, plugins) but
hidden from GDB's target description XML.
When a feature is registered as internal:
- It is excluded from the target.xml sent to GDB
- Its XML is not served via qXfer:features:read
- 'p'/'P' packets for internal register numbers return E14
This is useful for exposing system registers to the HMP monitor without
affecting GDB's register namespace. Exposing aliases or synthetic
registers through GDB's target description can cause problems: name
collisions with GDB's pseudo-registers, independent register caching,
and missing DWARF unwind rules.
Internal features must be registered after all GDB-visible features to
keep remote register numbering consistent. This ordering is enforced
with an assertion.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
gdbstub/gdbstub.c | 71 ++++++++++++++++++++++++++++++++++++++++++++------
include/exec/gdbstub.h | 21 +++++++++++++++
2 files changed, 84 insertions(+), 8 deletions(-)
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 912e31b66744..ac0f76d62333 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -54,6 +54,7 @@ typedef struct GDBRegisterState {
gdb_get_reg_cb get_reg;
gdb_set_reg_cb set_reg;
const GDBFeature *feature;
+ bool internal; /* hidden from GDB, accessible via API/monitor */
} GDBRegisterState;
GDBState gdbserver_state;
@@ -385,6 +386,11 @@ static const char *get_feature_xml(const char *p, const char **newp,
}
for (guint i = 0; i < cpu->gdb_regs->len; i++) {
r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
+ /* The core target.xml feature must never be internal. */
+ g_assert(i > 0 || !r->internal);
+ if (r->internal) {
+ continue;
+ }
g_ptr_array_add(
xml,
g_markup_printf_escaped("<xi:include href=\"%s\"/>",
@@ -400,6 +406,9 @@ static const char *get_feature_xml(const char *p, const char **newp,
/* Is it one of the features? */
for (guint i = 0; i < cpu->gdb_regs->len; i++) {
r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
+ if (r->internal) {
+ continue;
+ }
if (strncmp(p, r->feature->xmlname, len) == 0) {
return r->feature->xml;
}
@@ -563,13 +572,14 @@ int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg)
static void gdb_register_feature(CPUState *cpu, int base_reg,
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
- const GDBFeature *feature)
+ const GDBFeature *feature, bool internal)
{
GDBRegisterState s = {
.base_reg = base_reg,
.get_reg = get_reg,
.set_reg = set_reg,
- .feature = feature
+ .feature = feature,
+ .internal = internal
};
trace_gdbxml_register_feature(feature->name, feature->xmlname,
@@ -605,7 +615,7 @@ void gdb_init_cpu(CPUState *cpu)
assert(feature->base_reg == 0);
gdb_register_feature(cpu, 0,
cc->gdb_read_register, cc->gdb_write_register,
- feature);
+ feature, false);
cpu->gdb_next_base_reg = cpu->gdb_num_g_regs = feature->num_regs;
} else {
cpu->gdb_next_base_reg = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
@@ -616,20 +626,26 @@ void gdb_init_cpu(CPUState *cpu)
cc->gdb_num_core_regs);
}
-void gdb_register_coprocessor(CPUState *cpu,
- gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
- const GDBFeature *feature)
+static void do_gdb_register_coprocessor(CPUState *cpu,
+ gdb_get_reg_cb get_reg,
+ gdb_set_reg_cb set_reg,
+ const GDBFeature *feature,
+ bool internal)
{
GDBRegisterState *s;
guint i;
int base_reg = cpu->gdb_next_base_reg;
for (i = 0; i < cpu->gdb_regs->len; i++) {
- /* Check for duplicates. */
s = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
+ /* Check for duplicates. */
if (s->feature == feature) {
return;
}
+ /* GDB-visible features must not follow internal ones. */
+ if (!internal) {
+ g_assert(!s->internal);
+ }
}
if (base_reg < feature->base_reg) {
@@ -637,12 +653,27 @@ void gdb_register_coprocessor(CPUState *cpu,
feature->base_reg);
base_reg = feature->base_reg;
}
- gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature);
+ gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature, internal);
/* Add to end of list. */
cpu->gdb_next_base_reg = base_reg + feature->num_regs;
}
+void gdb_register_coprocessor(CPUState *cpu,
+ gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
+ const GDBFeature *feature)
+{
+ do_gdb_register_coprocessor(cpu, get_reg, set_reg, feature, false);
+}
+
+void gdb_register_internal_coprocessor(CPUState *cpu,
+ gdb_get_reg_cb get_reg,
+ gdb_set_reg_cb set_reg,
+ const GDBFeature *feature)
+{
+ do_gdb_register_coprocessor(cpu, get_reg, set_reg, feature, true);
+}
+
void gdb_unregister_coprocessor_all(CPUState *cpu)
{
/*
@@ -1223,6 +1254,18 @@ static void handle_remove_bp(GArray *params, void *user_ctx)
* the remote gdb to fallback to older methods.
*/
+static bool gdb_register_is_internal(CPUState *cpu, int reg)
+{
+ for (guint i = 0; i < cpu->gdb_regs->len; i++) {
+ GDBRegisterState *r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
+
+ if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) {
+ return r->internal;
+ }
+ }
+ return false;
+}
+
static void handle_set_reg(GArray *params, void *user_ctx)
{
int reg_size;
@@ -1232,6 +1275,12 @@ static void handle_set_reg(GArray *params, void *user_ctx)
return;
}
+ if (gdb_register_is_internal(gdbserver_state.g_cpu,
+ gdb_get_cmd_param(params, 0)->val_ull)) {
+ gdb_put_packet("E14");
+ return;
+ }
+
reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2;
gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size);
gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data,
@@ -1248,6 +1297,12 @@ static void handle_get_reg(GArray *params, void *user_ctx)
return;
}
+ if (gdb_register_is_internal(gdbserver_state.g_cpu,
+ gdb_get_cmd_param(params, 0)->val_ull)) {
+ gdb_put_packet("E14");
+ return;
+ }
+
reg_size = gdb_read_register(gdbserver_state.g_cpu,
gdbserver_state.mem_buf,
gdb_get_cmd_param(params, 0)->val_ull);
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index 75eb4d9c365e..1bc778324758 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -40,6 +40,27 @@ void gdb_register_coprocessor(CPUState *cpu,
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
const GDBFeature *feature);
+/**
+ * gdb_register_internal_coprocessor() - register internal registers
+ *
+ * Like gdb_register_coprocessor(), but the feature is NOT exposed to
+ * GDB via the target description XML. The registers are only
+ * accessible through QEMU's internal APIs (HMP monitor ``$register``
+ * expressions via gdb_get_register_list()).
+ *
+ * Internal features must be registered after all GDB-visible features
+ * to avoid register numbering mismatches with GDB.
+ *
+ * @cpu - the CPU associated with registers
+ * @get_reg - get function
+ * @set_reg - set function
+ * @feature - the GDBFeature describing the registers
+ */
+void gdb_register_internal_coprocessor(CPUState *cpu,
+ gdb_get_reg_cb get_reg,
+ gdb_set_reg_cb set_reg,
+ const GDBFeature *feature);
+
/**
* gdb_unregister_coprocessor_all() - unregisters supplemental set of registers
* @cpu - the CPU associated with registers
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 05/10] target/i386: expose segment limits via GDB
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
` (3 preceding siblings ...)
2026-10-12 20:34 ` [PATCH v2 04/10] gdbstub: add gdb_register_internal_coprocessor() Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 06/10] target/m68k: expose system registers " Marc-André Lureau
` (4 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
Add an i386-segments.xml GDB feature that exposes the six segment
limit registers (cs, ds, es, ss, fs, gs) as read-only system
registers through the GDB coprocessor interface. This makes them
accessible to both GDB and HMP without relying on the legacy
MonitorDef table, which will be removed in a subsequent commit.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
configs/targets/i386-softmmu.mak | 2 +-
configs/targets/x86_64-softmmu.mak | 2 +-
gdbstub/gdb-xml/i386-segments.xml | 14 ++++++++++++++
target/i386/gdbstub.c | 24 ++++++++++++++++++++++++
4 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak
index 38a8f85201f9..89cdaa072ef7 100644
--- a/configs/targets/i386-softmmu.mak
+++ b/configs/targets/i386-softmmu.mak
@@ -1,6 +1,6 @@
TARGET_ARCH=i386
TARGET_KVM_HAVE_GUEST_DEBUG=y
TARGET_KVM_HAVE_RESET_PARKED_VCPU=y
-TARGET_XML_FILES= i386-32bit.xml
+TARGET_XML_FILES= i386-32bit.xml i386-segments.xml
TARGET_LONG_BITS=32
TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y
diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak
index c7f8746b4f58..0af699971de0 100644
--- a/configs/targets/x86_64-softmmu.mak
+++ b/configs/targets/x86_64-softmmu.mak
@@ -2,6 +2,6 @@ TARGET_ARCH=x86_64
TARGET_BASE_ARCH=i386
TARGET_KVM_HAVE_GUEST_DEBUG=y
TARGET_KVM_HAVE_RESET_PARKED_VCPU=y
-TARGET_XML_FILES= i386-64bit.xml i386-64bit-apx.xml
+TARGET_XML_FILES= i386-64bit.xml i386-64bit-apx.xml i386-segments.xml
TARGET_LONG_BITS=64
TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y
diff --git a/gdbstub/gdb-xml/i386-segments.xml b/gdbstub/gdb-xml/i386-segments.xml
new file mode 100644
index 000000000000..6fbf3f91957e
--- /dev/null
+++ b/gdbstub/gdb-xml/i386-segments.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!--
+ SPDX-License-Identifier: GPL-2.0-or-later
+-->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<!-- Segment-cache values are read-only; writes are ignored. -->
+<feature name="org.qemu.gdb.i386.segments">
+ <reg name="cs.limit" bitsize="32" type="uint32" group="qemu-debug" save-restore="no"/>
+ <reg name="ds.limit" bitsize="32" type="uint32" group="qemu-debug" save-restore="no"/>
+ <reg name="es.limit" bitsize="32" type="uint32" group="qemu-debug" save-restore="no"/>
+ <reg name="ss.limit" bitsize="32" type="uint32" group="qemu-debug" save-restore="no"/>
+ <reg name="fs.limit" bitsize="32" type="uint32" group="qemu-debug" save-restore="no"/>
+ <reg name="gs.limit" bitsize="32" type="uint32" group="qemu-debug" save-restore="no"/>
+</feature>
diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c
index e636ca0f4ae7..088bc930548a 100644
--- a/target/i386/gdbstub.c
+++ b/target/i386/gdbstub.c
@@ -496,6 +496,25 @@ static int i386_cpu_gdb_set_egprs(CPUState *cs, uint8_t *mem_buf, int n)
}
#endif
+#ifndef CONFIG_USER_ONLY
+static int x86_gdb_read_segment_limit(CPUState *cs, GByteArray *buf, int n)
+{
+ CPUX86State *env = cpu_env(cs);
+ static const int segments[] = { R_CS, R_DS, R_ES, R_SS, R_FS, R_GS };
+
+ if (n < 0 || n >= ARRAY_SIZE(segments)) {
+ return 0;
+ }
+ return gdb_get_reg32(buf, env->segs[segments[n]].limit);
+}
+
+static int x86_gdb_write_segment_limit(CPUState *cs, uint8_t *buf, int n)
+{
+ /* Segment caches are exposed for inspection only. Ignore writes. */
+ return 4;
+}
+#endif
+
void x86_cpu_gdb_init(CPUState *cs)
{
#ifdef TARGET_X86_64
@@ -517,4 +536,9 @@ void x86_cpu_gdb_init(CPUState *cs)
gdb_find_static_feature("i386-32bit-linux.xml"));
#endif
#endif
+#ifndef CONFIG_USER_ONLY
+ gdb_register_coprocessor(cs, x86_gdb_read_segment_limit,
+ x86_gdb_write_segment_limit,
+ gdb_find_static_feature("i386-segments.xml"));
+#endif
}
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 06/10] target/m68k: expose system registers via GDB
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
` (4 preceding siblings ...)
2026-10-12 20:34 ` [PATCH v2 05/10] target/i386: expose segment limits via GDB Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 07/10] target/sparc64: " Marc-André Lureau
` (3 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
Add an m68k-system.xml GDB feature that exposes supervisor stack
pointers (ssp, usp, isp), function code registers (sfc, dfc), MMU
registers (urp, srp, dttr0/1, ittr0/1, mmusr) as read-only system
registers through the GDB coprocessor interface.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
configs/targets/m68k-softmmu.mak | 2 +-
gdbstub/gdb-xml/m68k-system.xml | 32 +++++++++++++++++++++++++++
target/m68k/helper.c | 47 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 80 insertions(+), 1 deletion(-)
diff --git a/configs/targets/m68k-softmmu.mak b/configs/targets/m68k-softmmu.mak
index 0bc889f326e3..f3ac9b74c862 100644
--- a/configs/targets/m68k-softmmu.mak
+++ b/configs/targets/m68k-softmmu.mak
@@ -1,4 +1,4 @@
TARGET_ARCH=m68k
TARGET_BIG_ENDIAN=y
-TARGET_XML_FILES= cf-core.xml cf-fp.xml m68k-core.xml m68k-fp.xml
+TARGET_XML_FILES= cf-core.xml cf-fp.xml m68k-core.xml m68k-fp.xml m68k-system.xml
TARGET_LONG_BITS=32
diff --git a/gdbstub/gdb-xml/m68k-system.xml b/gdbstub/gdb-xml/m68k-system.xml
new file mode 100644
index 000000000000..0a91be0edc00
--- /dev/null
+++ b/gdbstub/gdb-xml/m68k-system.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!--
+ SPDX-License-Identifier: GPL-2.0-or-later
+-->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<!-- System-register values are read-only; writes are ignored. -->
+<feature name="org.qemu.gdb.m68k.system">
+ <reg name="ssp" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="usp" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="isp" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="sfc" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="dfc" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="urp" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="srp" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="dttr0" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="dttr1" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="ittr0" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="ittr1" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="mmusr" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+</feature>
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 5f91d206f596..68fa60c701a6 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -123,6 +123,48 @@ static int m68k_fpu_gdb_set_reg(CPUState *cs, uint8_t *mem_buf, int n)
return 0;
}
+#ifndef CONFIG_USER_ONLY
+static int m68k_gdb_read_system_register(CPUState *cs, GByteArray *buf, int n)
+{
+ CPUM68KState *env = cpu_env(cs);
+ static const int stacks[] = { M68K_SSP, M68K_USP, M68K_ISP };
+
+ if (n >= 0 && n < ARRAY_SIZE(stacks)) {
+ int sp = stacks[n];
+
+ return gdb_get_reg32(buf, env->sp[sp]);
+ }
+ switch (n) {
+ case 3:
+ return gdb_get_reg32(buf, env->sfc);
+ case 4:
+ return gdb_get_reg32(buf, env->dfc);
+ case 5:
+ return gdb_get_reg32(buf, env->mmu.urp);
+ case 6:
+ return gdb_get_reg32(buf, env->mmu.srp);
+ case 7:
+ return gdb_get_reg32(buf, env->mmu.ttr[M68K_DTTR0]);
+ case 8:
+ return gdb_get_reg32(buf, env->mmu.ttr[M68K_DTTR1]);
+ case 9:
+ return gdb_get_reg32(buf, env->mmu.ttr[M68K_ITTR0]);
+ case 10:
+ return gdb_get_reg32(buf, env->mmu.ttr[M68K_ITTR1]);
+ case 11:
+ return gdb_get_reg32(buf, env->mmu.mmusr);
+ default:
+ return 0;
+ }
+}
+
+static int m68k_gdb_write_system_register(CPUState *cs, uint8_t *buf, int n)
+{
+ /* These registers are exposed for inspection only. Ignore writes. */
+ return 4;
+}
+#endif
+
void m68k_cpu_init_gdb(M68kCPU *cpu)
{
CPUState *cs = CPU(cpu);
@@ -135,6 +177,11 @@ void m68k_cpu_init_gdb(M68kCPU *cpu)
gdb_register_coprocessor(cs, m68k_fpu_gdb_get_reg, m68k_fpu_gdb_set_reg,
gdb_find_static_feature("m68k-fp.xml"));
}
+#ifndef CONFIG_USER_ONLY
+ gdb_register_internal_coprocessor(cs, m68k_gdb_read_system_register,
+ m68k_gdb_write_system_register,
+ gdb_find_static_feature("m68k-system.xml"));
+#endif
/* TODO: Add [E]MAC registers. */
}
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 07/10] target/sparc64: expose system registers via GDB
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
` (5 preceding siblings ...)
2026-10-12 20:34 ` [PATCH v2 06/10] target/m68k: expose system registers " Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
2026-10-13 6:57 ` Philippe Mathieu-Daudé
2026-10-12 20:34 ` [PATCH v2 08/10] target/riscv: bypass smstateen check in debugger mode Marc-André Lureau
` (2 subsequent siblings)
9 siblings, 1 reply; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
Add a sparc64-system.xml GDB feature that exposes asi, pstate,
cansave, canrestore, otherwin, wstate, and cleanwin as read-only
system registers through the GDB coprocessor interface.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
configs/targets/sparc64-softmmu.mak | 2 +-
gdbstub/gdb-xml/sparc64-system.xml | 22 +++++++++++++++++++++
target/sparc/gdbstub.c | 38 +++++++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak
index 4e621fb8e39e..7f2789136653 100644
--- a/configs/targets/sparc64-softmmu.mak
+++ b/configs/targets/sparc64-softmmu.mak
@@ -1,7 +1,7 @@
TARGET_ARCH=sparc64
TARGET_BASE_ARCH=sparc
TARGET_BIG_ENDIAN=y
-TARGET_XML_FILES=sparc64-cpu.xml sparc64-fpu.xml sparc64-cp0.xml
+TARGET_XML_FILES=sparc64-cpu.xml sparc64-fpu.xml sparc64-cp0.xml sparc64-system.xml
TARGET_LONG_BITS=64
TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y
TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y
diff --git a/gdbstub/gdb-xml/sparc64-system.xml b/gdbstub/gdb-xml/sparc64-system.xml
new file mode 100644
index 000000000000..e00baedb467e
--- /dev/null
+++ b/gdbstub/gdb-xml/sparc64-system.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!--
+ SPDX-License-Identifier: GPL-2.0-or-later
+-->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<!-- System-register values are read-only; writes are ignored. -->
+<feature name="org.qemu.gdb.sparc.system">
+ <reg name="asi" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="pstate" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="cansave" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="canrestore" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="otherwin" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="wstate" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+ <reg name="cleanwin" bitsize="32" type="uint32"
+ group="qemu-debug" save-restore="no"/>
+</feature>
diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c
index 2874ce1b3501..efbe16f38aa9 100644
--- a/target/sparc/gdbstub.c
+++ b/target/sparc/gdbstub.c
@@ -266,6 +266,39 @@ static int sparc_cp0_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
#endif
}
+#if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
+static int sparc64_gdb_read_system_register(CPUState *cs, GByteArray *buf,
+ int n)
+{
+ CPUSPARCState *env = cpu_env(cs);
+
+ switch (n) {
+ case 0:
+ return gdb_get_reg32(buf, env->asi);
+ case 1:
+ return gdb_get_reg32(buf, env->pstate);
+ case 2:
+ return gdb_get_reg32(buf, env->cansave);
+ case 3:
+ return gdb_get_reg32(buf, env->canrestore);
+ case 4:
+ return gdb_get_reg32(buf, env->otherwin);
+ case 5:
+ return gdb_get_reg32(buf, env->wstate);
+ case 6:
+ return gdb_get_reg32(buf, env->cleanwin);
+ default:
+ return 0;
+ }
+}
+
+static int sparc64_gdb_write_system_register(CPUState *cs, uint8_t *buf, int n)
+{
+ /* These registers are exposed for inspection only. Ignore writes. */
+ return 4;
+}
+#endif
+
void sparc_cpu_register_gdb_regs(CPUState *cs)
{
#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64)
@@ -282,5 +315,10 @@ void sparc_cpu_register_gdb_regs(CPUState *cs)
gdb_register_coprocessor(cs, sparc_cp0_gdb_read_register,
sparc_cp0_gdb_write_register,
gdb_find_static_feature("sparc64-cp0.xml"));
+#ifndef CONFIG_USER_ONLY
+ gdb_register_internal_coprocessor(cs, sparc64_gdb_read_system_register,
+ sparc64_gdb_write_system_register,
+ gdb_find_static_feature("sparc64-system.xml"));
+#endif
#endif
}
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 08/10] target/riscv: bypass smstateen check in debugger mode
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
` (6 preceding siblings ...)
2026-10-12 20:34 ` [PATCH v2 07/10] target/sparc64: " Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 09/10] target/riscv: expose register aliases via GDB Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 10/10] monitor/hmp: remove legacy MonitorDef infrastructure Marc-André Lureau
9 siblings, 0 replies; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
Allow to read CSRs gated by Smstateen extension when env->debugger.
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Reviewed-by: Chao Liu <chao.liu@processmission.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
target/riscv/tcg/csr.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
index 061bc9db77b3..9da05a0507d5 100644
--- a/target/riscv/tcg/csr.c
+++ b/target/riscv/tcg/csr.c
@@ -53,7 +53,8 @@ RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit)
{
bool virt = env->virt_enabled;
- if (env->priv == PRV_M || !riscv_cpu_cfg(env)->ext_smstateen) {
+ if (env->debugger || env->priv == PRV_M ||
+ !riscv_cpu_cfg(env)->ext_smstateen) {
return RISCV_EXCP_NONE;
}
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 09/10] target/riscv: expose register aliases via GDB
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
` (7 preceding siblings ...)
2026-10-12 20:34 ` [PATCH v2 08/10] target/riscv: bypass smstateen check in debugger mode Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 10/10] monitor/hmp: remove legacy MonitorDef infrastructure Marc-André Lureau
9 siblings, 0 replies; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
The core GDB XML uses ABI names (zero, ra, sp, fp, ft0, fs0, etc.)
but users also refer to registers by their numeric names (x0-x31,
f0-f31) and the s0 alias for fp. The legacy MonitorDef mechanism
supported both via split "x0/zero" name matching.
Register a feature that maps:
- x0-x31 and s0 to the corresponding core integer registers
- x0h-x31h and their ABI aliases (zeroh, rah, ...) to env->gprh[]
- f0-f31 to the corresponding FPU registers (when FPU is present)
This preserves full HMP $register compatibility after the MonitorDef
removal.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
configs/targets/riscv32-linux-user.mak | 2 +-
configs/targets/riscv32-softmmu.mak | 2 +-
configs/targets/riscv64-bsd-user.mak | 2 +-
configs/targets/riscv64-linux-user.mak | 2 +-
configs/targets/riscv64-softmmu.mak | 2 +-
gdbstub/gdb-xml/riscv-32bit-alias.xml | 105 ++++++++++++++++++++++++++++++
gdbstub/gdb-xml/riscv-32bit-fpu-alias.xml | 40 ++++++++++++
gdbstub/gdb-xml/riscv-64bit-alias.xml | 105 ++++++++++++++++++++++++++++++
gdbstub/gdb-xml/riscv-64bit-fpu-alias.xml | 46 +++++++++++++
target/riscv/gdbstub.c | 47 +++++++++++++
10 files changed, 348 insertions(+), 5 deletions(-)
diff --git a/configs/targets/riscv32-linux-user.mak b/configs/targets/riscv32-linux-user.mak
index d88fdf5e1b2f..8ee14fa14383 100644
--- a/configs/targets/riscv32-linux-user.mak
+++ b/configs/targets/riscv32-linux-user.mak
@@ -1,7 +1,7 @@
TARGET_ARCH=riscv32
TARGET_BASE_ARCH=riscv
TARGET_ABI_DIR=riscv
-TARGET_XML_FILES= riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-32bit-virtual.xml
+TARGET_XML_FILES= riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-32bit-virtual.xml riscv-32bit-alias.xml riscv-32bit-fpu-alias.xml riscv-64bit-fpu-alias.xml
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
TARGET_SYSTBL_ABI=32
diff --git a/configs/targets/riscv32-softmmu.mak b/configs/targets/riscv32-softmmu.mak
index 5d5016d00832..c717940344f0 100644
--- a/configs/targets/riscv32-softmmu.mak
+++ b/configs/targets/riscv32-softmmu.mak
@@ -1,6 +1,6 @@
TARGET_ARCH=riscv32
TARGET_BASE_ARCH=riscv
-TARGET_XML_FILES= riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-32bit-virtual.xml
+TARGET_XML_FILES= riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-32bit-virtual.xml riscv-32bit-alias.xml riscv-32bit-fpu-alias.xml riscv-64bit-fpu-alias.xml
# needed by boot.c
TARGET_NEED_FDT=y
TARGET_LONG_BITS=32
diff --git a/configs/targets/riscv64-bsd-user.mak b/configs/targets/riscv64-bsd-user.mak
index 5b4e138099fc..1d37e018c871 100644
--- a/configs/targets/riscv64-bsd-user.mak
+++ b/configs/targets/riscv64-bsd-user.mak
@@ -1,6 +1,6 @@
TARGET_ARCH=riscv64
TARGET_BASE_ARCH=riscv
TARGET_ABI_DIR=riscv
-TARGET_XML_FILES= riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-virtual.xml
+TARGET_XML_FILES= riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-virtual.xml riscv-64bit-alias.xml riscv-32bit-fpu-alias.xml riscv-64bit-fpu-alias.xml
TARGET_LONG_BITS=64
TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y
diff --git a/configs/targets/riscv64-linux-user.mak b/configs/targets/riscv64-linux-user.mak
index 35621520c560..564d15aaca8c 100644
--- a/configs/targets/riscv64-linux-user.mak
+++ b/configs/targets/riscv64-linux-user.mak
@@ -1,7 +1,7 @@
TARGET_ARCH=riscv64
TARGET_BASE_ARCH=riscv
TARGET_ABI_DIR=riscv
-TARGET_XML_FILES= riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-virtual.xml
+TARGET_XML_FILES= riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-virtual.xml riscv-64bit-alias.xml riscv-32bit-fpu-alias.xml riscv-64bit-fpu-alias.xml
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
TARGET_SYSTBL_ABI=64
diff --git a/configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak
index a10dc03c04c8..28bfcdc575fb 100644
--- a/configs/targets/riscv64-softmmu.mak
+++ b/configs/targets/riscv64-softmmu.mak
@@ -1,7 +1,7 @@
TARGET_ARCH=riscv64
TARGET_BASE_ARCH=riscv
TARGET_KVM_HAVE_GUEST_DEBUG=y
-TARGET_XML_FILES= riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-virtual.xml riscv-32bit-cpu.xml riscv-32bit-virtual.xml
+TARGET_XML_FILES= riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-virtual.xml riscv-32bit-cpu.xml riscv-32bit-virtual.xml riscv-32bit-alias.xml riscv-64bit-alias.xml riscv-32bit-fpu-alias.xml riscv-64bit-fpu-alias.xml
# needed by boot.c
TARGET_NEED_FDT=y
TARGET_LONG_BITS=64
diff --git a/gdbstub/gdb-xml/riscv-32bit-alias.xml b/gdbstub/gdb-xml/riscv-32bit-alias.xml
new file mode 100644
index 000000000000..a00a86ebdf95
--- /dev/null
+++ b/gdbstub/gdb-xml/riscv-32bit-alias.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<!--
+ SPDX-License-Identifier: GPL-2.0-or-later
+-->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.qemu.gdb.riscv.alias">
+ <reg name="x0" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x1" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x2" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x3" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x4" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x5" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x6" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x7" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x8" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x9" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x10" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x11" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x12" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x13" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x14" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x15" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x16" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x17" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x18" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x19" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x20" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x21" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x22" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x23" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x24" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x25" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x26" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x27" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x28" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x29" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x30" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x31" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s0" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x0h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="zeroh" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x1h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="rah" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x2h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="sph" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x3h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="gph" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x4h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="tph" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x5h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="t0h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x6h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="t1h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x7h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="t2h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x8h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s0h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x9h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s1h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x10h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="a0h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x11h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="a1h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x12h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="a2h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x13h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="a3h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x14h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="a4h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x15h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="a5h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x16h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="a6h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x17h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="a7h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x18h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s2h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x19h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s3h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x20h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s4h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x21h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s5h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x22h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s6h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x23h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s7h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x24h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s8h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x25h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s9h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x26h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s10h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x27h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="s11h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x28h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="t3h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x29h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="t4h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x30h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="t5h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="x31h" bitsize="32" type="int" group="qemu-debug"/>
+ <reg name="t6h" bitsize="32" type="int" group="qemu-debug"/>
+</feature>
diff --git a/gdbstub/gdb-xml/riscv-32bit-fpu-alias.xml b/gdbstub/gdb-xml/riscv-32bit-fpu-alias.xml
new file mode 100644
index 000000000000..3935f72ea289
--- /dev/null
+++ b/gdbstub/gdb-xml/riscv-32bit-fpu-alias.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!--
+ SPDX-License-Identifier: GPL-2.0-or-later
+-->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.qemu.gdb.riscv.fpu-alias">
+ <reg name="f0" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f1" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f2" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f3" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f4" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f5" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f6" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f7" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f8" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f9" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f10" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f11" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f12" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f13" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f14" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f15" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f16" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f17" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f18" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f19" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f20" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f21" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f22" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f23" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f24" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f25" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f26" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f27" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f28" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f29" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f30" bitsize="32" type="ieee_single" group="qemu-debug"/>
+ <reg name="f31" bitsize="32" type="ieee_single" group="qemu-debug"/>
+</feature>
diff --git a/gdbstub/gdb-xml/riscv-64bit-alias.xml b/gdbstub/gdb-xml/riscv-64bit-alias.xml
new file mode 100644
index 000000000000..28781b230226
--- /dev/null
+++ b/gdbstub/gdb-xml/riscv-64bit-alias.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<!--
+ SPDX-License-Identifier: GPL-2.0-or-later
+-->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.qemu.gdb.riscv.alias">
+ <reg name="x0" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x1" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x2" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x3" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x4" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x5" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x6" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x7" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x8" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x9" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x10" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x11" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x12" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x13" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x14" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x15" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x16" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x17" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x18" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x19" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x20" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x21" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x22" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x23" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x24" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x25" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x26" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x27" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x28" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x29" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x30" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x31" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s0" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x0h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="zeroh" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x1h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="rah" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x2h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="sph" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x3h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="gph" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x4h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="tph" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x5h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="t0h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x6h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="t1h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x7h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="t2h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x8h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s0h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x9h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s1h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x10h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="a0h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x11h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="a1h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x12h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="a2h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x13h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="a3h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x14h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="a4h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x15h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="a5h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x16h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="a6h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x17h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="a7h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x18h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s2h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x19h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s3h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x20h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s4h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x21h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s5h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x22h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s6h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x23h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s7h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x24h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s8h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x25h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s9h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x26h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s10h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x27h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="s11h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x28h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="t3h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x29h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="t4h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x30h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="t5h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="x31h" bitsize="64" type="int" group="qemu-debug"/>
+ <reg name="t6h" bitsize="64" type="int" group="qemu-debug"/>
+</feature>
diff --git a/gdbstub/gdb-xml/riscv-64bit-fpu-alias.xml b/gdbstub/gdb-xml/riscv-64bit-fpu-alias.xml
new file mode 100644
index 000000000000..13e6b341a0f8
--- /dev/null
+++ b/gdbstub/gdb-xml/riscv-64bit-fpu-alias.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!--
+ SPDX-License-Identifier: GPL-2.0-or-later
+-->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.qemu.gdb.riscv.fpu-alias">
+
+ <union id="riscv_double">
+ <field name="float" type="ieee_single"/>
+ <field name="double" type="ieee_double"/>
+ </union>
+
+ <reg name="f0" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f1" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f2" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f3" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f4" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f5" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f6" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f7" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f8" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f9" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f10" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f11" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f12" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f13" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f14" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f15" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f16" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f17" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f18" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f19" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f20" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f21" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f22" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f23" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f24" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f25" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f26" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f27" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f28" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f29" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f30" bitsize="64" type="riscv_double" group="qemu-debug"/>
+ <reg name="f31" bitsize="64" type="riscv_double" group="qemu-debug"/>
+</feature>
diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
index f279f4ab2461..cc7f17491efb 100644
--- a/target/riscv/gdbstub.c
+++ b/target/riscv/gdbstub.c
@@ -336,10 +336,42 @@ static GDBFeature *ricsv_gen_dynamic_vector_feature(CPUState *cs, int base_reg)
return &cpu->dyn_vreg_feature;
}
+/* x0-x31, s0, then pairs of numeric and ABI high-half aliases. */
+static int riscv_gdb_get_alias(CPUState *cs, GByteArray *buf, int n)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ uint64_t val;
+
+ if (n < 33) {
+ return riscv_cpu_gdb_read_register(cs, buf, n == 32 ? 8 : n);
+ }
+
+ val = cpu->env.gprh[(n - 33) / 2];
+ if (riscv_cpu_is_32bit(cpu)) {
+ return gdb_get_reg32(buf, val);
+ }
+ return gdb_get_reg64(buf, val);
+}
+
+static int riscv_gdb_set_alias(CPUState *cs, uint8_t *buf, int n)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ const unsigned regsz = riscv_cpu_is_32bit(cpu) ? 4 : 8;
+
+ if (n < 33) {
+ return riscv_cpu_gdb_write_register(cs, buf, n == 32 ? 8 : n);
+ }
+
+ env->gprh[(n - 33) / 2] = ldn(env, buf, regsz);
+ return regsz;
+}
+
void riscv_cpu_register_gdb_regs_for_features(CPUState *cs)
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
+
if (env->misa_ext & RVD) {
gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu,
gdb_find_static_feature("riscv-64bit-fpu.xml"));
@@ -380,4 +412,19 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs)
feature);
}
#endif
+
+ gdb_register_internal_coprocessor(cs, riscv_gdb_get_alias, riscv_gdb_set_alias,
+ gdb_find_static_feature(riscv_cpu_is_32bit(cpu) ?
+ "riscv-32bit-alias.xml" :
+ "riscv-64bit-alias.xml"));
+
+ if (env->misa_ext & RVD) {
+ gdb_register_internal_coprocessor(cs, riscv_gdb_get_fpu,
+ riscv_gdb_set_fpu,
+ gdb_find_static_feature("riscv-64bit-fpu-alias.xml"));
+ } else if (env->misa_ext & RVF) {
+ gdb_register_internal_coprocessor(cs, riscv_gdb_get_fpu,
+ riscv_gdb_set_fpu,
+ gdb_find_static_feature("riscv-32bit-fpu-alias.xml"));
+ }
}
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 10/10] monitor/hmp: remove legacy MonitorDef infrastructure
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
` (8 preceding siblings ...)
2026-10-12 20:34 ` [PATCH v2 09/10] target/riscv: expose register aliases via GDB Marc-André Lureau
@ 2026-10-12 20:34 ` Marc-André Lureau
9 siblings, 0 replies; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-12 20:34 UTC (permalink / raw)
To: qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée,
Philippe Mathieu-Daudé, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans, Marc-André Lureau
Now that all targets expose their system registers via internal GDB
register descriptions (i386 segment limits, m68k, sparc64, RISC-V
aliases), the legacy MonitorDef table and
SysemuCPUOps::monitor_get_register callback are no longer needed.
Remove MonitorDef struct, the per-target monitor_defs arrays (i386,
m68k, sparc64), riscv_monitor_get_register_legacy(), and the
get_monitor_def() fallback in expr_unary(). HMP $register expressions
now use gdb_get_register_list() exclusively.
Known behavioural changes:
- x86: $pc no longer works; use $eip or $rip instead. The old
MonitorDef provided a synthetic $pc alias, but a generic $pc is
misleading on x86 and the new GDB-based lookup does not synthesize
it.
- RISC-V: CSR lookup is now case-sensitive (consistent with all other
targets). Vector registers report "unknown register" instead of the
target-specific "Unable to print the value of vector vreg" message.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
include/hw/core/sysemu-cpu-ops.h | 16 -----
include/monitor/hmp.h | 6 --
include/qemu/typedefs.h | 1 -
monitor/hmp.c | 44 +-----------
target/i386/cpu.c | 32 ---------
target/m68k/cpu.c | 22 ------
target/riscv/cpu.c | 1 -
target/riscv/internals.h | 3 -
target/riscv/monitor.c | 140 ---------------------------------------
target/sparc/cpu.c | 19 ------
10 files changed, 2 insertions(+), 282 deletions(-)
diff --git a/include/hw/core/sysemu-cpu-ops.h b/include/hw/core/sysemu-cpu-ops.h
index d125540262d2..daac2a3b13b1 100644
--- a/include/hw/core/sysemu-cpu-ops.h
+++ b/include/hw/core/sysemu-cpu-ops.h
@@ -103,22 +103,6 @@ typedef struct SysemuCPUOps {
*/
bool (*internal_is_big_endian)(CPUState *cpu);
- /**
- * @monitor_get_register: Callback to fill @pval with register @name value.
- * This field is legacy, use @gdb_core_xml_file
- * to dump registers instead.
- * Returns: 0 on success or negative errno on failure.
- */
- int (*monitor_get_register)(CPUState *cs, const char *name, int64_t *pval);
-
-#ifdef CONFIG_HMP
- /**
- * @monitor_defs: Array of MonitorDef entries. This field is legacy,
- * use @gdb_core_xml_file to dump registers instead.
- */
- const MonitorDef *monitor_defs;
-#endif
-
/**
* @legacy_vmsd: Legacy state for migration.
* Do not use in new targets, use #DeviceClass::vmsd instead.
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index ef53e87f7608..950dd931d452 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -31,12 +31,6 @@ MonitorHMP *monitor_cur_hmp(void);
g_assert_not_reached(); \
}
-struct MonitorDef {
- const char *name;
- int offset;
- int64_t (*get_value)(MonitorHMP *hmp, const MonitorDef *md, int offset);
-};
-
void monitor_new_hmp(const char *id, const char *chardev_id,
bool use_readline, Error **errp);
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 2344c92182ea..5580e1fc4aba 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -72,7 +72,6 @@ typedef struct MemoryRegionSection MemoryRegionSection;
typedef struct MigrationIncomingState MigrationIncomingState;
typedef struct MigrationState MigrationState;
typedef struct Monitor Monitor;
-typedef struct MonitorDef MonitorDef;
typedef struct MSIMessage MSIMessage;
typedef struct NetClientState NetClientState;
typedef struct NetFilterState NetFilterState;
diff --git a/monitor/hmp.c b/monitor/hmp.c
index 10e19e417aba..ab8465ebb0d7 100644
--- a/monitor/hmp.c
+++ b/monitor/hmp.c
@@ -24,8 +24,8 @@
#include "qemu/osdep.h"
#include <dirent.h>
+#include "hw/core/cpu.h"
#include "hw/core/qdev.h"
-#include "hw/core/sysemu-cpu-ops.h"
#include "monitor-internal.h"
#include "monitor-hmp-internal.h"
#include "monitor/hmp.h"
@@ -443,8 +443,6 @@ static bool gdb_get_register(MonitorHMP *hmp, int64_t *pval, const char *name)
static const char *pch;
static sigjmp_buf expr_env;
-static int get_monitor_def(MonitorHMP *mon, int64_t *pval, const char *name);
-
static G_NORETURN G_GNUC_PRINTF(2, 3)
void expr_error(MonitorHMP *mon, const char *fmt, ...)
{
@@ -526,8 +524,7 @@ static int64_t expr_unary(MonitorHMP *mon)
pch++;
}
*q = 0;
- if (!gdb_get_register(mon, ®, buf)
- && get_monitor_def(mon, ®, buf) < 0) {
+ if (!gdb_get_register(mon, ®, buf)) {
expr_error(mon, "unknown register");
}
n = reg;
@@ -1719,43 +1716,6 @@ void monitor_register_hmp_info_hrt(const char *name,
g_assert_not_reached();
}
-/*
- * Set @pval to the value in the register identified by @name.
- * return 0 if OK, -1 if not found
- */
-static int get_monitor_def(MonitorHMP *hmp, int64_t *pval, const char *name)
-{
- CPUState *cs = monitor_hmp_get_cpu(hmp);
- const MonitorDef *md = NULL;
- void *ptr;
-
- if (cs == NULL) {
- return -1;
- }
- md = cs->cc->sysemu_ops->monitor_defs;
- if (md == NULL) {
- return -1;
- }
-
- for (; md->name != NULL; md++) {
- if (hmp_compare_cmd(name, md->name)) {
- if (md->get_value) {
- *pval = md->get_value(hmp, md, md->offset);
- } else {
- CPUArchState *env = monitor_hmp_get_cpu_env(hmp);
- ptr = (uint8_t *)env + md->offset;
- *pval = *(int32_t *)ptr;
- }
- return 0;
- }
- }
-
- if (!cs->cc->sysemu_ops->monitor_get_register) {
- return -1;
- }
- return cs->cc->sysemu_ops->monitor_get_register(cs, name, pval);
-}
-
int monitor_hmp_vprintf(MonitorHMP *hmp, const char *fmt, va_list ap)
{
g_autofree char *buf = g_strdup_vprintf(fmt, ap);
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 74eebeabd7a7..191837a586d4 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -42,7 +42,6 @@
#include "exec/watchpoint.h"
#ifndef CONFIG_USER_ONLY
#include "confidential-guest.h"
-#include "monitor/hmp.h"
#include "system/reset.h"
#include "qapi/qapi-commands-machine.h"
#include "system/address-spaces.h"
@@ -10878,34 +10877,6 @@ static const Property x86_cpu_properties[] = {
#ifndef CONFIG_USER_ONLY
-#ifdef CONFIG_HMP
-static int64_t monitor_get_pc(MonitorHMP *hmp, const struct MonitorDef *md,
- int offset)
-{
- CPUArchState *env = monitor_hmp_get_cpu_env(hmp);
- int64_t ret = env->eip + env->segs[R_CS].base;
-
- if (!(env->hflags & HF_CS64_MASK)) {
- ret = (int32_t)ret;
- }
- return ret;
-}
-
-static const MonitorDef x86_monitor_defs[] = {
-#define SEG(name, seg) \
- { name ".limit", offsetof(CPUX86State, segs[seg].limit) },
- SEG("cs", R_CS)
- SEG("ds", R_DS)
- SEG("es", R_ES)
- SEG("ss", R_SS)
- SEG("fs", R_FS)
- SEG("gs", R_GS)
- { "pc", 0, monitor_get_pc, },
- { NULL },
-#undef SEG
-};
-#endif
-
#include "hw/core/sysemu-cpu-ops.h"
static const struct SysemuCPUOps i386_sysemu_ops = {
@@ -10919,9 +10890,6 @@ static const struct SysemuCPUOps i386_sysemu_ops = {
.write_elf64_note = x86_cpu_write_elf64_note,
.write_elf32_qemunote = x86_cpu_write_elf32_qemunote,
.write_elf64_qemunote = x86_cpu_write_elf64_qemunote,
-#ifdef CONFIG_HMP
- .monitor_defs = x86_monitor_defs,
-#endif
.legacy_vmsd = &vmstate_x86_cpu,
};
#endif
diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c
index 9b52ad5fc234..ad7303305848 100644
--- a/target/m68k/cpu.c
+++ b/target/m68k/cpu.c
@@ -25,7 +25,6 @@
#include "disas/capstone.h"
#ifndef CONFIG_USER_ONLY
#include "migration/vmstate.h"
-#include "monitor/hmp.h"
#endif
#include "cpu.h"
@@ -666,32 +665,11 @@ static const VMStateDescription vmstate_m68k_cpu = {
},
};
-#ifdef CONFIG_HMP
-static const MonitorDef m68k_monitor_defs[] = {
- { "ssp", offsetof(CPUM68KState, sp[0]) },
- { "usp", offsetof(CPUM68KState, sp[1]) },
- { "isp", offsetof(CPUM68KState, sp[2]) },
- { "sfc", offsetof(CPUM68KState, sfc) },
- { "dfc", offsetof(CPUM68KState, dfc) },
- { "urp", offsetof(CPUM68KState, mmu.urp) },
- { "srp", offsetof(CPUM68KState, mmu.srp) },
- { "dttr0", offsetof(CPUM68KState, mmu.ttr[M68K_DTTR0]) },
- { "dttr1", offsetof(CPUM68KState, mmu.ttr[M68K_DTTR1]) },
- { "ittr0", offsetof(CPUM68KState, mmu.ttr[M68K_ITTR0]) },
- { "ittr1", offsetof(CPUM68KState, mmu.ttr[M68K_ITTR1]) },
- { "mmusr", offsetof(CPUM68KState, mmu.mmusr) },
- { NULL },
-};
-#endif
-
#include "hw/core/sysemu-cpu-ops.h"
static const struct SysemuCPUOps m68k_sysemu_ops = {
.has_work = m68k_cpu_has_work,
.get_phys_addr_debug = m68k_cpu_get_phys_addr_debug,
-#ifdef CONFIG_HMP
- .monitor_defs = m68k_monitor_defs,
-#endif
};
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index da4c1a090b9b..7c4cadfc44b2 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -3018,7 +3018,6 @@ static const struct SysemuCPUOps riscv_sysemu_ops = {
.legacy_vmsd = &vmstate_riscv_cpu,
#ifdef CONFIG_TCG
.translate_for_debug = riscv_cpu_translate_for_debug,
- .monitor_get_register = riscv_monitor_get_register_legacy,
#endif
};
#endif
diff --git a/target/riscv/internals.h b/target/riscv/internals.h
index 5d84e4de960a..09aa2701197e 100644
--- a/target/riscv/internals.h
+++ b/target/riscv/internals.h
@@ -263,7 +263,4 @@ static inline int insn_len(uint16_t first_word)
return (first_word & 3) == 3 ? 4 : 2;
}
-int riscv_monitor_get_register_legacy(CPUState *cs, const char *name,
- int64_t *pval);
-
#endif
diff --git a/target/riscv/monitor.c b/target/riscv/monitor.c
index 7e61ae2a4717..20f3ba57ae57 100644
--- a/target/riscv/monitor.c
+++ b/target/riscv/monitor.c
@@ -19,10 +19,7 @@
*/
#include "qemu/osdep.h"
-#include "qemu/ctype.h"
-#include "qemu/qemu-print.h"
#include "cpu.h"
-#include "target/riscv/tcg/csr.h"
#include "cpu_bits.h"
#include "monitor/monitor.h"
#include "monitor/hmp.h"
@@ -247,140 +244,3 @@ void hmp_info_mem(MonitorHMP *hmp, const QDict *qdict)
mem_info_svxx(hmp, env);
}
#endif /* CONFIG_HMP */
-
-#ifdef CONFIG_TCG
-static bool reg_is_ulong_integer(CPURISCVState *env, const char *name,
- target_ulong *val, bool is_gprh)
-{
- const char * const *reg_names;
- uint64_t *vals;
-
- if (is_gprh) {
- reg_names = riscv_int_regnamesh;
- vals = env->gprh;
- } else {
- reg_names = riscv_int_regnames;
- vals = env->gpr;
- }
-
- for (int i = 0; i < 32; i++) {
- g_auto(GStrv) reg_name = g_strsplit(reg_names[i], "/", 2);
-
- g_assert(reg_name[0]);
- g_assert(reg_name[1]);
-
- if (g_ascii_strcasecmp(reg_name[0], name) == 0 ||
- g_ascii_strcasecmp(reg_name[1], name) == 0) {
- *val = vals[i];
- return true;
- }
- }
-
- return false;
-}
-
-static bool reg_is_u64_fpu(CPURISCVState *env, const char *name, uint64_t *val)
-{
- if (qemu_tolower(name[0]) != 'f') {
- return false;
- }
-
- for (int i = 0; i < 32; i++) {
- g_auto(GStrv) reg_name = g_strsplit(riscv_fpr_regnames[i], "/", 2);
-
- g_assert(reg_name[0]);
- g_assert(reg_name[1]);
-
- if (g_ascii_strcasecmp(reg_name[0], name) == 0 ||
- g_ascii_strcasecmp(reg_name[1], name) == 0) {
- *val = env->fpr[i];
- return true;
- }
- }
-
- return false;
-}
-
-static bool reg_is_vreg(const char *name)
-{
- if (qemu_tolower(name[0]) != 'v' || strlen(name) > 3) {
- return false;
- }
-
- for (int i = 0; i < 32; i++) {
- if (g_ascii_strcasecmp(name, riscv_rvv_regnames[i]) == 0) {
- return true;
- }
- }
-
- return false;
-}
-
-int riscv_monitor_get_register_legacy(CPUState *cs, const char *name,
- int64_t *pval)
-{
- RISCVCPU *hart = RISCV_CPU(cs);
- CPURISCVState *env = cpu_env(cs);
- target_ulong val = 0;
- uint64_t val64 = 0;
- int i;
-
- if (reg_is_ulong_integer(env, name, &val, false) ||
- reg_is_ulong_integer(env, name, &val, true)) {
- *pval = riscv_cpu_is_32bit(hart) ? (int32_t)val : val;
- return 0;
- }
-
- if (reg_is_u64_fpu(env, name, &val64)) {
- *pval = val64;
- return 0;
- }
-
- if (reg_is_vreg(name)) {
- if (!riscv_cpu_cfg(env)->ext_zve32x) {
- return -EINVAL;
- }
-
- qemu_printf("Unable to print the value of vector "
- "vreg '%s' from this API\n", name);
-
- /*
- * We're returning 0 because returning -EINVAL triggers
- * an 'unknown register' message in exp_unary() later,
- * which feels ankward after our own error message.
- */
- *pval = 0;
- return 0;
- }
-
- for (i = 0; i < ARRAY_SIZE(csr_ops); i++) {
- RISCVException res;
- int csrno = i;
-
- /*
- * Early skip when possible since we're going
- * through a lot of NULL entries.
- */
- if (csr_ops[csrno].predicate == NULL) {
- continue;
- }
-
- if (g_ascii_strcasecmp(csr_ops[csrno].name, name) != 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) {
- *pval = riscv_cpu_is_32bit(hart) ? (int32_t)val : val;
- return 0;
- }
- }
-
- return -EINVAL;
-}
-#endif
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
index ae9bdca9df82..89dfb091ca87 100644
--- a/target/sparc/cpu.c
+++ b/target/sparc/cpu.c
@@ -995,31 +995,12 @@ static const Property sparc_cpu_properties[] = {
#ifndef CONFIG_USER_ONLY
-#ifdef TARGET_SPARC64
-#include "monitor/hmp.h"
-#ifdef CONFIG_HMP
-static const MonitorDef sparc64_monitor_defs[] = {
- { "asi", offsetof(CPUSPARCState, asi) },
- { "pstate", offsetof(CPUSPARCState, pstate) },
- { "cansave", offsetof(CPUSPARCState, cansave) },
- { "canrestore", offsetof(CPUSPARCState, canrestore) },
- { "otherwin", offsetof(CPUSPARCState, otherwin) },
- { "wstate", offsetof(CPUSPARCState, wstate) },
- { "cleanwin", offsetof(CPUSPARCState, cleanwin) },
- { NULL },
-};
-#endif
-#endif
-
#include "hw/core/sysemu-cpu-ops.h"
static const struct SysemuCPUOps sparc_sysemu_ops = {
.has_work = sparc_cpu_has_work,
.get_phys_addr_debug = sparc_cpu_get_phys_addr_debug,
.legacy_vmsd = &vmstate_sparc_cpu,
-#if defined(TARGET_SPARC64) && defined(CONFIG_HMP)
- .monitor_defs = sparc64_monitor_defs,
-#endif
};
#endif
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v2 07/10] target/sparc64: expose system registers via GDB
2026-10-12 20:34 ` [PATCH v2 07/10] target/sparc64: " Marc-André Lureau
@ 2026-10-13 6:57 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 16+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-10-13 6:57 UTC (permalink / raw)
To: Marc-André Lureau, qemu-devel
Cc: Dr. David Alan Gilbert, Alex Bennée, Zhao Liu, Peter Maydell,
Chinmay Rath, Nicholas Piggin, Glenn Miles, Harsh Prateek Bora,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Filippov,
qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini, Laurent Vivier,
Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans
On 12/10/26 22:34, Marc-André Lureau wrote:
> Add a sparc64-system.xml GDB feature that exposes asi, pstate,
> cansave, canrestore, otherwin, wstate, and cleanwin as read-only
> system registers through the GDB coprocessor interface.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> configs/targets/sparc64-softmmu.mak | 2 +-
> gdbstub/gdb-xml/sparc64-system.xml | 22 +++++++++++++++++++++
> target/sparc/gdbstub.c | 38 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 61 insertions(+), 1 deletion(-)
>
> diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak
> index 4e621fb8e39e..7f2789136653 100644
> --- a/configs/targets/sparc64-softmmu.mak
> +++ b/configs/targets/sparc64-softmmu.mak
> @@ -1,7 +1,7 @@
> TARGET_ARCH=sparc64
> TARGET_BASE_ARCH=sparc
> TARGET_BIG_ENDIAN=y
> -TARGET_XML_FILES=sparc64-cpu.xml sparc64-fpu.xml sparc64-cp0.xml
> +TARGET_XML_FILES=sparc64-cpu.xml sparc64-fpu.xml sparc64-cp0.xml sparc64-system.xml
> TARGET_LONG_BITS=64
> TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y
> TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y
> diff --git a/gdbstub/gdb-xml/sparc64-system.xml b/gdbstub/gdb-xml/sparc64-system.xml
> new file mode 100644
> index 000000000000..e00baedb467e
> --- /dev/null
> +++ b/gdbstub/gdb-xml/sparc64-system.xml
> @@ -0,0 +1,22 @@
> +<?xml version="1.0"?>
> +<!--
> + SPDX-License-Identifier: GPL-2.0-or-later
> +-->
> +<!DOCTYPE feature SYSTEM "gdb-target.dtd">
> +<!-- System-register values are read-only; writes are ignored. -->
> +<feature name="org.qemu.gdb.sparc.system">
> + <reg name="asi" bitsize="32" type="uint32"
> + group="qemu-debug" save-restore="no"/>
> + <reg name="pstate" bitsize="32" type="uint32"
> + group="qemu-debug" save-restore="no"/>
> + <reg name="cansave" bitsize="32" type="uint32"
> + group="qemu-debug" save-restore="no"/>
> + <reg name="canrestore" bitsize="32" type="uint32"
> + group="qemu-debug" save-restore="no"/>
> + <reg name="otherwin" bitsize="32" type="uint32"
> + group="qemu-debug" save-restore="no"/>
> + <reg name="wstate" bitsize="32" type="uint32"
> + group="qemu-debug" save-restore="no"/>
> + <reg name="cleanwin" bitsize="32" type="uint32"
> + group="qemu-debug" save-restore="no"/>
> +</feature>
> diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c
> index 2874ce1b3501..efbe16f38aa9 100644
> --- a/target/sparc/gdbstub.c
> +++ b/target/sparc/gdbstub.c
> @@ -266,6 +266,39 @@ static int sparc_cp0_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
> #endif
> }
>
> +#if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
> +static int sparc64_gdb_read_system_register(CPUState *cs, GByteArray *buf,
> + int n)
> +{
> + CPUSPARCState *env = cpu_env(cs);
> +
> + switch (n) {
> + case 0:
> + return gdb_get_reg32(buf, env->asi);
> + case 1:
> + return gdb_get_reg32(buf, env->pstate);
> + case 2:
> + return gdb_get_reg32(buf, env->cansave);
> + case 3:
> + return gdb_get_reg32(buf, env->canrestore);
> + case 4:
> + return gdb_get_reg32(buf, env->otherwin);
> + case 5:
> + return gdb_get_reg32(buf, env->wstate);
> + case 6:
> + return gdb_get_reg32(buf, env->cleanwin);
> + default:
> + return 0;
> + }
> +}
Why not remove the sparc64_monitor_defs[] array at the same time?
If doable that would ease logical review.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 04/10] gdbstub: add gdb_register_internal_coprocessor()
2026-10-12 20:34 ` [PATCH v2 04/10] gdbstub: add gdb_register_internal_coprocessor() Marc-André Lureau
@ 2026-10-13 8:42 ` Alex Bennée
2026-10-13 13:00 ` Marc-André Lureau
0 siblings, 1 reply; 16+ messages in thread
From: Alex Bennée @ 2026-10-13 8:42 UTC (permalink / raw)
To: Marc-André Lureau
Cc: qemu-devel, Dr. David Alan Gilbert, Philippe Mathieu-Daudé,
Zhao Liu, Peter Maydell, Chinmay Rath, Nicholas Piggin,
Glenn Miles, Harsh Prateek Bora, Palmer Dabbelt, Alistair Francis,
Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei, Chao Liu,
Max Filippov, qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini,
Laurent Vivier, Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans
Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> Add support for registering coprocessor features that are only visible
> to QEMU's internal APIs (HMP monitor $register expressions, plugins) but
> hidden from GDB's target description XML.
>
> When a feature is registered as internal:
> - It is excluded from the target.xml sent to GDB
> - Its XML is not served via qXfer:features:read
> - 'p'/'P' packets for internal register numbers return E14
>
> This is useful for exposing system registers to the HMP monitor without
> affecting GDB's register namespace. Exposing aliases or synthetic
> registers through GDB's target description can cause problems: name
> collisions with GDB's pseudo-registers, independent register caching,
> and missing DWARF unwind rules.
Hmm are they *that* useful for HMP then?
The front-ends do limit what they export to gdb (see for example
ARM_CP_NO_GDB) generally because reading them causes side-effects. Those
restrictions are also a reason to limit them from the HMP interface.
>
> Internal features must be registered after all GDB-visible features to
> keep remote register numbering consistent. This ordering is enforced
> with an assertion.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> gdbstub/gdbstub.c | 71 ++++++++++++++++++++++++++++++++++++++++++++------
> include/exec/gdbstub.h | 21 +++++++++++++++
> 2 files changed, 84 insertions(+), 8 deletions(-)
>
> diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
> index 912e31b66744..ac0f76d62333 100644
> --- a/gdbstub/gdbstub.c
> +++ b/gdbstub/gdbstub.c
> @@ -54,6 +54,7 @@ typedef struct GDBRegisterState {
> gdb_get_reg_cb get_reg;
> gdb_set_reg_cb set_reg;
> const GDBFeature *feature;
> + bool internal; /* hidden from GDB, accessible via API/monitor */
> } GDBRegisterState;
>
> GDBState gdbserver_state;
> @@ -385,6 +386,11 @@ static const char *get_feature_xml(const char *p, const char **newp,
> }
> for (guint i = 0; i < cpu->gdb_regs->len; i++) {
> r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
> + /* The core target.xml feature must never be internal. */
> + g_assert(i > 0 || !r->internal);
> + if (r->internal) {
> + continue;
> + }
> g_ptr_array_add(
> xml,
> g_markup_printf_escaped("<xi:include href=\"%s\"/>",
> @@ -400,6 +406,9 @@ static const char *get_feature_xml(const char *p, const char **newp,
> /* Is it one of the features? */
> for (guint i = 0; i < cpu->gdb_regs->len; i++) {
> r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
> + if (r->internal) {
> + continue;
> + }
> if (strncmp(p, r->feature->xmlname, len) == 0) {
> return r->feature->xml;
> }
> @@ -563,13 +572,14 @@ int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg)
>
> static void gdb_register_feature(CPUState *cpu, int base_reg,
> gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
> - const GDBFeature *feature)
> + const GDBFeature *feature, bool internal)
> {
> GDBRegisterState s = {
> .base_reg = base_reg,
> .get_reg = get_reg,
> .set_reg = set_reg,
> - .feature = feature
> + .feature = feature,
> + .internal = internal
> };
>
> trace_gdbxml_register_feature(feature->name, feature->xmlname,
> @@ -605,7 +615,7 @@ void gdb_init_cpu(CPUState *cpu)
> assert(feature->base_reg == 0);
> gdb_register_feature(cpu, 0,
> cc->gdb_read_register, cc->gdb_write_register,
> - feature);
> + feature, false);
> cpu->gdb_next_base_reg = cpu->gdb_num_g_regs = feature->num_regs;
> } else {
> cpu->gdb_next_base_reg = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
> @@ -616,20 +626,26 @@ void gdb_init_cpu(CPUState *cpu)
> cc->gdb_num_core_regs);
> }
>
> -void gdb_register_coprocessor(CPUState *cpu,
> - gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
> - const GDBFeature *feature)
> +static void do_gdb_register_coprocessor(CPUState *cpu,
> + gdb_get_reg_cb get_reg,
> + gdb_set_reg_cb set_reg,
> + const GDBFeature *feature,
> + bool internal)
> {
> GDBRegisterState *s;
> guint i;
> int base_reg = cpu->gdb_next_base_reg;
>
> for (i = 0; i < cpu->gdb_regs->len; i++) {
> - /* Check for duplicates. */
> s = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
> + /* Check for duplicates. */
> if (s->feature == feature) {
> return;
> }
> + /* GDB-visible features must not follow internal ones. */
> + if (!internal) {
> + g_assert(!s->internal);
> + }
> }
>
> if (base_reg < feature->base_reg) {
> @@ -637,12 +653,27 @@ void gdb_register_coprocessor(CPUState *cpu,
> feature->base_reg);
> base_reg = feature->base_reg;
> }
> - gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature);
> + gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature, internal);
>
> /* Add to end of list. */
> cpu->gdb_next_base_reg = base_reg + feature->num_regs;
> }
>
> +void gdb_register_coprocessor(CPUState *cpu,
> + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
> + const GDBFeature *feature)
> +{
> + do_gdb_register_coprocessor(cpu, get_reg, set_reg, feature, false);
> +}
> +
> +void gdb_register_internal_coprocessor(CPUState *cpu,
> + gdb_get_reg_cb get_reg,
> + gdb_set_reg_cb set_reg,
> + const GDBFeature *feature)
> +{
> + do_gdb_register_coprocessor(cpu, get_reg, set_reg, feature, true);
> +}
> +
> void gdb_unregister_coprocessor_all(CPUState *cpu)
> {
> /*
> @@ -1223,6 +1254,18 @@ static void handle_remove_bp(GArray *params, void *user_ctx)
> * the remote gdb to fallback to older methods.
> */
>
> +static bool gdb_register_is_internal(CPUState *cpu, int reg)
> +{
> + for (guint i = 0; i < cpu->gdb_regs->len; i++) {
> + GDBRegisterState *r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
> +
> + if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) {
> + return r->internal;
> + }
> + }
> + return false;
> +}
> +
> static void handle_set_reg(GArray *params, void *user_ctx)
> {
> int reg_size;
> @@ -1232,6 +1275,12 @@ static void handle_set_reg(GArray *params, void *user_ctx)
> return;
> }
>
> + if (gdb_register_is_internal(gdbserver_state.g_cpu,
> + gdb_get_cmd_param(params, 0)->val_ull)) {
> + gdb_put_packet("E14");
> + return;
> + }
> +
> reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2;
> gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size);
> gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data,
> @@ -1248,6 +1297,12 @@ static void handle_get_reg(GArray *params, void *user_ctx)
> return;
> }
>
> + if (gdb_register_is_internal(gdbserver_state.g_cpu,
> + gdb_get_cmd_param(params, 0)->val_ull)) {
> + gdb_put_packet("E14");
> + return;
> + }
> +
> reg_size = gdb_read_register(gdbserver_state.g_cpu,
> gdbserver_state.mem_buf,
> gdb_get_cmd_param(params, 0)->val_ull);
> diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
> index 75eb4d9c365e..1bc778324758 100644
> --- a/include/exec/gdbstub.h
> +++ b/include/exec/gdbstub.h
> @@ -40,6 +40,27 @@ void gdb_register_coprocessor(CPUState *cpu,
> gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
> const GDBFeature *feature);
>
> +/**
> + * gdb_register_internal_coprocessor() - register internal registers
> + *
> + * Like gdb_register_coprocessor(), but the feature is NOT exposed to
> + * GDB via the target description XML. The registers are only
> + * accessible through QEMU's internal APIs (HMP monitor ``$register``
> + * expressions via gdb_get_register_list()).
> + *
> + * Internal features must be registered after all GDB-visible features
> + * to avoid register numbering mismatches with GDB.
> + *
> + * @cpu - the CPU associated with registers
> + * @get_reg - get function
> + * @set_reg - set function
> + * @feature - the GDBFeature describing the registers
> + */
> +void gdb_register_internal_coprocessor(CPUState *cpu,
> + gdb_get_reg_cb get_reg,
> + gdb_set_reg_cb set_reg,
> + const GDBFeature *feature);
> +
> /**
> * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers
> * @cpu - the CPU associated with registers
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 04/10] gdbstub: add gdb_register_internal_coprocessor()
2026-10-13 8:42 ` Alex Bennée
@ 2026-10-13 13:00 ` Marc-André Lureau
2026-10-13 13:26 ` Peter Maydell
0 siblings, 1 reply; 16+ messages in thread
From: Marc-André Lureau @ 2026-10-13 13:00 UTC (permalink / raw)
To: Alex Bennée
Cc: qemu-devel, Dr. David Alan Gilbert, Philippe Mathieu-Daudé,
Zhao Liu, Peter Maydell, Chinmay Rath, Nicholas Piggin,
Glenn Miles, Harsh Prateek Bora, Palmer Dabbelt, Alistair Francis,
Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei, Chao Liu,
Max Filippov, qemu-arm, qemu-ppc, qemu-riscv, Paolo Bonzini,
Laurent Vivier, Mark Cave-Ayland, Artyom Tarasenko, Helge Deller,
Pierrick Bouvier, Warner Losh, Kyle Evans
Hi
On Tue, Oct 13, 2026 at 12:51 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > Add support for registering coprocessor features that are only visible
> > to QEMU's internal APIs (HMP monitor $register expressions, plugins) but
> > hidden from GDB's target description XML.
> >
> > When a feature is registered as internal:
> > - It is excluded from the target.xml sent to GDB
> > - Its XML is not served via qXfer:features:read
> > - 'p'/'P' packets for internal register numbers return E14
> >
> > This is useful for exposing system registers to the HMP monitor without
> > affecting GDB's register namespace. Exposing aliases or synthetic
> > registers through GDB's target description can cause problems: name
> > collisions with GDB's pseudo-registers, independent register caching,
> > and missing DWARF unwind rules.
>
> Hmm are they *that* useful for HMP then?
>
> The front-ends do limit what they export to gdb (see for example
> ARM_CP_NO_GDB) generally because reading them causes side-effects. Those
> restrictions are also a reason to limit them from the HMP interface.
I am not a user, but I can imagine they are useful for developers.
I would like to be able to implement "info registers" purely based on
QMP for ex.
>
> >
> > Internal features must be registered after all GDB-visible features to
> > keep remote register numbering consistent. This ordering is enforced
> > with an assertion.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> > gdbstub/gdbstub.c | 71 ++++++++++++++++++++++++++++++++++++++++++++------
> > include/exec/gdbstub.h | 21 +++++++++++++++
> > 2 files changed, 84 insertions(+), 8 deletions(-)
> >
> > diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
> > index 912e31b66744..ac0f76d62333 100644
> > --- a/gdbstub/gdbstub.c
> > +++ b/gdbstub/gdbstub.c
> > @@ -54,6 +54,7 @@ typedef struct GDBRegisterState {
> > gdb_get_reg_cb get_reg;
> > gdb_set_reg_cb set_reg;
> > const GDBFeature *feature;
> > + bool internal; /* hidden from GDB, accessible via API/monitor */
> > } GDBRegisterState;
> >
> > GDBState gdbserver_state;
> > @@ -385,6 +386,11 @@ static const char *get_feature_xml(const char *p, const char **newp,
> > }
> > for (guint i = 0; i < cpu->gdb_regs->len; i++) {
> > r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
> > + /* The core target.xml feature must never be internal. */
> > + g_assert(i > 0 || !r->internal);
> > + if (r->internal) {
> > + continue;
> > + }
> > g_ptr_array_add(
> > xml,
> > g_markup_printf_escaped("<xi:include href=\"%s\"/>",
> > @@ -400,6 +406,9 @@ static const char *get_feature_xml(const char *p, const char **newp,
> > /* Is it one of the features? */
> > for (guint i = 0; i < cpu->gdb_regs->len; i++) {
> > r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
> > + if (r->internal) {
> > + continue;
> > + }
> > if (strncmp(p, r->feature->xmlname, len) == 0) {
> > return r->feature->xml;
> > }
> > @@ -563,13 +572,14 @@ int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg)
> >
> > static void gdb_register_feature(CPUState *cpu, int base_reg,
> > gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
> > - const GDBFeature *feature)
> > + const GDBFeature *feature, bool internal)
> > {
> > GDBRegisterState s = {
> > .base_reg = base_reg,
> > .get_reg = get_reg,
> > .set_reg = set_reg,
> > - .feature = feature
> > + .feature = feature,
> > + .internal = internal
> > };
> >
> > trace_gdbxml_register_feature(feature->name, feature->xmlname,
> > @@ -605,7 +615,7 @@ void gdb_init_cpu(CPUState *cpu)
> > assert(feature->base_reg == 0);
> > gdb_register_feature(cpu, 0,
> > cc->gdb_read_register, cc->gdb_write_register,
> > - feature);
> > + feature, false);
> > cpu->gdb_next_base_reg = cpu->gdb_num_g_regs = feature->num_regs;
> > } else {
> > cpu->gdb_next_base_reg = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
> > @@ -616,20 +626,26 @@ void gdb_init_cpu(CPUState *cpu)
> > cc->gdb_num_core_regs);
> > }
> >
> > -void gdb_register_coprocessor(CPUState *cpu,
> > - gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
> > - const GDBFeature *feature)
> > +static void do_gdb_register_coprocessor(CPUState *cpu,
> > + gdb_get_reg_cb get_reg,
> > + gdb_set_reg_cb set_reg,
> > + const GDBFeature *feature,
> > + bool internal)
> > {
> > GDBRegisterState *s;
> > guint i;
> > int base_reg = cpu->gdb_next_base_reg;
> >
> > for (i = 0; i < cpu->gdb_regs->len; i++) {
> > - /* Check for duplicates. */
> > s = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
> > + /* Check for duplicates. */
> > if (s->feature == feature) {
> > return;
> > }
> > + /* GDB-visible features must not follow internal ones. */
> > + if (!internal) {
> > + g_assert(!s->internal);
> > + }
> > }
> >
> > if (base_reg < feature->base_reg) {
> > @@ -637,12 +653,27 @@ void gdb_register_coprocessor(CPUState *cpu,
> > feature->base_reg);
> > base_reg = feature->base_reg;
> > }
> > - gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature);
> > + gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature, internal);
> >
> > /* Add to end of list. */
> > cpu->gdb_next_base_reg = base_reg + feature->num_regs;
> > }
> >
> > +void gdb_register_coprocessor(CPUState *cpu,
> > + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
> > + const GDBFeature *feature)
> > +{
> > + do_gdb_register_coprocessor(cpu, get_reg, set_reg, feature, false);
> > +}
> > +
> > +void gdb_register_internal_coprocessor(CPUState *cpu,
> > + gdb_get_reg_cb get_reg,
> > + gdb_set_reg_cb set_reg,
> > + const GDBFeature *feature)
> > +{
> > + do_gdb_register_coprocessor(cpu, get_reg, set_reg, feature, true);
> > +}
> > +
> > void gdb_unregister_coprocessor_all(CPUState *cpu)
> > {
> > /*
> > @@ -1223,6 +1254,18 @@ static void handle_remove_bp(GArray *params, void *user_ctx)
> > * the remote gdb to fallback to older methods.
> > */
> >
> > +static bool gdb_register_is_internal(CPUState *cpu, int reg)
> > +{
> > + for (guint i = 0; i < cpu->gdb_regs->len; i++) {
> > + GDBRegisterState *r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
> > +
> > + if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) {
> > + return r->internal;
> > + }
> > + }
> > + return false;
> > +}
> > +
> > static void handle_set_reg(GArray *params, void *user_ctx)
> > {
> > int reg_size;
> > @@ -1232,6 +1275,12 @@ static void handle_set_reg(GArray *params, void *user_ctx)
> > return;
> > }
> >
> > + if (gdb_register_is_internal(gdbserver_state.g_cpu,
> > + gdb_get_cmd_param(params, 0)->val_ull)) {
> > + gdb_put_packet("E14");
> > + return;
> > + }
> > +
> > reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2;
> > gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size);
> > gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data,
> > @@ -1248,6 +1297,12 @@ static void handle_get_reg(GArray *params, void *user_ctx)
> > return;
> > }
> >
> > + if (gdb_register_is_internal(gdbserver_state.g_cpu,
> > + gdb_get_cmd_param(params, 0)->val_ull)) {
> > + gdb_put_packet("E14");
> > + return;
> > + }
> > +
> > reg_size = gdb_read_register(gdbserver_state.g_cpu,
> > gdbserver_state.mem_buf,
> > gdb_get_cmd_param(params, 0)->val_ull);
> > diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
> > index 75eb4d9c365e..1bc778324758 100644
> > --- a/include/exec/gdbstub.h
> > +++ b/include/exec/gdbstub.h
> > @@ -40,6 +40,27 @@ void gdb_register_coprocessor(CPUState *cpu,
> > gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
> > const GDBFeature *feature);
> >
> > +/**
> > + * gdb_register_internal_coprocessor() - register internal registers
> > + *
> > + * Like gdb_register_coprocessor(), but the feature is NOT exposed to
> > + * GDB via the target description XML. The registers are only
> > + * accessible through QEMU's internal APIs (HMP monitor ``$register``
> > + * expressions via gdb_get_register_list()).
> > + *
> > + * Internal features must be registered after all GDB-visible features
> > + * to avoid register numbering mismatches with GDB.
> > + *
> > + * @cpu - the CPU associated with registers
> > + * @get_reg - get function
> > + * @set_reg - set function
> > + * @feature - the GDBFeature describing the registers
> > + */
> > +void gdb_register_internal_coprocessor(CPUState *cpu,
> > + gdb_get_reg_cb get_reg,
> > + gdb_set_reg_cb set_reg,
> > + const GDBFeature *feature);
> > +
> > /**
> > * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers
> > * @cpu - the CPU associated with registers
>
> --
> Alex Bennée
> Virtualisation Tech Lead @ Linaro
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 04/10] gdbstub: add gdb_register_internal_coprocessor()
2026-10-13 13:00 ` Marc-André Lureau
@ 2026-10-13 13:26 ` Peter Maydell
2026-10-13 14:34 ` Dr. David Alan Gilbert
0 siblings, 1 reply; 16+ messages in thread
From: Peter Maydell @ 2026-10-13 13:26 UTC (permalink / raw)
To: Marc-André Lureau
Cc: Alex Bennée, qemu-devel, Dr. David Alan Gilbert,
Philippe Mathieu-Daudé, Zhao Liu, Chinmay Rath,
Nicholas Piggin, Glenn Miles, Harsh Prateek Bora, Palmer Dabbelt,
Alistair Francis, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
Chao Liu, Max Filippov, qemu-arm, qemu-ppc, qemu-riscv,
Paolo Bonzini, Laurent Vivier, Mark Cave-Ayland, Artyom Tarasenko,
Helge Deller, Pierrick Bouvier, Warner Losh, Kyle Evans
On Tue, 13 Oct 2026 at 14:01, Marc-André Lureau
<marcandre.lureau@redhat.com> wrote:
>
> Hi
>
> On Tue, Oct 13, 2026 at 12:51 PM Alex Bennée <alex.bennee@linaro.org> wrote:
> >
> > Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> >
> > > Add support for registering coprocessor features that are only visible
> > > to QEMU's internal APIs (HMP monitor $register expressions, plugins) but
> > > hidden from GDB's target description XML.
> > >
> > > When a feature is registered as internal:
> > > - It is excluded from the target.xml sent to GDB
> > > - Its XML is not served via qXfer:features:read
> > > - 'p'/'P' packets for internal register numbers return E14
> > >
> > > This is useful for exposing system registers to the HMP monitor without
> > > affecting GDB's register namespace. Exposing aliases or synthetic
> > > registers through GDB's target description can cause problems: name
> > > collisions with GDB's pseudo-registers, independent register caching,
> > > and missing DWARF unwind rules.
> >
> > Hmm are they *that* useful for HMP then?
> >
> > The front-ends do limit what they export to gdb (see for example
> > ARM_CP_NO_GDB) generally because reading them causes side-effects. Those
> > restrictions are also a reason to limit them from the HMP interface.
>
> I am not a user, but I can imagine they are useful for developers.
>
> I would like to be able to implement "info registers" purely based on
> QMP for ex.
Yes, getting rid of the current hacks that "info registers" does will
be a very nice cleanup.
I think that the reason we get this internal-only interface is
that gdb itself sets up pseudo-registers for various things that
are nice for a human to have but are not directly exposed that
way in the XML. So for HMP we can either:
(a) not expose that human-facing convenience at all
(b) reimplement the equivalent of the pseudo-registers and
aliases, to the extent that we care to do so, in a way
that they don't appear in the XML we expose to gdb
Personally I'm sceptical of the utility of the "info registers"
interface -- if you want to see what the guest CPU is doing then
you're going to have a much better time connecting gdb to the debug
stub rather than using the extremely rudimentary monitor commands --
but "preserve the existing functionality" is a much easier path
towards getting this cleaned up than trying to get agreement that
we can drop some aspects of it, so I support doing that.
thanks
-- PMM
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 04/10] gdbstub: add gdb_register_internal_coprocessor()
2026-10-13 13:26 ` Peter Maydell
@ 2026-10-13 14:34 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 16+ messages in thread
From: Dr. David Alan Gilbert @ 2026-10-13 14:34 UTC (permalink / raw)
To: Peter Maydell
Cc: Marc-André Lureau, Alex Bennée, qemu-devel,
Philippe Mathieu-Daudé, Zhao Liu, Chinmay Rath,
Nicholas Piggin, Glenn Miles, Harsh Prateek Bora, Palmer Dabbelt,
Alistair Francis, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
Chao Liu, Max Filippov, qemu-arm, qemu-ppc, qemu-riscv,
Paolo Bonzini, Laurent Vivier, Mark Cave-Ayland, Artyom Tarasenko,
Helge Deller, Pierrick Bouvier, Warner Losh, Kyle Evans
* Peter Maydell (peter.maydell@linaro.org) wrote:
> On Tue, 13 Oct 2026 at 14:01, Marc-André Lureau
> <marcandre.lureau@redhat.com> wrote:
> >
> > Hi
> >
> > On Tue, Oct 13, 2026 at 12:51 PM Alex Bennée <alex.bennee@linaro.org> wrote:
> > >
> > > Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> > >
> > > > Add support for registering coprocessor features that are only visible
> > > > to QEMU's internal APIs (HMP monitor $register expressions, plugins) but
> > > > hidden from GDB's target description XML.
> > > >
> > > > When a feature is registered as internal:
> > > > - It is excluded from the target.xml sent to GDB
> > > > - Its XML is not served via qXfer:features:read
> > > > - 'p'/'P' packets for internal register numbers return E14
> > > >
> > > > This is useful for exposing system registers to the HMP monitor without
> > > > affecting GDB's register namespace. Exposing aliases or synthetic
> > > > registers through GDB's target description can cause problems: name
> > > > collisions with GDB's pseudo-registers, independent register caching,
> > > > and missing DWARF unwind rules.
> > >
> > > Hmm are they *that* useful for HMP then?
> > >
> > > The front-ends do limit what they export to gdb (see for example
> > > ARM_CP_NO_GDB) generally because reading them causes side-effects. Those
> > > restrictions are also a reason to limit them from the HMP interface.
> >
> > I am not a user, but I can imagine they are useful for developers.
> >
> > I would like to be able to implement "info registers" purely based on
> > QMP for ex.
>
> Yes, getting rid of the current hacks that "info registers" does will
> be a very nice cleanup.
>
> I think that the reason we get this internal-only interface is
> that gdb itself sets up pseudo-registers for various things that
> are nice for a human to have but are not directly exposed that
> way in the XML. So for HMP we can either:
> (a) not expose that human-facing convenience at all
> (b) reimplement the equivalent of the pseudo-registers and
> aliases, to the extent that we care to do so, in a way
> that they don't appear in the XML we expose to gdb
>
> Personally I'm sceptical of the utility of the "info registers"
> interface -- if you want to see what the guest CPU is doing then
> you're going to have a much better time connecting gdb to the debug
> stub rather than using the extremely rudimentary monitor commands --
> but "preserve the existing functionality" is a much easier path
> towards getting this cleaned up than trying to get agreement that
> we can drop some aspects of it, so I support doing that.
It's useful for getting hints on hung VMs; getting a gdb into a container
on some cluster of hosts and someone letting you poke with it is
often pretty hard.
Getting an admin to run one command when they find a hung VM is more doable.
(Similarly in cases where they wont send you a full vm dump)
Dave
> thanks
> -- PMM
--
-----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] 16+ messages in thread
end of thread, other threads:[~2026-10-13 14:35 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-12 20:34 [PATCH v2 00/10] monitor/hmp: replace MonitorDef with GDB registers Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 01/10] hmp: don't crash on invalid register Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 02/10] gdbstub: fix next register base after register gaps Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 03/10] gdbstub: set feature->base_reg for dynamic features Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 04/10] gdbstub: add gdb_register_internal_coprocessor() Marc-André Lureau
2026-10-13 8:42 ` Alex Bennée
2026-10-13 13:00 ` Marc-André Lureau
2026-10-13 13:26 ` Peter Maydell
2026-10-13 14:34 ` Dr. David Alan Gilbert
2026-10-12 20:34 ` [PATCH v2 05/10] target/i386: expose segment limits via GDB Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 06/10] target/m68k: expose system registers " Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 07/10] target/sparc64: " Marc-André Lureau
2026-10-13 6:57 ` Philippe Mathieu-Daudé
2026-10-12 20:34 ` [PATCH v2 08/10] target/riscv: bypass smstateen check in debugger mode Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 09/10] target/riscv: expose register aliases via GDB Marc-André Lureau
2026-10-12 20:34 ` [PATCH v2 10/10] monitor/hmp: remove legacy MonitorDef infrastructure Marc-André Lureau
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.