GPU autodetection on FreeBSD — kmodloader vs. xconfig vs. initgfx vs. xlibre vs. Linux

An honest comparison of how this project's kmodloader stacks up against the GPU/driver autodetection mechanisms in GhostBSD, NomadBSD, the xlibre X server, and Linux's udev+modprobe stack. Includes the failure-mode analysis: what happens when a DRM kmod loads but doesn't recognize your GPU.

TL;DR

1. Comparison matrix

Eight axes that matter for GPU autodetection on FreeBSD.

Axis kmodloader (this project) GhostBSD xconfig NomadBSD initgfx xlibre Linux udev+modprobe macOS kextd / IOKit
Implementation language Objective-C (Foundation) under launchd POSIX shell (1411 LOC) POSIX shell (467 LOC) + sourced DB (1782 LOC) C, inside Xorg server C (libkmod) inside udevd C++ (kextd, IOKit), launchd-supervised
Cadence One-shot at boot (RunAtLoad); Phase 2 will add hot-plug Boot via rc.d, every boot unconditionally Boot via rc.d, but PCI-hash short-circuit if hardware unchanged Server start; not autoload-related Hot-plug + boot, event-driven Boot (kextcache prelink) + hot-plug (IOKit matching notifications)
Detection input pciconf -l (class 0x0300xx) + devmatch(8) pciconf -lv grep + kern.vm_guest + dmesg pciconf -lv awk-parsed + hw.pci.default_vgapci_unit libpciaccess + udev DRM enum + drmGetVersion kernel MODALIAS uevent → pattern match IOKit IORegistry walk; IOService::probe per matching kext
(vendor:device) → driver source 3 vendor IDs in code; for AMD split, 699-ID frozen list sourced from drm-kmod's drm_pciids.h 701-ID inline regex sourced from drm-kmod's drm_pciids.h; NVIDIA branches by marketing-name string ~507 AMD IDs + ~56 legacy Intel IDs in sourced DB; NVIDIA branches by marketing-name string Inline switch(vendor) in C; modern Intel falls through to modesetting MODULE_DEVICE_TABLE in each .ko; depmod emits modules.alias IOKitPersonalities dict in each .kext/Info.plist with IOPCIMatch arrays
Validation that the driver actually works No — load and trust No — load and trust Yestest_x runs xinit with the proposed config; rollback on failure n/a (post-kmod) No at module level (PCI probe is the validation) YesIOService::probe returns IOProbeScore; highest-scoring kext wins; non-matching kexts never attach
Fallback to scfb/vesa/efifb Implicit: vt_efifb stays in console if no DRM attaches; user/Xorg picks scfb Explicit scfb default; vesa manual subcommand Explicit, picks vesa on BIOS / scfb on EFI from machdep.bootmethod Falls back through fbdev/scfb/vesa in compile-time order n/a (kmod stays loaded; userspace X handles fallback) n/a — Apple ships hardware + drivers as a unit; "no matching driver" effectively can't happen
"Kmod loaded but didn't attach" cleanup No — ~10MB stuck per stale kmod No Indirecttest_x failure rolls back next-boot config but doesn't kldunload n/a No — same shape: probe-fail leaves module resident Yes — kexts that don't pass probe never load; kextcache only fuses kexts whose match succeeds
Hot-plug coverage Phase 2 — planned but not shipped No Non-DRM only via dsbdriverd (DRM kmods explicitly excluded) n/a Yes — udev rules trigger libkmod on every uevent Yes — IOKit matching notifications fire on every IORegistry change
Multi-GPU / Optimus Both vendors load (Intel + NVIDIA), each driver attaches to its own card First vendor wins (Intel → AMD → NVIDIA); single GPU loaded Walks each vgapci device and tries handlers; default vgapci first Iterates platform devices, multi-card supported All matching modules load; kernel binds each to its card IOService probe scores per device; multiple GPUs each get their best-scoring kext
NVIDIA driver-version selection One package only (current). Gap: no 340/390/470 legacy support Marketing-name pattern picks nvidia-driver/-470/-390/-340 at runtime via pkg install All four NVIDIA branches pre-staged at image-build time; runtime symlinks the right one Lists nvidia as one option; selection is xorg.conf-side Per-distro packaging picks one branch n/a since 10.13 (NVIDIA support dropped); historically Apple shipped the kext, no version selection
Scope Whole-system: GPU + NICs + WiFi + storage + USB classes + VM guest additions GPU + Xorg config only GPU + Xorg config only (non-GPU via separate dsbdriverd) X server only All hardware classes All hardware classes (IOKit owns everything driver-shaped)
Pre-link / boot speedup No — load on demand at boot No No n/a No at the module-load layer (initramfs is a separate concept) Yeskextcache fuses kernel + matched kexts into a single boot image

