qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 1/3] gdbstub: add basic infrastructure to support switchable endi


From: Changbin Du
Subject: [PATCH 1/3] gdbstub: add basic infrastructure to support switchable endianness
Date: Mon, 23 Aug 2021 22:20:02 +0800

Some architectures (e.g. ARM versions 3 and above, RISC-V, PowerPC, Alpha,
MIPS, IA-64...) allow switchable endianness. Now our emulation code can
handle both endianness well, but the gdbstub can only handle one of them.

For example, it is problematic to debug a ARM big endian guest on x86 host.
This because the GDB remote protocol transfers values in target byte order
but qemu always take it as little endian on x86 host.

To support switchable endianness targets, this patch introduces:
  - a new sub-option 'endianness' for '-gdb'.
  - common interfaces to swap byte order according to host and target
    byte order.
  - a new configuration option TARGET_SWICHABLE_ENDIANNESS.

For example, to debug a arm64 big endian target, you could start qemu as
below:

  $ qemu-system-aarch64 -gdb tcp::1234,endianness=big ...

Latter we will add switchable endianness support for ARM and RISC-V
targets. For other switchable targets them can be supported in future.

Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
 gdbstub.c              | 11 +++++++
 include/exec/gdbstub.h | 72 +++++++++++++++++++++++++++++++-----------
 qemu-options.hx        |  7 ++--
 softmmu/vl.c           | 50 ++++++++++++++++++++++++++++-
 4 files changed, 119 insertions(+), 21 deletions(-)

diff --git a/gdbstub.c b/gdbstub.c
index 52bde5bdc9..ec67d6a299 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -62,6 +62,17 @@
 static int phy_memory_mode;
 #endif
 
+#ifdef HOST_WORDS_BIGENDIAN
+const bool gdb_host_bigendian = true;
+#else
+const bool gdb_host_bigendian = false;
+#endif
+#ifdef TARGET_WORDS_BIGENDIAN
+bool gdb_target_bigendian = true;
+#else
+bool gdb_target_bigendian = false;
+#endif
+
 static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr,
                                          uint8_t *buf, int len, bool is_write)
 {
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index a024a0350d..2c6f90fc28 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -84,9 +84,17 @@ void gdb_register_coprocessor(CPUState *cpu,
                               gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
                               int num_regs, const char *xml, int g_pos);
 
+extern const bool gdb_host_bigendian;
+extern bool gdb_target_bigendian;
+
+/* The GDB remote protocol transfers values in target byte order. */
+static inline bool gdb_bswap_needed(void)
+{
+    return gdb_host_bigendian != gdb_target_bigendian;
+}
+
 /*
- * The GDB remote protocol transfers values in target byte order. As
- * the gdbstub may be batching up several register values we always
+ * As the gdbstub may be batching up several register values we always
  * append to the array.
  */
 
@@ -98,21 +106,21 @@ static inline int gdb_get_reg8(GByteArray *buf, uint8_t 
val)
 
 static inline int gdb_get_reg16(GByteArray *buf, uint16_t val)
 {
-    uint16_t to_word = tswap16(val);
+    uint16_t to_word = gdb_bswap_needed() ? bswap16(val) : val;
     g_byte_array_append(buf, (uint8_t *) &to_word, 2);
     return 2;
 }
 
 static inline int gdb_get_reg32(GByteArray *buf, uint32_t val)
 {
-    uint32_t to_long = tswap32(val);
+    uint32_t to_long = gdb_bswap_needed() ? bswap32(val) : val;
     g_byte_array_append(buf, (uint8_t *) &to_long, 4);
     return 4;
 }
 
 static inline int gdb_get_reg64(GByteArray *buf, uint64_t val)
 {
-    uint64_t to_quad = tswap64(val);
+    uint64_t to_quad = gdb_bswap_needed() ? bswap64(val) : val;
     g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
     return 8;
 }
@@ -121,17 +129,20 @@ static inline int gdb_get_reg128(GByteArray *buf, 
uint64_t val_hi,
                                  uint64_t val_lo)
 {
     uint64_t to_quad;
-#ifdef TARGET_WORDS_BIGENDIAN
-    to_quad = tswap64(val_hi);
-    g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
-    to_quad = tswap64(val_lo);
-    g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
-#else
-    to_quad = tswap64(val_lo);
-    g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
-    to_quad = tswap64(val_hi);
-    g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
-#endif
+
+    if (gdb_bswap_needed()) {
+        if (gdb_target_bigendian) {
+            to_quad = bswap64(val_hi);
+            g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+            to_quad = bswap64(val_lo);
+            g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+        } else {
+            to_quad = bswap64(val_lo);
+            g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+            to_quad = bswap64(val_hi);
+            g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+        }
+    }
     return 16;
 }
 
@@ -157,13 +168,38 @@ static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, 
int len)
     return buf->data + buf->len - len;
 }
 
