← Back · builds on the in-kernel IOKit matcher, the ko→kext conversion, and the shipped drm-kmod → IntelGraphics/AMDGraphics/RadeonGraphics kexts

NextBSD graphics: virtual-GPU DRM kexts, an end-to-end CI test, and the NVIDIA / VirtualBox tracks

NextBSD already builds drm-kmod (Intel/AMD/Radeon) into Apple-style .kext bundles that the in-kernel IOKit matcher autoloads. But that whole match → autoload → bind → KMS path is untested end-to-end — qemu emulates no real Intel/AMD/Radeon GPU, so those drivers can never reach attach in CI. A DRM driver for a virtual GPU fixes both gaps at once: real KMS graphics in VMs, and the first true end-to-end graphics CI test. This plan scopes the open virtual-GPU ports (bochs, virtio-gpu, vmwgfx, VirtualBox vboxvideo), the repo architecture (an unmodified drm-kmod fork + a single nextbsd-graphics kext factory), and the separate, license-constrained NVIDIA legacy track.

2026-06-09. Synthesized from a 7-agent scope: a 5-agent virtual-GPU workflow (bochs / virtio-gpu / vmwgfx + build architecture + CI/kext/nvidia-drm) plus dedicated VirtualBox-vboxvideo and NVIDIA-legacy studies. Citations to Linux 6.6 drivers/gpu/drm, freebsd/drm-kmod (branch 6.6-lts), freebsd-ports, and the nextbsd-redux repos.

Guiding principle Start with bochs. It binds qemu’s default -vga std device (1234:1111) with no qemu reconfiguration, and matches as a raw IOPCIPrimaryMatch PCI node — exactly the personality shape ko2kext.sh already emits for i915/amd/radeon. So it unlocks the first end-to-end CI test by reusing the existing match→kextd→load→bind machinery unchanged. The real cost is not the driver; it is the missing DRM GEM helper(s) that drm-kmod never compiles. virtio-gpu and vmwgfx are larger follow-ons; NVIDIA is a separate, license-shaped repo.

Status 2026-08-16 — arm64 split out This plan treats virtio-gpu and vmwgfx as “larger follow-ons” to bochs. That framing held for amd64, where bochs and vboxvideo now ship — but not for arm64, where neither binds anything an ARM VM presents and no FreeBSD system has ever had /dev/dri in an ARM virtual machine. The arm64 question turned out to be a different problem with six candidate answers, a licensing decision and two blocking unknowns, so it has its own page: NextBSD arm64 KMS — six routes to /dev/dri/card0 in an ARM virtual machine, which also corrects two claims made here and in the surrounding work.

That question is now decided, and the work split into two independent plans:

