the art of firmware logging
If something goes wrong if usually is very helpful to have log files at hand. Virtual machine firmware is no exception here. So, lets have a look at common practices here.
qemu debug console
On the x86 platform qemu provides a isa-debugcon
device. That is the simplest possible logging device, featuring a
single ioport. Reading from the ioport returns a fixed value,
which can be used to detect whenever the device is present or not.
Writing to the ioport sends the character written to the chardev
backend linked to device.
By convention the qemu firmware -- both seabios and OVMF -- uses the ioport address 0x402. So getting the firmware log printed on your terminal works this way:
qemu-system-x86_64 \
-chardev stdio,id=fw \
-device isa-debugcon,iobase=0x402,chardev=fw
When using libvirt you can use this snippet (in
the <devices> section) to send the firmware log
to file:
<serial type='null'>
<log file='/path/to/firmware.log' append='off'/>
<target type='isa-debug' port='1'>
<model name='isa-debugcon'/>
</target>
<address type='isa' iobase='0x402'/>
</serial>
Note that virsh console will connect to the first
serial device specified in the libvirt xml, so this should be
inserted after other serial devices to avoid breaking your
serial console setup.
edk2 firmware on arm
On the arm virt platform there is no special device for the firmware log. The logs are sent to the serial port instead. That is inconvinient when using a serial console though, so linux distros typically ship two variants of the arm firmware image. One with logging turned on, and one with logging turned off (on RHEL and Fedora the latter have 'silent' in the filename). By default libvirt uses the silent builds, so in case you need the debug log you have to change the VM configuration to use the other variant.
Recently (end of 2023) the arm builds learned a new trick. In case two serial ports are present the output will be split. The first serial port is used for the console, the second port for the debug log. With that the console is actually usable with the verbose firmware builds. To enable that you simply need two serial devices in your libvirt config:
<serial type='pty'>
<log file='/path/to/console.log' append='off'/>
<target type='system-serial' port='0'>
<model name='pl011'/>
</target>
</serial>
<serial type='null'>
<log file='/path/to/firmware.log' append='off'/>
<target type='system-serial' port='1'>
<model name='pl011'/>
</target>
</serial>edk2 memory log buffer
Starting with the edk2-stable202508 tag OVMF supports
logging to a memory buffer. The feature is disabled by default and
must be turned on at compile time using the -D
DEBUG_TO_MEM=TRUE option when building the firmware.
There are multiple ways to access the log memory buffer. First is
a small efi application which can print the log to the efi console
(source
code,
x64
binary). Pass -p or pager as argument
on the command line to enable the build-in pager.
Second way is a recent linux kernel, version 6.17 got a new bool
config option: OVMF_DEBUG_LOG. When enabled the linux
kernel will make the firmware log available via sysfs. If supported
by both kernel and firmware the log will show up in
the /sys/firmware/efi directory with the
filename ovmf_debug_log.
Third option is a qemu monitor command. The changes just landed in
qemu master branch and will be available in the next release qemu
(10.2) expected later this year. Both a qmp command
(query-firmware-log) and a hmp command (info
firmware-log) are availablke. This is useful to diagnose
firmware failures happen early enough in boot that the other options
can not be used.