The bootloader–kernel handoff wall, what Linux's initramfs really does about it, and what the NextBSD equivalent looks like. Background research for nextbsd#288.
/dev/iso9660/<LABEL> — is created by the kernel, after the loader has already handed off and exited. Swapping bootloaders (GRUB, Apple's boot-132, …) changes nothing, because none of them is present when that device is supposed to appear.ExitBootServices() the UEFI Block I/O the loader used is destroyed (spec-illegal to call afterward); on legacy BIOS an amd64 long-mode kernel physically cannot call real-mode INT 13h. So a Ventoy vdisk or an iPXE SAN device — which exist only as firmware emulation — vanish the instant the kernel starts.dm-linear segment remap). For iPXE it fetches the root over the network (dracut livenet / casper netboot=url, NFS, or iSCSI+iBFT). "Ubuntu boots over iPXE" means network-fetch-to-RAM, not a mounted CD.mfsroot + /init (nextbsd-work build.sh). The work is teaching /init the same find-the-root smarts: an ordered probe — local cd9660 → Ventoy reconstruct → network fetch.da*) — fixes CD/DVD/USB/VM and likely the real-hardware case in hand; Phase 1 network-fetch for iPXE (the Ubuntu path); Phase 2 Ventoy after a feasibility spike (exFAT vs. baked-in geom_ventoy vs. a user-formatted data partition).The NextBSD live ISO boots end-to-end from a real USB stick and in a VM, but #288 reports it failing under Ventoy and PXE/iPXE with a tell-tale console trail:
[init] media: rootfs.uzip-MISSING
mdconfig: realpath: No such file or directory
[init] uzip dev: md1.uzip-ABSENT
mount: /dev/md1.uzip: No such file or directory
[init] rofs lower: /rofs-EMPTY
[init] union assembled; ...
exec: /sbin/launchd: not found -> init died
panic: Going nowhere without my init!
Crucially, this is not the classic upstream "cd9660 mountroot error 19" panic. NextBSD's kernel root is ufs:/dev/md0 — the in-RAM mfsroot — which mounts fine on every transport. The failure is one layer up, in the userland /init script: it mounts cd9660:/dev/iso9660/NEXTBSD to reach a loose rootfs.uzip on the boot media, and that device never appears. The same trail can also show up on real hardware (observed on a Dell Wyse 5070 / Intel J4105 thin client) — see §9 for why the same symptom there usually has a different, easier cause.
The central misconception is "the bootloader failed to produce /dev/cd9660." The bootloader does not produce that device at all. There are two separate device worlds with a hard wall between them:
BOOTLOADER world | ExitBootServices | KERNEL world
(FreeBSD loader / GRUB / boot-132) | -- handoff -- | (FreeBSD kernel)
| |
reads files via FIRMWARE: | | builds /dev from SCRATCH
* UEFI Block I/O protocol | | via its OWN native drivers:
* BIOS INT 13h | | * xhci/umass -> da0
"disk0:" / currdev | | * ahci/ata -> cd0/ada0
---> firmware dies | * geom_label -> /dev/iso9660/LABEL
When you boot via Ventoy or iPXE, the bootloader succeeds — firmware (Ventoy's INT 13h hook / iPXE's EFI Block I/O) is alive and serving the emulated disk, so the kernel and mfsroot load. Then ExitBootServices() fires, the firmware disk service is destroyed, and the kernel starts over with its own drivers talking to real silicon. /dev/cd9660 (really /dev/cd0 or /dev/iso9660/<LABEL>) is created here, by the kernel, only when geom_label tastes a provider carrying the ISO PVD magic CD001 at offset 0x8000 (sys/geom/label/g_label_iso9660.c).
Therefore a different bootloader cannot fix this. Every bootloader does the same thing: read via firmware, hand off, exit. None is present when the kernel builds /dev. (And Apple's boot-132 boots a Mach-O XNU kernel, not the FreeBSD ELF kernel NextBSD runs — it's both the wrong layer and the wrong kernel.)
| Transport | What the kernel sees | Why no /dev/cd9660 |
|---|---|---|
| Burned CD/DVD | cd0 via ahci/ata — real | Works — geom_label tastes it |
| dd'd USB stick | da0 via xhci/umass — real, whole-device ISO | Works — modulo discovery timing |
| VM virtual CD | cd0/ada0 — real emulated controller | Works |
| Ventoy | da0 = the raw USB stick; the ISO is a (often fragmented) file in Ventoy's exFAT partition | No CD001 at da0 offset 0x8000 → geom_label finds nothing. Device is real, just not an ISO image at sector 0. |
| iPXE sanboot | nothing — the "disk" was firmware/network emulation, gone at handoff | No hardware for any native driver to attach |
| PXE / MEMDISK | nothing — INT 13h RAM-disk shim, invisible to a long-mode kernel | Same firmware wall |
The important nuance: Ventoy is not a firmware-wall problem. The USB stick is real hardware the kernel enumerates as da0; the ISO is merely a fragmented file on it that nothing reconstructs. iPXE/PXE/MEMDISK are the true firmware-wall cases (no real device exists).
ExitBootServicesUEFI splits into Boot Services (the handle/protocol machinery, incl. EFI_BLOCK_IO_PROTOCOL and all firmware device drivers) and a tiny Runtime Services table (GetTime, GetVariable, ResetSystem, …). There is no block, disk, file, or network I/O in Runtime Services. The spec: after ExitBootServices() "it is illegal to call any boot service." The loader calls it on every boot; the kernel keeps only Runtime Services (EFI vars / RTC / reset).
INT 13h from a long-mode kernelINT 13h is a real-mode entry point. amd64 runs in long mode, which has no virtual-8086 mode, so the i386-era "thunk to V8086 to call BIOS" trick is architecturally impossible. FreeBSD's V8086 code (sys/i386/i386/vm86.c) is i386-only and never used for disk I/O. The BIOS disk path lives only in stand/, before the jump to the kernel.
md -t preload / MD_ROOT (the md_image path) is fully RAM-resident — the loader copies the whole image into staging memory (bounded by EFI_STAGING_SIZE, historically ~64 MB). The only file-backed mode, md -t vnode, needs the backing file on an already-mounted filesystem — circular for our case.
"Keep rootfs.uzip on the media, decompress on demand" + "firmware-emulated medium" is fundamentally contradictory. For Ventoy/iPXE/PXE the only technically-real choices are RAM-load the root (loader delivers it) or network-root (NFS/iSCSI). There is no third option.
Linux hits the same wall and "fixes" it by never asking the kernel to mount the boot device. Everything hard happens in the initramfs — a RAM image the bootloader hands over before ExitBootServices, so it survives the wall. The kernel's mountroot stays dumb; the initramfs is full userland and does the work, split by transport:
The kernel sees the real USB stick (sdX); the ISO is just a fragmented file on it. The initramfs reconstructs it two ways:
iso-scan/findiso hooks do. The enabler: Linux has had exFAT in-kernel since 5.4, and Ventoy's data partition is exFAT.dm-linear target that remaps the ISO's on-disk byte segments into a contiguous virtual device (used mainly for persistence). This is the Linux analog of FreeBSD's geom_ventoy.No real device exists, so the initramfs pulls the root over the network into RAM:
root=live:http://… / livenet, or casper netboot=url url=http://…iso (downloads + loop-mounts). The common path. Root in RAM./ over NFS.Debunking "Linux mounts the ISO over iPXE." It does not. "Ubuntu boots fine over iPXE" means kernel + initrd loaded by iPXE, then the initrd fetches the squashfs/ISO over the network into RAM. The block device is never mounted by the running kernel. So "boot like Ubuntu" is the fetch-to-RAM pattern.
Linux's whole trick — kernel mountroot stays dumb, the initramfs is smart — maps directly onto NextBSD, because NextBSD already has the initramfs: the mfsroot + /init (the md_image the loader preloads, then init_path=/init). Today /init does exactly one thing — wait for /dev/iso9660/NEXTBSD, mount cd9660, find rootfs.uzip — then assemble a tmpfs+unionfs overlay and sysctl vfs.pivot=/rofs into it.
The fix is to make /init an ordered probe, the same shape as Linux's iso-scan → livenet chain:
locate rootfs.uzip:
1. LOCAL cd9660 -> CD/DVD, dd-USB, VM virtual CD (wait on cd0/da*/iso9660-label)
2. VENTOY -> mount the data partition, loop-mount the .iso, cd9660 it
3. NETWORK -> dhclient + fetch the uzip/ISO over HTTP/NFS (iPXE/PXE)
else panic with a clear, specific message
Each strategy just has to end with a path to rootfs.uzip; the existing union + vfs.pivot + exec /sbin/launchd tail is unchanged.
| Linux | NextBSD |
|---|---|
| initramfs (RAM, bootloader-delivered) | mfsroot md_image + /init |
iso-scan / loop-mount exFAT | gap — no in-base exFAT (see §7.2) |
vtoyboot dm-linear remap | geom_ventoy GEOM remap (see §7.1) |
dracut livenet / casper netboot=url | /init dhclient + fetch the uzip (see §8 Phase 1) |
NFS root (root=nfs:) | in-base vfs.root.mountfrom="nfs:" (diskless) |
iSCSI + iBFT (rd.iscsi.ibft) | isboot + native iscsi — but a kld (see §7.3) |
geom_ventoy — BSD-licensed, but bake-in doesn't self-trigger caveatSPDX-License-Identifier: BSD-2-Clause-FreeBSD, a documented derivative of FreeBSD's sys/geom/concat/g_concat.c (the pjd 2004–2005 copyright is retained). No GPL taint — the GPL parts are Ventoy's GRUB layer, never linked into the kernel. Safe to bake in and redistribute (as a NextBSD-owned patch/overlay, never an edit to the fork).G_VERSION; a 15.x/ source dir already exists and g_ventoy_class is byte-identical to 14.x. Per-major refresh cost is small.hint.ventoy.* loader hints — only when it detects and injects its own geom_ventoy.ko anchor (it keys on /boot/kernel/geom_ventoy.ko in the ISO). A statically baked-in class with no .ko on the ISO gets neither the map nor the hints, so it tastes nothing. Making bake-in actually function requires Ventoy-side cooperation: ship a stub .ko anchor so Ventoy fills the map, or get Ventoy to emit hint.ventoy.* for NextBSD unconditionally.The cleanest "do what Linux does" for Ventoy is mount the data partition + loop-mount the ISO file. FreeBSD base has no exFAT — support is the FUSE port fusefs-exfat (a module + userland), impractical in early /init. Options: port/bake a read-only exFAT reader (most work), or require the Ventoy data partition be a FreeBSD-mountable FS (UFS, or FAT32 for ISOs < 4 GB) and loop-mount that — zero kernel work, but a user-formatting requirement.
iPXE sanboot iscsi: writes an iBFT table; FreeBSD's isboot reads it and re-attaches the target with the native initiator, so the kernel genuinely sees a persistent device at runtime — the only non-RAM, in-kernel iPXE path. But isboot is a port/module (net/isboot-kmod). NextBSD uses kexts, not kldload, and prefers baked-into-kernel; a baked-in iSCSI-root would have to compile the initiator + iBFT path into the NEXTBSD kernel. Heavier than the network-fetch path and target-infrastructure-dependent.
All of this is module-free — /init + loader.conf + baked-in-kernel bits. No klds, no bootloader swap, no edit to the freebsd-src fork (loader/userland changes ride the overlay-patch / build mechanism the same way kernel patches do).
cd0/1, da0..N, and iso9660/*, not just two node names. The timeout is an upper bound with early-exit — fast media still proceeds in ~1–2 s (see §9)./media/rootfs.uzip exists (so a whole-device dd'd ISO is found as da0)./init + loader.conf./init brings up the NIC (dhclient), then fetch -o /tmp/rootfs.uzip ${live_url}/rootfs.uzip into tmpfs and mdconfig -f it — literally casper netboot=url, FreeBSD-side.live.fetch_url), set in the iPXE script's imgargs / DHCP option.dhclient+fetch added to the mfsroot tool set. Root is RAM-resident here — unavoidable (no device exists), and exactly what Ubuntu does.Pick among: (2a) bake a read-only exFAT reader so /init can loop-mount the ISO (most Linux-faithful, most work); (2b) bake in geom_ventoy + ship a stub anchor and confirm Ventoy fills the map (§7.1); (2c) require a FreeBSD-mountable Ventoy data partition (UFS/FAT32) + loop-mount in /init (zero kernel work, documented stopgap).
/init ordered-probe scaffold + local-media hardening + timeout. Self-contained. (nextbsd-work)config/NEXTBSD + add dhclient/fetch to mfsroot. (nextbsd-work + nextbsd-kernel)On a real machine (e.g. the Dell Wyse 5070 / J4105) the same rootfs.uzip-MISSING line usually has a different, easier cause than Ventoy:
| Cause | Tell | Fix |
|---|---|---|
| Device appears late (slow USB/CAM probe) or under an unscanned name | Can mount it by hand after waiting | Phase 0 (timing/discovery) |
| Device truly absent (USB/xHCI or eMMC driver) | /dev/da0 never appears even after waiting | Deeper driver bug (Gemini Lake) |
| Firmware-emulated medium (Ventoy/iPXE) | Never mountable by hand at all | §5–§8 |
The disambiguating test (boot the ISO with -s — /init drops to a miniroot shell; wait ~10–15 s, then):
ls /dev/da* /dev/cd* /dev/iso9660/
mount -t cd9660 -o ro /dev/da0 /media # try da1/cd0 too
ls /media # rootfs.uzip there?
# if present: geom disk list / camcontrol devlist
On a real dd'd stick you can always eventually mount it by hand — that proves timing/discovery (Phase 0), not the firmware wall. On Ventoy you never can. That single manual mount tells you which problem you're solving before writing any code.
On NextBSD's mountroot: the kernel roots from ufs:/dev/md0 (the mfsroot), present instantly — so vfs.mountroot.timeout barely applies. The wait that matters is the userland /init poll loop, which early-exits the moment the device node exists. Raising it to 10 s costs nothing on fast media; the full 10 s is only spent when no local cd9660 is coming — which is precisely the signal to fall through to the next strategy.
/init — nextbsd-work/build.sh (the /init heredoc, zz-live.conf, makefs mfsroot step)nextbsd-kernel/src-overlay/sys/kern/vfs_pivot.croot_mount_timeout, the device-present poll, the mountroot prompt0x8000, the CD001 magic, iso9660/<LABEL>getrootmount / currdev → vfs.root.mountfromSynthesized from the nextbsd#288 investigation — parallel research passes across freebsd-src (loader→kernel root handoff, geom_label, vfs_mountroot), Ventoy/vtoyboot and iPXE prior art, the UEFI/BIOS firmware limits, and the local NextBSD mfsroot/vfs_pivot build. Corrects two common assumptions: that a bootloader swap could help, and that Linux mounts the ISO device at runtime over iPXE. Background reference for the #288 fix plan.