microsoft/openvmm
Publicmirrored fromhttps://github.com/microsoft/openvmmAvailable
Guide/src/reference/dev_feats/gdbstub.md
171lines · modecode
| 1 | # Hardware Debugging (gdbstub) |
| 2 | |
| 3 | Think EXDI from Hyper-V, except instead of using the EXDI interface, we use the |
| 4 | [GDB Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html) |
| 5 | (via the [`gdbstub`](https://github.com/daniel5151/gdbstub/) Rust library). |
| 6 | |
| 7 | Hardware debugging has several benefits over using an in-guest / in-kernel debugger: |
| 8 | |
| 9 | - Debugging early-boot scenarios (before UEFI / Windows / Linux debuggers are set up) |
| 10 | - Debugging low-level ISRs |
| 11 | - Non-intrusive debugging = easier to repro certain bugs |
| 12 | - Debugging SNP/TDX/VBS Confidential VMs |
| 13 | |
| 14 | ## Enabling the Debugger |
| 15 | |
| 16 | ### OpenVMM |
| 17 | |
| 18 | 1. Pass the `--gdb <port>` flag at startup to enable the |
| 19 | debug worker. e.g., `--gdb 9001` |
| 20 | |
| 21 | To pause the VM until the debugger has been attached, pass `--paused` at startup. |
| 22 | |
| 23 | ### OpenHCL |
| 24 | |
| 25 | 1. Pass the `OPENHCL_GDBSTUB=1` `OPENHCL_GDBSTUB_PORT=<gdbstub port>` parameters to enable gdbstub. e.g., `Set-VmFirmwareParameters -Name UhVM -CommandLine OPENHCL_GDBSTUB=1 OPENHCL_GDBSTUB_PORT=5900`. |
| 26 | 2. To expose a TCP port, run `ohcldiag-dev.exe <name> vsock-tcp-relay --allow-remote --reconnect <gdbstub port> <tcp port>`. |
| 27 | |
| 28 | To pause VTL0 boot until desired, pass `OPENHCL_VTL0_STARTS_PAUSED=1` as a parameter. Then once the debugger is attached, you can start VTL0 with `ohcldiag-dev.exe <name> resume`. |
| 29 | |
| 30 | ## Connecting via GDB |
| 31 | |
| 32 | The quickest way to get connected to a OpenVMM VM is via `gdb` directly. |
| 33 | |
| 34 | Note that GDB does _not_ support debugging PDBs, so if you're trying to debug |
| 35 | Windows, you'll be limited to plain disassembly. See the [`Connecting via |
| 36 | WinDbg`](#connecting-via-windbg) section below if this is your use-case. |
| 37 | |
| 38 | On the flipside, if you're trying to debug ELF images with DWARF debug info |
| 39 | (e.g., a vmlinux binary), then you'll likely want to use `gdb` directly, as it |
| 40 | will support source-level debugging with symbols, whereas WinDbg will not. |
| 41 | |
| 42 | You can install `gdb` via your distro's package manager. e.g., on Ubuntu: |
| 43 | |
| 44 | ```bash |
| 45 | sudo apt install gdb |
| 46 | ``` |
| 47 | |
| 48 | Once `gdb` is installed, run it, and enter the following `gdb` command (swapping |
| 49 | `9001` for whatever port you specified at the CLI) |
| 50 | |
| 51 | ```text |
| 52 | target remote :9001 |
| 53 | ``` |
| 54 | |
| 55 | If all goes well, you should get output similar to this: |
| 56 | |
| 57 | ```text |
| 58 | (gdb) target remote :9001 |
| 59 | Remote debugging using :9001 |
| 60 | warning: No executable has been specified and target does not support |
| 61 | determining executable automatically. Try using the "file" command. |
| 62 | 0xfffff8015c054c1f in ?? () |
| 63 | (gdb) |
| 64 | ``` |
| 65 | |
| 66 | At this point, you can try some basic GDB commands to make sure things are working. |
| 67 | |
| 68 | e.g., start / interrupt the VM's execution using `cont` and `ctrl-c` |
| 69 | |
| 70 | ```text |
| 71 | (gdb) cont |
| 72 | Continuing. |
| 73 | ^C # <-- hit ctrl-c in the terminal |
| 74 | Thread 1 received signal SIGINT, Interrupt. |
| 75 | 0xfffff8015c054c1f in ?? () |
| 76 | (gdb) |
| 77 | ``` |
| 78 | |
| 79 | e.g., inspecting register state |
| 80 | |
| 81 | ```text |
| 82 | (gdb) info registers |
| 83 | rax 0x0 0 |
| 84 | rbx 0x0 0 |
| 85 | rcx 0x40086 262278 |
| 86 | rdx 0x0 0 |
| 87 | rsi 0xffff960d4eea5010 -116491073990640 |
| 88 | rdi 0x0 0 |
| 89 | rbp 0x0 0x0 |
| 90 | rsp 0xfffff8015b3f5ec8 0xfffff8015b3f5ec8 |
| 91 | r8 0x0 0 |
| 92 | r9 0xffffffff 4294967295 |
| 93 | r10 0xfffff8015bfff1f0 -8790254554640 |
| 94 | r11 0x0 0 |
| 95 | r12 0xffffffff 4294967295 |
| 96 | ...etc... |
| 97 | ``` |
| 98 | |
| 99 | e.g., setting data breakpoints |
| 100 | |
| 101 | ```text |
| 102 | (gdb) awatch *0xfffff804683190e0 |
| 103 | Hardware access (read/write) watchpoint 1: *0xfffff804683190e0 |
| 104 | ``` |
| 105 | |
| 106 | e.g., single stepping |
| 107 | |
| 108 | ```text |
| 109 | 0xfffff8047a309686 in ?? () |
| 110 | (gdb) si |
| 111 | 0xfffff8047a309689 in ?? () |
| 112 | ``` |
| 113 | |
| 114 | You may find [this blog post](https://blog.mattjustice.com/2018/08/24/gdb-for-windbg-users/) |
| 115 | useful, as it includes a table of common `gdb` commands along with their WinDbg |
| 116 | counterparts. |
| 117 | |
| 118 | ## Connecting via WinDbg |
| 119 | |
| 120 | WinDbg doesn't understand the GDB Remote Serial Protocol directly, but |
| 121 | thankfully, some smart folks over on the WinDbg team have developed a GDB Remote |
| 122 | Serial Protocol <-> WinDbg translation layer! |
| 123 | |
| 124 | For more information, see |
| 125 | [Setting Up QEMU Kernel-Mode Debugging using EXDI](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-qemu-kernel-mode-debugging-using-exdi) |
| 126 | |
| 127 | Getting this working with OpenVMM or OpenHCL is as easy as following the guide, |
| 128 | except you'll need to [enable our debugger](#enabling-the-debugger) instead of |
| 129 | running QEMU. |
| 130 | |
| 131 | It's easiest to connect through the GUI. The steps are relatively simple: Open Windbgx -> File -> Attach to kernel -> EXDI. On the form, fill out: |
| 132 | |
| 133 | - Target Type: `QEMU` |
| 134 | - Target Architecture: `X64` |
| 135 | - Target OS: `Windows` |
| 136 | - Image Screening heuristic size: `0xFFFE - NT` |
| 137 | - Gdb server and port: `<server>:<port>` e.g., `127.0.0.1:1337` (use whatever port you set above) |
| 138 | |
| 139 | ### Known WinDbg Bugs |
| 140 | |
| 141 | - Hardware breakpoints are issued with [`ba`](https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/ba--break-on-access-). The `Access Size` parameter is incorrectly multiplied by 8 when sent to the stub. Consequently, it _must_ be set to 1. |
| 142 | - Unlike GDB, WinDbg doesn't implicitly set software breakpoints via our offered write_addrs implementation. |
| 143 | |
| 144 | --- |
| 145 | |
| 146 | ## Supported Features |
| 147 | |
| 148 | At the time of writing (8/16/24) the debugger supports the following operations: |
| 149 | |
| 150 | - read/write guest memory |
| 151 | - read guest registers \* |
| 152 | - start/interrupt execution |
| 153 | - watchpoints |
| 154 | - hardware breakpoints |
| 155 | - single stepping |
| 156 | |
| 157 | ## TODO Features |
| 158 | |
| 159 | If you're looking for work, and want to improve the debugging experience for |
| 160 | everyone, consider implementing one or more of the following features: |
| 161 | |
| 162 | - \* reading _all_ guest registers, including fpu, xmm, and various key msrs |
| 163 | - software breakpoints: |
| 164 | - Intercept guest breakpoint exceptions into VTL2 |
| 165 | - writing guest registers |
| 166 | - exposing the OpenVMM interactive console via the |
| 167 | [`MonitorCmd`](https://docs.rs/gdbstub/latest/gdbstub/target/ext/monitor_cmd/trait.MonitorCmd.html) |
| 168 | interface |
| 169 | - Custom commands sent using `monitor` (gdb) / `.exdicmd` (WinDbg) |
| 170 | - e.g., being able to invoke `x device/to/inspect` directly from the debugger |
| 171 | - [any other features supported by the `gdbstub` library](https://github.com/daniel5151/gdbstub#debugging-features) |