]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib/ukdebug: Add breakpoints to set up and connect to stub
authorThassilo Schulze <thassilo@unikraft.io>
Tue, 4 Jun 2024 09:25:48 +0000 (11:25 +0200)
committerUnikraft Bot <monkey@unikraft.io>
Fri, 13 Sep 2024 20:51:57 +0000 (20:51 +0000)
If the gdb stub is enabled, it registers a function in the inittab
that tries to connect to the gdb host. This function first checks
if the console with the ID from `debug.gdbcon` is available. If so,
this function executes a breakpoint instruction to enter the gdb
stub. If the console is not available, the breakpoint is skipped.

Checkpatch-Ignore: LONG_LINE_STRING
Signed-off-by: Thassilo Schulze <thassilo@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/arch/arm64/gdbsup.h
lib/ukdebug/arch/x86_64/gdbsup.h
lib/ukdebug/gdbstub.c

index 122f9a4c371b7d8010c878b7e8c83ca5407cd672..11dac6bfd332493a60f02d8173167c8a3ff5b7ca 100644 (file)
@@ -58,4 +58,9 @@ __ssz gdb_arch_read_register(int regnr, struct __regs *regs,
 __ssz gdb_arch_write_register(int regnr, struct __regs *regs,
                              void *buf, __sz buf_len);
 
+static inline void gdb_arch_brk(void)
+{
+       __asm__ __volatile__("brk #0" : : : "memory");
+}
+
 #endif /* __UKDEBUG_ARCH_GDBSUP_H__ */
index 86da87aa49190284e9595a2d361d66908d64a52f..c17ad6fb2a17903cb432a6a1bf95ea2bccb60fca 100644 (file)
@@ -68,4 +68,9 @@ __ssz gdb_arch_read_register(int regnr, struct __regs *regs,
 __ssz gdb_arch_write_register(int regnr, struct __regs *regs,
                              void *buf, __sz buf_len);
 
+static inline void gdb_arch_brk(void)
+{
+       __asm__ __volatile__("int3" : : : "memory");
+}
+
 #endif /* __UKDEBUG_ARCH_GDBSUP_H__ */
index 7e775456803f19f8865730726c43d17cf36934c0..1764de2f1427d957c890db5c444ccc6ed2f1270e 100644 (file)
@@ -17,6 +17,8 @@
 #include <uk/console.h>
 #include <uk/console/driver.h>
 #include <uk/libparam.h>
+#include <uk/init.h>
+#include <uk/prio.h>
 
 #if CONFIG_HAVE_PAGING
 #include <uk/plat/paging.h>
@@ -1094,6 +1096,11 @@ int gdb_dbg_trap(int errnr, struct __regs *regs)
                return GDB_DBG_CONT;
        }
 
+       if (!gdb_backing_console) {
+               ukplat_lcpu_restore_irqf(irqs);
+               return -ENODEV;
+       }
+
        nest_cnt++;
        r = gdb_main_loop(&g);
        nest_cnt--;
@@ -1102,3 +1109,70 @@ int gdb_dbg_trap(int errnr, struct __regs *regs)
 
        return r;
 }
+
+static int gdb_setup_backing_console(void)
+{
+       /* Ignore breakpoints if the backing console is not registered.
+        * Maybe the backing console will be initialized later.
+        */
+       if (gdb_backing_console_id >= uk_console_count()) {
+               uk_pr_warn("con%" __PRIu16 " not available\n",
+                          gdb_backing_console_id);
+               return -ENXIO;
+       }
+
+       gdb_backing_console = uk_console_get(gdb_backing_console_id);
+       UK_ASSERT(gdb_backing_console);
+
+       uk_console_init(&gdb_virt_console, "GDB virtual console",
+                       &gdb_virt_ops, 0); /* Don't enable flags yet */
+       if (gdb_mode_is_shared()) {
+               uk_console_register(&gdb_virt_console);
+       } else if (gdb_mode_is_auto()) {
+               /* Must check before diabling the flags (see below) */
+               if (gdb_backing_console->flags)
+                       uk_console_register(&gdb_virt_console);
+       }
+
+       /* If we didn't disable the STDOUT and STDIN flags, calls to
+        * `uk_console_[out|in]` that happen outside of the gdb stub
+        * could mess up the gdb communications. E.g., a call to
+        * `uk_console_out` could write some bytes to the device that
+        * look like garbage to the gdb host on the other end.
+        * We assume here that the backing device is registered with
+        * `ukconsole` exactly  once. This means we can also assume that
+        * disabling the STDOUT and STDIN flags makes sure that the
+        * underlying device is never used except explicitly stated with
+        * `uk_console_[out|in]_with(gdb_backing_console, ...)`.
+        */
+       gdb_backing_console->flags &= ~UK_CONSOLE_FLAG_STDOUT;
+       gdb_backing_console->flags &= ~UK_CONSOLE_FLAG_STDIN;
+
+       gdb_virt_console.flags |= UK_CONSOLE_FLAG_STDOUT;
+
+       return 0;
+}
+
+static int gdb_entry(struct uk_init_ctx *ctx __unused)
+{
+       /* This is the first time that the stub is entered. */
+       UK_ASSERT(!gdb_backing_console);
+
+       if (gdb_backing_console_id == GDB_BACKING_CONSOLE_NONE) {
+               uk_pr_info("GDB stub not configured\n");
+               return 0;
+       }
+
+       uk_pr_info("Waiting for debugger connection on con%" __PRIu16 "...\n",
+                  gdb_backing_console_id);
+
+       if (gdb_setup_backing_console() < 0)
+               uk_pr_warn("con%" __PRIu16 " does not exist or is not ready yet. Skipping wait for debugger ...\n",
+                          gdb_backing_console_id);
+       else
+               gdb_arch_brk();
+
+       return 0;
+}
+
+uk_plat_initcall_prio(gdb_entry, 0, UK_PRIO_LATEST);