Expand the org.qemu.gdb.i386.system feature to expose additional
read-only registers to HMP/monitor:

  - segment cache bases and flags (cs/ds/es/ss/fs/gs)
  - GDT/IDT base and limit
  - LDT/TR selector, base, limit, and flags
  - debug registers (dr0-dr3, dr6, dr7)
  - CPU status fields: CPL, interrupt-inhibit, A20, SMM, HLT
  - xcr0
  - abridged x87 tag word (ftw)

This will allow to implement an HMP "info registers" (cpu dump)
equivalent based on QMP register query some day.

Signed-off-by: Marc-André Lureau <[email protected]>
---
 gdbstub/gdb-xml/i386-system.xml |  41 ++++++++++++-
 target/i386/cpu-dump.c          |   5 +-
 target/i386/cpu.h               |  16 +++++
 target/i386/gdbstub.c           | 132 +++++++++++++++++++++++++++++++++++++---
 4 files changed, 181 insertions(+), 13 deletions(-)

diff --git a/gdbstub/gdb-xml/i386-system.xml b/gdbstub/gdb-xml/i386-system.xml
index 9868b887eab8..7e7a5fb2e21f 100644
--- a/gdbstub/gdb-xml/i386-system.xml
+++ b/gdbstub/gdb-xml/i386-system.xml
@@ -3,7 +3,7 @@
   SPDX-License-Identifier: GPL-2.0-or-later
 -->
 <!DOCTYPE feature SYSTEM "gdb-target.dtd">
-<!-- Segment-cache values are read-only; writes are ignored. -->
+<!-- System-register values are read-only; writes are ignored. -->
 <feature name="org.qemu.gdb.i386.system">
   <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"/>
@@ -11,4 +11,43 @@
   <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"/>
+  <!-- fs/gs in core regs -->
+  <reg name="cs.base" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="ds.base" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="es.base" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="ss.base" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="cs.flags" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="ds.flags" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="es.flags" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="ss.flags" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="fs.flags" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="gs.flags" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="gdt.base" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="gdt.limit" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="idt.base" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="idt.limit" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="ldt.selector" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="ldt.base" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="ldt.limit" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="ldt.flags" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="tr.selector" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="tr.base" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="tr.limit" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="tr.flags" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="dr0" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="dr1" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="dr2" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="dr3" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="dr6" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <reg name="dr7" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <!-- CPU status values, matching the fields printed by info registers. -->
+  <reg name="cpl" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <!-- ii: interrupt inhibit -->
+  <reg name="ii" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="a20" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="smm" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="hlt" bitsize="32" type="uint32" group="qemu-debug" 
save-restore="no"/>
+  <reg name="xcr0" bitsize="64" type="uint64" group="qemu-debug" 
save-restore="no"/>
+  <!-- Abridged x87 tag word: one bit per physical register, 1 = nonempty. -->
+  <reg name="ftw" bitsize="8" type="uint8" group="qemu-debug" 
save-restore="no"/>
 </feature>
diff --git a/target/i386/cpu-dump.c b/target/i386/cpu-dump.c
index 9e290c0b9b42..d8d7ebaf0e03 100644
--- a/target/i386/cpu-dump.c
+++ b/target/i386/cpu-dump.c
@@ -511,10 +511,7 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags)
                                      XSTATE_Hi16_ZMM_MASK | \
                                      XSTATE_YMM_MASK | XSTATE_SSE_MASK,
                        avx_mask = XSTATE_YMM_MASK | XSTATE_SSE_MASK;
-        fptag = 0;
-        for(i = 0; i < 8; i++) {
-            fptag |= ((!env->fptags[i]) << i);
-        }
+        fptag = x86_fptags_to_abridged(env);
         update_mxcsr_from_sse_status(env);
         qemu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n",
                      env->fpuc,
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 116e28a59701..cc06f1fb7b68 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -2998,6 +2998,22 @@ static inline void cpu_set_mxcsr(CPUX86State *env, 
uint32_t mxcsr)
  */
 uint16_t cpu_compute_fptag(CPUX86State *env);
 