+static inline uint8_t gdb_read_reg8(uint8_t *mem_buf)
+{
+    return *mem_buf;
+}
+
+static inline uint16_t gdb_read_reg16(uint8_t *mem_buf)
+{
+    uint16_t val = lduw_p(mem_buf);
+    return gdb_bswap_needed() ? bswap16(val) : val;
+}
+
+static inline uint32_t gdb_read_reg32(uint8_t *mem_buf)
+{
+    uint32_t val = ldl_p(mem_buf);
+    return gdb_bswap_needed() ? bswap32(val) : val;
+}
+
+static inline uint64_t gdb_read_reg64(uint8_t *mem_buf)
+{
+    uint64_t val = ldq_p(mem_buf);
+    return gdb_bswap_needed() ? bswap64(val) : val;
+}
+
 #if TARGET_LONG_BITS == 64
 #define gdb_get_regl(buf, val) gdb_get_reg64(buf, val)
-#define ldtul_p(addr) ldq_p(addr)
+#define gdb_read_regl(mem_buf) gdb_read_reg64(mem_buf)
 #else
 #define gdb_get_regl(buf, val) gdb_get_reg32(buf, val)
-#define ldtul_p(addr) ldl_p(addr)
+#define gdb_read_regl(mem_buf) gdb_read_reg32(mem_buf)
 #endif
+/* ldtul_p is deprecated */
+#define ldtul_p(mem_buf)       gdb_read_regl(mem_buf)
 
 #endif
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 83aa59a920..779b861331 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3954,9 +3954,12 @@ SRST
 ERST
 
 DEF("gdb", HAS_ARG, QEMU_OPTION_gdb, \
-    "-gdb dev        accept gdb connection on 'dev'. (QEMU defaults to 
starting\n"
+    "-gdb [dev=]device[,endianness=default|little|big]\n"
+    "                accept gdb connection on 'dev'. (QEMU defaults to 
starting\n"
     "                the guest without waiting for gdb to connect; use -S 
too\n"
-    "                if you want it to not start execution.)\n",
+    "                if you want it to not start execution.) The 
'endianness'\n"
+    "                specifies the endianness mode of the target which 
supports\n"
+    "                switchable endianness.\n",
     QEMU_ARCH_ALL)
 SRST
 ``-gdb dev``
diff --git a/softmmu/vl.c b/softmmu/vl.c
index 5ca11e7469..dc57d518ab 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -1405,6 +1405,48 @@ static void qemu_create_default_devices(void)
     }
 }
 
+static QemuOptsList qemu_gdb_opts = {
+    .name = "gdb",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_gdb_opts.head),
+    .implied_opt_name = "dev",
+    .desc = {
+        {
+            .name = "dev",
+            .type = QEMU_OPT_STRING,
+        },
+        {
+            .name = "endianness",
+            .type = QEMU_OPT_STRING,
+        },
+        { /* end of list */ }
+    },
+};
+
+static void configure_gdb(QemuOpts *opts)
+{
+    const char *dev = qemu_opt_get(opts, "dev");
+    const char *endianness = qemu_opt_get(opts, "endianness");
+
+    if (dev) {
+        add_device_config(DEV_GDB, dev);
+    }
+
+    if (endianness && strcmp(endianness, "default")) {
+#ifdef TARGET_SWICHABLE_ENDIANNESS
+        if (!strcmp(endianness, "little")) {
+            gdb_target_bigendian = false;
+        } else if (!strcmp(endianness, "big")) {
+            gdb_target_bigendian = true;
+        } else {
+            error_report("unknown endianness %s", endianness);
+        }
+#else
+        error_report("endianness is not switchable for current target");
+        exit(1);
+#endif
+    }
+}
+
 static int serial_parse(const char *devname)
 {
     int index = num_serial_hds;
@@ -2761,6 +2803,7 @@ void qemu_init(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_semihosting_config_opts);
     qemu_add_opts(&qemu_fw_cfg_opts);
     qemu_add_opts(&qemu_action_opts);
+    qemu_add_opts(&qemu_gdb_opts);
     module_call_init(MODULE_INIT_OPTS);
 
     error_init(argv[0]);
@@ -3014,7 +3057,12 @@ void qemu_init(int argc, char **argv, char **envp)
                 add_device_config(DEV_GDB, "tcp::" DEFAULT_GDBSTUB_PORT);
                 break;
             case QEMU_OPTION_gdb:
-                add_device_config(DEV_GDB, optarg);
+                opts = qemu_opts_parse_noisily(qemu_find_opts("gdb"),
+                                               optarg, true);
+                if (!opts) {
+                    exit(EXIT_FAILURE);
+                }
+                configure_gdb(opts);
                 break;
             case QEMU_OPTION_L:
                 if (is_help_option(optarg)) {
-- 
2.32.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]