mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
docs: Rewrite KDCOM debugging to use convey bridge
Replace the QEMU debugger VM approach with a simpler setup that runs WinDbg directly on a Windows host. Cloud Hypervisor exposes the guest serial port on a UNIX socket, socat turns it into a TCP listener, and the convey tool bridges the TCP endpoint to a named pipe that WinDbg attaches to. This drops the need for a second Windows VM and the socat SYSTEM shell script wrapping. Assisted-by: Claude:Opus-4.8 Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
@@ -1,51 +1,27 @@
|
||||
# Windows Kernel Debugging over serial (KDCOM)
|
||||
|
||||
This document describes serial-based (COM/KDCOM) kernel debugging of a Windows
|
||||
guest. The shell variables used below (`$WIN_ISO_FILE`, `$VIRTIO_ISO_FILE`,
|
||||
`$OVMF_DIR`, `$IMG_FILE`) are the ones introduced in the image preparation
|
||||
section of [Windows Support](windows.md).
|
||||
guest running under Cloud Hypervisor. The shell variables used below
|
||||
(`$OVMF_DIR`, `$IMG_FILE`) are the ones introduced in the image preparation
|
||||
section of [Windows Support](windows.md). The `$LINUX_HOST_IP` variable is the
|
||||
address of the Linux host that runs the debuggee.
|
||||
|
||||
The Windows guest debugging process relies heavily on QEMU and [socat](http://www.dest-unreach.org/socat/). The procedure requires two Windows VMs:
|
||||
The setup consists of two parts:
|
||||
|
||||
- A debugger VM running under QEMU.
|
||||
- A debuggee, a Windows VM that has been created in the previous steps, running under Cloud Hypervisor or QEMU.
|
||||
- A debuggee, the Windows guest running under Cloud Hypervisor on a Linux host.
|
||||
- A debugger, WinDbg running on any Windows machine that carries the debugging tools.
|
||||
|
||||
The connection between both guests happens over TCP, whereby on the guest side it is automatically translated to a COM port. Because the VMs are connected through TCP, the debugging infrastructure can be distributed over the network. The serial port, while slowly transferring data, is common enough to support a wide range of cases and tools.
|
||||
Cloud Hypervisor exposes the guest serial port on a UNIX socket. On the Linux host, [socat](http://www.dest-unreach.org/socat/) turns that socket into a TCP listener. On the Windows side, a bridge tool such as [convey](https://github.com/weltling/convey) turns the TCP endpoint back into a named pipe that WinDbg attaches to. Because the transport is TCP, the debugger and the debuggee can run on different machines across the network. The serial port, while slow, is common enough to support a wide range of cases and tools.
|
||||
|
||||
In this exercise, [WinDbg](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/) is used. Any other debugger of choice with the ability to use serial connection can be used instead.
|
||||
In this exercise, [WinDbg](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/) is used. Any other debugger of choice with the ability to use a serial connection can be used instead.
|
||||
|
||||
## Debugger and Debuggee
|
||||
|
||||
### WinDbg VM
|
||||
|
||||
For simplicity, the debugger VM is supposed to be only running under QEMU. It will require VGA and doesn't necessarily depend on UEFI. As an OS, it can carry any supported Windows OS where the debugger of choice can be installed. The simplest way is to follow the image preparation instructions from the previous chapter, but avoid using the OVMF firmware. It is also not required to use VirtIO drivers, whereby it might be useful in some case. Though, while creating the image file for the debugger VM, be sure to choose a sufficient disk size that counts in the need to save the corresponding debug symbols and sources.
|
||||
|
||||
To create the debugger Windows VM, the following command can be used:
|
||||
|
||||
```shell
|
||||
qemu-system-x86_64 \
|
||||
-machine q35,accel=kvm \
|
||||
-cpu host \
|
||||
-smp 1 \
|
||||
-m 4G \
|
||||
-cdrom ./$WIN_ISO_FILE \
|
||||
-drive file=./$VIRTIO_ISO_FILE,index=0,media=cdrom \
|
||||
-drive if=none,id=root,file=./windbg-disk.raw \
|
||||
-device virtio-blk-pci,drive=root,disable-legacy=on \
|
||||
-device virtio-net-pci,netdev=mynet0,disable-legacy=on \
|
||||
-netdev user,id=mynet0,net=192.168.178.0/24,host=192.168.178.1,dhcpstart=192.168.178.64,hostname=windbg-host \
|
||||
-vga std
|
||||
```
|
||||
|
||||
A non server Windows OS like Windows 10 can be used to carry the debugging tools in the debugger VM.
|
||||
|
||||
### Debuggee VM
|
||||
## Debuggee VM configuration
|
||||
|
||||
The debuggee VM is the one that we've learned to configure and run in the first section. There might be various reasons to debug. For example, there could be an issue in the Windows guest with an emulated device or an included driver. Or, we might want to develop a custom feature like a kernel driver to be available in the guest.
|
||||
|
||||
Note, that there are several ways to debug Windows, not all of them need to be enabled at the same time. For example, if developing a kernel module, the only useful options would be to configure for the serial debugging and enable the kernel debug. In that case, any crash or misbehavior in the boot loader or kernel would be ignored. The commands below must be run as administrator on the debuggee guest VM.
|
||||
|
||||
#### Turn On Serial Debugging
|
||||
### Turn On Serial Debugging
|
||||
|
||||
This will configure the debugging to be enabled and instruct to use the serial port for it.
|
||||
|
||||
@@ -53,25 +29,25 @@ This will configure the debugging to be enabled and instruct to use the serial p
|
||||
bcdedit /dbgsettings serial debugport:1 baudrate:115200
|
||||
```
|
||||
|
||||
#### Turn On Kernel Debugging
|
||||
### Turn On Kernel Debugging
|
||||
|
||||
```cmd
|
||||
bcdedit /debug on
|
||||
```
|
||||
|
||||
#### Turn On Boot Loader Debug
|
||||
### Turn On Boot Loader Debug
|
||||
|
||||
```cmd
|
||||
bcdedit /bootdebug on
|
||||
```
|
||||
|
||||
#### Turn on boot manager debug
|
||||
### Turn on boot manager debug
|
||||
|
||||
```cmd
|
||||
bcdedit /set {bootmgr} bootdebug on
|
||||
```
|
||||
|
||||
#### Disable Recovery Screen On Boot Failure
|
||||
### Disable Recovery Screen On Boot Failure
|
||||
|
||||
There could be a situation, where a crash is debugged. In such cases, the guest could be left in an inconsistent state. The default Windows behavior would be to boot into the recovery screen, however in some cases it might be not desired. To make Windows ignore failures and always proceed to booting the OS, use the command below:
|
||||
|
||||
@@ -81,67 +57,43 @@ bcdedit /set {default} bootstatuspolicy ignoreallfailures
|
||||
|
||||
## Debugging Process
|
||||
|
||||
### Invoke the WinDbg VM
|
||||
### On the Linux host
|
||||
|
||||
Run the Windows guest under Cloud Hypervisor and expose the guest serial port on a UNIX socket. Attach `--console off` so that the serial port carries the debugging protocol rather than a console:
|
||||
|
||||
```shell
|
||||
qemu-system-x86_64 \
|
||||
-machine q35,accel=kvm \
|
||||
-cpu host \
|
||||
-smp 1 \
|
||||
-m 4G \
|
||||
-drive if=none,id=root,file=./windbg-disk.raw \
|
||||
-device virtio-blk-pci,drive=root,disable-legacy=on \
|
||||
-serial tcp::4445,server,nowait \
|
||||
-device virtio-net-pci,netdev=mynet0,disable-legacy=on \
|
||||
-netdev user,id=mynet0,net=192.168.178.0/24,host=192.168.178.1,dhcpstart=192.168.178.64,hostname=windbg-host \
|
||||
-vga std
|
||||
sudo ./target/release/cloud-hypervisor \
|
||||
--cpus boot=4 \
|
||||
--memory size=8192M \
|
||||
--kernel ./$OVMF_DIR/CLOUDHV.fd \
|
||||
--disk path=./$IMG_FILE \
|
||||
--console off \
|
||||
--serial socket=/tmp/serial.sock \
|
||||
--net tap=tap0
|
||||
```
|
||||
|
||||
Note, this VM has the networking enabled. It is needed, because symbols and sources might need to be fetched from a network location.
|
||||
|
||||
Also, notice the `-serial` parameter - that's what does the magic on exposing the serial port to the guest while connecting the debugger VM with a client VM through the network. SAC/EMS needs to be disabled in the debugger VM, as otherwise the COM device might be blocked.
|
||||
|
||||
Hereafter, WinDbg can be started using a command below:
|
||||
|
||||
```cmd
|
||||
set _NT_DEBUG_PORT=com1
|
||||
set _NT_DEBUG_BAUD_RATE=115200
|
||||
|
||||
windbg -v -d -k
|
||||
```
|
||||
|
||||
Once started, WinDbg will wait for an incoming connection which is going to be initialized by the debuggee VM started in the next section.
|
||||
|
||||
### Invoke the Debuggee VM
|
||||
|
||||
#### Under QEMU
|
||||
|
||||
Essentially it would be the command like depicted in the guest preparation sections, with a few modifications:
|
||||
Then use `socat` to expose the UNIX socket as a TCP listener. Pick a free port, here `4445`:
|
||||
|
||||
```shell
|
||||
qemu-system-x86_64 \
|
||||
-machine q35,accel=kvm \
|
||||
-cpu host \
|
||||
-m 4G \
|
||||
-bios ./$OVMF_DIR/OVMF_CODE.fd \
|
||||
-cdrom ./$WIN_ISO_FILE \
|
||||
-drive file=./$VIRTIO_ISO_FILE,index=0,media=cdrom \
|
||||
-drive if=none,id=root,file=./$IMG_FILE \
|
||||
-device virtio-blk-pci,drive=root,disable-legacy=on \
|
||||
-device virtio-net-pci,netdev=mynet0,disable-legacy=on \
|
||||
-netdev user,id=mynet0 \
|
||||
-serial tcp:127.0.0.1:4445 \
|
||||
-vga std
|
||||
sudo socat TCP-LISTEN:4445,reuseaddr,fork UNIX-CONNECT:/tmp/serial.sock
|
||||
```
|
||||
|
||||
It is to see, that `-serial` parameter is used here, to establish the connection with the debugger VM.
|
||||
The debugger connects to this TCP port. If the debugger runs on a different machine, use the address of the Linux host in the next step.
|
||||
|
||||
To disable HPET, attach `--no-hpet`. To enable hypervisor reference timer, use `-cpu host,hv-time`. These and other options can be used to achieve better [Hyper-V compatibility](https://archive.fosdem.org/2019/schedule/event/vai_enlightening_kvm/attachments/slides/2860/export/events/attachments/vai_enlightening_kvm/slides/2860/vkuznets_fosdem2019_enlightening_kvm.pdf).
|
||||
### On the Windows host
|
||||
|
||||
#### Cloud Hypervisor
|
||||
WinDbg runs on any Windows machine that carries the debugging tools. In this setup the serial over TCP endpoint exposed above has to appear as a local COM port or named pipe that WinDbg can attach to. The [convey](https://github.com/weltling/convey) tool does this bridging and is the one that has been tested to work here. There are other open source and commercial COM to TCP bridge solutions that advertise similar capabilities, such as [com0com](https://sourceforge.net/projects/com0com/) with its com2tcp and hub4com companions, HW Virtual Serial Port, or Serial to Ethernet Connector, but they have not been tried in this setup.
|
||||
|
||||
The `socat` tool is used to establish the QEMU compatible behavior. Here as well, the Cloud Hypervisor command used to run the Windows guest is to be used. Put the command into a shell script:
|
||||
Start the bridge, pointing it at the Linux host address and the port chosen for `socat`:
|
||||
|
||||
`socat SYSTEM:"./ch-script",openpty,raw,echo=0 TCP:localhost:4445`
|
||||
```powershell
|
||||
convey --bridge --pipe-server \\.\pipe\kd0 tcp:$LINUX_HOST_IP:4445 --verbose
|
||||
```
|
||||
|
||||
The reason to pack the command into the shell script is that the command might contain a comma. When using SYSTEM, the shell command can't contain `,` or `!!`.
|
||||
Then attach WinDbg to the named pipe. The `resets=0` and `reconnect` options let the session survive target resets:
|
||||
|
||||
```powershell
|
||||
windbg -k com:pipe,port=\\.\pipe\kd0,resets=0,reconnect
|
||||
```
|
||||
|
||||
The bridge carries raw bytes only and reconnects on its own, which lets it survive a debuggee reset. Once WinDbg is attached and the debuggee VM boots, the kernel debugging session is established.
|
||||
|
||||
Reference in New Issue
Block a user