Companion to the HFS+ kernel module port plan and scopes issue #80 (boot-132 / modern derivative). The HFS+ kmod gets us read/write of HFS+ data partitions after the kernel is up; this plan addresses the prior question: how does the bootloader find the kernel if the boot partition is HFS+? Three options reviewed; recommendation: Option C (UFS for /boot, HFS+ for data) — zero bootloader work, and matches how every modern FreeBSD installer already lays out disks.
TL;DR. Don't port boot-132 or OpenCore. They're Darwin-platform loaders — you'd have to rip out the kext-cache + Mach boot-args + xnu-handoff to point them at FreeBSD, which is more work than writing an HFS+ reader from scratch. Just keep UFS for /boot like FreeBSD already does, and put HFS+ on the data partitions where the kmod can mount it. If a real "boot directly from HFS+ root" use case emerges later, add HFS+ to stand/libsa (~2-3 weeks) using Clover's VBoxHfs reader as the source.
stand/loader todaystand/libsa/boot, HFS+ for data (recommended)FreeBSD's stand/loader (the second-stage bootloader that runs after boot0/boot1) can read UFS, ZFS, ext2fs, ISO 9660, FAT/msdosfs, and NFS. It cannot read HFS+. So an installation with HFS+ on /boot can't start — the loader physically can't find kernel.bin or loader.conf on disk.
If we want HFS+ anywhere on a freebsd-launchd-mach system, three patterns satisfy that:
stand/libsa)./boot — use UFS for the boot partition, HFS+ for whatever data we want.The HFS+ kmod port (separate plan) handles all post-kernel HFS+ access regardless of which option we pick here.
| Attribute | Status |
|---|---|
| Repository | apple-oss-distributions/boot |
| Last release | boot-132, October 24, 2006. Final release Apple ever published. Seven tags total, nothing newer in ~20 years. |
| License | APSL 2.0 (Aug 6, 2003) |
| Architecture | i386 only. No x86_64, no PPC (PPC BootX was separate, also abandoned). |
| Size | ~30k LOC C + assembly (90% C, 6% asm) |
| Boot model | 3-stage BIOS chainloader. No UEFI. Pre-boot.efi. |
| Partition table | MBR only. GPT support added by community forks (Chameleon, Clover), not Apple. |
| Successor | boot.efi (10.6+), iBoot (iOS/Apple Silicon). Both closed-source; never published. |
What boot-132 actually does:
boot0 — MBR sector-0 stub; finds the active partition.boot1h — HFS+ partition boot sector; locates /boot in the HFS+ catalog B-tree.boot (stage 2) — reads mach_kernel + Extensions.mkext (kext cache) from HFS+; parses com.apple.Boot.plist for kernel args; sets up the Mach boot-args page; switches to protected mode; jumps to xnu's entry.The handoff at the end of stage 2 is completely Darwin-specific: Mach boot-args page, kext cache pointer, specific xnu entry contract. Pointing this code at FreeBSD's kernel.bin means replacing the entire handoff and the kext-cache logic with FreeBSD's elf_freebsd_exec/loader interaction — at which point you've reimplemented stand/loader, just badly.
mach_kernel, Extensions.mkext, com.apple.Boot.plist) — none of which a FreeBSD/NextBSD system produces — which is the core reason §5 recommends against porting it.loader.efi)boot-132 is a BIOS loader: the chain above (boot0 MBR sector → boot1h partition sector → the /boot file) is pure MBR/BIOS and has no UEFI equivalent. Under UEFI the firmware itself owns a filesystem driver, reads the GPT, and launches a PE/COFF .efi application off a partition — there is no boot0/boot1h sector chain at all. Three cases matter for HFS+:
libsa reader shared by loader/loader.efi (Option B)./System/Library/CoreServices/boot.efi, which loads the prelinkedkernel/kernelcache and hands off to XNU. "Boot from HFS+" worked because the firmware itself could read HFS+ — there was never a /boot file or a boot1h sector involved on EFI Macs.VBoxHfs.efi / HfsPlus.efi) — exactly what OpenCore/Clover ship, and the only reusable ~3–5k LOC buried in them (see §3).loader.efi, which finds the kernel through the shared stand/libsa filesystem readers. It can't read HFS+ today — the same gap as the BIOS loader, and fixable in the same place: a libsa HFS+ reader compiles into both loader (BIOS) and loader.efi (UEFI), so one reader covers both firmwares.This is why the recommendation is firmware-agnostic. Option C (UFS/ZFS for /boot + the FAT ESP that UEFI already requires, HFS+ for data) needs nothing on either BIOS or UEFI. Option B's single libsa reader serves both. Only boot-132 (Option A) is BIOS-only — UEFI support would mean bolting on a separate EFI HFS+ driver (OpenCore territory), which is the bulk of why §5 rejects it.
| Project | License | Status | FS support | Boot model |
|---|---|---|---|---|
| OpenCorePkg | BSD-3-Clause | Active; v1.0.7 March 20, 2025; 5,005 commits | HFS+ via closed-Apple-derived HfsPlus.efi binary blob in OcBinaryData; APFS via Apple's container-embedded driver; FAT native; ext4/btrfs via plug-ins |
Pure UEFI |
| Clover | BSD-2-Clause | Active; release-5172g, March 22, 2026; 2,489 commits | HFS+ via VBoxHfs.efi (VirtualBox-derived, LGPL-compatible); APFS via Apple's driver; FAT, ext, NTFS |
BIOS + UEFI |
| Chameleon (meklort fork) | APSL 2.0 | Dormant since ~2014 | HFS+, FAT32, ext2, GPT+MBR | BIOS (closest to boot-132) |
Architectural reality: all three exist to boot macOS/Darwin. Their reason for being is the Apple-specific bring-up dance (SMBIOS spoofing, ACPI patching, ApplePlatformInfo emulation, EfiBoot protocol, kext injection). For our purposes we'd be importing ~50–200k LOC of macOS-emulation work to use the ~3-5k LOC HFS+ reader buried inside. That's a maintenance trap.
What's useful from this ecosystem: the standalone HFS+ readers they each ship are extractable.
VBoxHfs — LGPL, EDK2-style EFI driver, ~3-5k LOC, derived from VirtualBox's HFS+ reader, read-only, byte-swappable, no Apple binary dependency. Best candidate for re-use as the basis of a libsa-side reader.OpenHfsPlus — the open variant; acidanthera bug #659 calls it slow and unaudited.HFSPlus_EFI — sparse, 6 commits, unclear licensing.i386/boot2/hfs.c — APSL 2.0, oldest, last touched 2006.stand/loader todayFrom freebsd-src/stand/libsa:
| Filesystem | File | Approx LOC |
|---|---|---|
| UFS | ufs.c, ufsread.c | ~1500 |
| ext2fs | ext2fs.c | ~1200 |
| ISO 9660 | cd9660.c, cd9660read.c | ~600 |
| FAT/msdosfs | dosfs.c | ~1100 |
| NFS | nfs.c | ~900 |
| ZFS | zfs/ subdir | ~larger |
| HFS+ | (none, never has been) | — |
The loader's plug-in FS ABI is simple: each filesystem implements fs_open/close/read/write/seek/stat/readdir via a struct fs_ops exposed through libsa. Adding a new reader is a single C file. UFS is ~1500 LOC, ext2fs ~1200, cd9660 ~600 — an HFS+ read-only reader for libsa would fit in the same ~1.5–3 kLOC envelope, far smaller than the EDK2-style EFI drivers because libsa provides its own buffered-block I/O (no UEFI protocol marshalling).
What it is: import Apple's boot-132 source (or OpenCore) into our tree, modify the stage-2 handoff to point at FreeBSD's kernel.bin instead of mach_kernel, replace the kext-cache logic with whatever FreeBSD's loader does to find modules, and wire it as the live ISO's bootloader.
Effort: rough lower bound 3 person-months. Most of the work isn't the HFS+ reader (which is already wired) — it's gutting the Darwin handoff and replacing it with FreeBSD's loader contract. Boot-132's BIOS-only model and i386-only support also mean we'd be writing the EFI side too. OpenCore brings EFI for free but drags in vastly more macOS-emulation code we'd have to either disable or maintain.
Tradeoffs:
Verdict: not recommended.
stand/libsaWhat it is: write a new stand/libsa/hfs.c that implements fs_ops for HFS+ read-only. Wire it into stand/loader.mk behind a LOADER_HFSPLUS_SUPPORT switch. Source material: port Clover's VBoxFsDxe/VBoxHfs* (LGPL, clean BSD/LGPL-compatible) into libsa's idiom, dropping the EDK2 protocol wrapping in favor of libsa's fs_ops ABI.
Effort: 2–3 weeks for someone experienced with libsa; ~1–1.5 person-months for a junior dev. Add a week if we also want an EFI-driver standalone version of the same reader for the ESP (EFI_SIMPLE_FILE_SYSTEM_PROTOCOL wrapping around the same core).
What it delivers:
/boot/loader can read kernel + modules + loader.conf from HFS+Tradeoffs:
hfs.c)/boot — loader never writesstand/ code — if we want it upstream eventually, FreeBSD-src patch flowVerdict: the right answer if a real use case emerges. Don't write it preemptively.
/boot, HFS+ for data recommendedWhat it is: partition the disk so /boot is UFS (or ZFS, or msdosfs ESP). HFS+ lives on data partitions where the kmod can mount it post-kernel-load. Modern FreeBSD installers already do this — EFI System Partition (FAT) + freebsd-boot + freebsd-ufs (root) + freebsd-swap; HFS+ becomes another freebsd-data-style partition.
Effort: zero. Nothing to build, nothing to change in the loader.
What it delivers: the HFS+ kmod handles all post-boot HFS+ workflows (mount Apple DMG images, read/write external Apple drives, Time Machine sparsebundle inspection, etc.). The cost is one extra small partition for /boot — which is standard hygiene anyway.
Tradeoffs:
| Dimension | A: Port boot-132/OpenCore | B: Add HFS+ to libsa | C: UFS for /boot |
|---|---|---|---|
| Effort | 3+ person-months | 2-3 weeks | 0 |
| Boots from HFS+ root | Yes | Yes | No (UFS root) |
| HFS+ data works (with kmod) | Yes | Yes | Yes |
| Mount Apple DMGs (with kmod + dmg2img) | Yes | Yes | Yes |
| Maintenance burden | High (~30k LOC bootloader) | Low (~1.5-3k LOC libsa file) | None |
| License posture | APSL 2.0 (Apple-aligned) | LGPL via VBoxHfs OR APSL 2.0 via boot-132 | n/a |
| Upstream-able to FreeBSD | No | Maybe (one-file libsa addition) | n/a |
m1n1 is a separate universe.apfs.efi blob, not portable. Different effort.boot.efi compatibility / Apple boot picker. We're not emulating a Mac; FreeBSD's loader UI is fine./boot is read-only from loader's perspective.Drafted 2026-05-26 from an agent research pass against Apple boot-132, OpenCore/Clover/Chameleon source repos, FreeBSD stand/libsa tree, and rEFInd/EDK2 HFS+ EFI driver ecosystem. Companion to HFS+ kernel module port plan and hdiutil port plan. Scopes issue #80. Sources: apple-oss-distributions/boot, OpenCorePkg, CloverBootloader, FreeBSD stand/libsa.