macintosh.world | Log In | Register
Today | News | Books | Recipes | Notes | YouTube | QuickTake
Translate | Wiki | Browse | Maps | Reference | Reddit | About

Introduction to UEFI HTTP(S) boot with Qemu/OVMF | Yet another enthusiast blog!

Open Original Page

Introduction to UEFI HTTP(S) boot with Qemu/OVMF | Yet another enthusiast blog!Yet another enthusiast blog!

There is no great achievement without great challenges.

Introduction to UEFI HTTP(S) boot with Qemu/OVMF

Jun 12, 2026
#qemu
#ovmf
#boot

The historic go-to solution for network booting is PXE. PXE is based on DHCP and
TFTP. It is tricky to correctly configure, even trickier to make it highly available and
good luck with the security with this clear-text unsigned protocol.

The modern web has long standardized on HTTPS with TLS certificates for server authentication,
integrity and confidentiality. Moreover, highly available setups are a solved problem when
it comes to HTTPS. Even better, the encryption layer makes it practical to boot over the
Internet without immediately facing the threat of a man-in-the-middle attack that would be
trivial with TFTP (remember, the leading t stands for "trivial", not "secure").

The good news is, most modern UEFI-based system support booting over HTTP(S).

In this post, we'll boot the snponly variant of netboot.xyz directly
from the official website. Be prepared for some fun with HTTPS.

All these tests were performed on Ubuntu 26.04 with the provided 1:10.2.1+ds-1ubuntu3 Qemu
and 2025.11-3ubuntu7 OVMF packages, unless otherwise stated. Note that, for reasons that will
become clear later in this post, older versions might actually work better 🙃.

Starting with the simple case: HTTP boot discovered over DHCP

(Righteously) Suspecting that the HTTPS variant would be a tough beast to beat, I started
this journey with a first test that side steps the certificate trust and other quirks at the
beginning.

The URL for the boot firmware is: http://boot.netboot.xyz/ipxe/netboot.xyz-snponly.efi.

The aim here is to demonstrate a minimal setup to make it easier to integrate it in your own
environment. We'll use a non-root Qemu machine with userland-based SLIRP networking and no
additional devices like storage for instance. The whole system will run in-memory.

Let's start with a first iteration based on:

The OVMF firmware, without secure boot to keep things simple

A network card that will emulate a DHCP server and point the UEFI to the HTTP boot target

Output on the console

1qemu-system-x86_64 \
2 -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
3 -nic user,bootfile=http://boot.netboot.xyz/ipxe/netboot.xyz-snponly.efi \
4 -nographic

This is promising, but it fails to boot over the network:

1BdsDxe: No bootable option or device was found.
2BdsDxe: Press any key to enter the Boot Manager Menu.

This is because the network stack in the OVMF requires a random number generator device to
work. When you think about it, this is almost obvious. Pretty much each layer of the network
stack requires randomness, from Ethernet collision avoidance to TLS through DHCP itself.

This can be enabled very easily by adding the following flag to the Qemu command line:

1-device virtio-rng-pci

Any other configuration change that would provide a random number generator would work equally
well. For example, one could enable KVM and use -cpu host which would grant access to the CPU's
random number generation instructions.

While somewhat obvious, this is pretty hard to figure out without help because of the lack of
error logs. In this case, I was helped by asking the free version of Claude.

I was wondering how one could figure this out by induction/deduction rather than brute-forcing
it with a (very helpful) LLM. It turns out the dependency is declared in the [Depex] section
of NetworkPkg/Library/DxeNetLib/DxeNetLib.inf:

1[Depex]
2 gEfiRngProtocolGuid

Under the hood, this pushes the GUID of the EFI random number generation protocol on the
dependency stack so that any EFI package linking against the DxeNetLib will implicitly require
it when the dispatcher evaluates the dependencies at runtime.

For future reference (to myself), one could work out the dependency at runtime from the
debug logs by enabling the "DEBUG_DISPATCH" flag of gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel
in OvmfPkg/OvmfPkgX64.dsc. Be prepared for a world of GUID based debugging!

(If you are frustrated by the brute-force approach and would rather see a log-based approach,
stay tuned for the HTTPS part.)

With this in place, tadaa!

