]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib/ukdebug: Add `q` commands to gdb stub for target information
authorMarc Rittinghaus <marc.rittinghaus@unikraft.io>
Mon, 19 Aug 2024 09:38:23 +0000 (11:38 +0200)
committerUnikraft Bot <monkey@unikraft.io>
Fri, 13 Sep 2024 20:51:57 +0000 (20:51 +0000)
The two `q` commands (`qSupported` and `qXfer:features:read:target.xml`)
allow the gdb host to retrieve information about the target system
that's being debugged.

Checkpatch-Ignore: AVOID_EXTERNS
Checkpatch-Ignore: UNNECESSARY_PARENTHESES
Checkpatch-Ignore: SPDX_LICENSE_TAG
Signed-off-by: Marc Rittinghaus <marc.rittinghaus@unikraft.io>
Reviewed-by: Michalis Pappas <michalis@unikraft.io>
Reviewed-by: Simon Kuenzer <simon@unikraft.io>
Approved-by: Simon Kuenzer <simon@unikraft.io>
GitHub-Closes: #1479

lib/ukdebug/Makefile.uk
lib/ukdebug/gdbstub.c
lib/ukdebug/gdbtargetxml.S [new file with mode: 0644]
lib/ukdebug/gdbtargetxml.ld [new file with mode: 0644]

index fcaf988b248f988e4f733ef43099e2b473f7b46b..ef9acbe438828a72ac8d046a77ce870064337ea5 100644 (file)
@@ -16,6 +16,9 @@ LIBUKDEBUG_SRCS-y += $(LIBUKDEBUG_BASE)/hexdump.c
 LIBUKDEBUG_SRCS-$(CONFIG_LIBZYDIS) += $(LIBUKDEBUG_BASE)/asmdump.c
 
 LIBUKDEBUG_SRCS-$(CONFIG_LIBUKDEBUG_GDBSTUB) += $(LIBUKDEBUG_BASE)/gdbstub.c|isr
+LIBUKDEBUG_SRCS-$(CONFIG_LIBUKDEBUG_GDBSTUB) += $(LIBUKDEBUG_BASE)/gdbtargetxml.ld
+LIBUKDEBUG_SRCS-$(CONFIG_LIBUKDEBUG_GDBSTUB) += $(LIBUKDEBUG_BASE)/gdbtargetxml.S
+LIBUKDEBUG_GDBTARGETXML_CDEPS += $(LIBUKDEBUG_BASE)/arch/$(CONFIG_UK_ARCH)/target.xml
 LIBUKDEBUG_SRCS-$(CONFIG_LIBUKDEBUG_GDBSTUB) += $(LIBUKDEBUG_BASE)/arch/$(CONFIG_UK_ARCH)/gdbsup.c|isr
 
 LIBUKDEBUG_SRCS-$(CONFIG_LIBUKDEBUG_TRACEPOINTS) += $(LIBUKDEBUG_BASE)/trace.c
index 41d28068671e7935d7eac80e226c43fe003ede1f..a8994ede39d5dec574e47baa4faa71ea2732b015 100644 (file)
@@ -30,6 +30,7 @@ struct gdb_excpt_ctx {
 };
 
 #define GDB_BUF_SIZE 2048
+#define GDB_BUF_SIZE_HEX_STR "800"
 static char gdb_recv_buffer[GDB_BUF_SIZE];
 static char gdb_send_buffer[GDB_BUF_SIZE];
 
@@ -52,6 +53,10 @@ struct gdb_cmd_table_entry {
        __sz cmd_len;
 };
 