2. kmodloader (this project)

An ObjC daemon at /usr/libexec/kmodloader, started by launchd's org.freebsd.kmodloader.plist with RunAtLoad=true, KeepAlive=false. Three match paths run sequentially in a single runOneShot:

+------------------+ +-------------------+ | pciconf -l | | devmatch | | class=0x0300xx | | (linker.hints) | +--------+---------+ +---------+---------+ | | v v vendor 0x8086 → i915kms if_em, if_iwlwifi, vendor 0x10de → nvidia-drm umass, ahci, ... vendor 0x1002 → frozen list (anything with radeon? radeonkms : amdgpu PNP_INFO macro) | | +-----------+----------------------+ | v NSMutableOrderedSet (dedupe) | v /sbin/kldload -n <name> (per kld)

2.1 What's distinctive

3. GhostBSD xconfig

Single shell script bin/xconfig (1411 lines) plus an rc.d wrapper. Runs every boot via rc.d (logged to /var/log/xconfig_boot.log), persists choices in /etc/rc.conf's kld_list.

3.1 How it works

  1. kern.vm_guest + dmesg grep for hypervisor type.
  2. pciconf -lv | grep -B4 VGA | grep -qi Intel|AMD|ATI|NVIDIA for vendor.
  3. For AMD: a giant inline 701-entry regex (RADEON_DEVICE at bin/xconfig:33) determines radeonkms vs amdgpu. Comment cites drm-kmod's drm_pciids.h as source.
  4. For NVIDIA: detect_nvidia_driver_version matches the GPU's pretty-print description against five marketing-name pipe-strings (latest / 580 / 470 / 390 / 340) checked newest-first. No PCI IDs for NVIDIA at all.
  5. Loads kmod via kldload, persists via sysrc -f /etc/rc.conf kld_list+=.... Generates X config: deletes xorg.conf for Intel/AMD (modesetting handles it); uses X -configure + sed for NVIDIA.
  6. Fallback: applies static XF86Config.scfb template pinning Driver "scfb". vesa only via manual subcommand.

3.2 What it doesn't do

3.3 NVIDIA delivery

Calls pkg install nvidia-driver-NNN at runtime, or pulls from offline /xdrivers/drivers-list for live ISOs. Means a live boot may need network for NVIDIA support unless the offline pkg cache is staged.

4. NomadBSD initgfx

Shell script config/etc/rc.d/initgfx (467 lines) plus sourced DB config/etc/initgfx_device.db (1782 lines) and Xorg templates. Runs at boot; persists via initgfx_kmods= in /etc/rc.conf; smart-skip on PCI hash unchanged.

4.1 The validation feature

The standout: test_x at initgfx:120-137. After loading the candidate kmod and writing the proposed Xorg config:

  1. Run a short xinit session against the proposed config (xmessage -timeout 3).
  2. If xinit exits non-zero, call __reset: revert config, try the next handler.
  3. For NVIDIA, on test_x failure fall through to setup_nv (basic nv driver) before going to vesa/scfb.

