Initial commit: CMP 170HX per-card check tooling

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.
This commit is contained in:
2026-09-27 21:02:27 +02:00
commit b30be648d7
13 changed files with 1562 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
# 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](https://github.com/amoghmunikote/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](https://github.com/amoghmunikote/170th-Street/blob/master/modifications/pcie-capacitor-mod.md) 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
1. Connect one card, make sure the host has Internet access, and run from this directory:
```bash
python3 check_cards.py
```
2. The first run downloads cmpunlocker, installs `pciutils`, build tools, matching kernel headers, and `nvidia-driver-610-open` when needed. It asks for sudo credentials if necessary. Package installation output streams to the terminal and is saved under `harness/logs/`. When it prints **cold shut down and power on**, shut down fully and start the host again:
```bash
sudo shutdown -h now
```
3. Run `python3 check_cards.py` again 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 to `cards/<serial>/extras/cmpunlocker_install_*.log` (or `unidentified/` 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](https://github.com/amoghmunikote/cmpunlocker#requirements).
`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
1. Detect one CMP 170HX (`10de:20c2` or `10de:2082`) and try to get its **serial number** automatically from `nvidia-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 in `unidentified/<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 to `cards/<serial>/`. Earlier unidentified runs remain separate. A GPU UUID is not used as a serial.
2. Read the endpoint's `LnkSta` lane width **before** unlocking. An x16 width passes the capacitor width check even at Gen1. An x4 or unreadable width immediately prints `CAPACITOR MOD CHECK FAILED` in 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.
3. Check memory size for that exact PCI address. Stock memory triggers the cmpunlocker installer; unlocked memory continues to verification. Run `cmpunlocker/verify.sh` when 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.
4. Capture PCIe configuration, AER counters, kernel lines that name this card since the run began, and `nvidia-smi` data. 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
```bash
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
```text
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](AGENTS.md).