1>>Start PXE over IPv4.
2 Station IP address is 10.0.2.15
3
4 Server IP address is 10.0.2.2
5 NBP filename is http://boot.netboot.xyz/ipxe/netboot.xyz-snponly.efi
6 NBP filesize is 0 Bytes
7 PXE-E23: Client received TFTP error from server.
8BdsDxe: failed to load Boot0002 "UEFI PXEv4 (MAC:525400123456)" from PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)/IPv4(0.0.0.0,0x0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0): Not Found
9
10>>Start PXE over IPv6.
11 PXE-E16: No valid offer received.
12BdsDxe: failed to load Boot0003 "UEFI PXEv6 (MAC:525400123456)" from PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)/IPv6(0000:0000:0000:0000:0000:0000:0000:0000,0x0,Static,0000:0000:0000:0000:0000:0000:0000:0000,0x40,0000:0000:0000:0000:0000:0000:0000:0000): Not Found
13
14>>Start HTTP Boot over IPv4....
15 Station IP address is 10.0.2.15
16
17 URI: http://boot.netboot.xyz/ipxe/netboot.xyz-snponly.efi
18 File Size: 310784 Bytes
19 Downloading...100%BdsDxe: loading Boot0004 "UEFI HTTPv4 (MAC:525400123456)" from PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)/IPv4(0.0.0.0,0x0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0)/Uri()
20BdsDxe: starting Boot0004 "UEFI HTTPv4 (MAC:525400123456)" from PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)/IPv4(0.0.0.0,0x0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0)/Uri()
21iPXE initialising devices...
22autoexec.ipxe... ok
23
24
25
26iPXE 2.0.0+ (g36e8c) -- Open Source Network Boot Firmware -- https://ipxe.org
27Features: DNS HTTP HTTPS iSCSI NFS TFTP VLAN AoE EFI Menu
28netboot.xyz - v3.x
29Hit the m key to open failsafe menu...

Did I mention that reaching this point takes ~1m 15s? 🐌

Speeding up HTTP boot, through configuration

One minute just to reach the one boot attempt we are interested in is horribly long and
not anywhere near an acceptable target. Now that it works, we can try to make it fast.
The good news is that it is relatively easy.

The UEFI network stack sequentially tries:

IPv4 PXE: The IPv4 part works, but the HTTP URL is invalid, a TFTP reference is expected.

IPv6 PXE: Nothing works as no IPv6 is configured.

IPv4 HTTP: This finally works.

IPv6 HTTP: This would have been the following attempt.

Ideally, we would need a mechanism to instruct the OVMF to avoid losing time on the legacy
PXE stuff.

Fortunately, Qemu has a way to pass options to the firmware using the -fw_cfg flag and
(some) documentation of the available OVMF settings is even available here:
https://github.com/tianocore/edk2/blob/master/OvmfPkg/RUNTIME_CONFIG.md

From the documentation, we can find good candidates:

opt/org.tianocore/IPv4PXESupport

opt/org.tianocore/IPv6PXESupport

We can even see etc/edk2/https/cacerts. This might come in handy for the next part. Who knows?

Wiring it all together, the final Qemu command line for HTTP boot looks like:

1qemu-system-x86_64 \
2 -device virtio-rng-pci \
3 -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
4 -nic user,bootfile=http://boot.netboot.xyz/ipxe/netboot.xyz-snponly.efi \
5 -fw_cfg name=opt/org.tianocore/IPv4PXESupport,string=no \
6 -fw_cfg name=opt/org.tianocore/IPv6PXESupport,string=no \
7 -nographic

This will boot to netboot.xyz in roughly 5 seconds, which is far more acceptable.

Another way to HTTP boot, through UEFI variables

We have reached a point that works but is hard to transpose to real hardware. On real
hardware, we do not have these convenient OVMF runtime tunables. What we have instead
are UEFI variables. Could we achieve the same replacing Qemu trickery with UEFI variables?
Ideally, we would need to pre-generate the variables before the first start of the VM
to reach parity with the previous approach.

(OK, OK, the real reason for this alternative is to smooth the path a bit to the HTTPS
variant.)

Ideally, we'd place the variables in a human-readable configuration file
would be the dream solution. Wait, did I say human-readable? Yes, Qemu has it since
10.0 and we have 10.2.1. See https://github.com/tianocore/edk2/blob/master/OvmfPkg/QEMU_PV_VARS.md.

Of course, the option is not enabled in the OVMF build in Ubuntu and I do not want to
rebuild the OVMF. That's too much of a hassle. (Remember, we are only at the easy HTTP step).

Fortunately, virt-fw-vars from https://gitlab.com/kraxel/virt-firmware can inject a "Next
Boot URI" directly into an OVMF_VARS_4M.fd EFI variable store:

1# Install firmware tools
2sudo apt install python3-virt-firmware
3
4# Inject the "Next Boot entry" in a fresh variable store
5virt-fw-vars --input /usr/share/OVMF/OVMF_VARS_4M.fd --set-boot-uri http://boot.netboot.xyz/ipxe/netboot.xyz-snponly.efi --output ./OVMF_VARS_4M.fd
6
7# Boot the VM
8qemu-system-x86_64 \
9 -device virtio-rng-pci \
10 -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
11 -drive if=pflash,format=raw,file=./OVMF_VARS_4M.fd \
12 -nic user \
13 -nographic

With this, we can boot a VM over HTTP with two methods:

The first, using DHCP bootfile.

The second by crafting the next boot UEFI variable.

Ready for adding the "S" in HTTPS?


Browse another page:

URL