This catches the "kmod loaded but didn't attach" case indirectly via Xorg failing to start. None of the other systems do this.

4.2 NVIDIA via per-version pre-staged trees

Build-time, NomadBSD packs every NVIDIA branch (340 / 390 / 470 / latest per build.cfg:NVIDIA_DRIVERS) into /usr/local/nvidia/<ver>/. At boot, setup_nvidia picks the right version from marketing-name match, then symlinks the version's libGL / Xorg modules into place, runs ldconfig -m, installs libmap.d/nvidia.conf. Trades image size (~150MB extra) for offline reliability.

4.3 Fallback

get_fallback_driver() {
    case "$(sysctl -n machdep.bootmethod)" in
        BIOS) echo vesa ;;
        UEFI) echo scfb ;;
    esac
}

Cleaner than GhostBSD's "always scfb." Matches the FreeBSD Handbook's Xorg recommendation exactly.

4.4 dsbdriverd cooperation

NomadBSD ships dsbdriverd for non-GPU drivers (NICs, sound, etc.). Its config explicitly excludes DRM kmods: exclude_kmods = { "radeonkms", "amdgpu", "i915kms" }. Two daemons, clean split. kmodloader collapses these into one process via the two-path scan.

5. xlibre detection (post-kmod)

xlibre is a fork of Xorg. Its detection runs at server start, after the DRM kmod is loaded. The vendor-switch table at hw/xfree86/common/xf86pciBus.c:1046-1264 maps PCI vendor → X driver name, not kmod:

VendorX driver(s) tried
0x1002 (AMD)ati (a metadriver dispatching to radeon/amdgpu/r128/mach64 in userspace)
0x8086 (Intel)intel for pre-Gen3 only; modern Intel falls through to modesetting
0x10de (NVIDIA)nouveau (Linux/NetBSD) → modesettingnvidianv

5.1 Why this doesn't help kmodloader

5.2 The PCI_TXT_IDS_PATH supplement (Linux only)

xf86MatchDriverFromFiles() at xf86pciBus.c:1266 reads a directory of <driver>.ids files at compile-time-configured path. Disabled by default; only meaningful on Linux. Lets external X drivers advertise (vendor:device) support without source patches. Not a model we'd benefit from copying for kmod loading.

6. Linux udev + modprobe

The reference design. Five steps:

  1. Compile time: each Linux kernel module declares MODULE_DEVICE_TABLE(pci, ...); emitted into the .ko's .modinfo ELF section as strings like pci:v00008086d00009A49sv*sd*bc03sc*i*.
  2. Module install time: depmod -a reads .modinfo from every .ko and writes /lib/modules/$(uname -r)/modules.alias.
  3. Boot/hotplug: kernel emits a uevent on netlink with MODALIAS=pci:....
  4. udev catches it. /lib/udev/rules.d/80-drivers.rules contains RUN{builtin}+="kmod load $env{MODALIAS}".
  5. The kmod builtin (linked into udevd) calls libkmod in-process; libkmod fnmatch-globs MODALIAS against modules.alias, finds i915, calls init_module(2).

6.1 The mapping to FreeBSD

This is exactly what FreeBSD already implements:

ConceptLinuxFreeBSD
Per-module device table embedded in .koMODULE_DEVICE_TABLEMODULE_PNP_INFO
Tool that grovels device tables out of modulesdepmodmodules.aliaskldxreflinker.hints
Hotplug dispatcherudev + libkmoddevd → devmatch → kldload
Boot enumerationkernel uevents on initdevmatch -a reads device tree at boot

6.2 The single difference that matters

Linux's drm modules (i915.ko, amdgpu.ko, nouveau.ko) all declare MODULE_DEVICE_TABLE. FreeBSD's drm-kmod (a LinuxKPI port of the same code) doesn't — the LinuxKPI shim doesn't translate MODULE_DEVICE_TABLE into MODULE_PNP_INFO. That single missing translation is why we need a GPU PCI scan at all. Devmatch handles every other driver in tree.