Status 2026-08-15 — IMPLEMENTED bochs works. BochsGraphics.kext binds and publishes /dev/dri/card0 on the shipped amd64 image, verified end-to-end in CI (nextbsd-kernel-modules#30):

drmn0: <drmn> on vgapci0
[drm] Found bochs VGA, ID 0xb0c5.
[drm] Initialized bochs-drm 1.0.0 for drmn0 on minor 0
VT: Replacing driver "efifb" with new "drmfb".
# ls -l /dev/dri
card0 -> ../drm/0

That efifb → drmfb line is the vt(4) framebuffer handover — the step this plan named as the canonical “binds but black screen” failure. It succeeded. This is also the first true match → load → bind → KMS test in the tree, and it needed no qemu reconfiguration: q35’s default -vga std is the 1234:1111 device the kext matches.

Correction The helper gate was over-estimated; the real cost was elsewhere. This plan called drm_gem_vram_helper (~1050 LOC, TTM-coupled) the make-or-break piece and budgeted most of 1–2 weeks for it. Measured against 6.12-lts, its entire FreeBSD delta was one argument:

ret = ttm_device_init(&vmm->bdev, &bo_driver, dev->dev,
#ifdef __linux__
                         dev->anon_inode->i_mapping,
#elif defined(__FreeBSD__)
                         NULL,     /* drm-kmod takes void *dummy here */
#endif

FreeBSD has no anon_inode, so drm-kmod’s ttm_device_init() takes a void *dummy; radeon_ttm.c already passes NULL through the same #ifdef. drm_simple_kms_helper.c compiled verbatim. The actual work was four latent drm-kmod/LinuxKPI defects that no shipped driver reaches.

Measured drm-kmod / LinuxKPI defects this port surfaced. All four are latent for any small DRM driver — i915, amdgpu and radeon dodge every one — so vboxvideo and vmwgfx will hit them too unless fixed once, centrally:

  1. drm_module_pci_driver_if_modeset() expands to a four-argument module_driver(); FreeBSD’s linuxkpi declares it with exactly three (Linux’s is variadic). The macro cannot expand at all.
  2. The whole drm_module_* family emits only SYSINITs and never DECLARE_MODULE, so a module built with it carries dependency metadata for a module that does not exist and kldload rejects it outright — surfacing as a bare ENOEXEC / “Exec format error”. Use LKPI_DRIVER_MODULE.
  3. drivers/gpu/drm/drm_gem_framebuffer_helper.c is a 76-line “Public domain” stub implementing three functions, while include/drm/drm_gem_framebuffer_helper.h still declares drm_gem_fb_create and friends. A driver using the generic helper compiles clean and dies at link.
  4. LinuxKPI routes PCI registration on the driver’s name: pdrv->isdrm = strcmp(pdrv->name, "drmn") == 0, choosing the vgapci devclass only when it matches. A DRM driver not called drmn registers on the plain pci devclass and silently never probes — it loads, sits resident, and prints nothing. i915 and radeon both carry an #ifdef __FreeBSD__ override for exactly this.

A fifth, cheaper one: a module that others MODULE_DEPEND on must declare MODULE_VERSION, or the dependant fails with “not available or version mismatch” — also reported as ENOEXEC.

Revised What changed in the plan’s shape.

Measured Which driver serves which architecture. Taken from upstream Linux’s own Kconfig gates plus the EDK2 firmware manifests, not from inference:

DriverLinux Kconfig gateamd64arm64Why
virtio-gpuDRM && VIRTIO_MENU && MMUyesyesNo arch gate, and ArmVirtQemu.dsc builds VirtioGpuDxe — so the firmware provides a GOP, giving efifb/vt at boot and then the same DRM handover amd64 demonstrated.
vmwgfx(X86 && HYPERVISOR_GUEST) || ARM64yesyesUpstream explicitly supports ARM64 — VMware Fusion runs arm64 guests on Apple Silicon. The only other dual-arch candidate.
bochsDRM && PCI && MMUyesno, in practiceThe driver has no arch gate and qemu’s bochs-display is a plain PCI device that attaches on virt — but ArmVirtQemu.dsc ships no QemuVideoDxe (x86’s OvmfPkgX64.dsc does). Nothing initializes the device until a KMS driver loads, so there is no boot framebuffer, no efifb, and nothing for the efifb → drmfb handover to replace. Observed directly: a UTM arm64 guest set to bochs sits at “guest has not initialized the display yet”.
vboxvideoDRM && X86 && PCIyesnoGated x86-only upstream; VirtualBox has no arm64 guests either.
i915yesnoIntel integrated graphics is x86.
NVIDIA (proprietary)yesnoThe FreeBSD port is amd64/i386 only.
amdgpu / radeonyespossibleDiscrete PCIe cards do work on ARM hosts in Linux; drm-kmod lists aarch64 as supported. Low priority for NextBSD, not blocked.

What to build and package, per arch. The driver matrix above says which driver can serve which arch; this is the shipping decision that follows from it. Note the first row: the core kexts are not drivers, they are the substrate every DRM driver links against, so an arm64 package without them carries nothing loadable.

Kextamd64arm64Rationale
DMABuf, IOGraphics, TTM, IOGraphicsExtras (core)buildbuild — requiredEvery DRM driver links against these; without them an arm64 driver kext has nothing to load onto.
VirtIOGraphics (virtio-gpu)buildbuild — the primaryFirmware-backed on ARM, so it also provides the boot console. The arm64 answer.
VMwareGraphics (vmwgfx)buildbuild, optionalOnly if VMware Fusion on Apple Silicon matters; upstream supports ARM64.
BochsGraphicsbuildskipNo firmware driver on ARM — nothing initializes the device before a KMS driver loads.
VBoxGraphics (vboxvideo)buildskipdepends on X86 upstream; VirtualBox has no arm64 guests.
IntelGraphics, NVIDIAbuildskipIntel iGPU is x86; the FreeBSD NVIDIA port is amd64/i386.
AMDGraphics, RadeonGraphicsbuilddeferNot x86-gated, and drm-kmod supports aarch64 — a discrete Radeon in an ARM workstation is a real configuration. But it is speculative hardware for this audience and the firmware bundles are hundreds of MB per package. Unblocked, not unsupported; revisit on demand.

Sequencing. There is no arm64 kext lane today, and virtio-gpu exists for neither arch. Adding the arm64 lane before virtio-gpu would ship a package containing core kexts and no driver — real plumbing, no user-visible effect. Prefer to build the arm64 lane with virtio-gpu, unless you deliberately want the modules-lane and nextbsd-pkg plumbing proven early to de-risk that work.

Delivery path. On amd64 nothing new is needed to ship these: nextbsd-pkg already pulls --pattern '*kext*.tar.gz' from nextbsd-kernel-modulescontinuous release, and the graphics bundle publishes as graphics-kexts.tar.gz, so it lands in NextBSD-kernel-extensions and thence into the image (which is assembled purely from pkg install NextBSD-everything). A kext-only change still needs nextbsd-pkg’s workflow_dispatch to repackage. arm64 does need work: that download is guarded by if [ "$ARCH" = amd64 ], and the package is amd64-only “until the arm64 kext build path exists”. So arm64 virtio-gpu needs three things, in order: an arm64 lane in the modules build, the arm64 kext download in nextbsd-pkg, and an arm64 end-to-end boot test.

Consequence for the roadmap: bochs remains the right P0 — it delivered the end-to-end amd64 test cheaply, and its helper work is what every later driver inherits — but it cannot be the arm64 story. arm64 needs virtio-gpu, whose remaining cost is unchanged from this plan (drm_gem_shmem_helper, the linuxkpi virtio bridge, and IOKit sub-bus matcher plumbing) minus the four defects and three helpers already closed by the bochs work.

Contents

  1. The opportunity & honest framing
  2. The candidates compared
  3. The cross-cutting gate: missing GEM helpers
  4. Recommendation
  5. Build architecture — fork + nextbsd-graphics
  6. Kext integration & the end-to-end CI test
  7. The NVIDIA legacy track (a separate, recipe-only repo)
  8. Repos & naming
  9. Phased roadmap
  10. Risks & open questions

1. The opportunity & honest framing

NextBSD builds the FreeBSD drm-kmod port (branch 6.6-lts) green in CI — the drm.ko/drmn core, ttm.ko, dmabuf.ko, linuxkpi, plus the i915kms/amdgpu/radeonkms drivers — each wrapped into an Apple-style .kext with IOPCIPrimaryMatch personalities so the in-kernel IOKit matcher autoloads the matched driver via kextd. That entire match→autoload→bind→KMS pipeline is, today, untested end-to-end: qemu emulates no real Intel/AMD/Radeon GPU, so those drivers can never device-match in CI. Their personalities are only unit-tested (personalities-selftest.sh); no driver ever reaches attach.

A DRM driver for a virtual GPU changes that on both axes: (a) real KMS graphics inside VMs, and (b) the first true end-to-end CI test, because a virtual-GPU device actually appears on the qemu PCI bus and binds.

The honest catch drm-kmod 6.6-lts vendors none of these drivers. Its drivers/gpu/drm/ holds only amd, display, i915, radeon, scheduler, ttm — no tiny/ (bochs), no virtio, no vmwgfx. (It once shipped vboxvideo + vmwgfx and removed them in commit 787a917 — unmaintained >1yr, drm-kmod issue #356.) So every candidate is a net-new port: vendor the Linux 6.6 source, write a FreeBSD bsd.kmod.mk Makefile, fill the gaps — all on top of the unmodified drm-kmod core.

2. The candidates compared

DriverTarget deviceWhere it bindsMissing helper to vendorHardest gapPoC effortKext matchCI value
bochsBochs VBE / stdvga 1234:1111qemu -vga std (default) — no config changedrm_gem_vram_helper + drm_simple_kms_helperTTM-coupled VRAM helper1–2 wksraw 0x11111234highest
vboxvideoVBoxVGA 80ee:beefVirtualBox (set adapter = VBoxVGA)drm_gem_vram_helper + fbdev-TTM (same VRAM helper as bochs)shares the bochs VRAM-helper gate~days after bochsraw 0xbeef80eemedium (needs VirtualBox)
virtio-gpuvirtio-gpu 1af4:1050 (virtio type 16)qemu -device virtio-gpu-pcidrm_gem_shmem_helperno linuxkpi virtio layer at all2–4 wks (revive prior art)virtio sub-bus (not raw PCI)high (different match path)
vmwgfxVMware SVGA II 15ad:0405qemu -vga vmware / real VMwarebrings its own ttm_object.cpage_dirty mmap write-protect over Linux MM3–6 wksraw 0x040515adlow (real value = VMware guests)

bochs

The cheapest path. A single ~950-LOC file (drivers/gpu/drm/tiny/bochs.c), pure MMIO (Bochs DISPI over a BAR offset, with an 0x1ce/0x1cf I/O-port fallback) — no command FIFO, no hypervisor backdoor, no virtio. It matches qemu’s default -vga std device, so the kext device-matches in CI with no qemu change (the same match covers -device bochs-display). The PCI register/autoload plumbing (linux_pci_register_drm_driver) already exists in drm-kmod’s linuxkpi. The work is the two missing helpers. The canonical failure mode (freebsd/drm-kmod issue #62, “binds but black screen”) is exactly the symptom of getting the TTM-coupled VRAM helper 90% right.

vboxvideo — a near-free follow-on to bochs

The mainline Linux drivers/gpu/drm/vboxvideo/ driver (a few kLOC; MMIO + the HGSMI guest↔host protocol, no firmware, single PCI id 80ee:beef) is its own small driver — and it needs the same drm_gem_vram_helper bochs forces. So once that helper exists in the shared module (§5), vboxvideo is days of work, not weeks. Note: FreeBSD’s emulators/virtualbox-ose-additions ships only the legacy Xorg vboxvideo_drv.so (UMS) + vboxguest.ko/vboxvfs.kono KMS, no /dev/dri, no Wayland — so a modern KMS vboxvideo kext is genuinely net-new value for VirtualBox guests. Caveat: VirtualBox’s newer VBoxSVGA adapter reports the VMware id 15ad:0405 and is driven by vmwgfx, not vboxvideo — so target/standardize on the VBoxVGA adapter for this kext.

virtio-gpu

The “modern VM” answer (qemu/KVM, crosvm, cloud-hypervisor), with abandoned prior art (Alex Richardson’s port on ravynsoft/drm-kmod branch virtio, orig. freebsd/drm-kmod PR #119 against Linux 5.5). Two blockers: (1) linuxkpi has zero virtio support — no <linux/virtio.h>, virtio_device, virtqueue, virtio_driver; FreeBSD’s native sys/dev/virtio is structurally different (newbus, virtio_alloc_virtqueues, virtqueue_enqueue), so a bridge must be hand-written. The ~208-LOC linux_virtio.c prototype maps the common calls but has compile errors, panic() stubs for the mandatory reset/del_vqs, and is against 5.5. (2) drm_gem_shmem_helper.c backs virtio-gpu’s entire buffer lifecycle and is absent. And it does not match as a raw PCI node — FreeBSD’s virtio_pci claims 1af4:1050 and presents a child keyed by a virtio device-type ivar (GPU = 16), so the IOPCIPrimaryMatch model doesn’t apply and new IOKit-matcher plumbing is needed.

vmwgfx

Architecturally friendly (TTM-based, no virtio, slots next to radeon/amdgpu) but large (~45k vendored lines) with genuinely hard linuxkpi gaps concentrated in vmwgfx_page_dirty.c (mmap write-protect / mkwrite dirty-tracking over Linux MM internals that linuxkpi does not expose). The small shims (asm/vmware.h, asm/hypervisor.h, linux/cc_platform.h, kmap_atomic_prot) are cheap; FreeBSD base already ships sys/x86/include/vmware.h. For the CI goal it is poor cost/benefit — bochs reaches the same milestone for a fraction of the code. vmwgfx’s value is real VMware/ESXi/Workstation guest support + 3D, not the CI test (qemu -vga vmware emulates only the 2D-incomplete SVGA2).

3. The cross-cutting gate: missing GEM helpers

The single most important finding: the real cost of every tiny virtual-GPU driver is a DRM GEM-helper layer that drm-kmod never compiles — not the driver .c files. drm-kmod ships ttm/ + drm_gem_ttm_helper, but lacks the convenience helpers these drivers sit on:

HelperNeeded byIn drm-kmod 6.6-lts?
drm_gem_vram_helper.c (~1050 LOC, TTM-backed VRAM GEM)bochs, vboxvideomissing
drm_simple_kms_helper.c (~550, pure-core)bochsmissing
drm_gem_shmem_helper.c (~1000, GPL-2.0)virtio-gpumissing
ttm/, drm_gem_ttm_helper, atomic/format/fbdev helpersallpresent

This is what makes bochs the right first step: porting drm_gem_vram_helper (the TTM-coupled, make-or-break piece) is most of the bochs effort — and it simultaneously unblocks vboxvideo. virtio-gpu needs a different helper (gem_shmem) and the virtio bridge; vmwgfx brings its own object manager. So the helper work isn’t throwaway: it is the foundation the open-driver track is built on.

4. Recommendation

P0 — bochs. The only candidate that (1) binds qemu’s default VGA with no reconfiguration, (2) matches as a raw IOPCIPrimaryMatch node — reusing the existing match→kextd→load→bind machinery unchanged, and (3) is small and self-contained. It is the cheapest thing that delivers the first true end-to-end graphics CI test. Substantive work = drm_simple_kms_helper.c (low risk) + drm_gem_vram_helper.c (the blocker).

P0.5 — vboxvideo, folded in right after bochs since it reuses the same VRAM helper. Cheap incremental KMS for VirtualBox guests.

P1 — virtio-gpu, the modern-VM follow-on — eyes open that the virtio KPI bridge is the dominant cost, not the driver. Plus drm_gem_shmem_helper and new sub-bus matcher plumbing. PoC ~2–4 wks given the prototype; production 2–4 months. virgl/3D explicitly out of scope.

P2 — vmwgfx, optional, justified only for real VMware guest support. The page_dirty MM port is the schedule risk; a first cut can stub it (losing the 2D dirty fast path) to get KMS up.

5. Build architecture — fork + nextbsd-graphics

Version target — planned bump NextBSD is moving its drm core 6.6 → 6.12. The fork tracks 6.12-lts (the branch exists), the new drivers vendor Linux 6.12 sources, and the kernel-baked linuxkpi becomes LINUXKPI_VERSION 61200. Because drm-612-kmod needs a newer linuxkpi than releng/15.0 bakes, this likely pulls the FreeBSD base to 15.1 / 15-stable — the maintainer accepts that bump. The NVIDIA bridge follows in lockstep: graphics/nvidia-drm-612-kmod (rebuilding the NVIDIA ports against 6.12 as needed). References to 6.6-lts / LINUXKPI_VERSION 60600 elsewhere in this plan describe today’s shipping state — read them as 6.12-lts / 61200 once the bump lands.

Keep the design intact: fork freebsd/drm-kmod UNMODIFIED (a SHA-pinned mirror under nextbsd-redux) and add a single companion repo — nextbsd-graphics — that vendors the extra drivers + Makefiles + helper sources + linuxkpi shims and builds against the fork. This mirrors how nextbsd-kernel-modules layers on the kernel, and keeps the fork a clean, rebaseable upstream mirror.

How the companion repo builds against the fork

drm-kmod builds out-of-tree: a top Makefile descends via bsd.subdir.mk into per-module dirs (dmabuf/ttm/drm/amd/radeon/i915), each a self-contained bsd.kmod.mk driven by SYSDIR=/usr/src/sys. A new driver is just another such subdir — a near-copy of i915/Makefile: the verbatim -I block (linuxkpi/gplv2/include, linuxkpi/bsd/include, ${SYSDIR}/compat/linuxkpi/common/include, the fork’s include/include/drm/include/uapi), -DLINUXKPI_VERSION=60600, per-driver -DCONFIG_* and the required string defines (-DKBUILD_MODNAME, -DLINUXKPI_PARAM_PREFIX). MODULE_DEPEND lines live in a glue C file (<driver>_freebsd.c): MODULE_DEPEND(<kmod>, drmn, 2,2,2) + ttm/linuxkpi/dmabuf as needed (linuxkpi is baked into the NextBSD kernel, so no OSBundleLibraries for it).

Key decision Where the missing helpers live. The GEM helpers are drm-core code, and the plan forbids modifying the fork — and if two drivers each bundle the same VRAM helper, EXPORT_SYMS collide. Resolution: a small companion drm_extra_helpers.ko that compiles the missing helpers once, exports them, and that each new driver MODULE_DEPENDs on. This preserves “fork unmodified” without symbol duplication. (drm_gem_shmem_helper.c is GPL-2.0 — fine in the gplv2 companion module, but isolate it.)

Pinning & CI

Today’s CI does git clone --depth 1 --branch 6.6-ltsnot reproducible (the branch moves, and the kernel-baked linuxkpi LINUXKPI_VERSION 60600 must match the fork’s linuxkpi_version.mk or symbols drift). The companion repo MUST pin the fork to a SHA (a git submodule with a locked commit; nesting also makes the relative -I paths resolve naturally). In CI: check out the pinned fork SHA, build it inside make.py … buildenv, then build the companion drivers + drm_extra_helpers.ko against that checkout, linking at load via MODULE_DEPEND. Always rebuild the fork and the extras together from the pinned pair.

6. Kext integration & the end-to-end CI test

Reuse ko2kext.sh and add per-driver personality generators modeled on gen-i915-personalities.sh (each parses its driver’s own PCI id table):

DriverKextMatch
bochsBochsGraphics.kextIOPCIPrimaryMatch 0x11111234 (ideally 4-tuple incl. subsystem 1af4:1100; do not add qxl 1b36:0100)
vboxvideoVBoxGraphics.kextIOPCIPrimaryMatch 0xbeef80ee (VBoxVGA)
virtio-gpuVirtIOGraphics.kextvirtio sub-bus (device-type ivar 16) — needs new matcher plumbing
vmwgfxVMwareGraphics.kextIOPCIPrimaryMatch 0x040515ad

All wrap with ko2kext.sh -l org.nextbsd.kext.iographics (the drm core); no -f firmware — virtual GPUs ship none (a welcome contrast to amdgpu’s hundreds of MB).

Kext roster — everything

The full set across the shipping work and this plan. Bundle ids follow org.nextbsd.kext.<name> (lowercased). ␀ = proposed name (open to change).

KextWraps (.ko)GPU / roleDevice matchDepends onStatus
IOGraphicsdrm.koDRM/KMS core (shared family)— (loaded as dep)DMABufshipping
DMABufdmabuf.kodma-buf sharing(linuxkpi baked)shipping
TTMttm.koGPU memory managerIOGraphicsshipping
IntelGraphicsi915kms.koIntel iGPU0x….8086IOGraphics, TTMshipping
AMDGraphicsamdgpu.koAMD GCN+/RDNA0x….1002IOGraphics, TTM, DMABufshipping
RadeonGraphicsradeonkms.koolder ATI/AMD Radeon0x….1002 (de-overlapped vs amdgpu)IOGraphics, TTMshipping
DRMExtraHelpersdrm_extra_helpers.kogem_vram + gem_shmem + simple_kms helpersIOGraphics, TTMplanned P0
BochsGraphicsbochsdrm.koBochs / stdvga (qemu default)0x11111234IOGraphics, TTM, DRMExtraHelpersplanned P0
VBoxGraphicsvboxvideo.koVirtualBox VBoxVGA0xbeef80eeIOGraphics, TTM, DRMExtraHelpersplanned P0.5
VirtIOGraphicsvirtio_gpu.kovirtio-gpu (qemu/KVM/cloud)virtio sub-bus (type 16)IOGraphics, DRMExtraHelpers, virtioplanned P1
VMwareGraphicsvmwgfx.koVMware SVGA II0x040515adIOGraphics, TTMplanned P2
NVIDIA — one kext per FreeBSD nvidia-kmod* port (separate recipe repo; see §7)
NVIDIAnvidia + nvidia-modeset + nvidia-drm (main)Turing+ (RTX 20xx→Blackwell)0x10de:* (main list)IOGraphics (via nvidia-drm)planned
NVIDIA580nvidia+modeset+drm (580)Maxwell/Pascal/Volta (900/1000)0x10de:* (580 list)IOGraphicsplanned
NVIDIALegacy470nvidia + nvidia-modeset (470)Kepler (600/700)0x10de:* (470 list)— (no KMS bridge; X11 only)planned
NVIDIALegacy390nvidia (390)Fermi (400/500)0x10de:* (390 list)— (X11 only)planned
NVIDIALegacy340nvidia (340)Tesla (8/9/200/300)0x10de:* (340 list)— (X11 only)planned
NVIDIALegacy304nvidia (304)GeForce 6/7 (Curie)0x10de:* (304 list)— (X11 only)planned

The CI test (bochs, the clean case). Boot the continuous image headless with qemu’s default -vga std. The device goes unmatched → device_nomatch → the IOKit matcher reads IOPCIPrimaryMatchkextd kextloads BochsGraphics.kextMODULE_DEPEND pulls drmn/ttm/drm_extra_helpers in order → driver attaches. Assertions: kextstat shows it loaded; dmesg shows drmn/bochs attach + [drm] fb + connector init; /dev/dri/card0 exists; sysctl dev.drmn.0 present; no panic. This is the first time the path is exercised against a device that actually appears. Watch two attach pitfalls: the aperture/vgaarb handoff (must release the boot/efifb framebuffer or the driver fights the console — a common KMS hang), and (virtio only) ensuring the DRM driver out-scores FreeBSD base’s native non-DRM virtio_gpu(4) scanout driver.

7. The NVIDIA legacy track (a separate, recipe-only repo)

NVIDIA is its own repo — not because of the kext mechanics (those are identical) but because of licensing and build shape. The idea: repackage only the kernel modules needed to load for X11/KMS — no libGL, no X driver, no Vulkan ICD — as per-legacy-branch kexts.

The module layout (confirmed)

Distribution — same as FreeBSD NVIDIA’s driver is redistributable: FreeBSD ships nvidia-driver/nvidia-kmod as binary packages under LICENSE_PERMS = dist-mirror no-dist-sell pkg-mirror no-pkg-sell auto-accept. NextBSD builds the three .kos from NVIDIA’s tarball exactly like the FreeBSD port (compile the glue against the local kernel, link the blob, wrap as a kext) and ships the result the same way — carrying NVIDIA’s doc/license.txt and not selling it. Building in a sibling repo vs a job inside nextbsd-graphics is a build-architecture choice, not a licensing one.

One kext per FreeBSD nvidia-kmod* port. FreeBSD ships the current driver plus five pinned legacy branches; each becomes a kext carrying that branch’s .kos and its own supported-GPU match table. Crucially, the DRM/KMS bridge (graphics/nvidia-drm-612-kmod, pinned to NextBSD’s drm 6.12 core — see the note in §5) is packaged only for the main and 580 branches; the four older branches are core / X11-only (proprietary /dev/nvidia path, no /dev/dri, no Wayland), so only main/580 depend on IOGraphics.

FreeBSD portBranchGPU generation(s)DRM/KMS bridge (drm 6.12)→ Kext
x11/nvidia-kmodmain / currentTuring+ (RTX 20xx → Blackwell)yes nvidia-drm-612-kmodNVIDIA.kext
x11/nvidia-kmod-580580.xxMaxwell / Pascal / Volta (GTX 900/1000)yes nvidia-drm-612-kmod-580NVIDIA580.kext
x11/nvidia-kmod-470470.xxKepler (GeForce 600/700)no — X11 onlyNVIDIALegacy470.kext
x11/nvidia-kmod-390390.xxFermi (GeForce 400/500)noNVIDIALegacy390.kext
x11/nvidia-kmod-340340.xxTesla (GeForce 8/9/200/300)noNVIDIALegacy340.kext
x11/nvidia-kmod-304304.xxGeForce 6/7 (Curie / NV4x–G7x)noNVIDIALegacy304.kext

(x11/nvidia-kmod-devel is the beta track — an optional NVIDIADevel.kext if wanted.) Matching: vendor 0x10de; a gen-nvidia-personalities parses each branch’s supportedchips.html (NVIDIA’s own per-branch device table) into a branch-specific IOPCIPrimaryMatch list (NVIDIA’s ids are non-contiguous, so exact lists, not ranges). Biggest blocker: the same linuxkpi/DRM KBI fragility — nvidia-drm.ko must be commit-matched to the drm 6.12 core, which is why build-from-source (not a stock pkg .ko) is mandatory. The maintainer accepts rebuilding the NVIDIA ports against drm 6.12 as part of this track.

8. Repos & naming

One plan, but the heterogeneity (open build-from-source vs. NVIDIA recipe-with-blob) is real, so it lands as three repos, with the per-driver-class differences expressed as jobs/subdirs inside the open one rather than a repo explosion:

RepoRolePublishes
nextbsd-redux/drm-kmodUnmodified, SHA-pinned mirror of freebsd/drm-kmod (the core + intel/amd/radeon source). Kept clean for rebasing.nothing (a source dep)
nextbsd-redux/nextbsd-graphicsThe open kext factory. Vendors the new drivers (bochs/vboxvideo/virtio/vmwgfx) + drm_extra_helpers.ko; builds them and wraps drm-kmod’s intel/amd/radeon, all via the shared ko2kext/gen-personalities toolchain.prebuilt .kexts → continuous
nextbsd-redux/nextbsd-nvidia (name TBD)The NVIDIA recipe (proprietary, per-legacy-branch). Builds on-target/at-build-time; hosts no binaries.a port/recipe, not binaries

Naming: nextbsd-graphics reads cleaner than nextbsd-graphics-modules (these are kexts, not kld modules, and “modules” collides confusingly with nextbsd-kernel-modules). The fork stays plainly drm-kmod. Migration: the current IntelGraphics/AMDGraphics/RadeonGraphics kext work stays in nextbsd-kernel-modules for now; the roadmap migrates it into nextbsd-graphics once that repo exists, so nothing has to move all at once.

9. Phased roadmap

P0 — bochs PoC → kext → CI bind test (the milestone)

  1. Stand up nextbsd-graphics; pin the drm-kmod fork by SHA (submodule); decide submodule layout (it dictates every Makefile’s -I block).
  2. Build drm_extra_helpers.ko: vendor + compile drm_simple_kms_helper.c (low risk first), then drm_gem_vram_helper.c (the TTM-coupled blocker); wire in drm_format_helper.c.
  3. Vendor bochs.c; write its bsd.kmod.mk + bochs_freebsd.c glue; resolve DISPI MMIO/io-port + the aperture handoff.
  4. Get it to compile, kldload, and bind under qemu -vga std; chase the “binds but black screen” symptom in the VRAM helper to a working KMS console.
  5. ko2kext + gen-bochs-personalities.shBochsGraphics.kext; add the qemu -vga std line + /dev/dri/card0 + kextstat + dmesg assertions to CI. First end-to-end graphics CI test.

P0.5 — vboxvideo (reuses the VRAM helper)

  1. Vendor vboxvideo + HGSMI; MODULE_DEPEND on drm_extra_helpers; VBoxGraphics.kext on 80ee:beef. Optional VirtualBox-guest CI if a runner exists.

P1 — virtio-gpu + the virtio KPI shim

  1. Build the linuxkpi virtio bridge: revive linux_virtio.c, fix the compile errors, implement the panic()-stubbed reset/del_vqs, settle the virtio_driver probe model. This is the dominant cost.
  2. Vendor drm_gem_shmem_helper.c into drm_extra_helpers.ko; re-port the 5.5→6.6 virtgpu deltas.
  3. Vendor the virtgpu_*.c set + glue; new IOKit-matcher plumbing for the virtio sub-bus; ensure the DRM driver beats base virtio_gpu(4).
  4. CI: add -device virtio-gpu-pci; assert bind + /dev/dri/card0 + renderD128. Descope virgl/3D.

P2 — vmwgfx (optional) · future — NVIDIA legacy recipe

  1. vmwgfx: the four small shims; vendor the 6.6 tree (incl. its ttm_object.c); stub vmwgfx_page_dirty.c for a first cut; VMwareGraphics.kext on 15ad:0405.
  2. NVIDIA: stand up nextbsd-nvidia as a recipe (fetch unmodified tarball at build; compile nvidia-kmod against the NextBSD kernel; wrap the 3 .kos); start with branch 470 (Kepler), then 390/340; gen-nvidia-personalities from supportedchips.html.

10. Risks & open questions

Open questions to settle first:

  1. Submodule-vs-sibling layout for nextbsd-graphics (dictates every Makefile’s -I block).
  2. Does NextBSD’s IOPCIPrimaryMatch honor the full 4-tuple (subvendor/subdevice), or only vendor/device? bochs ideally keys on subsystem 1af4:1100 to avoid grabbing non-qemu 1234:1111.
  3. Did drm-kmod’s core build with CONFIG_DRM_FBDEV_EMULATION? Without it KMS may work but the fbdev console is absent.
  4. The new IOKit-matcher plumbing for the virtio sub-bus, and how to out-score base virtio_gpu(4).
  5. The exact pinned-SHA pair (fork + kernel-baked LINUXKPI_VERSION 60600) and the CI rule that rebuilds both together.

Research scope: 5-agent virtual-GPU workflow (bochs/virtio-gpu/vmwgfx + build architecture + CI/kext/nvidia-drm) + dedicated VirtualBox-vboxvideo and NVIDIA-legacy studies. Primary sources: Linux 6.6 drivers/gpu/drm (tiny/bochs.c, virtio/, vmwgfx/, vboxvideo/), freebsd/drm-kmod (incl. issues #62/#356 and the removed vboxvideo/vmwgfx), ravynsoft/drm-kmod branch virtio (PR #119), freebsd-ports (x11/nvidia-kmod, graphics/nvidia-drm-kmod, emulators/virtualbox-ose-additions), and NVIDIA’s per-branch supportedchips.html + redistribution license. A plan, not a promise — the GEM-helper port and the virtio KPI bridge are the two pieces that decide the schedule.