+/* Linked to architecture-specfic target.xml description */
+extern const char __gdb_target_xml_start[];
+extern const char __gdb_target_xml_end[];
+
 int gdb_dbg_putc(char b __unused)
 {
        return -ENOTSUP;
@@ -71,6 +76,18 @@ static int gdb_starts_with(const char *buf, __sz buf_len,
        return (memcmp_isr(buf, prefix, prefix_len) == 0);
 }
 
+static int gdb_consume_str(char **buf, __sz *buf_len,
+                          const char *prefix, __sz prefix_len)
+{
+       if (!gdb_starts_with(*buf, *buf_len, prefix, prefix_len))
+               return 0;
+
+       *buf += prefix_len;
+       *buf_len -= prefix_len;
+
+       return 1;
+}
+
 static char gdb_checksum(const char *buf, __sz len)
 {
        char c = 0;
@@ -186,6 +203,37 @@ static __ssz gdb_hex2mem(char *mem, __sz mem_len, const char *hex, __sz hex_len)
        return hex_len / 2;
 }
 
+static __ssz gdb_mem2bin(char *bin, __sz bin_len, const char *mem, __sz mem_len)
+{
+       __sz rem_len = bin_len;
+
+       while (mem_len--) {
+               switch (*mem) {
+               /* The following characters must be escaped */
+               case '}':
+               case '#':
+               case '$':
+               case '*':
+                       if (rem_len < 2)
+                               return bin_len - rem_len;
+                       *bin++ = '}';
+                       *bin++ = *mem++ ^ 0x20;
+
+                       rem_len -= 2;
+                       break;
+               default:
+                       if (rem_len < 1)
+                               return bin_len - rem_len;
+                       *bin++ = *mem++;
+
+                       rem_len--;
+                       break;
+               }
+       }
+
+       return bin_len - rem_len;
+}
+
 static __ssz gdb_read_memory(unsigned long addr, __sz len,
                             void *buf, __sz buf_len)
 {
@@ -641,6 +689,135 @@ static int gdb_handle_step_sig(char *buf, __sz buf_len,
                        r : GDB_DBG_STEP);
 }
 
+static int gdb_handle_multiletter_cmd(struct gdb_cmd_table_entry *table,
+                                     __sz table_entries, char *buf,
+                                     __sz buf_len, struct gdb_excpt_ctx *g)
+{
+       __sz i, l, max_len_idx = 0, max_len = 0;
+
+       for (i = 0; i < table_entries; i++) {
+               l = table[i].cmd_len;
+
+               if (l > buf_len)
+                       continue;
+
+               if (memcmp_isr(buf, table[i].cmd, l) != 0)
+                       continue;
+
+               if (l > max_len) {
+                       max_len = l;
+                       max_len_idx = i;
+               }
+       }
+
+       if (max_len > 0 && max_len <= buf_len) {
+               /* If the remaining length if zero, prefer passing a NULL
+                * pointer over passing a pointer to garbage data.
+                */
+               buf = max_len == buf_len ? NULL : buf + max_len;
+               return table[max_len_idx].f(buf, buf_len - max_len, g);
+       }
+
+       /* Send empty packet to signal GDB that
+        * we do not support the command
+        */
+       GDB_CHECK(gdb_send_empty_packet());
+
+       return 0;
+}
+
+/* qSupported[:gdbfeature [;gdbfeature]... ] */
+static int gdb_handle_qsupported(char *buf __unused, __sz buf_len __unused,
+                                struct gdb_excpt_ctx *g __unused)
+{
+#define GDB_SUPPORT_STR \
+       "PacketSize=" GDB_BUF_SIZE_HEX_STR \
+       ";qXfer:features:read+"
+
+       GDB_CHECK(gdb_send_packet(GDB_STR_A_LEN(GDB_SUPPORT_STR)));
+
+       return 0;
+}
+
+static int gdb_qXfer_mem(const char *mem, __sz mem_len, __sz offset,
+                        __sz length)
+{
+       __ssz r;
+
+       if (offset >= mem_len) {
+               /* Send l. No more data to be read */
+               GDB_CHECK(gdb_send_packet(GDB_STR_A_LEN("l")));
+       } else {
+               length = MIN(length, mem_len - offset);
+               length = MIN(length, sizeof(gdb_send_buffer) - 1);
+
+               gdb_send_buffer[0] = 'm'; /* There is more data */
+
+               r = gdb_mem2bin(gdb_send_buffer + 1,
+                               sizeof(gdb_send_buffer) - 1,
+                               mem + offset, length);
+
+               UK_ASSERT(r > 0);
+
+               GDB_CHECK(gdb_send_packet(gdb_send_buffer, r + 1));
+       }
+
+       return 0;
+}
+
+/* qXfer:object:read:annex:offset,length */
+static int gdb_handle_qXfer(char *buf, __sz buf_len,
+                           struct gdb_excpt_ctx *g __unused)
+{
+       unsigned long offset, length;
+       char *buf_end = buf + buf_len;
+
+       if (!gdb_consume_str(&buf, &buf_len, GDB_STR_A_LEN(":features:read"))) {
+               /* Send empty packet. Unsupported object requested */
+               GDB_CHECK(gdb_send_empty_packet());
+               return 0;
+       }
+
+       if (!gdb_consume_str(&buf, &buf_len, GDB_STR_A_LEN(":target.xml:"))) {
+               /* Send E00. Annex invalid */
+               GDB_CHECK(gdb_send_error_packet(0x00));
+               return 0;
+       }
+
+       offset = gdb_hex2ulong(buf, buf_end - buf, &buf);
+       if ((buf >= buf_end) || (*buf++ != ',')) {
+               /* Send E00. Request malformed */
+               GDB_CHECK(gdb_send_error_packet(0x00));
+               return 0;
+       }
+
+       length = gdb_hex2ulong(buf, buf_end - buf, &buf);
+       if (buf != buf_end) {
+               /* Send E00. Request malformed */
+               GDB_CHECK(gdb_send_error_packet(0x00));
+               return 0;
+       }
+
+       return gdb_qXfer_mem(__gdb_target_xml_start,
+               __gdb_target_xml_end - __gdb_target_xml_start,
+               offset, length);
+}
+
+static struct gdb_cmd_table_entry gdb_q_cmd_table[] = {
+       { gdb_handle_qsupported, GDB_STR_A_LEN("Supported") },
+       { gdb_handle_qXfer, GDB_STR_A_LEN("Xfer") }
+};
+
+#define NUM_GDB_Q_CMDS (sizeof(gdb_q_cmd_table) / \
+               sizeof(struct gdb_cmd_table_entry))
+
+static int gdb_handle_q_cmd(char *buf, __sz buf_len,
+                           struct gdb_excpt_ctx *g)
+{
+       return gdb_handle_multiletter_cmd(gdb_q_cmd_table, NUM_GDB_Q_CMDS,
+               buf, buf_len, g);
+}
+
 static struct gdb_cmd_table_entry gdb_cmd_table[] = {
        { gdb_handle_stop_reason, GDB_STR_A_LEN("?") },
        { gdb_handle_read_registers, GDB_STR_A_LEN("g") },
@@ -650,7 +827,8 @@ static struct gdb_cmd_table_entry gdb_cmd_table[] = {
        { gdb_handle_continue, GDB_STR_A_LEN("c") },
        { gdb_handle_continue_sig, GDB_STR_A_LEN("C") },
        { gdb_handle_step, GDB_STR_A_LEN("s") },
-       { gdb_handle_step_sig, GDB_STR_A_LEN("S") }
+       { gdb_handle_step_sig, GDB_STR_A_LEN("S") },
+       { gdb_handle_q_cmd, GDB_STR_A_LEN("q") },
 };
 
 #define NUM_GDB_CMDS (sizeof(gdb_cmd_table) / \
diff --git a/lib/ukdebug/gdbtargetxml.S b/lib/ukdebug/gdbtargetxml.S
new file mode 100644 (file)
index 0000000..9b804f7
--- /dev/null
@@ -0,0 +1,2 @@
+.section .gdb.target.xml, "a"
+.incbin "target.xml"
diff --git a/lib/ukdebug/gdbtargetxml.ld b/lib/ukdebug/gdbtargetxml.ld
new file mode 100644 (file)
index 0000000..1e4887d
--- /dev/null
@@ -0,0 +1,11 @@
+SECTIONS
+{
+       /* Target description file for GDB stub */
+       .gdb.target.xml :
+       {
+               PROVIDE(__gdb_target_xml_start = .);
+               KEEP(*(.gdb.target.xml))
+               PROVIDE(__gdb_target_xml_end = .);
+       }
+}
+INSERT AFTER .rodata;