6.3 Linux "too new" handling

If the .ko loads but the live device ID isn't in the module's pci_ids: the driver's .probe() is never called, the device sits unbound. Modern kernels expose i915.force_probe=, amdgpu.force_probe= etc. for users with too-new hardware. There is no userspace "fall back to a different kernel module" mechanism — udev hands MODALIAS to libkmod, gets a hit or miss, and that's it.

7. macOS: kextd + IOKit (the reference architecture)

The original kmodloader plan was modeled directly on Apple's kextd + IOKit personality matching. Phase 1a-c shipped a literal port of that idea (.kext bundles in /System/Library/Extensions/, IOKitPersonalities dicts, integer probe scores, highest-scoring kext wins). Phase 1d ripped it out in favor of devmatch for FreeBSD-specific reasons. Worth showing the reference architecture both for design provenance and to answer "why don't you just do what Apple does?"

7.1 The kextd loop

kextd is a launchd-supervised daemon (com.apple.kextd.plist). On startup it:

  1. Walks /System/Library/Extensions/*.kext/ and /Library/Extensions/*.kext/.
  2. Reads each bundle's Contents/Info.plist, extracts the IOKitPersonalities dict.
  3. Hands all personalities to the kernel via kextload(8)-equivalent.
  4. Listens on a Mach port for kAppleEventLoopMessage-style notifications: when a new device appears in IOKit's IORegistry, kextd is told and re-runs matching for that device.

The kernel keeps a personality registry indexed by match category ("PCI", "USB", "USBClass", etc.). When IOKit sees a new device, it walks the registry, calls IOService::probe on every personality whose match dict could apply, and binds the device to the highest-scoring service.

7.2 IOPCIMatch + IOProbeScore (the design our Phase 1a-c shipped)

An IOKit personality looks like this (Apple's actual i915 driver, simplified):

<dict>
    <key>CFBundleIdentifier</key>
    <string>com.apple.driver.AppleIntelKBLGraphicsFramebuffer</string>
    <key>IOClass</key>          <string>AppleIntelKBLFramebuffer</string>
    <key>IOMatchCategory</key>  <string>IOFramebuffer</string>
    <key>IOPCIMatch</key>
    <array>
        <string>0x59168086</string> <!-- HD Graphics 620 -->
        <string>0x591B8086</string> <!-- HD Graphics 630 -->
        <!-- ... -->
    </array>
    <key>IOProbeScore</key>     <integer>90000</integer>
</dict>

The probe call returns either 0 (don't match, skip) or a positive score. Higher score wins; ties broken by personality registration order. Non-matching personalities never get a probe call — the match dict is the gate.

This is what kmodloader Phase 1a-c implemented in kmodloader/personalities/*.kext/Contents/Info.plist with our own probe-score algorithm. Worked correctly for hardware we hand-curated; failed scaling because FreeBSD's drm-kmod doesn't ship Info.plists with IOPCIMatch arrays, so we'd be hand-curating ~2000 device IDs ourselves — without Apple's full-time staff to maintain them.

7.3 kextcache prelinking

Apple's optimization for boot speed: kextcache fuses the kernel binary plus every kext that matched at last-boot into a single signed boot image (/System/Library/PrelinkedKernels/prelinkedkernel). At boot the loader maps the prelinked kernel directly — no per-boot scan of /System/Library/Extensions/, no per-kext load decisions, no probe loop. The matching work happened once at install time.

Plan §11 Phase 4 flagged this as a future optimization for kmodloader; it remains a Phase-4 idea. The Apple-shape parallel is roughly: at install time, run kmodloader's two-path scan, write the result to a manifest, have /boot/loader consume the manifest and pre-load on next boot. Defers all the "vendor scan + devmatch" work from boot to install.

7.4 DriverKit (modern Apple userspace drivers)

Since macOS 10.15 / iOS 14, Apple has been moving drivers out of the kernel and into sandboxed userspace processes called .dext bundles (DriverExtension). Each dext is a launchd-launched signed binary that talks to a small in-kernel "personality" stub via IOKit's DriverKit framework. Match logic stays the same (IOPCIMatch in the dext's Info.plist); the actual driver code runs in userspace.

Genuinely interesting as a long-term direction but not on kmodloader's roadmap. FreeBSD has no equivalent to DriverKit's userspace-driver kernel infrastructure today, and porting one is a project comparable to launchd itself.

7.5 Why we don't do exactly this on FreeBSD

We started here (Phase 1a-c) and reached a different place (Phase 1d-1g) for three reasons:

  1. Apple ships finite hardware. A handful of staff curate the IOPCIMatch tables for the SoCs/GPUs Apple ships. FreeBSD targets every PC ever made; hand-curated personality plists don't scale to that hardware breadth.
  2. FreeBSD already has the data, just in a different format. linker.hints built from MODULE_PNP_INFO macros gives us the same (vendor:device → kld) map for every in-tree driver. devmatch(8) is the canonical reader. We don't need to reinvent the registry; we need to use the one FreeBSD already has.
  3. The narrow gap (drm-kmod) is closeable with a 3-vendor map plus one frozen 699-ID list. No need for Apple-style full personality plists when the actual missing data is "which of three vendor IDs do I have."

The Apple-shape design we kept: launchd supervision, ObjC implementation, plist-based config, the conceptual model of "a daemon that owns kernel-extension loading." The Apple-shape design we dropped: the per-driver IOPCIMatch personality plist as the load-decision data structure.

8. The "too-new GPU" failure case

The user's specific concern: what happens in our case when the device is too new for the latest drm-kmods? Detailed answer with FreeBSD source citations.

7.1 Probe path (LinuxKPI shim)

drm-kmod's i915kms registers as a pci_driver via LinuxKPI. Newbus calls linux_pci_probe() at sys/compat/linuxkpi/common/src/linux_pci.c:506:

if ((pdrv = linux_pci_find(dev, &id)) == NULL)
    return (ENXIO);

linux_pci_find() walks the driver's id_table matching on vendor/device/subvendor/subdevice. If the chip isn't in the table, probe returns ENXIO. Newbus tries the next driver in priority order; vgapci(4) wins by default at BUS_PROBE_GENERIC.

If the chip is in the table but flagged require_force_probe, drm-kmod's i915_pci.c emits the canonical "too-new" dmesg line:

Your graphics device 0xNNNN is not properly supported by i915 in this
kernel version. To force driver probe anyway, use i915.force_probe=NNNN
module parameter or CONFIG_DRM_I915_FORCE_PROBE=NNNN configuration option,
or (recommended) check for kernel updates.

And returns -ENODEV. Same outcome: no drmn child attaches.

7.2 What the user sees

LayerState after probe failure
kmodStays loaded. kldstat shows i915kms.ko resident (~10MB). No CPU, no IRQ, no /dev/dri/cardN.
vgapci0Stays attached to the GPU's PCI slot. No regression.
Console (UEFI boot)vt_efifb stays in charge. Full-resolution framebuffer, unaccelerated. Works.
Console (BIOS boot)vt_vga stays in charge. Text mode. Works.
Xorg (any boot)Falls through modesetting (no DRM, fails) → scfb (UEFI) or vesa (BIOS). Native res on UEFI, low VESA modes on BIOS. No crash.

7.3 NVIDIA edge case

nvidia.ko + nvidia-modeset.ko + nvidia-drm.ko behave differently:

7.4 kmodloader's risk profile

Loading drm-kmod / nvidia kmods speculatively is safe. Probe rejection is the designed-in path. The only cost is ~10MB resident kernel memory per loaded-but-unbound kmod. No panic, no console disruption, no Xorg breakage. The vt_efifb framebuffer is the universal fallback and stays in place exactly because no DRM driver attached to take it over.

9. Scorecard — wins and gaps

8.1 Where kmodloader wins

Single ObjC daemon under launchd versus shell-script-per-purpose. Code is in one place, gets exercised by CI, has the GNUstep stack available for future Phase 2+ features (hot-plug via DISPATCH_SOURCE_TYPE_READ on /dev/devctl, structured logging, optional kldunload cleanup pass).
Three-path coverage: GPU + everything-else-with-PNP_INFO + VM additions. GhostBSD/NomadBSD only handle GPU. NomadBSD has dsbdriverd for everything else; GhostBSD relies on whatever's in GENERIC. Linux's udev handles all classes, but we get there with one daemon vs. udev's rule-file ecosystem.
AMD radeonkms split via upstream-frozen list. Same source GhostBSD uses (drm-kmod's drm_pciids.h), but baked into RadeonPCIIDs.h at build time so we can iterate the array directly in C without shell regex. amdgpu becomes the open default — no maintenance for new AMD silicon.
(Removed) Earlier draft included a hypervisor scan (kern.vm_guest → vbox/vmware kld lists). Pulled out: guest additions are services, not hardware drivers; they belong in their own launchd plist (org.freebsd.vboxservice.plist Phase 2+) which kldloads its supporting kmods before launching the userspace daemon.
Built on launchd not rc.d. Plist describes the job declaratively; the same launchd that runs everything else supervises it. No bespoke rc.d ordering.

8.2 Where kmodloader has gaps

No live validation step. NomadBSD's test_x runs a real xinit after loading the kmod and rolls back the choice if Xorg can't start. We don't. If our vendor map is wrong (shouldn't happen for the three vendors but possible if drm-kmod renames a kld), the user discovers it when X doesn't come up. Mitigation path: add an optional Phase 2+ check that dev.drmn.0.%pnpinfo exists after kmod load; if not, log warning. Doesn't replicate test_x's full smoke test but catches the obvious case.
No kldunload cleanup pass. If we load amdgpu on a system whose AMD GPU is in the radeonkms-frozen list (so our split routed correctly to radeonkms), amdgpu doesn't get loaded — fine. But if we load all four GPU kmods speculatively and only one binds, the others sit at ~10MB each. Phase 2+ enhancement: post-load, check for drmn children under each vgapci; kldunload the unbound ones. Current behavior is correct (we only load per-vendor, not all four), so this is a future polish, not a fix.
No NVIDIA driver-version selection. Older NVIDIA cards (Kepler / Fermi / Tesla) need nvidia-driver-470, -390, or -340 instead of current. We ship one nvidia-drm-latest-kmod. NomadBSD bakes all four branches at build time and picks at boot via marketing-name match; GhostBSD pkg installs the right one at runtime. Fix: add the three legacy nvidia-driver packages to pkglist.txt and extend the GPU-vendor scan to map NVIDIA marketing-name strings to specific kmod names. Cost: ~200MB on the ISO (NomadBSD's penalty for the same coverage).
No hot-plug yet. kmodloader Phase 1 is a one-shot at boot. Plug a USB ethernet adapter or hot-add a PCI device after boot and nothing fires. Linux/dsbdriverd both handle this; we don't. Phase 2 plan: DISPATCH_SOURCE_TYPE_READ on /dev/devctl, parse +/? events (attach / nomatch), re-run the appropriate match path.
Indirect efifb awareness only. When DRM kmod fails to bind, vt_efifb keeps the console on UEFI systems — works correctly without us doing anything. But we don't verify this; we don't generate an Xorg fallback config. Acceptable today (Xorg autoconfig handles scfb fallback); becomes a policy question if we ship an X server with strict driver requirements.

10. Specific takeaway: xlibre devmatch detection

The user's prompt asked specifically about xlibre's devmatch detection so kmodloader doesn't load too aggressively. Conclusion after deep-reading xlibre source:

xlibre's detection happens after the DRM kmod is loaded and therefore can't tell us which kmods to skip. Its vendor-switch table maps to userspace X driver names, not kmod names. The "ati" X driver (metadriver) handles all AMD generations regardless of whether we loaded amdgpu or radeonkms — xlibre couldn't object even if we loaded the wrong one. The X server will use whatever DRM device exists at /dev/dri/cardN and falls through to scfb/vesa/fbdev if none does. This is graceful but not a guidance signal.

The right framing is: don't expect xlibre to filter our kmod-load decisions. Filtering belongs to the kmod-load layer (us). What xlibre does provide is a clean fallback through its listPossibleVideoDrivers() chain (modesetting → fbdev/scfb → vesa) that handles "no DRM kmod attached" without panic — confirming that loading drm-kmod kmods speculatively is safe.

11. Recommendations

PriorityItemEffort
HighPhase 2 hot-plug (devctl source). Closes the biggest functional gap vs Linux.~300 LOC ObjC + plist KeepAlive=true.
MediumNVIDIA legacy driver-version selection. Add nvidia-driver-{470,390,340}-kmod to pkglist; extend GPU-vendor scan with marketing-name → kmod map.~50 LOC + ~200MB pkglist.
MediumOptional kldunload cleanup pass for unbound kmods. Check dev.drmn.* after load; unload if no children.~30 LOC.
LowLive validation lite: log warning if a loaded GPU kmod has no drmn child after a brief settle delay.~20 LOC.
Long-termUpstream PR to drm-kmod adding MODULE_PNP_INFO declarations from each driver's compiled-in supported list. Would let our GPU PCI scan collapse into devmatch.Out of our control; coordinate with drm-kmod maintainers.

12. Open TODOs — ports-shipped kld drivers

drm-kmod is the canonical case kmodloader's GPU PCI scan addresses. But it isn't the only ports kld package that may have the same problem — "the kld ships from ports without MODULE_PNP_INFO, so devmatch can't see it, so we'd need a vendor-scan-style fallback or speculative load." Each of these needs a one-shot investigation: install the package, run kldxref -d /boot/modules/linker.hints | grep <name>, and see whether devmatch picks it up. If yes, no kmodloader change — just add the package to pkglist.txt. If no, we need the same kind of targeted handling we do for GPUs.

12.1 Investigation procedure

For any ports kld package that ships an if_* / wmt_* / similar driver kmod:

# 1. Install on a test system
pkg install <package>

# 2. Re-build hints (pkg post-install does this, but be explicit)
kldxref /boot/modules

# 3. Look for the driver in the hints database
kldxref -d /boot/modules/linker.hints | grep -i <driver>

# 4. Look for PNP_INFO sections in the .ko binary
strings /boot/modules/<driver>.ko | grep -i pnp_info

# 5. Try devmatch directly (after kldunload to make the device unattached)
devmatch | grep -i <driver>

Outcomes:

12.2 Known candidates to investigate

Package What it ships Why it might need help Investigation status
net/realtek-re-kmod Newer Realtek 2.5G/5G ethernet driver (if_re) from Realtek's upstream source — supersedes the base if_re for RTL8125/8126 etc. Ports build of a vendor-supplied tarball; PNP_INFO presence is upstream-source-dependent. Common on motherboards with 2.5GbE NICs (most boards 2022+). TODO: install on a test system with an RTL8125 NIC, run the procedure above. Likely outcome: has PNP_INFO (Realtek's source declares device IDs); just needs adding to pkglist.
net/rtl8821au-kmod /
net/rtl88xxau-kmod /
net/rtw88-kmod
Realtek USB WiFi drivers (LinuxKPI ports of out-of-tree Linux drivers). LinuxKPI ports — the same drm-kmod issue applies. Linux's MODULE_DEVICE_TABLE doesn't auto-translate to FreeBSD's MODULE_PNP_INFO through the LinuxKPI shim. Almost certainly have the same gap as drm-kmod. TODO: confirm gap. If confirmed, options are: (a) speculative load — add to pkglist, kmodloader kldloads on USB-class match for known Realtek vendor IDs, similar to GPU pass; (b) extend devmatch's source data to include LinuxKPI's id-tables (upstream effort).
x11-drivers/xf86-input-evdev
+ kernel evdev support
Userspace X input driver consuming kernel evdev events. Touchscreens, multitouch trackpads, advanced HID devices. The kernel side (evdev, wmt, iichid) is in base and has PNP_INFO via standard USB/IIC class match. The Xorg driver loads on top. Likely no kmodloader work needed. Verify: run devmatch on a system with a Precision Touchpad — should suggest iichid/wmt. If yes, fine.
"utouch" family (USB touch controllers via uhid+hidraw) Various USB touchscreens, touch-bar-like devices, drawing tablets. Driven by uhid/ums/wmt in base; ports versions extend or supersede. Mostly works via base USB class drivers (uhid matches USB HID class generically). The "ports needed" cases are usually graphics tablets with proprietary protocols (Wacom etc.). Some have FreeBSD support via x11-drivers/xf86-input-wacom + evdev. TODO: list specific devices that don't work via base USB classes; investigate per-device.
graphics/drm-kmod-experimental /
future drm-kmod-next
Bleeding-edge drm-kmod for hardware too new for the stable release (e.g., Intel Battlemage, latest RDNA). Same shape as drm-kmod (no PNP_INFO via LinuxKPI). Our GPU PCI scan would route Intel/AMD GPUs to i915kms/amdgpu regardless — so users who install drm-kmod-experimental and uninstall drm-kmod get the right kld name; just a swap. Conflict if both installed. Trivial: document that experimental and stable are mutually exclusive; the kld name doesn't change so kmodloader's PCI scan handles either.
NVIDIA legacy nvidia-driver-{340,390,470}-kmod Older NVIDIA branches for Kepler / Fermi / Tesla generations. Already flagged as a gap in §9. NomadBSD bakes all four branches; we ship one. Solving this needs a marketing-name → kmod-name map at the GPU pass. Open; tracked in §11 Recommendations as medium priority.
sysutils/xen-kmod Xen guest extensions kld for newer Xen versions than base supports. Xen guest base support is in GENERIC kernel. Ports kld is for newer features (PCI passthrough, etc.). Probably not auto-load territory; user-driven. Defer: not a common live-ISO scenario.
net/wireguard-kmod WireGuard VPN as a kld (when not built into kernel). Already in GENERIC kernel as of 13.x. Kld package is for older releases. Not relevant on 14.x/15.x targets. n/a on our target.
multimedia/v4l_compat /
multimedia/webcamd
Video4Linux compat for webcams + USB webcam daemon. webcamd is a userspace daemon that talks to USB devices; not a kld. v4l_compat is a header-only port. Neither is a kmodloader concern; they're launchd-service-shaped. n/a for kmodloader; relevant for a future "media services" plan.

12.3 Strategy summary

For each port-shipped kld package we want on the ISO, the decision tree is:

  1. Install on a test system; run the §12.1 procedure. Get an empirical answer — has PNP_INFO or doesn't.
  2. If it has PNP_INFO: add the package to pkglist.txt with a comment citing the test result. Done. devmatch handles it on every boot via the existing path.
  3. If it doesn't: pick a path:
  4. Add the test result + chosen path to a per-package comment in pkglist.txt so future maintenance has the audit trail.

None of these candidates are blocking Phase 1 functionality; they're TODOs to work through as users surface needs. The shape of the answer is the same for each: investigate empirically, then either pkglist-only or pkglist+code.

13. References

kmodloader

GhostBSD xconfig

NomadBSD initgfx

xlibre

Linux

FreeBSD failure-mode

macOS / IOKit