-
Notifications
You must be signed in to change notification settings - Fork 6
Unlock Driver Patches
The shipping CMP 170HX unlock is not a firmware flash, not a userspace daemon and not a binary
edit. It is six numbered patch files applied to an unmodified checkout of NVIDIA's
open-gpu-kernel-modules at version 610.43.03 or 610.43.02, compiled locally, and installed as
five kernel modules. This page walks through each patch: what it changes, in which source file,
why it is needed, and what fails if you drop it.
The whole unlock (privilege-level mask opening, the compute-throttle writes, the memory-geometry
writes) lives in patch 0001, inside src/nvidia/src/kernel/gpu/gsp/kernel_gsp.c. The other
five patches exist because a GA100 that suddenly claims 64 GiB of framebuffer breaks several
downstream assumptions in the driver: fatal assertions fire, the PRAMIN window lands out of
range, the physical memory allocator never learns about the new memory, the copy-engine scrubber
picks the wrong PTE kind, and RM tears down software state between clients. Each of 0002 to 0006
repairs exactly one of those.
If you only remember one command from this page:
sudo dmesg | grep SEC2_DEBUGAlmost every line the patch set emits carries that prefix. The one exception is the downgraded
WPR2 warning from patch 0001, WPR2 already up before GSP boot; continuing for recovery, which
is printed at LEVEL_WARNING with no SEC2_DEBUG: tag.
Applied in filename order, with patch -p1, against a freshly extracted stock tree.
| # | File | Bytes | Lines | Primary source file | Role |
|---|---|---|---|---|---|
| 0001 | 0001-sec2-postbl-plm-ss-cfg.patch |
19,278 | 463 | gpu/gsp/kernel_gsp.c |
The exploit: opens PLMs, writes SS0/SS1/CFG1/LMR, widens fb_length
|
| 0002 | 0002-booter-verify.patch |
3,901 | 87 | gpu/gsp/arch/turing/kernel_gsp_tu102.c |
Softens four fatal asserts, prints the post-boot readback proof |
| 0003 | 0003-late-pma.patch |
10,317 | 263 |
gpu/mem_mgr/mem_mgr.c, nvalloc/unix/src/osinit.c
|
Registers the new memory with the physical memory allocator |
| 0004 | 0004-bar0-pramin-clamp.patch |
841 | 20 | gpu/bus/arch/maxwell/kern_bus_gm107.c |
Keeps the PRAMIN window inside reachable BAR0 space |
| 0005 | 0005-ce-scrub-workarounds.patch |
1,604 | 38 |
mem_mgr/arch/turing/mem_mgr_tu102.c, mem_mgr/mem_scrub.c
|
Forces the scrubber into physical mode with a plain PTE kind |
| 0006 | 0006-persistent-sw-state.patch |
584 | 19 | kernel-open/nvidia/nv.c |
Sets the persistent-software-state flag at PCI probe |
Total: 36,525 bytes across six patches, touching ten distinct source files. Line counts
include diff headers. Byte counts are the repository blob sizes with LF line endings. A checkout
made on Windows with core.autocrlf=true inflates every file by one byte per line, which is
where the frequently quoted 37,415 byte total comes from.
The full list of files the series touches:
src/nvidia/generated/g_kernel_gsp_nvoc.h
src/nvidia/src/kernel/gpu/gsp/kernel_gsp.c
src/nvidia/src/kernel/gpu/gsp/arch/turing/kernel_gsp_tu102.c
src/nvidia/arch/nvalloc/unix/src/osinit.c
src/nvidia/inc/kernel/gpu/mem_mgr/mem_mgr.h
src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c
src/nvidia/src/kernel/gpu/bus/arch/maxwell/kern_bus_gm107.c
src/nvidia/src/kernel/gpu/mem_mgr/arch/turing/mem_mgr_tu102.c
src/nvidia/src/kernel/gpu/mem_mgr/mem_scrub.c
kernel-open/nvidia/nv.c
driver/build.sh downloads
https://github.com/NVIDIA/open-gpu-kernel-modules/archive/refs/tags/${VERSION}.tar.gz, caches
it under driver/.build/, deletes and re-extracts a clean tree on every run, then loops:
for p in "${patches[@]}"; do patch -p1 < "${p}"; doneThe script runs under set -euo pipefail, so a single rejected hunk aborts the build. Because
the loop is a plain glob over driver/patches/*.patch, a third-party diff dropped in as
0007-*.patch composes cleanly with the series. See P2P for the one
recorded case of that.
Five modules are built and installed to /lib/modules/$(uname -r)/updates/cmpunlocker/:
nvidia.ko, nvidia-modeset.ko, nvidia-uvm.ko, nvidia-drm.ko, nvidia-peermem.ko. Only
nvidia.ko carries unlock code; the other four are stock rebuilds that exist so the whole set
has a matching srcversion.
Every unlock site tests the upper 16 bits of the PCI device ID against the two known 170HX
SKUs. Patch 0001 adds the shared helper to kernel_gsp.c:
#define SEC2_POSTBL_TIMING_CMP_170HX_8GB_PCI_DEVICE_ID 0x20C2
#define SEC2_POSTBL_TIMING_CMP_170HX_10GB_PCI_DEVICE_ID 0x2082
static NvBool _kgspSec2PostblTimingEnabled(OBJGPU *pGpu)
{
NvU32 devId = pGpu->idInfo.PCIDeviceID >> 16;
return (devId == SEC2_POSTBL_TIMING_CMP_170HX_8GB_PCI_DEVICE_ID ||
devId == SEC2_POSTBL_TIMING_CMP_170HX_10GB_PCI_DEVICE_ID);
}Consequences worth internalising:
- Any other GA100 SKU, including an A100 in the same chassis, boots the completely stock path with the patched modules installed. The patch is inert on it.
-
install.shgrepslspcifor10de:20b0|10de:20c2|10de:2082, so a20b0card installs successfully and then never unlocks, because the in-driver gate does not list0x20B0. The installer says so:This card reports 0x${DEVID}; install will continue, but unlock may not activate. - Patch 0006 is the one exception to the
>> 16form. It runs inkernel-open/nvidia/nv.cat PCI probe, before anOBJGPUexists, so it compares the rawnv->pci_info.device_id.
Geometry is selected at runtime from the device ID, not at install time. Both profiles are
baked into patch 0001, so --profile= on the installer only changes the printed banner and the
metadata files. See Memory geometry.
This is the patch. Everything else is support. It is 19,278 bytes, roughly 460 diff lines, and it makes seven distinct changes.
Inserted into src/nvidia/generated/g_kernel_gsp_nvoc.h immediately after
MEMORY_DESCRIPTOR *pSignatureMemdesc;, at hunk anchor @@ -544,6 +544,8 @@:
NvU8 *pStockSignatureData;
NvU64 stockSignatureSize;The real GSP firmware signature is copied aside into these before the payload overwrites the
buffer, and logged as SEC2_DEBUG: saved stock signature (4096 bytes).
Caution
Restore stock GSP firmware first
If the machine ever ran cmpunlocker's firmware-patching predecessor, restore
gsp_tu10x.bin to stock before installing the driver patch. The driver saves whatever
signature the firmware carries as "stock". If the firmware on disk is still patched, it
saves the exploit payload as stock, and the subsequent clean GSP-RM boot DMAs the wrong
ROP chain.
GSP_DIR=/lib/firmware/nvidia/610.43.03
sudo cp $GSP_DIR/gsp_tu10x.bin.cmpunlocker.bak $GSP_DIR/gsp_tu10x.bin_kgspCreateSignatureMemdesc normally allocates NV_ALIGN_UP(pGspFw->signatureSize, 256) bytes,
which is 4096 in practice. When the device gate is true it instead allocates
SEC2_POSTBL_TIMING_SIGNATURE_SIZE = 0x0000f800ULL (63,488 bytes) in ADDR_SYSMEM, 256-byte
aligned because the SEC2 Booter's DMA engine requires it.
That oversize buffer is the whole exploit vehicle: the signed Booter reads it with an unbounded DMA and smashes its own stack. The mechanism is covered in The ROP chain and Falcon and Booter.
_kgspSec2PostblTimingFillPayload fills the entire buffer with the dword
SEC2_POSTBL_TIMING_FILL_DWORD = 0x000004a7, then overwrites exactly 24 dwords, all written
little-endian by a byte-wise helper _kgspSec2PostblTimingPutU32:
| Offset | Value | Role |
|---|---|---|
0x1100 |
0x00000007 |
stack/control word |
0x5b40 |
0xc0deca7e |
fake stack canary |
0xf754 |
writeValue |
patched per PLM pass |
0xf758 |
0xc0deca7e |
canary |
0xf75c |
0x00000cbd |
gadget |
0xf76c |
writeAddr |
patched per PLM pass |
0xf774 |
0x00001fbd |
gadget |
0xf780 |
0x00000000 |
|
0xf788 |
0x000010aa |
the BAR0 write gadget |
0xf78c |
0x0000815a |
tail |
0xf790 |
0x00008e18 |
tail |
0xf794 |
0xc0deca7e |
canary |
0xf798 |
0x0000815a |
tail |
0xf79c |
0x00000000 |
|
0xf7a0 |
0xc0deca7e |
canary |
0xf7a4 |
0x00001fbd |
gadget |
0xf7b0 |
0x0000ffbc |
tail |
0xf7b8 |
0x0000582d |
tail |
0xf7c4 |
0xc0deca7e |
canary |
0xf7c8 |
0x00000cbd |
gadget |
0xf7d8 |
0x00000003 |
|
0xf7e0 |
0x00001fbd |
gadget |
0xf7f4 |
0x00000ccb |
tail |
0xf7f8 |
0x00007f2f |
tail |
Only two of the 24 change between passes: writeAddr at 0xf76c and writeValue at 0xf754.
The chain is a generic single-register write primitive, refired once per target.
The payload can be overridden from disk. _kgspCreateSignatureMemdesc first tries
os_open_and_read_file("/lib/firmware/nvidia/ga100/gsp/dmem.bin", pSignatureVa, 0xf800). On
success it logs SEC2_DEBUG: loaded 63488 bytes from .... On the normal path that file does not
exist, and the driver logs a benign status 0x59 and falls back to the built-in fill:
SEC2_DEBUG: /lib/firmware/nvidia/ga100/gsp/dmem.bin not found (0x59), using built-in payload
The built-in default arms writeAddr = 0x009a0148, writeValue = 0xffffffff, which the PLM loop
immediately overwrites per iteration.
Note
0xFACEB13D is not the shipping canary
Earlier standalone harnesses used 0xFACEB13D at CANARY_ADDR = 0x6340 with
DMA_TARGET = 0x0800. That is the same slot as the shipping 0x5b40 canary
(0x5b40 + 0x0800 = 0x6340) with a different literal. Reading shipping code, expect
0xc0deca7e.
The inserted block sits in the kgspInitRm path in kernel_gsp.c, immediately after
kgspPrepareForBootstrap_HAL(...) returns, not inside it. Hunk anchor @@ -4821,6 +4844,117 @@.
Four privilege-level masks, up to two attempts each. A PLM is a per-register access-control register: it decides which privilege level may read or write the register it guards. See Privilege level masks.
/* plmTable[] entries, verbatim from patch 0001 */
{ 0x001fa7ccU, 0xfffff0ffU, "WPR_CFG" },
{ 0x009a0148U, 0xffffffffU, "FBPA" },
{ 0x001fa7c4U, 0xffffffffU, "WPR" },
{ 0x00823804U, 0xffffffffU, "FEAT" },Warning
Three of four target 0xffffffff, WPR_CFG does not
WPR_CFG at 0x001fa7cc is deliberately opened to 0xfffff0ff. The loop's success
predicate is if (regVal == plmTable[plmIdx].value), so 0xfffff0ff is a pass. The
project's own docs/DEBUGGING.md says "All the PLMs must show 0xffffffff" and the
README.md says "Expected: PLMs opening to 0xffffffff". Both are loose wording. Do not read
WPR_CFG=0xfffff0ff as a failure.
Around each attempt the driver saves and restores the write-protected-region-2 bounds. Before the
loop it reads wpr2Lo = GPU_REG_RD32(pGpu, 0x001fa824U) and
wpr2Hi = GPU_REG_RD32(pGpu, 0x001fa828U); on every attempt it writes both back, refills the
payload for that {address, value} pair, then calls:
kgspExecuteBooterLoad_HAL(pGpu, pKernelGsp,
memdescGetPhysAddr(pKernelGsp->pWprMetaDescriptor, AT_GPU, 0));and reads the target register back. After the loop it restores wpr2Lo/wpr2Hi one final time.
Worst case that is eight Booter Load executions before the normal bootstrap Booter Load.
Note
status=0xffff on every payload pass is expected
s_executeBooterUcode_TU102 finds an error code left in mailbox0 by the seccode after every
run, so kgspExecuteBooterLoad_HAL returns NV_ERR_GENERIC (0xffff) unconditionally on
payload passes. That is the invalid-signature complaint raised after the injected chain has
already executed. The register readback is the only valid success criterion.
kgspExecuteBooterLoad_TU102 also performs kflcnReset(SEC2) before every run, so SEC2
accumulates no state across passes. Booter status 0x31 during these early passes is also
tolerated.
Failure prints FAILED to open %s after 2 attempts. Success prints:
SEC2_DEBUG: PLMs: FEAT=0xffffffff FBPA=0xffffffff WPR=0xffffffff WPR_CFG=0xfffff0ff
Once the PLMs are open, the host CPU writes the unlock registers directly. No exploit is involved in this step, which is the key architectural insight: the ROP chain only has to buy write access, after which everything is an ordinary MMIO store.
GPU_REG_WR32(pGpu, 0x0082381cU, 0x88888888U); /* SS0 */
GPU_REG_WR32(pGpu, 0x00823820U, 0x00000008U); /* SS1 */
GPU_REG_WR32(pGpu, 0x009a0204U, cfg1Value); /* FBPA CFG1 */
GPU_REG_WR32(pGpu, 0x00100ce0U, lmrValue); /* MMU LMR */| Device ID | Card | cfg1Value |
lmrValue |
targetFbBytes |
Result |
|---|---|---|---|---|---|
0x20C2 |
8 GB | 0x02779000 |
0x0000020B |
0x0000001000000000 |
65536 MiB |
0x2082 |
10 GB | 0x02669000 |
0x0000028A |
0x0000000A00000000 |
40960 MiB |
SS0 and SS1 are written unconditionally for both SKUs. common/constants.yaml agrees with
the code (ss0: "0x88888888", ss1: "0x00000008").
Caution
The documentation branch is wrong about SS0 and SS1
docs/ARCHITECTURE.md claims cmpunlocker writes 0xffffffff to both SS0 and SS1 and shows
expected dmesg lines to match. It does not. Validating an unlock against those strings will
make a working card look broken. The code has written 0x88888888 / 0x00000008 since
2026-07-18. See Compute throttle.
A readback line follows:
SEC2_DEBUG: POST-WRITE SS0=... SS1=... CFG1=... LMR=... (devId=0x%x)
kgspSec2PostblTimingRebuildStockSignature() frees and re-creates pSignatureMemdesc at
NV_ALIGN_UP(stockSignatureSize, 256) with MEMDESC_FLAGS_ALLOC_IN_UNPROTECTED_MEMORY, copies
the saved stock signature back, and resets pWprMeta->sysmemAddrOfSignature and
pWprMeta->sizeOfSignature to the new descriptor. If it returns anything other than NV_OK,
_kgspBootGspRm propagates that status and the boot aborts with
SEC2_DEBUG: rebuild stock signature failed: 0x%x.
kgspPopulateWprMeta_HAL() is then called a second time (it was already called in the stock
position before the PLM work). The first call logs SEC2_DEBUG: WPR meta fbSize=... wprEnd=... heapSize=...; the second logs SEC2_DEBUG: WPR meta updated fbSize=... wprStart=... wprEnd=... heapOffset=... heapSize=.... The second call is what makes the driver's WPR2 placement agree
with the now-enlarged geometry.
Two more edits complete the patch.
The "WPR2 already up" fatal error is downgraded, not deleted. Inside the existing
if (kgspIsWpr2Up_HAL(...) && !pGpu->getProperty(pGpu, PDB_PROP_GPU_PREINITIALIZED_WPR_REGION))
guard, at anchor @@ -4805,14 +4820,22 @@, two NV_PRINTF(LEVEL_ERROR, ...) lines and
return NV_ERR_INVALID_STATE; become one line:
NV_PRINTF(LEVEL_WARNING, "WPR2 already up before GSP boot; continuing for recovery\n");Execution falls straight through to kgspPopulateWprMeta_HAL. This is mandatory: the unlock runs
Booter Load up to eight times, and Booter Load leaves WPR2 up.
Then, at @@ -5164,6 +5285,53 @@, after kgspInitRm receives GSP static config info, the patch
rewrites it: pGSCI->fb_length = targetFbBytes, and if the last FB region's limit is below
targetFbBytes - 1 it sets limit = targetFbBytes - 1, reserved = limit - base + 1,
supportCompressed = NV_TRUE, supportISO = NV_TRUE, performance = 20. Logged as
SEC2_DEBUG: static-info BEFORE/AFTER.
Everything. There is no unlock. The card boots stock at 8192 or 10240 MiB with the compute throttle in place.
Touches only src/nvidia/src/kernel/gpu/gsp/arch/turing/kernel_gsp_tu102.c. Three jobs.
It names five registers. These defines are purely for the logging in this file:
#define SEC2_DEBUG_PRI_FEATURE_OVERRIDE_PLM 0x00823804
#define SEC2_DEBUG_PRI_FEATURE_OVERRIDE_SM_SPEED 0x0082381c
#define SEC2_DEBUG_PRI_FEATURE_OVERRIDE_SM_SPEED_1 0x00823820
#define SEC2_DEBUG_PRI_FBPA_CFG1 0x009a0204
#define SEC2_DEBUG_PRI_MMU_LMR 0x00100ce0Note the names: FEATURE_OVERRIDE_SM_SPEED and _SM_SPEED_1 for SS0 and SS1. Those are the
code's own names for the compute-throttle registers, and they read as clock or throughput
controls rather than a cluster enable bitmask.
It converts four fatal assertions into logged status checks in kgspBootstrap_TU102: the
NV_ASSERT_OR_RETURN on pPreparedFwsecCmd != NULL, NV_ASSERT_OK_OR_RETURN(kflcnReset_HAL),
the FWSEC status assert, and NV_ASSERT_OK_OR_RETURN(kflcnResetIntoRiscv_HAL).
It prints the definitive proof line. After the real kgspExecuteBooterLoad_HAL, for the two
device IDs only, it prints SEC2_DEBUG: normal BooterLoad status=0x%x, and then, only when that
status is NV_OK:
SEC2_DEBUG: POST-BooterLoad verify PLM=... SS0=... SS1=... CFG1=... LMR=...
That is the readback showing the unlock survived the genuine GSP boot, not just the payload passes.
Control flow does not actually change: the replacements still return status on any non-NV_OK
result from FWSEC or either Falcon reset, exactly as the assertions did. What 0002 buys is
diagnosis. Without it nothing names which of the four steps failed or with what status, and the
only post-boot verification line disappears, which is the line every troubleshooting flow asks
for. See Verify.
Patch 0001 tells GSP-RM that the framebuffer is bigger. That is not the same as making the extra memory allocatable. The physical memory allocator (PMA) still has to be told about it. Patch 0003 has four hunks plus a hook.
- A forward declaration of
memmgrSec2DebugLateExtendHighPmaRegioninsrc/nvidia/inc/kernel/gpu/mem_mgr/mem_mgr.h. - A diagnostic after
kmemsysPostHeapCreate_HAL:SEC2_DEBUG_HEAP: fbAddrSpace=... mapRam=... fbTotal=... fbUsable=... heapTotal=... regionBytes=... publicBytes=... numRegions=... - The roughly 180-line
memmgrSec2DebugLateExtendHighPmaRegion()itself. - The other CeUtils virtual-mode exclusion, appended to the compbit-backing condition in
mem_mgr.c. The first is in patch 0005 (mem_scrub.c); there are two in the whole series. Patch 0005's second hunk is the PTE-kind override (NV_MMU_PTE_KIND_GENERIC_MEMORY), not a virtual-mode guard.
Plus the hook in src/nvidia/arch/nvalloc/unix/src/osinit.c that calls it late in
RmInitAdapter and logs SEC2_DEBUG: late PMA extension status=0x%x.
The function itself:
stockFbBytes = 0x200000000ULL /* 8 GiB, hard-coded for BOTH SKUs */
candidate = highest-limit Ram.fbRegion[] entry with
bRsvdRegion && !bInternalHeap && limit >= stockFbBytes
pmaRegion = [ NV_MAX(base, stockFbBytes), limit ], bSupportCompressed = NV_TRUE
early-out if pmaIsPmaManaged already covers the range
pmaRegisterRegion(pPma, numPmaRegions, NV_FALSE, &pmaRegion, 0, NULL)
then, only on NV_OK:
split -> append public FB_REGION_DESCRIPTOR [8 GiB, limit] with
bRsvdRegion = NV_FALSE, bInternalHeap = NV_FALSE,
bSupportCompressed = NV_FALSE; clamp the reserved region to stockFbBytes - 1
or
in-place-> clear bRsvdRegion
finally memmgrRegenerateFbRegionPriority()
If a split is needed and numFBRegions >= MAX_FB_REGIONS, it returns
NV_ERR_INSUFFICIENT_RESOURCES rather than splitting.
Note
stockFbBytes is 8 GiB even on a 10 GB card
0x200000000ULL is hard-coded for both profiles. On a 0x2082 card the boundary is
therefore 8 GiB, not the card's native 10 GiB. This is in the shipping code and is not a
typo in this wiki.
The extra framebuffer is reported but never handed to PMA, so the region stays reserved and allocations above the stock size fail. This is the difference between "nvidia-smi shows 65536 MiB" and "you can actually allocate 63 GB".
The smallest interesting patch: a single ten-line hunk in
src/nvidia/src/kernel/gpu/bus/arch/maxwell/kern_bus_gm107.c. PRAMIN is a sliding window in
BAR0 through which the CPU reaches framebuffer memory. Stock code places it at the top of the
framebuffer:
offsetBar0 = (pMemoryManager->Ram.fbAddrSpaceSizeMb << 20) - DRF_SIZE(NV_PRAMIN);The patch adds, for 0x20C2 or 0x2082 when Ram.fbAddrSpaceSizeMb > 0x2000 (8192 MB):
offsetBar0 = (0x2000ULL << 20) - DRF_SIZE(NV_PRAMIN);Note the threshold is 8192 MB, so the clamp engages on a stock 10 GB card too, not only on an unlocked one.
The PRAMIN aperture is computed from 65536 MB and lands outside reachable BAR0 space. GA100 BAR0 is a 16 MiB PRI aperture; a window placed at the top of a 64 GiB address space is simply not addressable through it.
Two hunks on master.
In mem_mgr_tu102.c, the scrubber's PTE-kind helper obtains the GPU with
ENG_GET_GPU(pMemoryManager) and returns early for the two device IDs:
*pteKind = NV_MMU_PTE_KIND_GENERIC_MEMORY; /* instead of */
/* NV_MMU_PTE_KIND_GENERIC_MEMORY_COMPRESSIBLE_DISABLE_PLC */In mem_scrub.c, the if (memmgrUseVasForCeMemoryOps(pMemoryManager)) guard gains:
&& ((pGpu->idInfo.PCIDeviceID >> 16) != 0x20C2
&& (pGpu->idInfo.PCIDeviceID >> 16) != 0x2082)so DRF_DEF(0050, _CEUTILS_FLAGS, _VIRTUAL_MODE, _TRUE) is never set and the copy-engine
scrubber runs in physical mode. The other virtual-mode exclusion is in patch 0003.
The scrubber attempts virtual-mode copy-engine operations with a compression-capable PTE kind across a framebuffer whose geometry the MMU tables were not built for. This is the patch you are most likely to be tempted to skip and should not.
Nineteen lines total, nine of them added code, in kernel-open/nvidia/nv.c at
@@ -1521,6 +1521,15 @@, inserted after the IRQ-setup error path and before
(void)rm_get_gpu_uuid_raw(sp, nv);:
if (nv->pci_info.device_id == 0x20C2)
{
nv->flags |= NV_FLAG_PERSISTENT_SW_STATE;
}
else if (nv->pci_info.device_id == 0x2082)
{
nv->flags |= NV_FLAG_PERSISTENT_SW_STATE;
}The two arms are identical and could be one condition. Cosmetic, not a bug.
NV_FLAG_PERSISTENT_SW_STATE is an existing nv.h flag intended for SR-IOV virtual functions.
Repurposed here, it stops RM tearing down software state when the last client closes. It is
effectively built-in persistence mode, and it is why the current design needs no systemd
watchdog. Earlier generations of the tooling did need one; remove.sh still cleans up a legacy
cmpunlocker.service and /opt/cmpunlocker/daemon/watchdog.py that the current installer never
creates. See Tool lineage.
RM tears down GPU software state whenever the last client exits. On a card whose geometry was established during driver load, that teardown-and-rebuild cycle is exactly what you do not want between CUDA processes.
sudo dmesg | grep SEC2_DEBUGExpected sequence, in order:
| Line | From | Meaning |
|---|---|---|
saved stock signature (4096 bytes) |
0001 | Real signature copied aside |
<dmem.bin path> not found (0x59), using built-in payload |
0001 | Normal, no override file |
WPR meta fbSize=... wprEnd=... heapSize=... |
0001 | First WPR meta pass |
per-PLM lines, status=0xffff
|
0001 | Expected on every payload pass |
PLMs: FEAT=0xffffffff FBPA=0xffffffff WPR=0xffffffff WPR_CFG=0xfffff0ff |
0001 | All four open |
POST-WRITE SS0=... SS1=... CFG1=... LMR=... (devId=0x...) |
0001 | Unlock registers written |
WPR meta updated fbSize=... |
0001 | Second WPR meta pass |
normal BooterLoad status=0x0 |
0002 | The genuine boot succeeded |
POST-BooterLoad verify PLM=... SS0=... SS1=... CFG1=... LMR=... |
0002 | Survived the real boot |
static-info BEFORE/AFTER |
0001 |
fb_length widened, once GSP has returned its static config |
late PMA extension status=0x0 |
0003 | New memory is allocatable |
One reference capture from a working two-card 8 GB boot on 610.43.03 contained 152
SEC2_DEBUG lines (medium confidence, single capture). Absence of the trail is not proof of
failure on its own, because kernel ring buffers rotate.
Note
Line counts are not a reliable cross-build fingerprint
34 (Gen1 build) / 80 (Gen2 build) is recorded at high confidence, while a separate Gen2-branch 610.43.03 boot counted 152 at medium confidence. Do not read a mismatch as a failed install.
The patch camouflages itself with fictional NVIDIA-style names. SEC2_DEBUG_PRI_*,
kgspSec2PostblTiming* and the SEC2_DEBUG: log prefix appear nowhere in the stock 610.43.03
source. "PostBL Timing" is a plausible-sounding but invented feature name. Two independent
reviews read this as deliberate disguise of exploit code as a manufacturing or debug feature,
and as evidence against NVIDIA-internal authorship, since a person with legitimate internal
access would not need the disguise. The practical consequence is the useful one: a single grep
finds every unlock line in a boot log.
Note
Invented acronym expansions to ignore
The docs branch expands PLM as "Program Logic Modules", SS0/SS1 as "Suspension State"
registers, PMA as "Power Management Array", and introduces a "SEC2 Booter PMM" that appears
nowhere in the code. All four are wrong. PLM is the privilege level mask; PMA is the
physical memory allocator (pmaRegisterRegion, pmaGetFreeMemory, PMA_REGION_DESCRIPTOR,
pmaIsPmaManaged); the code's own names for SS0/SS1 are
FEATURE_OVERRIDE_SM_SPEED and _SM_SPEED_1. Do not propagate the expansions.
Several widely repeated claims describe artefacts other than cmpunlocker master. Verified
absent from all six patches:
| Claim | Reality |
|---|---|
gpuValidateRegOps stubbed to return NV_OK
|
Not present. No change to subdevice_ctrl_gpu_regops.c at all. The pre-release patch.diff and, apparently, the leaked package did carry this unconditional bypass of register-operation permission validation; it was dropped before release. That is a real safety improvement, not cleanup. |
CMP170HX_WPR2_SAFE_LIMIT 0x0A00000000ULL clamp on pWprMeta->fbSize
|
Proposed, never shipped. The released design widens fb_length and the last FB region and clamps only the BAR0/PRAMIN window. |
Mailbox-value tolerance treating anything but 0x00000000/0x00000031 as alive |
Proposed, never shipped. The literal 0x00000031 appears nowhere in the repository. |
The 0x00100ce4 LMR-lock clear/re-lock workaround |
Appears nowhere in shipping code. Documented as an untested contingency in a 40 GB guide, whose snippet also targets the wrong file (kernel_gsp_tu102.c; the LMR write is in kernel_gsp.c). |
Any ELF surgery on gsp_tu10x.bin
|
None. That was the generation-1 approach, replaced on 2026-07-18. |
PCIe Gen2 patches 0007/0008
|
Not on master. Branch-only. See PCIe Gen2. |
A kflcnIsRiscvActive bypass |
Not present in any of the six patches. |
Master's patch set is 0001 through 0006 and nothing else.
- How the unlock works for the end-to-end boot story
- Privilege level masks for the four PLM targets in detail
- Memory geometry for CFG1, LMR and the capacity arithmetic
- Compute throttle for SS0 and SS1
- Driver versions for why 610.43.0x and what the backports do
- Register reference and the register index
-
Troubleshooting when the
SEC2_DEBUGtrail stops early
Start
Hardware
- Board And Variants
- Fuses And OTP
- GA100 Silicon
- Memory Subsystem
- NVLink Hardware
- Overview
- PCIe Subsystem
- Power Delivery
- Thermals
- VBIOS
Unlock
- Compute Throttle
- Driver Patches
- Falcon And Booter
- How It Works
- Memory Geometry
- Overview
- PCIe Gen2
- Privilege Level Masks
- Register Reference
- ROP Chain
Procedures
Operations
Frontier
History
Appendix