← Back · part of NextBSD Research · companion to the arm64 KMS options and the driver-delivery plan
The target is a Raspberry Pi 500+ — BCM2712 D0 silicon, 16 GB, built-in 256 GB NVMe, and a keyboard that sits behind PCIe. worproject/rpi5-uefi cannot boot it, and that is not bad luck: the repo has been archived since Feb 2025, its only releases target pre-D0 BCM2712C1, and board type 0x19 was never added. This page lays out every option for booting it natively — one of which, unexpectedly, needs zero new boot code — and maps every device on the board to a driver we can port today, port from Linux, or must write new.
Survey, not a decision Nothing here was chosen when this was written — see the status block below for what has since been settled on the hardware. Every route is costed with its evidence so the calls can be made from the material rather than from a recommendation. The open decisions are gathered in §9; the sequencing in §7 is a dependency order (what unblocks what), not a schedule or a commitment.
Status · 2026-08-23 The board boots NextBSD. Much of what this page presents as open has been settled by running it, and the decisions below are annotated in §9. In short:
kernel8.img directly, there is no loader, and the shipping image is built that way (BOARD=rpi500 sh build.sh). The kext premise bends on arm64 rather than the boot route bending.NEXTBSD-RPI5 — nothing in it is specific to the 500+. Only the 500+ has been booted.mountroot. RP1 registers as an interrupt controller and its children get IRQs.What now gates a published image is no longer “does it boot” but is it a machine you can sit down at: video, keyboard, mouse and Ethernet. That bar, and what blocks each part, is in the image plan and tracked in nextbsd#416.
Correction · supersedes §3 The serial console is UART10 inside the SoC at 0x10_7d00_1000, reached through the 3-pin JST-SH connector on the board — not RP1 UART0 on the 40-pin header, at either 0x1F_0003_0000 or 0x1C_0003_0000. RP1 is a PCIe endpoint and console attach happens at cninit(), long before any PCIe host bridge; so the 40-pin UART can never be a boot console on this board, however complete the PCIe and RP1 drivers become. It also dies the moment a driver takes the controller, which makes a working driver look exactly like a dead one. Find that connector before starting the PCIe work, not during it.
Sub-plan · 2026-08-22 The boot partition, and where the image gets built — written after phase 0 was proven on this hardware (serial console live). Covers what kernel8.img actually is, why this board can never boot an ISO, why the image target belongs in nextbsd/build.sh rather than nextbsd-userland, and the loader-less consequence for kexts. It also records one likely correction to §3 below: the firmware reports RP1 UART at 0x1C_0003_0000, not the 0x1F_0003_0000 this page specifies for EARLY_PRINTF.
Settled 2026-08-17 FreeBSD can already be booted directly by the Raspberry Pi firmware, with no U-Boot, no UEFI, and no new boot code. sys/arm64/conf/std.arm64:70 sets options LINUX_BOOT_ABI; sys/conf/Makefile.arm64:87 has a kernel.bin target that runs arm_kernel_boothdr.awk -v hdrtype=v8booti; and write_v8booti() emits a genuine arm64 Linux Image_header. The Pi 5 firmware parses exactly that header. The two halves of this contract were built for unrelated reasons and happen to fit.
Taken from the running board (Debian 13, kernel 6.12.47+rpt-rpi-2712) rather than from a spec sheet, because the board and its firmware disagree about what it is.
| Property | Value | Consequence |
|---|---|---|
| OTP revision | e04190 → Pi 500+ Rev 1.0, 16 GB, Sony UK | type 0x19, memory bits 0b110 |
| Reported model | Raspberry Pi 500 Rev 1.0 | firmware appends the revision at runtime |
| DTB loaded | bcm2712-rpi-500.dtb | no 500+ DTB exists on any branch — the 500 DTS covers both |
| Stepping | D0 — /proc/device-tree/axi/gpu/compatible reads brcm,bcm2712d0-vc6 | the reason UEFI fails (§2) |
| EEPROM | 2025-06-13; latest 2025-11-05 | update available and probably wanted |
| Secure firmware | atf@0 { reg = <0 0 0 0x80000>; no-map; }, PSCI v1.1 over SMC, SMCCC v1.2 | real TF-A BL31 — SMP is free |
| UEFI | efi: UEFI not found | the firmware boots a kernel directly today |
| Console | 107d001000.serial [arm,pl011] = ttyAMA10 @115200 | FreeBSD already has uart_dev_pl011 |
| Framebuffer | no simple-framebuffer node anywhere; only raspberrypi,rpi-firmware-kms-2712 | serial is the only console until vc4 — see §6 |
Both of the things you need to use this machine — the keyboard and the disk — are behind PCIe:
BCM2712
├─ pcie1 0001:00:00.0 [brcm,bcm2712-pcie] MSI: mip1
│ └─ 0001:01:00.0 NVMe 1d97:5216 FORESEE XP1000F256G, 238.5 G ← built-in 500+ SSD
└─ pcie2 0002:00:00.0 [brcm,bcm2712-pcie] MSI: mip0, Gen2 x4
└─ 0002:01:00.0 RP1 southbridge 1de4:0001
├─ 1f00100000 ethernet [raspberrypi,rp1-gem, cdns,macb] ← Cadence GEM
├─ 1f00200000 usb [snps,dwc3] → xhci-hcd.0
└─ 1f00300000 usb [snps,dwc3] → xhci-hcd.1
└─ 3-2 2e8a:0011 "Raspberry Pi Ltd Pi 500+ Keyboard (ANSI)" usbhid
That product string is read from the device's own USB descriptor, so it is the real 500+ keyboard. Nothing types on this machine until PCIe, RP1, dwc3, xHCI and USB-HID all work — five layers. A serial cable is not a convenience here, it is the only input channel for the entire bring-up.
Three independent reasons, all verified, in order of severity.
1. Wrong silicon. bcm2712-rpi-500.dts opens with #include "bcm2712d0-rpi-5-b.dts" — the Pi 500/500+ is a D0 part. worproject's README: “tested only on early Raspberry Pi 5B models with the BCM2712C1 SoC. Recent D0 models may require a newer EEPROM firmware, which has been reported to break the graphical output in UEFI.” The D0 build was never released — test builds only.
2. Board type 0x19 does not exist in the firmware. It is absent from BoardRevisionHelperLib on every branch, and no bcm2712-rpi-500.dtb ships in any image. In DT mode it hands the OS a Pi 5B DTB describing hardware this machine does not have, and omitting the M.2 and keyboard wiring.
3. The repository is archived read-only (2025-02-04). No fixes are coming.
16 GB is not the problem, contrary to the obvious guess. 256MB << 6 is decoded correctly and mapped above 4 GB. (The 3 GB cap people remember is pftf/RPi4, a real BCM2711 DMA erratum. BCM2712 has no such limit.) The failures are D0 display and DTB failures.
And there will never be a pftf/RPi5. Pete Batard, pftf/RPi4#243: “we have moved on to other endeavours and have currently no scope (or interest) to invest our time to do the same for the Pi 5.” Mario Bălănică, rpi5-uefi's own author, in the same thread: “most of us have simply moved on… I upstreamed my TF-A port for the RPi 5 over a year ago… they still haven't contributed any of those changes back.”
Note which half survived: the TF-A port was upstreamed (PLAT=rpi5 is in mainline TF-A) and lives on inside the EEPROM. The EDK2 platform half was never upstreamed, and died. That is the argument for putting our effort where upstream will carry it.
The one live UEFI option, if we want it: NumberOneGit/rpi5-uefi — not archived, last push 2026-08-02, maintained by the person who did the D0 debugging in the archived tracker. Its README: “This fork remaps pinctrl for the D0 revision of the BCM2712… for users with D0 models — including the 16 GB… variants.” It fixes reasons 1 and 3. It does not fix reason 2 — board type 0x19 would be our port. Also worth watching: pi-bmc/rpi5-uefi-build, third-party, pushed 2026-08-17, carrying an Rp1GemDxe UEFI driver for RP1's Cadence GEM — precisely the ethernet gap every other fork has.
The Pi 5 family firmware is entirely self-contained in the SPI EEPROM. There is no bootcode.bin, no start*.elf, no fixup*.dat — the docs are explicit: “The Raspberry Pi 5 does not use elf files… the bootloader has an embedded version of start.elf.”
1. boot ROM → SPI EEPROM 2nd stage
2. VPU bootloader:
• loads RP1 firmware (~46 KB) into RP1 SRAM over I2C ← concurrent with DDR init
(RP1 missing = FATAL: 4 long / 3 short blinks)
• releases RP1_RUN → RP1's Cortex-M3 boots → its PCIe endpoint comes alive
• init clocks + LPDDR4X, walk BOOT_ORDER, train the x4 link, enumerate 1de4:0001
3. loads armstub (TF-A BL31) at 0x0, kernel at 0x200000, DTB, initramfs
4. unless pciex4_reset=0: resets the x4 controller before handoff
5. EL3 TF-A BL31 → eret → EL2 non-secure: kernel entry, x0 = physical DTB pointer
Two things follow that are worth stating plainly, because they invert the usual assumption that the Pi is a hostile boot target:
MSIX_CFG_n, populate the DT subtree. Linux's rp1_pci.c probe is a few dozen lines, and it detects a firmware-less RP1 by checking whether BAR1 has grown from 64 KB to 4 MB.kernel= contract. FreeBSD's problem on this board was never the boot protocol.Presented so they can be compared, not ranked. (a) and (c) are not mutually exclusive — they share every driver below the boot layer, and (a) doubles as the debugging harness for (c).
kernel.bin directlyVerified in freebsd-src main:
| Piece | Location | What it does |
|---|---|---|
LINUX_BOOT_ABI | sys/arm64/conf/std.arm64:70 | included by GENERIC; enables the booti path |
kernel.bin target | sys/conf/Makefile.arm64:87 | strips ELF, prepends a boot header blob |
write_v8booti() | sys/tools/arm_kernel_boothdr.awk:150 | emits a real arm64 Image_header, magic 0x644d5241 |
| FDT pointer | sys/arm64/arm64/locore.S | x0 != 0 && x0 < KERNBASE → treat as physical FDT |
| boot args | sys/arm64/arm64/machdep_boot.c:62 | #define CMDLINE_GUARD "FreeBSD:" — reads /chosen/bootargs after the guard |
So make kernel.bin, copy it as kernel8.img, ship bcm2712-rpi-500.dtb beside it, and the firmware parses the header, loads at 0x200000, and jumps at EL2 with the DTB in x0. FreeBSD boots.
# config.txt
kernel=kernel8.img
device_tree=bcm2712-rpi-500.dtb
enable_rp1_uart=1 # firmware inits RP1 UART0 @115200 and does not reset RP1
pciex4_reset=0 # required companion to the above
enable_uart=1
dtparam=uart0_console # 2712-only: console to UART0 on 40-pin pins 6, 8, 10
dtparam=nvme
# cmdline.txt
FreeBSD: vfs.root.mountfrom=ufs:/dev/... -v
Code to write for the boot itself: none. Everything after is drivers.
Cost: ~1–3 days to a serial banner and a mountroot prompt. Trade: no loader means no .ko at boot — the kernel must be monolithic, which collides with the kext model until a loader exists (§5). No loader.conf, no boot menu, no boot environments. Best understood as a bring-up and driver-development harness; whether it is also a shipping route is an open decision.
loader.efiBCM2712 landed in U-Boot v2024.04; PCIe in v2026.07. The model table already has [0x19] = { "500", … "bcm2712-rpi-500.dtb" }. FreeBSD's sysutils/u-boot-master is pinned at 2025.10, whose pcie_brcmstb.c matches only brcm,bcm2711-pcie.
But it does not solve the problem we have. U-Boot on BCM2712 has no USB driver at all — RP1's controllers are snps,dwc3 MMIO blocks inside BAR1, not PCI-class xHCI functions, so CONFIG_USB_XHCI_PCI never matches. No keyboard. Ethernet is likewise absent (raspberrypi,rp1-gem, no driver; Duwe's RP1+MACB series is posted but unmerged as of v2026.10-rc2). CONFIG_NVME_PCI is absent from the defconfig.
And a hazard worth recording: arch/arm/mach-bcm283x/init.c maps only the first 1 GiB of DRAM on BCM2712 while gd->ram_size still reports 16 GB, which is what feeds the EFI memory map.
Cost: ~2–4 days. Viable as a stopgap to chainload loader.efi from microSD while driver work proceeds. The USB gap is upstream work someone else may land — Duwe's RP1+MACB series is written and posted, just unmerged — so this option's value could change without us doing anything.
loader.efiFork NumberOneGit/rpi5-uefi; add board type 0x19 to BoardRevisionHelperLib; ship bcm2712-rpi-500.dtb and select it for 0x19; verify the D0 pinctrl remap covers the 500+'s different GPIO wiring (M2_DET_WAKE/M2_PWR_EN on gio 14/15, KEYB_BOOTSEL/KEYB_RUN on 18/21).
The payoff is that ~95% already exists, including a documented FreeBSD boot — worproject's own compatibility table lists FreeBSD | 13.2 | Display, UART, USB, SD, PCIe. That is an existence proof that a FreeBSD arm64 kernel reaches a usable system on BCM2712 with a working keyboard.
Cost: ~2–5 weeks. Risk: an under-staffed upstream (“won't be able to maintain or add features alone”); RP1 ethernet, GPIO and PWM stay broken, so the fan runs at full speed and a USB NIC is required.
FreeBSD's kernel.bin is the shim; route (a) subsumes this. The only reason to touch armstub= would be to replace TF-A BL31 — which would cost us PSCI and secondary-core startup for nothing.
Bonus accelerator: kboot. share/mk/src.opts.mk confirms LOADER_KBOOT is available on aarch64. Boot Raspberry Pi OS — which works perfectly on this board — then kexec into the NextBSD kernel from a fully configured machine. That gives a real loader, real metadata, and a known-good hardware state to iterate against. ~1 week, and it restores module loading that route (a) costs us. Note LOADER_UBOOT (ubldr) is explicitly broken on aarch64: “UBOOT is only for arm, and big-endian powerpc.”
There are two entirely different UARTs, and only one of them works before PCIe:
| UART10 (debug) | RP1 UART0 | |
|---|---|---|
| Lives in | BCM2712 itself | RP1, behind PCIe |
| Physical | 0x10_7d00_1000 | 0x1F_0003_0000 |
| Compatible | arm,pl011 + arm,primecell | arm,pl011-axi |
| Connector | 3-pin JST-SH between the HDMI ports on a Pi 5B | 40-pin header pins 6 / 8 / 10 |
| Needs RP1? | No | Yes |
On a 500+ the 3-pin debug connector may not be physically exposed — the signals exist (bcm2712-rpi-500.dts still names AON_GPIO_13 = "UART_TX_FS") but it is inside a keyboard enclosure and no teardown confirms a populated header. The 40-pin header is the path that definitely works: pin 6 GND, pin 8 TXD, pin 10 RXD, 3.3 V, 115200 8N1, with enable_rp1_uart=1 + pciex4_reset=0 so the firmware initialises it and declines to reset RP1. For EARLY_PRINTF, hard-code the PL011 at 0x1F_0003_0000 — the firmware has already set the baud rate, so only the FIFO needs poking.
USB-C will not work. The Pi 500+'s USB-C port is power only. Verified on the board: /sys/class/udc/ is empty (no USB Device Controller, so no gadget mode), there is no dwc2 node, no dr_mode property anywhere in the device tree, and no usb_role switch. /chosen/power/usbpd_power_data_objects decodes to a 5 V / 5 A fixed-supply PDO — it negotiates power and nothing else. The Pi 4's USB-C could do OTG/gadget via dwc2; on the Pi 5 family all USB goes through RP1's two host-only snps,dwc3 controllers and the USB-C goes to the PMIC. A USB-A-to-USB-A cable does not help either — both ends are hosts.
A USB-to-TTL serial adapter is required: a USB device to the Mac on one side, 3.3 V UART pins on the other.
The Raspberry Pi Debug Probe ($12, in production until at least Jan 2030) is the right tool here for a reason specific to this board: it ships with cables for both UART paths — a 3-pin JST-SH → JST-SH lead that mates directly with the Pi 5-family debug connector, and JST-SH → 0.1″ female and male header leads for the 40-pin GPIO header. One purchase covers whichever connector turns out to be reachable.
It is also CDC-ACM class-compliant, so macOS needs no driver at all — it enumerates as /dev/cu.usbmodem*. That matters more than it sounds: generic CP2102 / CH340 dongles may want a vendor kext depending on the macOS version, which is friction you do not want in the middle of a bring-up. FTDI FT232R parts are the next-best option (macOS has carried an in-box FTDI driver for years).
Stocked by Raspberry Pi's own reseller list, and directly by Pimoroni, SparkFun, Micro Center, CanaKit and Vilros. Prices and stock as of 2026-08-17 — check before ordering.
Done · 2026-08-22 This was carried out and the console works — it went on to carry the whole of phases 0–3. Step-by-step, with drawn diagrams of the probe end and the header and the pin-1 orientation confirmed against the case silkscreen: Wiring a serial console to the Raspberry Pi 500+.
Probe lead colours are orange = TX (out of probe), yellow = RX (into probe), black = GND. Serial is crossed, so:
Probe orange (TX) ──▶ Pi pin 10 GPIO15 / RXD
Probe yellow (RX) ◀── Pi pin 8 GPIO14 / TXD
Probe black (GND) ─── Pi pin 6 GND
Two ways to damage the board. Use a 3.3 V adapter — a 5 V one can destroy the SoC's UART pins. And leave the adapter's VCC / 5 V line disconnected: the Pi powers itself, and back-feeding it through a GPIO pin is a common way to kill a board. Ground, TX and RX only — three wires, never four.
ls /dev/cu.usb* # find it
brew install tio
tio /dev/cu.usbmodem1101 -b 115200
Use /dev/cu.*, not /dev/tty.*. On macOS the tty. node blocks on carrier-detect and will simply hang; cu. (call-up) does not. And prefer tio over screen for one reason that matters enormously here: it reconnects automatically when the device disappears. Driver bring-up means power-cycling the board constantly, and screen dies on every reboot.
| UART10 — 3-pin debug | RP1 UART0 — 40-pin pins 8/10 | |
|---|---|---|
| Survives a PCIe / RP1 reset | Yes — it is in the BCM2712 | No — it is inside RP1 |
Needs enable_rp1_uart=1 + pciex4_reset=0 | No | Yes |
| Exposed on a 500+ | unconfirmed signals exist (AON_GPIO_13 = "UART_TX_FS") but no teardown confirms a populated connector | yes rear edge, as on the Pi 400/500 |
The 40-pin header is the path that certainly works, and is the right choice for phases 0–2. But note what it costs from phase 3 onward: the console would live on the very hardware being brought up. The first time a half-written bcm2712-pcie or rpone driver resets the link, output stops — and a lost console is indistinguishable from a hang. That is a concrete reason to open the case early and look for the 3-pin JST-SH connector, before the PCIe work starts rather than during it.
Headline OpenBSD 7.8 already ships a complete, native, ISC-licensed BCM2712 + RP1 stack — 4,113 LOC across 12 files. It boots the Pi 5 with working onboard ethernet. FreeBSD, NetBSD and DragonFly have nothing BCM2712-specific. For the boot-critical tier, OpenBSD is the source, not Linux — roughly 4× smaller than the Linux equivalent, and ISC rather than GPL.
Worth settling explicitly, because the drm-kmod success makes it the natural assumption. LinuxKPI cannot host a devicetree/platform driver at all:
/* sys/compat/linuxkpi/common/include/linux/platform_device.h */
platform_driver_register(...) → pr_debug("TODO"); return (-ENXIO);
to_platform_device(dev) → (NULL)
dev_is_platform(dev) → (false)
linux/of.h is a 1,486-byte stub whose entire body is #include <linux/kobject.h>. linux/clk.h, linux/reset.h, linux/pinctrl/, linux/gpio/ and linux/dmaengine.h do not exist. regmap.h, component.h, of_address.h and property.h exist only as 0-byte files in linuxkpi/dummy/include/. There is no linux_of.c, linux_platform.c or linux_clk.c, and no evidence anyone has attempted one. Building that layer is a multi-year subsystem project, not a shim.
FreeBSD's own equivalent is healthy and is where this belongs: sys/dev/clk/, sys/dev/regulator/, sys/dev/hwreset/, sys/dev/phy/, sys/dev/syscon/, sys/dev/ofw/, sys/dev/fdt/fdt_pinctrl.c.
RP1 is a PCIe endpoint that exposes an entire simple-bus of peripherals behind a BAR. OpenBSD solves it in four layers, and the trick is worth copying verbatim:
bcmpcie matches brcm,bcm2712-pcie and simultaneously inherits simplebus_softc, so the PCIe node doubles as an FDT bus for RP1's subnodes.bcmmip remaps MSI to GIC SPIs.rpone attaches at pci? on 1de4:0001, maps the MSI-X and peripheral BARs, builds a synthetic bus_space that rebases RP1 MMIO, and registers an FDT interrupt controller demuxing 61 MSI-X vectors — in 219 lines.FreeBSD has every primitive, already exported as public API in sys/dev/fdt/simplebus.h: simplebus_init(), simplebus_add_device(), simplebus_attach_impl(), and DECLARE_CLASS(simplebus_driver) for inheritance. simple_mfd.c is the in-tree precedent. This was the blocker that stalled earlier FreeBSD attempts — mkarels reported “the rp1 node doesn't get probed and attached” — and it is not actually a missing primitive, just a missing driver.
| Device | Compatible | Source | Verdict |
|---|---|---|---|
| PL011 console | arm,pl011 | — | have it uart_dev_pl011 |
| GIC-400 / timer / PSCI | arm,gic-400, arm,armv8-timer, arm,psci-1.0 | — | have it |
| xHCI / NVMe / dwc3 | snps,dwc3 | — | have it dwc3.c matches snps,dwc3 directly |
| VideoCore mailbox | brcm,bcm2835-mbox | — | have it — survived on BCM2712 at a new address |
| PCIe root complex | brcm,bcm2712-pcie | OpenBSD bcm2711_pcie.c (1,205, ISC) | port covers both SoCs in one file |
| MSI controller | brcm,bcm2712-mip | OpenBSD bcm2712_mip.c (177, ISC) | port only BSD impl anywhere |
| RP1 bridge | PCI 1de4:0001 | OpenBSD rpone.c (219, ISC) | port the single most valuable file |
| RP1 clocks | raspberrypi,rp1-clocks | OpenBSD rpiclock.c (585, ISC) | port |
| RP1 GPIO / pinmux | raspberrypi,rp1-gpio | OpenBSD rpigpio.c (429, ISC) | port |
| Ethernet | raspberrypi,rp1-gem, cdns,macb | FreeBSD if_cgem.c + OpenBSD's 2-line quirk | graft same Cadence GEM IP |
| SD/eMMC | brcm,bcm2712-sdhci | OpenBSD sdhc_fdt.c:133 + vmmc-supply | graft onto sdhci_fdt; vmmc-supply powers the WiFi chip |
| L2 interrupt ctrl | brcm,bcm7271-l2-intc, brcm,l2-intc | OpenBSD bcmstbintc.c (247, ISC) | port add the second compat string |
| Reset / rescal | brcm,brcmstb-reset | OpenBSD bcmstbreset.c + bcmstbrescal.c | port |
| SoC pinctrl / GPIO | brcm,bcm2712d0-pinctrl | OpenBSD bcmstbpinctrl.c (267), bcmstbgpio.c (408) | port the d0 variant this board uses is covered |
| Device | Source | Verdict |
|---|---|---|
RNG (brcm,bcm2711-rng200) | FreeBSD bcm2835_rng.c | have it already matches this string |
| Watchdog, SPI, firmware/vcio/OTP | FreeBSD bcm2835_* | have it |
| RTC, thermal, PWM (fan) | OpenBSD rpirtc.c, bcm2711_tmon.c, rpipwm.c | port — PWM matters: without it the fan runs flat out |
| WiFi BCM43455 (SDIO) | OpenBSD/NetBSD bwfm, or FreeBSD's ISC-licensed sys/contrib/dev/broadcom/brcm80211/brcmfmac | two options FreeBSD's is in-tree but BRCMFMAC_SDIO=0 and needs MMCCAM |
| Bluetooth BCM4345C0 (UART) | NetBSD btuart.c + netbt | hard OpenBSD has no BT stack at all; FreeBSD's netgraph BT is USB-only |
I2C (brcm,brcmstb-i2c) | — | net new OpenBSD's bcmbsc is a different IP block |
IOMMU (brcm,bcm2712-iommu) | — | defer OpenBSD boots without it |
dw_axi_dmac, rp1-pio, rp1-adc, rp1-mbox | — | net new no BSD has any of these |
| Device | Source | Verdict |
|---|---|---|
v3d (brcm,2712-v3d) | Linux drivers/gpu/drm/v3d/, 6,091 LOC, GPL-2.0+ | port from Linux the only realistic LinuxKPI candidate — §6 |
vc4 display (brcm,bcm2712d0-vc6) | Linux drivers/gpu/drm/vc4/, 23,773 LOC, mostly GPL-2.0-only | port from Linux hard — §6 |
pispbe ISP, rpi-hevc-dec | Linux, blocked on V4L2 + videobuf2 + Media Controller | drop 30–40k LOC of absent infrastructure with no other consumer |
NextBSD has so far vendored only GPL-2.0-or-later. That rule alone excludes a large share of the Linux candidates: irq-bcm2712-mip, sdhci-brcmstb, macb_main, irq-brcmstb-l2, gpio-brcmstb, i2c-brcmstb, iproc-rng200 and most of vc4 are all GPL-2.0-only. The OpenBSD route sidesteps the question entirely — all 4,113 LOC are ISC, which is strictly more permissive than anything in the Linux tree and imposes nothing on kext packaging. RTEMS learned this the expensive way: they got PCIe link training working by studying Circle (GPLv3) and Linux, then had to discard it — “we cannot accept GPL code into rtems.”
The driver-delivery plan settled a three-tier split for amd64. Mapping it onto this board gives the shape below — if arm64 follows the same model, which is decision 3:
| Tier | Form | Contents here |
|---|---|---|
| 1 | compiled into the kernel | PL011, GIC-400, ARMv8 timer, PSCI, bcm2712-pcie, bcm2712-mip, RP1 bridge, dwc3/xHCI, USB HID, NVMe, and the FDT/simplebus plumbing |
| 2 | plain kld, devmatch-autoloaded | ethernet, SD, GPIO, pinctrl, I2C, SPI, RNG, watchdog, thermal, PWM, RTC |
| 3 | kext | firmware-bearing or multi-version: WiFi (BCM43455 blob), Bluetooth (patchram), and the graphics stack |
The sharpest conflict on this page. Booting kernel.bin directly means no loader, therefore no modules at boot at all — tiers 2 and 3 are both unavailable until root is mounted. For a first bring-up that is fine and arguably desirable. But an image whose premise is “all possible drivers as kernel extensions” needs a loader. So either route (a) is a bring-up harness and the shipping image rides (c) or kboot, or the kext premise bends on arm64. That is decisions 1 and 2, and it is better decided deliberately than discovered at packaging time. It also means mach.ko must be compiled in or deferred on any route-(a) kernel.
The naming is confusing: BCM2712 has VideoCore VII, the DT compatible says vc6, and the Linux driver is still called vc4 (from VideoCore IV, where it began). All three name the same lineage. On this board it resolves to two independent drivers:
| What it is | LOC (v6.12) | SPDX | 2712 in mainline 6.12? | |
|---|---|---|---|---|
vc4 → card1 | display — HDMI, HVS, pixelvalve, TXP | 23,773 | mostly GPL-2.0-only | No — landed v6.14 |
v3d → card0 | 3D/compute — V3D 7.1 | 6,091 | GPL-2.0+ | Yes — brcm,2712-v3d |
vc4 gives a picture; v3d gives acceleration. Since this board has no simple-framebuffer node and no UEFI GOP, there is no display path at all without vc4 — which is why serial is mandatory and why graphics cannot be an early shortcut.
No BSD anywhere has either driver; tree-wide searches of all four found zero. The only prior art is NetBSD's GSoC 2022 vc4 attempt (loaded on a real RPi 3B+, then crashed startx in drm_gem_object_put_unlocked, never merged) and a FreeBSD compile-only skeleton whose last commit reads “now comes the hard part of actually getting it to work on hardware.”
nextbsd-kernel-modules#33 has already measured the foundation: drm.ko, ttm.ko, dmabuf.ko, radeonkms.ko and drm_extra_helpers.ko build and fully symbol-resolve on aarch64 against the NextBSD kernel. That settles the question the ports tree appears to answer negatively — graphics/drm-612-kmod is ONLY_FOR_ARCHS = amd64, but we build from source, and the upstream tree's SUPPORTED_ARCH does list aarch64. The port's gate is conservative, not load-bearing.
That PR also found a real upstream bug: kconfig.mk gates DRM_AMD_DC_FP to amd64 while the references stay compiled unconditionally, so amdgpu.ko builds clean on aarch64 and dies at kldload with five unresolved dal_hw_* / dscl*_spl_calc_lb_num_partitions symbols. Worth reporting upstream.
6.12-lts: drm_gem_shmem_helper.c (782, v3d's GEM backend), drm_gem_dma_helper.c (604, vc4's), drm_of.c (546), drm_simple_kms_helper.c (457), drm_fbdev_dma.c (346), drm_fb_dma_helper.c (195). graphics/drm_extra_helpers/ is already the designated home for exactly this.drm.ko has hosted so far is PCI. bsd-sbc-drm's drm-kmod branch sbc-nopci is a one-commit guard on to_pci_dev() in drm_sysctl_freebsd.c. Its sibling drm-subtree (Jesper Schmitz Mouridsen, jsm@, pushed 2026-08-10) runs native FreeBSD arm64 KMS for Rockchip/Allwinner/komeda plus Panfrost on stock drm.ko — a working existence proof of the exact shape needed.graphics/mesa-dri ships no vc4, no v3d, no broadcom. A loading v3d.kext renders nothing without a separate ports task. panfrost is enabled on aarch64, so arm64 SoC Gallium is shippable — just not Broadcom's yet.v3d first minimises engineering risk — a quarter the size, inside the licence rule, already 2712-capable at 6.12, no component framework, no ASoC, no CEC, and its own MMU so no dependency on the BCM2712 IOMMU. It proves the FDT-driver-on-drm.ko plumbing cheaply. It also delivers nothing visible, and needs Mesa before it delivers anything at all.
vc4 first is the only path to a screen on this board, and pays the full cost up front. Three walls:
vc4_hdmi.c includes sound/soc.h, sound/hdmi-codec.h, sound/jack.h, sound/dmaengine_pcm.h. FreeBSD has no ALSA/ASoC at all and it is a hard Kconfig depends on. ~450 lines must be surgically removed; no HDMI audio in v1.component_master_add_with_match() over eight OF sub-devices with a binding-order constraint. linux/component.h is 0 bytes. Implement over newbus, or flatten as the NetBSD port did.rpi-6.12.y: v6.12 → 5,866 changed lines; v6.18 → 2,619. Take vc4 from v6.18, which also brings GEN_6_C/GEN_6_D — and this board is D0.Two easy scope cuts: dropping the eight MIT-licensed VC4-2.x 3D-submission files removes ~4,700 LOC and the licence wrinkle (2712's 3D is V3D 7.1, so they are dead code), and skipping downstream-only vc4_firmware_kms.c (2,079). Realistic KMS-only target: ~14.5k LOC vendored + 3–5k of new shim, HDMI only.
What unblocks what. This is a dependency graph, not a schedule — phases 0–2 are common to every route, and where the paths diverge is called out in §9.
| Phase | Work | Unblocks |
|---|---|---|
| 0 | Serial on 40-pin pins 6/8/10; confirm with Raspberry Pi OS and earlycon=pl011,0x1f00030000,115200n8 | everything — this is the only input channel |
| 1 | make kernel.bin → kernel8.img + bcm2712-rpi-500.dtb; EARLY_PRINTF at 0x1F_0003_0000 | a FreeBSD banner on real hardware, no new code |
| 2 | Confirm SMP on all four A76s via PSCI; verify mountroot | proves TF-A hand-off and the FDT path |
| 3 | SOC_BRCM_BCM2712 option; port bcm2712-pcie + bcm2712-mip from OpenBSD | PCIe and MSI |
| 4 | RP1 bridge (rpone shape) on simplebus_init() + an intrng PIC over 61 MSI-X vectors | everything behind RP1 |
| 5 | dwc3/xHCI glue → keyboard; NVMe on pcie1 → root | a self-hosting machine |
| 6 | cgem + the 2-line RP1-GEM quirk → ethernet; sdhci_fdt + brcmstb | networking, SD |
| 7 | Split tiers 2 and 3 into klds and kexts; requires a loader (route c or kboot) | the kext image |
| 8 | Graphics: sbc-nopci + six helpers → v3d → vc4 (from v6.18) → Mesa | a display |
Phase 3 has an upstream dependency worth engaging rather than forking around: D58105, Michal Meloun's bus-view DMA implementation, created 2026-07-08 and still being revised. BCM2712's pcie2 has a non-identity dma-ranges — a 64 GiB aperture mapping PCIe 0x10_0000_0000 → CPU 0x0 — and FreeBSD has zero dma-ranges parsing anywhere. The Pi 4 driver dodges it by hardcoding its window, which does not generalise. mmel's own comment: “this minimal implementation targets RPi5 support for now.” Every PCIe driver is gated on it.
| Risk | Mitigation | |
|---|---|---|
| high | Nobody has publicly run any BSD on a Pi 500 or 500+. Zero mentions in any BSD source, wiki or mailing list. Same BCM2712-D0 + RP1 silicon, and OpenBSD's bcmstbpinctrl does cover the d0 stepping — but treat it as untested. | phase 1 is cheap and answers it immediately |
| high | Is the 3-pin debug header physically present on a 500+? Unconfirmed — it is inside a keyboard enclosure and no teardown shows it. | use the 40-pin header route, which definitely works |
| med | Secondary cores: MPIDR Aff1, not Aff0. On Cortex-A76/BCM2712 the CPU index lives in Aff1. Circle passes (u64)nCore << 8 to PSCI CPU_ON. A reported CPU_ON returning ALREADY_ON is almost certainly this bug. | FreeBSD reads MPIDR from the DT reg property (0x000/0x100/0x200/0x300), so it should be right — verify in phase 2 |
| med | L3 coherency. DC CSW reported insufficient on BCM2712's 2 MB shared L3 — eviction lands in L3, not DRAM — needing explicit DC CIVAC. Will bite SMP startup and early DMA. | watch for it in phases 2–4 |
| med | NVMe boot. OpenBSD cannot boot from NVMe on this platform (U-Boot CONFIG_PCI_NVME disabled; nvme scan hangs). Our root disk is the built-in SSD. | route (a) bypasses U-Boot entirely; the firmware sets dtparam=nvme automatically when booted from NVMe |
| low | No BCM2712 datasheet exists and never will. A Raspberry Pi engineer: “the majority of the BCM2712 IP is owned by Broadcom… The Linux driver is the documentation.” The RP1 datasheet (RP-008370-DS) is still “Final draft” v1.1 from Nov 2023, omits the Cortex-M3 entirely, and does not contain the 61 MSI-X vector map. | OpenBSD's ISC source is the licence-clean substitute for both |
These are the only items the evidence closes off by itself. Each is reversible if the constraint changes.
extres subsystems already do the job. Constraint: platform_driver_register() returns -ENXIO.pispbe and rpi-hevc-dec. 4,745 + 2,335 LOC sitting on 30–40k LOC of absent V4L2/videobuf2/Media-Controller infrastructure with no other consumer. Constraint: FreeBSD has no V4L2 in kernel. Software HEVC on four A76 cores is the fallback.raspberrypi/linux as copied sources. GPLv3 and GPLv2 respectively. Circle is the most complete bare-metal BCM2712 source in existence and we cannot take code from it. Constraint: licence. Both remain usable as behavioural references for clean-room work — which is how the register maps get confirmed.Everything above is evidence. These are the calls it does not make for us, each with what would settle it.
| # | Decision | Options | What would settle it |
|---|---|---|---|
| 1 | Which boot route ships | (a) direct kernel.bin · (c) EDK2 fork · kboot · some combination | Settled Route (a) ships. It was run, the board boots, and the shipping image is built that way. Original reasoning: run (a) first — it is 1–3 days and answers whether the board boots FreeBSD at all. That result reprices every other option. Note (a) and (c) share all driver work below the boot layer. |
| 2 | Whether the shipping image can be loader-less | monolithic kernel · loader + klds/kexts | Settled Yes, loader-less. The tension resolved in favour of the boot route: the kext premise bends on arm64. Original reasoning: directly determined by #1. “All drivers as kexts” and route (a) are in tension (§5) — this is the sharpest conflict on the page. |
| 3 | Kext granularity for arm64 | boot-minimum compiled in, rest kexts · everything a kext, empty /boot/kernel · defer the split until after bring-up | Settled Boot-minimum compiled in, rest kexts — forced, not chosen: PCIe delivers both the disk and the keyboard, so a kext providing it would have to come off a disk that only exists once it is loaded. Original reasoning: the three-tier split in the driver-delivery plan already has a shape for amd64; whether arm64 follows it or diverges is open. A bring-up with no compiled-in fallback is hard to debug over a serial line. |
| 4 | Native FDT vs LinuxKPI, per tier | native newbus/FDT for Tier A + LinuxKPI for graphics · native everywhere · invest in the LinuxKPI platform layer | Largely forced for Tier A (§4) — the shim does not exist. The genuinely open part is graphics, where drm-kmod is proven for us but the drivers are FDT. |
| 5 | Whether to engage D58105 upstream or carry a local dma-ranges patch | help land mmel's review · fork · wait | Settled Carried as an overlay patch (patches/0016, plus 0017 guarding it for non-arm64). Original reasoning: every PCIe driver is gated on it. Upstream has reviewers engaged and hardware context; a local patch is faster but permanent maintenance. |
| 6 | Graphics order | v3d first (cheap, invisible) · vc4 first (expensive, produces a screen) · neither yet | Whether a display on this board is a goal or a nice-to-have. With no framebuffer of any kind (§1), vc4 is the only path to a screen — but it also needs a Mesa ports task before it means anything. |
| 7 | vc4 baseline, if pursued | v6.12 (matches our base) · v6.18 (halves the delta) | Measured: v6.12 → 5,866 changed lines vs downstream; v6.18 → 2,619. But v6.18 against a 6.12 DRM core risks API drift — unquantified. |
| 8 | WiFi source | OpenBSD/NetBSD bwfm (native, clean) · FreeBSD's in-tree ISC brcmfmac (needs BRCMFMAC_SDIO=1 + MMCCAM) | Confirmed in the tree sys/contrib/dev/broadcom/brcm80211/brcmfmac/sdio.c exists and is ISC; sys/modules/brcm80211/brcmfmac/Makefile ships it with BRCMFMAC_SDIO= 0. So this is enabling an in-tree driver, not porting one — OpenBSD's bwfm is the fallback rather than the first choice. Still true that nobody appears to have run that path against a BCM43455. Deferred past milestone 1; tracked in kernel#99. |
| 9 | Whether to update the EEPROM first | update to 2025-11-05 · stay on 2025-06-13 | Settled Updated to 2025-11-05, as a side effect of a BOOT_ORDER change — rpi-eeprom-config --apply rebuilds from the newest bootloader image on disk. Two variables moved, not one. Original reasoning: a newer EEPROM may ship a 500+-aware DTB and changes the firmware-KMS behaviour. It also invalidates any baseline measured before it. Decide before phase 1, not during. |
Two of these — #1 and #9 — are worth settling before any code is written, because they change what a phase-1 result means. The rest can wait for the first boot.
Hardware inventory captured 2026-08-17 over ssh from the board itself. Source claims re-verified against freebsd/freebsd-src main (std.arm64, Makefile.arm64, arm_kernel_boothdr.awk, machdep_boot.c, platform_device.h, of.h, simplebus.h), openbsd/src master, raspberrypi/linux rpi-6.12.y, u-boot/u-boot master, and nextbsd-redux/nextbsd-kernel-modules PR #33. Planning only — no code has been written against this board.