Skip to content

Debugger QEMU Platforms

This document described different ways to debug on the QEMU platforms.

Debugging using UEFI debugger

Both Q35 and SBSA are setup to debug debugged using the MU debugger package. for more details on using this debugger, see the FeatureDebuggerPkg Readme.

By default the debugger is enabled to break in on exceptions, but not to break in on an initial breakpoint. To enable the initial breakpoint in DXE, you must pass:

stuart_build -c Platforms\QemuQ35Pkg\PlatformBuild.py BLD_*_DXE_DBG_BRK=TRUE --FlashRom

On Q35 this allows for debugging over a different port then the usual debug output because Q35 has a seperate serial port available to it. On SBSA the serial port will be shared with the logging output. As a result, on Q35, if SERIAL_PORT=<PORT> is not passed on the cmdline, a default serial port at 50001 will be created. On SBSA, no default port is set because it will prevent output from occuring on stdout, which makes even telling if an exception occurred difficult.

Currently this will only enable the DXE debugger. The MM debugger will be added to Q35 once it support supervised Standalone MM.

When an exception occurs, the debugger will break in and wait for a debugger client to connect. This makes Q35 and SBSA very debuggable, because they will break in on exceptions always, meaning a reboot is not required.

Debugging using QEMU GDB Server

QEMU has the ability to expose a GDB port for debugging. This can be leveraged several ways. To start enable the GDB server, add the following parameter when launched QEMU through the QEMU runner.

GDB_SERVER=<port number>

Example ports can be: 1234, 5000, 5001, etc.

Windbg Integration

Windbg supports source debugging QEMU through an EXDI interface with the GDB port. Details can be found on the github readme. This supplies a EXDI server binary, a configuration file, and a script to start Windbg bound to the EXDI interface.

Once Windbg is connected, following the instruction in the readme, the symbols and source can be loaded. This is most easily done using the UEFI debugger extension. This can also be done manually by scanning memory for images using the image scan command. For example:

kd> r rip
rip=000000007d1fc64b
kd> .imgscan /l /r 0x07d000000 0x07e000000

This will scan for image headers between the specified addresses and load their symbols. More information on this command can be found in the Windbg help window.

Debugging Using GDB in VS Code

For GCC builds, GDB can be used to debug instead. The symbols can be loaded by running source MU_BASECORE/BaseTools/Scripts/efi_gdb.py from within GDB while stopped. To connect GDB to the built in VS Code debugger, you can use the following launch configuration to connect to a running instance of QEMU.

{
    "name": "Connect to GDB Server (X64)",
    "type": "cppdbg",
    "request": "launch",
    "program": "${workspaceRoot}/Build/QemuQ35Pkg/DEBUG_GCC5/X64/DxeCore.debug",
    "miDebuggerServerAddress": "localhost:1234",
    "cwd": "${workspaceRoot}",
    "environment": [],
    "MIMode": "gdb",
    "miDebuggerPath": "gdb",
    "stopAtConnect": true
},
{
    "name": "Connect to GDB Server (AARCH64)",
    "type": "cppdbg",
    "request": "launch",
    "program": "${workspaceRoot}/Build/QemuSbsaPkg/DEBUG_GCC5/AARCH64/DxeCore.debug",
    "miDebuggerServerAddress": "localhost:1234",
    "cwd": "${workspaceRoot}",
    "environment": [],
    "MIMode": "gdb",
    "miDebuggerPath": "gdb-multiarch",
    "stopAtConnect": true
},

Note that the program value must be a legitimate binary, but does not seem to have any affect on the debugging. Additionally, gdb may need to be used instead of gdb if debugging x64 from an x64 machine.

Once attached, you can stop and run -exec source MU_BASECORE/BaseTools/Scripts/efi_gdb.py from the DEBUG CONSOLE tab to load the EFI symbols and commands. You may need to single step after this for symbols to take affect.

Debugging Windows on QEMU

Boot to OS

In order to kernel debug the OS, once need to boot to OS first.

  1. Download original OS image in the format of VHDX or QCOW2. If the image has never been booted before, one can boot this image using Hyper-V once to get through the OOBE process, if any. Related reads:

  2. Although QEMU supports both VHDX and QCOW2 images. For the sake of fail proof, it is recommended to use QCOW2 format image, which can be converted from VHDX image with:

    qemu-img convert -f vhdx -p -c -O qcow2 foo.vhdx foo.qcow2

  3. With the above QCOW2 image, set up the PATH_TO_OS parameter when launching QemuRunner.py:

    PATH_TO_OS=<absolute path to your OS image> SERIAL_PORT=<port number>

Debug Windows with WinDbg on QEMU

Unlike EXDI debugger introduced above, this method will enlighten the target OS and debug the Windows more "traditionally", where Windows can communicate to the attached debugger with proper transports.

Configurations on Target QEMU

After booting to the desktop or command prompt of target Windows by following above steps, issue below commands:

bcdedit /dbgsettings serial debugport:1 baudrate:115200
bcdedit /set {default} debug on
# One can potentially enable boot debugger as well
bcdedit /set {default} bootdebug on

Configurations on Host that runs QEMU

Once the QEMU boots to the point where the target is accepting debugger commands, launch the WinDbg using:

windbg.exe -k com:ipport=<port number>,port=127.0.0.1 -v

where the <port number> is the number you set when launching QEMU

Serial Console for UEFI

Launch terminal application such as Putty or Tera Term to connect to <port number> you set when launching QEMU at 127.0.0.1 through raw TCP/IP protocol.

Note:

  • One needs to release this console in order for the KD to attach.
  • Some terminal software would enable "local line editing" for raw connection, this needs to be turned off to prevent garbage keystrokes.