The Linux kernel does not reliably auto-detect serial consoles on headless systems. While the docs claim serial is used as a fallback when no VGA card is found, in practice CONFIG_VT's dummy console (dummycon) registers early and satisfies the kernel's console requirement, preventing the serial fallback from ever triggering. The ACPI SPCR table can help on ARM/RISC-V where QEMU generates it, but x86 QEMU does not produce SPCR, and SPCR cannot describe virtio consoles at all. This means UKIs booted via sd-stub in headless VMs produce no visible console output unless console= is explicitly passed on the kernel command line. Fix this by having sd-stub auto-detect the console type and append an appropriate console= argument when one isn't already present. Detection priority: 1. VirtIO console PCI device (vendor 0x1AF4, device 0x1003): if exactly one is found, append console=hvc0. This takes highest priority since a VirtIO console is explicitly configured by the VMM (e.g. systemd-vmspawn's virtconsole device). If multiple VirtIO console devices exist, we cannot determine which hvc index is correct, so we skip this path entirely. 2. EFI Graphics Output Protocol (GOP): if present, don't add any console= argument. The kernel will use the framebuffer console by default, and adding a serial console= would redirect the primary console away from the display. 3. Serial console: first, we count the total number of serial devices via EFI_SERIAL_IO_PROTOCOL. If there are zero or more than one, we bail out — with multiple UARTs, the kernel assigns ttyS indices based on its own enumeration order and we cannot determine which index the console UART will receive. Only when exactly one serial device exists (guaranteeing it will be ttyS0) do we proceed to verify it's actually used as a console by checking for UART device path nodes (MESSAGING_DEVICE_PATH + MSG_UART_DP). The firmware's ConOut handle is checked first; if it has no device path (common with OVMF's ConSplitter virtual handle when using -nographic -nodefaults), we fall back to enumerating all EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL handles and checking each one's device path. The architecture-specific console argument is then appended: - x86: console=ttyS0 - ARM: console=ttyAMA0 - Others: console=ttyS0 (RISC-V, LoongArch, MIPS all use ttyS0) Note on OVMF's VirtioSerialDxe: it exposes virtio serial ports with the same UART device path nodes as real serial ports (ACPI PNP 0x0501 + MSG_UART_DP), making them indistinguishable from real UARTs via device path inspection alone. This is why we check for the VirtIO console PCI device via EFI_PCI_IO_PROTOCOL before falling back to device path analysis. Also add a minimal EFI_PCI_IO_PROTOCOL definition (proto/pci-io.h) with just enough to call Pci.Read for vendor/device ID enumeration, and add the MSG_UART_DP subtype to the device path header. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
System and Service Manager
Details
Most documentation is available on systemd's web site.
Assorted, older, general information about systemd can be found in the systemd Wiki.
Information about build requirements is provided in the README file.
Consult our NEWS file for information about what's new in the most recent systemd versions.
Please see the Code Map for information about this repository's layout and content.
Please see the Hacking guide for information on how to hack on systemd and test your modifications.
Please see our Contribution Guidelines for more information about filing GitHub Issues and posting GitHub Pull Requests.
When preparing patches for systemd, please follow our Coding Style Guidelines.
If you are looking for support, please contact our mailing list, join our IRC channel #systemd on libera.chat or Matrix channel
Stable branches with backported patches are available in the stable repo.
We have a security bug bounty program sponsored by the Sovereign Tech Fund hosted on YesWeHack
Repositories with distribution packages built from git main are available on OBS, and also repositories with packages built from the latest stable release
