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.