How to Use QEMUs “debugcon”-device (and Write Debug Information to the Terminal or a File)
Update 2024-01-30: The same underlying mechanism was just merged in Cloud Hypervisor as well! It is exposed as --debug-console <target>
. This article mostly applies to Cloud Hypervisor as well.
Problem: Getting Early Debug Output as Kernel Developer
QEMU is a VMM often used for low-level OS/kernel development and testing. When you develop your own kernel or firmware, things can get really hard and difficult to debug sometimes. Especially when something badly fails in early boot phases, you usually can’t write to the file system or the screen, as the relevant sub system is not initialized yet. So how can you see what happens inside the guest and find out why it fails?
Solution: The debugcon device
The QEMU debugcon
device is a very easy solution to that problem! At least, when you are using QEMU with x86/x86_64, as this feature is limited to x86. “debugcon” can also be read as “debug console” or “debug connection”.
It is a part of the virtual hardware a guest sees, just like the other devices (LAPIC, PIT, PIC, serial device). Guests interact with it no different from other hardware. The debugcon
device is only exposed via port I/O, not vie MMIO.
QEMU exposes the debugcon
device at I/O port 0xe9
to a guest. It needs zero configuration from a guest, and you can directly start writing ASCII, UTF-8, or whatever data you like to it. On the VMM side, one can connect the I/O port to the terminal, a file, or a device, such as -debugcon stdio
, -debugcon file:debugcon.txt
, or -debugcon /dev/<device-file>
.
It is similar to the -serial
device, but needs no configuration and its throughput is not artificially limited by the (emulated) baud rate. But the serial device also accepts input (VMM to guest), whereas the debugcon device only outputs data.
Write to debugcon Device from a Guest
You need the outb
instruction of x86
. If you are not writing a program in pure assembly, you have to use inline assembly or use a library, such as the x86 crate.
Your code might look like this:
// The check for QEMU is not necessarily needed. On most or even all platforms, // writes to unknown ports should be ignored. if runs_inside_qemu() { unsafe { x86::io::outb(0xe9, b'H'); x86::io::outb(0xe9, b'e'); x86::io::outb(0xe9, b'l'); x86::io::outb(0xe9, b'l'); x86::io::outb(0xe9, b'o'); x86::io::outb(0xe9, b'\n'); } }
In this blog post, you can find a more comprehensive version of how to write to the device from code.
Example QEMU config
The following snippet shows how a working QEMU configuration. ./kernel
is a bootable kernel that prints to the debugcon device:
$ qemu-system-x86_64 -kernel ./kernel -debugcon stdio
Trivia
As many things in low-level space, QEMU (and especially the debug console) is poorly documented IMHO. However, the debug console is really handy to debug things, as it can be used in the earliest phases of the boot already. Usage is quite easy in the end, once you are familiar with it!
From my short research, the debug console originates from Bochs, a x86
emulator, but QEMU has it for some time now.
0 Comments