This page follows a frame from a Metal app down to the GPU, then covers how the driver is installed, removed, and recovered. Paths are the ones on an installed Mac; the source folder for each piece is in brackets.
The driver is new. It is tested on an RTX 5060 under macOS 15.7.x, and it may not work on every card, board or OpenCore setup. If your Mac does not boot macOS with OpenCore yet, set that up first with the OpenCore Install Guide, then install the driver.
Metal app / WindowServer / Core Image / MPS / OpenGL (Apple's GL-on-Metal)
|
v
NVMTLDriver.bundle (Metal driver plugin) [plugin/]
| shaders: Apple AIR -> SPIR-V [translator/]
v
NVK (Mesa's Vulkan driver for NVIDIA, with the NAK compiler) [nvk/nvk-macos.patch]
| SPIR-V -> NVIDIA machine code, command buffers
v
NVRM.kext (NVIDIA's open GPU kernel modules, r610, ported to XNU) [kexts/NVRM]
| talks to the GPU's GSP firmware
v
GeForce RTX GPU (GSP firmware from /Users/Shared/nvfw/nvidia/610.57.04)
Display and compositing run beside that path:
WindowServer -> IOFramebuffer = NVRMFB.kext (NVKMS heads, modes, vblank) [kexts/NVRMFB]
-> IOAccelerator = NVAccel.kext (surfaces WindowServer composites)
-> AppleGraphicsDeviceControl policy = NVRMAGDC.kext [kexts/NVRMAGDC]
NVRM.kext. NVIDIA's open-source resource manager, built for macOS with an XNU OS layer (os-xnu*.cpp) in place of
Linux's. At start it:
- claims the PCI device, maps its BARs, and places BAR1 (the GPU-visible window into video memory) outside the firmware console;
- holds the I/O registry busy so WindowServer does not start on the firmware screen;
- on its own thread, loads the GSP firmware from
/Users/Shared/nvfw/nvidia/610.57.04/and boots the GPU withrm_init_adapter(); - brings up NVKMS (NVIDIA's mode-setting) and releases the hold once the display is armed.
The boot argument -nvoff makes NVRM leave the card alone. The recovery path below uses it.
NVRM is also a power child of the GPU's PCI device: on system sleep it suspends NVKMS and puts RM in standby with all of video memory preserved, and on wake it restores the PCI state and resumes both. NVRMFB then does the wake work IOGraphics expects. SLEEP-WAKE.md has the details.
NVRMFB.kext is the macOS framebuffer (an IOFramebuffer). It publishes each NVKMS head as a display, reads real
mode timings and EDIDs, and drives vblank from the GPU. NVAccel.kext is the IOAccelerator that WindowServer and
IOSurface use for the shared surfaces the plugin renders into. NVRMAGDC.kext answers Apple's graphics-device-control
policy queries for the card.
All four kexts install into /Library/Extensions and load from the Auxiliary Kernel Collection that install.sh
rebuilds with kmutil. They are not injected by OpenCore: the display kexts link against Apple frameworks
(IOGraphicsFamily, IOAcceleratorFamily2) that only exist in macOS's own kernel collections.
NVMTLDriver.bundle (/Library/GPUBundles/) is the Metal driver Apple's Metal.framework loads for the card. It
implements Metal's device, queues, command buffers, encoders, resources, argument buffers, ray tracing and mesh
pipelines on top of Vulkan (nvmtl_vk.c), with contract notes for Apple-specific behavior (sample positions, sampler
descriptors, read-only metadata, physical bounds) in plugin/nvmtl_*.h.
Shader translation (libnvmtl_translate.dylib, translator/) converts Apple AIR, the bitcode Metal shaders compile
to, into SPIR-V that NVK can compile. It is based on metal2vulkan (LGPL-3.0).
NVK (/Library/GPUBundles/nvmtl/) is Mesa's Vulkan driver for NVIDIA plus the NAK shader compiler, patched to run
on macOS against NVRM instead of the Linux DRM interface (nvk/nvk-macos.patch on Mesa 17ca6174).
NVIDIAShared.bundle carries NVIDIA's NVVM compiler libraries and air2nvvm.py, the AIR to NVVM path. The
plugin's MPS convolution fast path is plugin/nvconv.metal.
| Setting | Value | Why |
|---|---|---|
NVRAM > Add > 7C436110-…-FE41995C9F82 > boot-args |
nvfb=1 nvaccel=1 nvfbheads=4 -nvkmsnosmooth amfi_get_out_of_my_way=0x1 amfi=0x80 ipc_control_port_options=0 |
turns on the framebuffer and accelerator, 4 display heads; the AMFI arguments let WindowServer load the driver bundle |
NVRAM > Add > … > csr-active-config |
<430A0000> |
the SIP value the driver was tested with (its kexts are not Apple-signed) |
Misc > Security > SecureBootModel |
Disabled |
Apple Secure Boot refuses kexts Apple did not sign |
UEFI > Quirks > ResizeGpuBars |
13 |
8 GB BAR1 for full memory bandwidth; NVRM then moves BAR1 away from the boot screen (tested on the RTX 5060) |
Booter > Quirks > ResizeAppleGpuBars |
-1 |
macOS sees the full BAR |
Kernel > Block |
com.apple.iokit.IONDRVSupport, Exclude |
otherwise the firmware framebuffer takes display index 0 from NVRMFB |
| BIOS | Above 4G Decoding on, CSM off | the card maps memory above 4 GB |
Optional boot arguments, for troubleshooting: -nvrmnoflip keeps NVAccel on the copy path (no zero-copy scan-out
of the composited surface) and is the first thing to try when the desktop flashes or tears while the cursor moves;
-nvrmnogo leaves the GPU idle until nvrmctl go <pass>; -nvrmnobootscreen skips the boot-screen snapshot.
Installing macOS itself needs different values. The macOS installer has no NVIDIA driver, so it runs on the firmware's
screen, which only survives macOS's PCI setup with a small BAR: ResizeAppleGpuBars = 0, ResizeGpuBars = -1, and
IONDRVSupport not excluded. 1401 builds the installer that way. The 1401 Mac app switches to the table above when it
installs the driver.
1401.app lives in the 1401 repository (mac/, Swift + WebKit; the window is
HTML/JS in mac/Resources/). It installs a driver package with that package's own install.sh. Every change to the system is
made by one root script, nullmoth-setup.sh, which the app runs through macOS's administrator prompt.
Finding OpenCore. The script reads OpenCore's boot-path NVRAM variable, which names the partition OpenCore started
from, and uses that partition. If the variable is missing, it searches every EFI and FAT32 partition and keeps only a config whose SMBIOS model matches this Mac, so a rescue stick or another machine's EFI is never edited; if none matches it stops and asks for the boot disk or stick to be plugged in. OpenCore may live
in EFI/OC or, when OpenCore.efi is the firmware's BOOTx64.efi, in EFI/BOOT; both are found. If more than one
OpenCore is found, the app asks which one.
Install (--dry lists the same changes and makes none):
- checks the driver package's SHA-256;
- test-builds the Auxiliary Kernel Collection before changing anything, so a Mac that cannot take the driver is left as it was;
- backs up
config.plistnext to it, then makes only the changes in section 4 that are missing, and lints the result (any failure restores the backup); - adds 1401: Remove NVIDIA driver to the boot picker (
Misc > Tools, theNullMothSafe.efitool); - runs the package's
install.sh: copies the files and builds the Auxiliary Kernel Collection (if it fails, the config backup is put back); - writes an install record to
/Library/NullMoth/stateand installs the recovery daemon.
If SIP is still fully on in the running system, the first run sets only SIP, Secure Boot, the boot arguments and the boot picker entry, and asks for a restart; the second run installs the driver and switches the BAR.
Remove. If the config is unchanged since the install, the backup is copied back. Otherwise only the driver's own
edits are undone. Then uninstall.sh removes the files and rebuilds the kernel collection.
The way back if the driver stops macOS starting. Choosing 1401: Remove NVIDIA driver in the boot picker runs
NullMothSafe.efi (1401's mac/efi-safe/, Rust UEFI). It adds -nvoff to the boot arguments for the next start and sets the
NVRAM flag nullmoth-remove=1. macOS then starts without the driver, the LaunchDaemon com.nullmoth.recover sees the
flag, runs nullmoth-setup.sh --remove, and restarts.
Verbose startup. Turn verbose on/off adds or removes the boot argument -v in the OpenCore config and in NVRAM,
so the next start shows macOS's startup text instead of the Apple logo.
USB map. The app watches the USB ports while you plug a device into each one, then writes UTBMap.kext (for
USBToolBox) into the OpenCore Kexts folder.
Crash reports. If the driver panics the Mac, the app offers at the next login to write a text report to the Desktop: hardware, macOS version, and what failed, with names, serial numbers, network addresses and paths removed. Nothing is sent; you can upload the file on the site yourself.
package/install.sh and uninstall.sh work without the app (README, Install — prebuilt). The tarball holds
pkgroot/ with every file at its install path plus SHA256SUMS.
See the README, Build from source. The kexts need NVIDIA's open-gpu-kernel-modules at tag 610.57.04; NVK needs
Mesa 17ca6174 with nvk/nvk-macos.patch.