Add check_cards.py CLI and cxlib.py host helpers for preparing, unlocking, and PCIe-checking NVIDIA CMP 170HX cards, plus operator README, agent docs, and bundled nvflash tooling.
CMP 170HX unlock and PCIe check
This tool handles one NVIDIA CMP 170HX at a time on an Ubuntu 26.04 x86-64 host. It prepares a compatible open NVIDIA driver, measures PCIe lane width before unlocking, installs cmpunlocker if the card still reports stock memory, waits for the required cold boot, then verifies unlocked memory and the final PCIe link. The 170th-Street capacitor mod guide documents x16 as the expected width after the hardware modification, even at Gen1. The final card check requires Gen2 x16 after cmpunlocker.
The program downloads a cmpunlocker source archive over HTTPS. It does not run Git remote commands. The source is pinned to upstream v0.3 commit f51da030ef4260656288edf07e95ae45f339aea8 and checked against the archive SHA256 in cxlib.py. The 170th-Street guide supplies the width criterion and is linked above.
On a fresh Ubuntu 26.04 host
-
Connect one card, make sure the host has Internet access, and run from this directory:
python3 check_cards.py -
The first run downloads cmpunlocker, installs
pciutils, build tools, matching kernel headers, andnvidia-driver-610-openwhen needed. It asks for sudo credentials if necessary. Package installation output streams to the terminal and is saved underharness/logs/. When it prints cold shut down and power on, shut down fully and start the host again:sudo shutdown -h now -
Run
python3 check_cards.pyagain after each requested cold boot. It reads lane width before attempting cmpunlocker. If memory is still stock, it runs the pinned upstream installer with its supported default options. The installer configures its PCIe Gen2 service and IOMMU passthrough settings. Its output streams to the terminal and tocards/<serial>/extras/cmpunlocker_install_*.log(orunidentified/if the serial is not readable). Cold shut down and power on again, then rerun the script. Once unlocked memory is active, it measures the final PCIe link. A card that was already unlocked proceeds directly to verification.
The upstream installer builds and installs patched kernel modules and requires a compatible active nvidia-open 610 driver, matching headers, root access, and disabled Secure Boot. It downloads NVIDIA source during the build. See the upstream requirements and installation instructions.
python3 check_cards.py --preflight performs setup only. It may also return 4 to signal a required cold boot. If the driver remains inactive after that boot, the script stops with a diagnostic instead of installing it repeatedly.
If a completed reboot still leaves the driver or unlock inactive, inspect the card report and its installer log. The pending marker in harness/unlock_pending.json records the installed card's physical serial or internal GPU UUID when available, so a different card after reboot can be checked independently. The UUID is never used as a folder name. After resolving a failed unlock on the same card, remove that marker only when you intentionally want the script to retry installation.
Per-card flow
- Detect one CMP 170HX (
10de:20c2or10de:2082) and try to get its serial number automatically fromnvidia-smi, PCI VPD, or a card-scoped query if the installed nvflash supports one. No serial is requested from the operator. If the physical serial is unavailable, checks continue inunidentified/<run-id>_<PCI-address>/; a completed run fails with a clear serial error. If the serial appears later in the same run, those diagnostics move tocards/<serial>/. Earlier unidentified runs remain separate. A GPU UUID is not used as a serial. - Read the endpoint's
LnkStalane width before unlocking. An x16 width passes the capacitor width check even at Gen1. An x4 or unreadable width immediately printsCAPACITOR MOD CHECK FAILEDin red on a terminal. If the current user cannot read link details, the script requests sudo access and retries. The width measurement is independent of cmpunlocker; the normal script flow still completes host preflight before reaching it. - Check memory size for that exact PCI address. Stock memory triggers the cmpunlocker installer; unlocked memory continues to verification. Run
cmpunlocker/verify.shwhen its installed GPU inventory matches the current card, then capture the final PCIe link. Gen2 x16 is the only passing final link result. If the upstream inventory describes an earlier card, the script reports that verifier as skipped and uses live per-card memory and PCIe measurements. - Capture PCIe configuration, AER counters, kernel lines that name this card since the run began, and
nvidia-smidata. Other recent PCIe/GPU kernel lines go in a separate log and do not count as card errors. Optional actions run afterward.
| Negotiated link | Capacitor width check | Final card check after unlock |
|---|---|---|
| Gen2 x16 | Pass | Pass |
| Gen1 x16 | Pass | Fail: Gen2 is required |
| Gen2 x4 | Fail: x16 is required | Fail |
| Unknown speed, x16 width | Pass | Fail: Gen2 cannot be confirmed |
| Unknown width | Fail: x16 cannot be confirmed | Fail |
The tool cannot inspect physical capacitors. A passing width result means x16 was observed on this host; a passing final card result also requires unlocked memory and Gen2. A failing width result does not by itself identify whether the capacitor mod, slot, or riser limited the link. Failure text remains explicit when color is unavailable in redirected output.
Optional actions
python3 check_cards.py --bios # VBIOS backup in cards/<serial>/extras/
python3 check_cards.py --link-tests # PCIe retrain and bus rescan
python3 check_cards.py --vulkan ask # offer a Vulkan capability probe
python3 check_cards.py --vulkan always # run the optional probe
python3 check_cards.py --no-install # inspect only; no package or unlock install
python3 check_cards.py --no-download # use existing harness/ sources
python3 check_cards.py --demo # synthetic output without a GPU
VBIOS backup is opt in and never flashes the card. The bundled nvflash_5.867_linux/x64/nvflash is used automatically; --nvflash PATH selects another binary. Both the nvflash save attempt and sysfs ROM fallback target the detected PCI address. If nvflash cannot save the ROM, the script tries the PCI sysfs ROM for that card. An existing bios.rom larger than 512 bytes and containing nonzero data is preserved; the script does not authenticate its contents.
The Vulkan action is disabled by default and needs vulkaninfo installed separately. It lists generic Vulkan capabilities; it does not test this card's VRAM or prove that Vulkan uses the CMP 170HX.
Output and status
cards/<serial>/
├── report.json latest run
├── runs/<timestamp>/
│ ├── report.json archived run
│ ├── lspci_before_unlock.txt capacitor width check
│ ├── lspci.txt
│ ├── lspci_after.txt
│ ├── config_space.hex
│ ├── dmesg.log
│ ├── dmesg_other_pcie.log
│ └── verify_unlocker.log
└── extras/
├── bios.rom if --bios was requested
└── cmpunlocker_install_*.log if installation was attempted
If no physical serial is readable, the same report layout is written beneath unidentified/<run-id>_<PCI-address>/ instead of cards/<serial>/.
result is PASS, WARN, FAIL, or PENDING_REBOOT. A link other than Gen2 x16, inactive unlock, or missing physical serial fails the check. Correctable AER errors or failed optional backup produce a warning.
| Exit code | Meaning |
|---|---|
0 |
PASS or WARN |
1 |
FAIL |
2 |
No card selected |
3 |
Preflight blocked |
4 |
Cold boot required |
The tool never flashes the card. The required cmpunlocker installation changes host kernel modules and boot configuration. PCIe retrain and rescan happen only with --link-tests.
The script runs no Git remote commands. Guidance for coding agents is in AGENTS.md.