+/**
+ * x86_fptags_to_abridged - Pack fptags[] into the 8-bit abridged tag word.
+ *
+ * Returns the FXSAVE/XSAVE-style tag byte: bit i is 1 when ST(i) is
+ * nonempty (fptags[i] == 0) and 0 when empty (fptags[i] == 1).
+ */
+static inline uint8_t x86_fptags_to_abridged(const CPUX86State *env)
+{
+    uint8_t ftw = 0;
+
+    for (int i = 0; i < 8; i++) {
+        ftw |= (!env->fptags[i]) << i;
+    }
+    return ftw;
+}
+
 static inline void cpu_set_fpuc(CPUX86State *env, uint16_t fpuc)
 {
      env->fpuc = fpuc;
diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c
index 2b4c28aac162..9a46c46ecff9 100644
--- a/target/i386/gdbstub.c
+++ b/target/i386/gdbstub.c
@@ -510,21 +510,135 @@ static int i386_cpu_gdb_set_egprs(CPUState *cs, uint8_t 
*mem_buf, int n)
 #endif
 
 #ifndef CONFIG_USER_ONLY
+enum {
+    /* Segment limits: cs, ds, es, ss, fs, gs (6 × 32-bit) */
+    X86_GDB_SEG_LIMIT_FIRST,
+    X86_GDB_SEG_LIMIT_LAST = X86_GDB_SEG_LIMIT_FIRST + 5,
+    /* Segment bases: cs, ds, es, ss (4 × 64-bit; fs/gs in core regs) */
+    X86_GDB_SEG_BASE_FIRST,
+    X86_GDB_SEG_BASE_LAST = X86_GDB_SEG_BASE_FIRST + 3,
+    /* Segment flags: cs, ds, es, ss, fs, gs (6 × 32-bit) */
+    X86_GDB_SEG_FLAGS_FIRST,
+    X86_GDB_SEG_FLAGS_LAST = X86_GDB_SEG_FLAGS_FIRST + 5,
+    /* Descriptor tables */
+    X86_GDB_GDT_BASE,
+    X86_GDB_GDT_LIMIT,
+    X86_GDB_IDT_BASE,
+    X86_GDB_IDT_LIMIT,
+    X86_GDB_LDT_SELECTOR,
+    X86_GDB_LDT_BASE,
+    X86_GDB_LDT_LIMIT,
+    X86_GDB_LDT_FLAGS,
+    X86_GDB_TR_SELECTOR,
+    X86_GDB_TR_BASE,
+    X86_GDB_TR_LIMIT,
+    X86_GDB_TR_FLAGS,
+    /* Debug registers: dr0-dr3, dr6, dr7 (6 × 64-bit) */
+    X86_GDB_DR0,
+    X86_GDB_DR1,
+    X86_GDB_DR2,
+    X86_GDB_DR3,
+    X86_GDB_DR6,
+    X86_GDB_DR7,
+    /* CPU status */
+    X86_GDB_CPL,
+    X86_GDB_II,
+    X86_GDB_A20,
+    X86_GDB_SMM,
+    X86_GDB_HLT,
+    X86_GDB_XCR0,
+    X86_GDB_FTW,
+    X86_GDB_SYSTEM_COUNT,
+};
+
 static int x86_gdb_read_system(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)) {
+    switch (n) {
+    case X86_GDB_SEG_LIMIT_FIRST ... X86_GDB_SEG_LIMIT_LAST:
+        return gdb_get_reg32(buf,
+                             env->segs[segments[n - 
X86_GDB_SEG_LIMIT_FIRST]].limit);
+    case X86_GDB_SEG_BASE_FIRST ... X86_GDB_SEG_BASE_LAST:
+        return gdb_get_reg64(buf,
+                             env->segs[segments[n - 
X86_GDB_SEG_BASE_FIRST]].base);
+    case X86_GDB_SEG_FLAGS_FIRST ... X86_GDB_SEG_FLAGS_LAST:
+        return gdb_get_reg32(buf,
+                             env->segs[segments[n - 
X86_GDB_SEG_FLAGS_FIRST]].flags);
+    case X86_GDB_GDT_BASE:
+        return gdb_get_reg64(buf, env->gdt.base);
+    case X86_GDB_GDT_LIMIT:
+        return gdb_get_reg32(buf, env->gdt.limit);
+    case X86_GDB_IDT_BASE:
+        return gdb_get_reg64(buf, env->idt.base);
+    case X86_GDB_IDT_LIMIT:
+        return gdb_get_reg32(buf, env->idt.limit);
+    case X86_GDB_LDT_SELECTOR:
+        return gdb_get_reg32(buf, env->ldt.selector);
+    case X86_GDB_LDT_BASE:
+        return gdb_get_reg64(buf, env->ldt.base);
+    case X86_GDB_LDT_LIMIT:
+        return gdb_get_reg32(buf, env->ldt.limit);
+    case X86_GDB_LDT_FLAGS:
+        return gdb_get_reg32(buf, env->ldt.flags);
+    case X86_GDB_TR_SELECTOR:
+        return gdb_get_reg32(buf, env->tr.selector);
+    case X86_GDB_TR_BASE:
+        return gdb_get_reg64(buf, env->tr.base);
+    case X86_GDB_TR_LIMIT:
+        return gdb_get_reg32(buf, env->tr.limit);
+    case X86_GDB_TR_FLAGS:
+        return gdb_get_reg32(buf, env->tr.flags);
+    case X86_GDB_DR0 ... X86_GDB_DR3:
+        return gdb_get_reg64(buf, env->dr[n - X86_GDB_DR0]);
+    case X86_GDB_DR6:
+        return gdb_get_reg64(buf, env->dr[6]);
+    case X86_GDB_DR7:
+        return gdb_get_reg64(buf, env->dr[7]);
+    case X86_GDB_CPL:
+        return gdb_get_reg32(buf, env->hflags & HF_CPL_MASK);
+    case X86_GDB_II:
+        return gdb_get_reg32(buf, !!(env->hflags & HF_INHIBIT_IRQ_MASK));
+    case X86_GDB_A20:
+        return gdb_get_reg32(buf, (env->a20_mask >> 20) & 1);
+    case X86_GDB_SMM:
+        return gdb_get_reg32(buf, !!(env->hflags & HF_SMM_MASK));
+    case X86_GDB_HLT:
+        return gdb_get_reg32(buf, cs->halted);
+    case X86_GDB_XCR0:
+        return gdb_get_reg64(buf, env->xcr0);
+    case X86_GDB_FTW:
+        return gdb_get_reg8(buf, x86_fptags_to_abridged(env));
+    default:
         return 0;
     }
-    return gdb_get_reg32(buf, env->segs[segments[n]].limit);
 }
 
 static int x86_gdb_write_system(CPUState *cs, uint8_t *buf, int n)
 {
-    /* Segment caches are exposed for inspection only. Ignore writes. */
-    return 4;
+    /* System registers are exposed for inspection only. Ignore writes. */
+    static const uint8_t sizes[X86_GDB_SYSTEM_COUNT] = {
+        [X86_GDB_SEG_LIMIT_FIRST ... X86_GDB_SEG_LIMIT_LAST] = 4,
+        [X86_GDB_SEG_BASE_FIRST  ... X86_GDB_SEG_BASE_LAST]  = 8,
+        [X86_GDB_SEG_FLAGS_FIRST ... X86_GDB_SEG_FLAGS_LAST] = 4,
+        [X86_GDB_GDT_BASE] = 8, [X86_GDB_GDT_LIMIT] = 4,
+        [X86_GDB_IDT_BASE] = 8, [X86_GDB_IDT_LIMIT] = 4,
+        [X86_GDB_LDT_SELECTOR] = 4, [X86_GDB_LDT_BASE] = 8,
+        [X86_GDB_LDT_LIMIT] = 4, [X86_GDB_LDT_FLAGS] = 4,
+        [X86_GDB_TR_SELECTOR] = 4, [X86_GDB_TR_BASE] = 8,
+        [X86_GDB_TR_LIMIT] = 4, [X86_GDB_TR_FLAGS] = 4,
+        [X86_GDB_DR0 ... X86_GDB_DR7] = 8,
+        [X86_GDB_CPL] = 4, [X86_GDB_II] = 4,
+        [X86_GDB_A20] = 4, [X86_GDB_SMM] = 4,
+        [X86_GDB_HLT] = 4, [X86_GDB_XCR0] = 8,
+        [X86_GDB_FTW] = 1,
+    };
+
+    if (n < 0 || n >= X86_GDB_SYSTEM_COUNT) {
+        return 0;
+    }
+    return sizes[n];
 }
 #endif
 
@@ -559,9 +673,11 @@ void x86_cpu_gdb_init(CPUState *cs)
 #endif
 #endif
 #ifndef CONFIG_USER_ONLY
-    gdb_register_internal_coprocessor(
-        cs, x86_gdb_read_system,
-        x86_gdb_write_system,
-        gdb_find_static_feature("i386-system.xml"));
+    {
+        const GDBFeature *sysregs = gdb_find_static_feature("i386-system.xml");
+        g_assert(sysregs->num_regs == X86_GDB_SYSTEM_COUNT);
+        gdb_register_internal_coprocessor(cs, x86_gdb_read_system,
+                                          x86_gdb_write_system, sysregs);
+    }
 #endif
 }

-- 
2.56.0.116.g6de20f6092dc


Reply via email to