diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml deleted file mode 100644 index 12293b7..0000000 --- a/.github/workflows/pylint.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Pylint - -on: [push] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.8", "3.9", "3.10"] - steps: - - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pylint -r requirements.txt - - name: Analysing the code with pylint - run: | - pylint $(git ls-files '*.py') diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index f7c7ddf..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Tests - -on: [push] - -jobs: - test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.8", "3.9", "3.10"] - steps: - - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest -r requirements.txt - - name: Run tests - run: | - pytest tests/ -v diff --git a/.gitignore b/.gitignore index 9b699e9..5198961 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ env/ .coverage htmlcov/ .tox/ +__pycache__/ +tests/__pycache__/ .vscode/ .idea/ .claude/ @@ -34,3 +36,6 @@ credentials.json *.cmpunlocker.bak *.cmpunlocker.patched *.backup +driver/.build/ +logs/ +*.tar.gz \ No newline at end of file diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index b8e68e1..0000000 --- a/.pylintrc +++ /dev/null @@ -1,8 +0,0 @@ -[MESSAGES CONTROL] -disable= - missing-module-docstring, - missing-function-docstring, - missing-class-docstring, - wrong-import-position, - broad-exception-caught, - import-outside-toplevel, diff --git a/README.md b/README.md index a2b449b..c4811c8 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,130 @@ # cmpunlocker -Unlock tool for the NVIDIA CMP 170HX (GA100) mining card. Removes throttling and restrictions imposed, restoring the card to full A100 compute throughput. +Unlock tool for the NVIDIA CMP 170HX (GA100) mining card. Restores full SM +compute throughput and unlocked HBM2e memory geometry that are restricted in +firmware/OTP configuration. -Targets **nvidia-open driver 580.x** on Linux. +Targets **nvidia-open driver 610.43.0x** on Linux. cmpunlocker +does **not** install the full NVIDIA userspace package — it patches and +installs open kernel modules only. -> **AI agents:** before making any changes to this codebase, read `.ai/CONTEXT.md` for essential project context, and rules you must follow. +> **AI agents:** before making changes, read `.ai/CONTEXT.md`. --- ## Background -The CMP 170HX is a physically complete GA100 die — the same silicon as the A100 datacenter GPU — with compute throughput, memory capacity, and other features artificially restricted via OTP fuses and firmware-enforced register locks. This tool restores those capabilities on hardware you own. +The CMP 170HX is a physically complete GA100 die (same silicon as the A100) +with compute and memory artificially limited. This tool applies an in-driver +unlock path (SEC2 Booter PLM open + host SS0/SS1/CFG1/LMR writes + FB/PMA +adjustments) that runs automatically every time the patched modules boot GSP +for PCI ID `0x20C2`. + +Card size selects the memory geometry: + +| Physical card | Unlock geometry | CFG1 | LMR | +|---|---|---|---| +| **8 GB** | **64 GB** | `0x02779000` | `0x0000020B` | +| **10 GB** | **40 GB** | `0x02669000` | `0x0000028A` | --- ## Requirements - Linux (x86-64) -- Python 3.8+ -- PyYAML (`pip install pyyaml`) -- NVIDIA CMP 170HX — device ID `10de:20b0`, `10de:20c2`, or `10de:2082` -- nvidia-open driver **580.x** installed with GSP firmware present at `/lib/firmware/nvidia/580.*/gsp_tu10x.bin` - Root access +- NVIDIA CMP 170HX (`10de:20c2` preferred; `20b0` / `2082` detected but unlock is `0x20C2`-gated) +- **nvidia-open 610.43.0x already installed** (libs + firmware) +- Kernel headers matching the running kernel (`linux-headers-$(uname -r)` / `kernel-devel`) +- Secure Boot disabled (patched modules are unsigned) +- Network access on first install (downloads matching stock `open-gpu-kernel-modules` sources) +- Python 3 (used at build time to select 8GB/10GB geometry) --- ## Install -Run once. Applies the unlock immediately and installs a systemd daemon that reapplies it automatically after every reboot or driver reload. +One command. Auto-detects 8GB vs 10GB from stock `nvidia-smi` memory, then builds +patched open kernel modules into `/lib/modules/$(uname -r)/updates/cmpunlocker/`. ```bash sudo ./install.sh ``` -That is the only command needed. +Force a profile if detection is wrong or `nvidia-smi` is unavailable: ---- +```bash +sudo ./install.sh --profile=8gb # 8GB card → 64GB unlock +sudo ./install.sh --profile=10gb # 10GB card → 40GB unlock +``` -## Verification +Then perform a **cold reboot** (full power off, then boot) if modules did not +hot-reload cleanly, or if memory still shows the stock size. -Check that the SM clock cap is gone: +--- + +## Verify ```bash -nvidia-smi --query-gpu=clocks.max.sm --format=csv,noheader -``` +nvidia-smi +# 8GB card: expect ~65536 MiB +# 10GB card: expect ~40960 MiB -Follow the daemon log: +nvidia-smi --query-gpu=memory.total,clocks.max.sm --format=csv -```bash -journalctl -u cmpunlocker -f +sudo dmesg | grep SEC2_DEBUG +# Expected: PLMs opening to 0xffffffff, CFG1/LMR/SS0/SS1 writes, late PMA + +cat /lib/modules/$(uname -r)/updates/cmpunlocker/card_profile +# 8gb or 10gb ``` +Booter status codes such as `0x31` / `0xffff` during the early PLM Booter +passes can appear and are often harmless if the final boot succeeds. + --- ## What gets unlocked | Feature | Status | |---|---| -| Full SM compute throughput | ✅ Working | -| 40GB/64GB HBM2e memory | 🔧 In development | -| ECC | 🔬 Planned | -| PCIe Gen 2 | 🔬 Planned | -| NVLink | 🔬 Planned | +| Full SM compute throughput (SS0/SS1) | Working | +| Memory geometry (64GB on 8GB cards, 40GB on 10GB cards) | Working | +| Persistence across reboot (patched modules) | Working | +| PCIe Gen2 x4 | Platform-dependent (no separate Root-port patch) | +| ECC | Planned | +| NVLink | Planned | --- ## Persistence -The unlock does not survive reboots or driver reloads on its own. The installed daemon (`cmpunlocker.service`) handles this automatically: +No systemd daemon is required. Unlock logic is compiled into the patched +`nvidia` modules and re-applies on every GSP init for `0x20C2`. + +--- + +## Uninstall + +Restore stock module loading: + +```bash +sudo ./remove.sh --yes +``` + +This removes `/lib/modules/*/updates/cmpunlocker/`, runs `depmod`, and +attempts to reload stock NVIDIA modules. Reboot if the GPU does not come back +cleanly. + +--- -- **On boot**: runs the full unlock pipeline before the display manager starts -- **Every second**: checks SS0/SS1 via BAR0 and rewrites them if reset -- **On driver reload**: detects a closed PLM and reruns the full pipeline -- **Multiple cards**: all CMP 170HX GPUs present in the system are handled +## How it works -The daemon is enabled at boot via systemd and restarts automatically on failure. +1. `install.sh` checks for a CMP 170HX and nvidia-open **610.43.03 or 610.43.02** +2. Selects **8gb** or **10gb** profile (auto or `--profile=`) +3. `driver/build.sh` downloads matching stock `open-gpu-kernel-modules` sources +4. Applies patches from `driver/patches/`, then rewrites CFG1/LMR/`fb_length` for the profile +5. Builds and installs modules to `updates/cmpunlocker/` (higher priority via depmod) +6. Rebuilds initramfs so early boot loads the patched modules (not leftover DKMS stock) +7. On load, `_kgspBootGspRm` opens PLMs via SEC2 Booter, writes SS0/SS1/CFG1/LMR, + restores the stock GSP signature, boots GSP-RM, then extends FB length / PMA diff --git a/common/constants.py b/common/constants.py deleted file mode 100644 index 9db4b07..0000000 --- a/common/constants.py +++ /dev/null @@ -1,28 +0,0 @@ -from functools import lru_cache -from pathlib import Path -from typing import Any - -import yaml - -_CONSTANTS_PATH = Path(__file__).parent / "constants.yaml" - - -def _load_constants() -> dict: - with open(_CONSTANTS_PATH, "r", encoding="utf-8") as f: - return yaml.safe_load(f) - - -@lru_cache(maxsize=1) -def get_constants() -> dict: - return _load_constants() - - -def get(key: str, default: Any = None) -> Any: - parts = key.split(".") - val = get_constants() - for part in parts: - if isinstance(val, dict) and part in val: - val = val[part] - else: - return default - return val diff --git a/common/constants.yaml b/common/constants.yaml index e3ee258..23336c2 100644 --- a/common/constants.yaml +++ b/common/constants.yaml @@ -1,41 +1,35 @@ -dmem_layout: - dma_target: 0x0800 - payload_size: 0xF800 - guard_addr: 0x6340 - canary: 0xFACEB13D - bar0_mmap_size: 0x1000000 - -host_bar0_writes: - feat_ovr_plm: - addr: 0x00823804 - value: 0xFFFFFFFF - note: "Feature override PLM (unlock state indicator)" - ss0: - addr: 0x0082381C - value: 0x88888888 - note: "FEAT_OVR_SM_SPD (all SMs max speed)" - ss1: - addr: 0x00823820 - value: 0x00000008 - note: "FEAT_OVR_SM_SPD_1 (IMLA4 override)" - -booter_addrs: - bar0_write_gadget: 0x10B9 - -payload_frames: - frame_start_addr: 0xFF48 - frame_stride: 0x18 - frame_field_offsets: - r0: 0x00 - r1: 0x04 - r2: 0x08 - r3: 0x0C - saved_reg: 0x10 - return_addr: 0x14 +# cmpunlocker — supported nvidia-open targets for the in-driver unlock path +driver_versions: + - "610.43.03" + - "610.43.02" +# CMP 170HX PCI device IDs (vendor 10de). Unlock code gates on 0x20C2. gpu: - device_ids: ["20b0", "20c2", "2082"] + vendor_id: "10de" + device_ids: + - "20c2" # primary (in-driver unlock enabled) + - "20b0" + - "2082" + +# Shared compute unlock registers (same on both card sizes) +compute: + ss0: "0x88888888" + ss1: "0x00000008" -elf: - header_magic: 0x7F454C46 - signature_section: ".fwsignature_ga100" +# Card physical size → unlock geometry profile +# Selected automatically by install.sh from stock nvidia-smi memory, or via --profile= +profiles: + "8gb": + stock_mib: 8192 + unlocked_mib: 65536 + cfg1: "0x02779000" + lmr: "0x0000020B" + fb_bytes: "0x0000001000000000" # 64 GiB + comment: "8GB physical card → 64GB geometry" + "10gb": + stock_mib: 10240 + unlocked_mib: 40960 + cfg1: "0x02669000" + lmr: "0x0000028A" + fb_bytes: "0x0000000A00000000" # 40 GiB + comment: "10GB physical card → 40GB geometry" diff --git a/daemon/__init__.py b/daemon/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/daemon/cmpunlocker.service b/daemon/cmpunlocker.service deleted file mode 100644 index 742dfe7..0000000 --- a/daemon/cmpunlocker.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=CMP 170HX Compute Unlock Daemon -After=systemd-modules-load.service -Before=display-manager.service - -[Service] -Type=simple -ExecStart=/usr/bin/python3 /opt/cmpunlocker/daemon/watchdog.py -Restart=on-failure -RestartSec=30 -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=multi-user.target diff --git a/daemon/watchdog.py b/daemon/watchdog.py deleted file mode 100644 index cfe8c4b..0000000 --- a/daemon/watchdog.py +++ /dev/null @@ -1,69 +0,0 @@ -import logging -import sys -import time - -sys.path.insert(0, "/opt/cmpunlocker") - -from payload.gpu import find_all_gpus -from payload.pipeline import run_full_unlock -from unlock.compute import apply_unlock, is_plm_open, is_unlocked - -CHECK_INTERVAL = 1 # seconds - -logging.basicConfig( - level=logging.INFO, - format="%(asctime)s cmpunlocker[%(process)d]: %(message)s", - stream=sys.stdout, -) -log = logging.getLogger("cmpunlocker") - - -def _unlock_card(pci: str) -> None: - try: - run_full_unlock(pci) - except Exception as exc: - log.error("[%s] Full unlock failed: %s", pci, exc) - - -def _check_card(pci: str) -> None: - try: - if is_unlocked(pci): - return - - if is_plm_open(pci): - ok, msg = apply_unlock(pci) - if ok: - log.info("[%s] Reapplied SS0/SS1", pci) - else: - log.warning("[%s] Quick reapply failed: %s", pci, msg) - else: - log.warning("[%s] PLM closed — re-running full unlock", pci) - _unlock_card(pci) - - except Exception as exc: - log.error("[%s] Monitor error: %s", pci, exc) - - -def main() -> None: - log.info("cmpunlocker daemon starting") - - gpus = find_all_gpus() - if not gpus: - log.error("No compatible GPU found (10de:20b0/20c2/2082)") - sys.exit(1) - - log.info("Found %d GPU(s): %s", len(gpus), ", ".join(gpus)) - - for pci in gpus: - log.info("[%s] Running initial unlock", pci) - _unlock_card(pci) - - log.info("Entering monitor loop (interval=%ds)", CHECK_INTERVAL) - while True: - for pci in gpus: - _check_card(pci) - time.sleep(CHECK_INTERVAL) - - -if __name__ == "__main__": - main() diff --git a/driver/VERSION b/driver/VERSION new file mode 100644 index 0000000..a37573b --- /dev/null +++ b/driver/VERSION @@ -0,0 +1,2 @@ +610.43.03 +610.43.02 diff --git a/driver/build.sh b/driver/build.sh new file mode 100755 index 0000000..dee5623 --- /dev/null +++ b/driver/build.sh @@ -0,0 +1,275 @@ +#!/bin/bash +set -euo pipefail + +############################################################################# +# cmpunlocker — build & install patched open kernel modules for 610.43.0x +############################################################################# + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +mapfile -t SUPPORTED_VERSIONS < <(grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' "${SCRIPT_DIR}/VERSION") +DEFAULT_VERSION="${SUPPORTED_VERSIONS[0]:-}" +VERSION="${CMPUNLOCKER_DRIVER_VERSION:-${DEFAULT_VERSION}}" +PATCH_DIR="${SCRIPT_DIR}/patches" +BUILD_ROOT="${CMPUNLOCKER_BUILD_DIR:-${SCRIPT_DIR}/.build}" +SRC_NAME="open-gpu-kernel-modules-${VERSION}" +SRC_DIR="${BUILD_ROOT}/${SRC_NAME}" +TARBALL="${BUILD_ROOT}/${SRC_NAME}.tar.gz" +TARBALL_URL="https://github.com/NVIDIA/open-gpu-kernel-modules/archive/refs/tags/${VERSION}.tar.gz" +KVER="$(uname -r)" +KSRC="/lib/modules/${KVER}/build" +INSTALL_MOD_DIR="/lib/modules/${KVER}/updates/cmpunlocker" + +if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then + RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' +else + RED=""; GREEN=""; YELLOW=""; CYAN=""; NC="" +fi + +info() { echo -e "${CYAN}[INFO]${NC} $*"; } +ok() { echo -e "${GREEN}[ OK ]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +die() { echo -e "${RED}[FAIL]${NC} $*" >&2; exit 1; } + +version_supported() { + local v="$1" + local s + for s in "${SUPPORTED_VERSIONS[@]}"; do + [[ "${v}" == "${s}" ]] && return 0 + done + return 1 +} + +[[ "${EUID}" -eq 0 ]] || die "Run as root: sudo ${SCRIPT_DIR}/build.sh" +[[ -n "${VERSION}" ]] || die "No driver version set (driver/VERSION empty and CMPUNLOCKER_DRIVER_VERSION unset)" +version_supported "${VERSION}" || die "Unsupported driver version '${VERSION}' (supported: ${SUPPORTED_VERSIONS[*]})" +[[ -d "${PATCH_DIR}" ]] || die "Missing patches directory: ${PATCH_DIR}" +[[ -d "${KSRC}" ]] || die "Kernel headers not found at ${KSRC}. Install linux-headers-${KVER} (or kernel-devel)." +command -v python3 &>/dev/null || die "python3 is required to apply the card memory profile" +info "Building against open-gpu-kernel-modules ${VERSION}" + +mkdir -p "${BUILD_ROOT}" + +############################################################################# +# Fetch + extract clean stock sources +############################################################################# + +if [[ ! -f "${TARBALL}" ]]; then + info "Downloading open-gpu-kernel-modules ${VERSION}..." + curl -L --fail -o "${TARBALL}.partial" "${TARBALL_URL}" + mv "${TARBALL}.partial" "${TARBALL}" + ok "Downloaded ${TARBALL}" +else + ok "Using cached tarball ${TARBALL}" +fi + +info "Extracting clean stock sources..." +rm -rf "${SRC_DIR}" +tar -xzf "${TARBALL}" -C "${BUILD_ROOT}" +if [[ ! -d "${SRC_DIR}" ]]; then + extracted="$(find "${BUILD_ROOT}" -maxdepth 1 -type d -name "${SRC_NAME}*" | head -1)" + [[ -n "${extracted}" ]] || die "Extracted source tree not found" + mv "${extracted}" "${SRC_DIR}" +fi +ok "Sources ready: ${SRC_DIR}" + +############################################################################# +# Apply patches +############################################################################# + +info "Applying unlock patches..." +cd "${SRC_DIR}" +shopt -s nullglob +patches=("${PATCH_DIR}"/*.patch) +[[ ${#patches[@]} -gt 0 ]] || die "No patches found in ${PATCH_DIR}" +for p in "${patches[@]}"; do + info " $(basename "${p}")" + patch -p1 < "${p}" +done +ok "All patches applied" + +############################################################################# +# Card profile geometry (8gb → 64GB unlock, 10gb → 40GB unlock) +############################################################################# + +# Default matches values baked into 0001-*.patch (8GB physical → 64GB geometry). +PROFILE="${CMPUNLOCKER_CARD_PROFILE:-8gb}" +GSP_C="${SRC_DIR}/src/nvidia/src/kernel/gpu/gsp/kernel_gsp.c" +[[ -f "${GSP_C}" ]] || die "Missing ${GSP_C} after patching" + +case "${PROFILE}" in + 8gb|8GB) + PROFILE="8gb" + CFG1="0x02779000" + LMR="0x0000020B" + FB_BYTES="0x0000001000000000" + UNLOCK_LABEL="64GB" + ;; + 10gb|10GB) + PROFILE="10gb" + CFG1="0x02669000" + LMR="0x0000028A" + FB_BYTES="0x0000000A00000000" + UNLOCK_LABEL="40GB" + ;; + *) + die "Unknown CMPUNLOCKER_CARD_PROFILE='${PROFILE}' (use 8gb or 10gb)" + ;; +esac + +info "Applying memory profile ${PROFILE} (${UNLOCK_LABEL} geometry)..." +# Patches ship the 8gb/64GB constants; rewrite in-tree for 10gb/40GB. +python3 - "${GSP_C}" "${CFG1}" "${LMR}" "${FB_BYTES}" "${UNLOCK_LABEL}" <<'PY' +import pathlib, re, sys +path, cfg1, lmr, fb, label = sys.argv[1:6] +text = pathlib.Path(path).read_text() +text2, n1 = re.subn( + r"(NvU32 cfg1Value = )0x[0-9A-Fa-f]+(U;)", + rf"\g<1>{cfg1}\g<2>", + text, + count=1, +) +text2, n2 = re.subn( + r"(NvU32 lmrValue\s*=\s*)0x[0-9A-Fa-f]+(U;)", + rf"\g<1>{lmr}\g<2>", + text2, + count=1, +) +text2, n3 = re.subn( + r"(NvU64 targetFbBytes = )0x[0-9A-Fa-f]+ULL;\s*/\*[^*]*\*/", + rf"\g<1>{fb}ULL; /* {label} */", + text2, + count=1, +) +if n1 != 1 or n2 != 1 or n3 != 1: + raise SystemExit( + f"geometry rewrite failed (cfg1={n1} lmr={n2} fb={n3}); check kernel_gsp.c markers" + ) +pathlib.Path(path).write_text(text2) +print(f"cfg1={cfg1} lmr={lmr} fb={fb} ({label})") +PY +ok "Memory profile ${PROFILE}: CFG1=${CFG1} LMR=${LMR} fb=${FB_BYTES} (${UNLOCK_LABEL})" +mkdir -p "${INSTALL_MOD_DIR}" +printf '%s\n' "${VERSION}" > "${INSTALL_MOD_DIR}/driver_version" +printf '%s\n' "${PROFILE}" > "${INSTALL_MOD_DIR}/card_profile" +printf '%s\n' "${UNLOCK_LABEL}" > "${INSTALL_MOD_DIR}/unlock_geometry" + +############################################################################# +# Build +############################################################################# + +info "Building modules for kernel ${KVER}..." +cd "${SRC_DIR}" +find . -name "*.sh" -exec chmod +x {} + 2>/dev/null || true +rm -rf src/nvidia/_out src/nvidia-modeset/_out kernel-open/conftest 2>/dev/null || true +make clean 2>/dev/null || true +JOBS="$(nproc)" +make -j"${JOBS}" modules SYSSRC="${KSRC}" +ok "Modules built" + +############################################################################# +# Install +############################################################################# + +info "Installing modules to ${INSTALL_MOD_DIR}..." +mkdir -p "${INSTALL_MOD_DIR}" + +mapfile -t KO_FILES < <(find "${SRC_DIR}" -type f \( \ + -name 'nvidia.ko' -o -name 'nvidia-modeset.ko' -o -name 'nvidia-uvm.ko' \ + -o -name 'nvidia-drm.ko' -o -name 'nvidia-peermem.ko' \) \ + ! -path '*/conftest/*' | sort -u) +[[ ${#KO_FILES[@]} -gt 0 ]] || die "No built nvidia*.ko found" + +for ko in "${KO_FILES[@]}"; do + base="$(basename "${ko}")" + install -m 0644 "${ko}" "${INSTALL_MOD_DIR}/${base}" + ok "Installed ${base}" +done + +depmod -a "${KVER}" +ok "depmod complete" + +############################################################################# +# Initramfs — required so early boot does not keep loading stock DKMS +############################################################################# + +# NVIDIA often loads from initramfs. If only updates/dkms is packed there, +# stock modules win at boot even when updates/cmpunlocker is preferred by depmod. +rebuild_initramfs() { + if command -v update-initramfs &>/dev/null; then + info "Rebuilding initramfs (update-initramfs) so patched modules load at boot..." + update-initramfs -u -k "${KVER}" + ok "initramfs rebuilt" + return 0 + fi + if command -v dracut &>/dev/null; then + info "Rebuilding initramfs (dracut) so patched modules load at boot..." + dracut --force --kver "${KVER}" + ok "initramfs rebuilt" + return 0 + fi + if command -v mkinitcpio &>/dev/null; then + info "Rebuilding initramfs (mkinitcpio) so patched modules load at boot..." + mkinitcpio -P + ok "initramfs rebuilt" + return 0 + fi + warn "No initramfs tool found — rebuild manually before rebooting" + return 1 +} + +rebuild_initramfs || true + +resolved="$(modprobe -n -v nvidia 2>/dev/null | awk '/insmod/ {print $2; exit}' || true)" +if [[ -n "${resolved}" ]]; then + info "modprobe will load: ${resolved}" + if [[ "${resolved}" != *"/updates/cmpunlocker/"* ]]; then + warn "Resolved nvidia.ko is not under updates/cmpunlocker/ — stock may still win" + fi +fi + +############################################################################# +# Reload +############################################################################# + +info "Attempting to unload existing NVIDIA modules..." +systemctl stop nvidia-persistenced 2>/dev/null || true +systemctl stop nvidia-fabricmanager 2>/dev/null || true + +reload_ok=0 +if lsmod | grep -q '^nvidia'; then + for mod in nvidia_drm nvidia_uvm nvidia_modeset nvidia; do + modprobe -r "${mod}" 2>/dev/null || true + done + sleep 1 +fi + +if ! lsmod | grep -q '^nvidia '; then + if modprobe nvidia && modprobe nvidia-modeset; then + modprobe nvidia-uvm 2>/dev/null || true + modprobe nvidia-drm 2>/dev/null || true + reload_ok=1 + ok "Patched NVIDIA modules loaded" + running_src="$(cat /sys/module/nvidia/srcversion 2>/dev/null || true)" + patched_src="$(modinfo -F srcversion "${INSTALL_MOD_DIR}/nvidia.ko" 2>/dev/null || true)" + if [[ -n "${running_src}" && -n "${patched_src}" && "${running_src}" != "${patched_src}" ]]; then + warn "Loaded nvidia srcversion (${running_src}) != patched (${patched_src})" + reload_ok=0 + fi + else + warn "modprobe failed after install" + fi +else + warn "Could not unload nvidia modules (in use) — cold reboot required" +fi + +echo "" +if [[ "${reload_ok}" -eq 1 ]]; then + ok "Build and install finished. Verify with: nvidia-smi" + info "If memory still shows stock size, do a cold shutdown (power off), then boot." +else + warn "Modules installed but the running driver is still stock (or unload failed)." + info "Perform a cold reboot: shutdown -h now (then power on)" + info "After boot, confirm: cat /proc/driver/nvidia/version (should NOT say dvs-builder)" + info "And: sudo dmesg | grep SEC2_DEBUG" +fi +echo "" diff --git a/driver/patches/0001-sec2-postbl-plm-ss-cfg.patch b/driver/patches/0001-sec2-postbl-plm-ss-cfg.patch new file mode 100644 index 0000000..c7f3d0a --- /dev/null +++ b/driver/patches/0001-sec2-postbl-plm-ss-cfg.patch @@ -0,0 +1,445 @@ +diff -Naur a/src/nvidia/generated/g_kernel_gsp_nvoc.h b/src/nvidia/generated/g_kernel_gsp_nvoc.h +--- a/src/nvidia/generated/g_kernel_gsp_nvoc.h 2026-07-18 11:26:26.833302560 -0700 ++++ b/src/nvidia/generated/g_kernel_gsp_nvoc.h 2026-07-18 11:26:26.834341525 -0700 +@@ -544,6 +544,8 @@ + RM_RISCV_UCODE_DESC *pGspRmBootUcodeDesc; + MEMORY_DESCRIPTOR *pGspUCodeRadix3Descriptor; + MEMORY_DESCRIPTOR *pSignatureMemdesc; ++ NvU8 *pStockSignatureData; ++ NvU64 stockSignatureSize; + GspSystemInfo *pSystemInfo; + NvU32 regTableSize; + PACKED_REGISTRY_TABLE *pRegTable; +diff -Naur a/src/nvidia/src/kernel/gpu/gsp/kernel_gsp.c b/src/nvidia/src/kernel/gpu/gsp/kernel_gsp.c +--- a/src/nvidia/src/kernel/gpu/gsp/kernel_gsp.c 2026-07-18 11:26:26.827164897 -0700 ++++ b/src/nvidia/src/kernel/gpu/gsp/kernel_gsp.c 2026-07-18 11:26:26.828254361 -0700 +@@ -102,6 +102,21 @@ + + #include "crashcat/crashcat_report.h" + ++#define SEC2_POSTBL_TIMING_CMP_170HX_PCI_DEVICE_ID 0x20C2 ++#define SEC2_POSTBL_TIMING_SIGNATURE_SIZE 0x0000f800ULL ++#define SEC2_POSTBL_TIMING_FILL_DWORD 0x000004a7U ++#define SEC2_POSTBL_TIMING_DMEM_PATH "/lib/firmware/nvidia/ga100/gsp/dmem.bin" ++ ++NV_STATUS kgspSec2PostblTimingRefillPayload(OBJGPU *pGpu, KernelGsp *pKernelGsp, ++ NvU32 writeAddr, NvU32 writeValue); ++NV_STATUS kgspSec2PostblTimingRebuildStockSignature(OBJGPU *pGpu, KernelGsp *pKernelGsp); ++ ++static NvBool ++_kgspSec2PostblTimingEnabled(OBJGPU *pGpu) ++{ ++ NvU32 devId = pGpu->idInfo.PCIDeviceID >> 16; ++ return (devId == SEC2_POSTBL_TIMING_CMP_170HX_PCI_DEVICE_ID); ++} + + struct MIG_CI_UPDATE_CALLBACK_PARAMS + { +@@ -4805,14 +4820,22 @@ + if (kgspIsWpr2Up_HAL(pGpu, pKernelGsp) && + (!pGpu->getProperty(pGpu, PDB_PROP_GPU_PREINITIALIZED_WPR_REGION))) + { +- NV_PRINTF(LEVEL_ERROR, "unexpected WPR2 already up, cannot proceed with booting GSP\n"); +- NV_PRINTF(LEVEL_ERROR, "(the GPU is likely in a bad state and may need to be reset)\n"); +- return NV_ERR_INVALID_STATE; ++ NV_PRINTF(LEVEL_WARNING, ++ "WPR2 already up before GSP boot; continuing for recovery\n"); + } + +- // Populate WPR meta structure (requires knowing FB size on dGPU, which depends on GFW_BOOT) + NV_CHECK_OK_OR_RETURN(LEVEL_ERROR, kgspPopulateWprMeta_HAL(pGpu, pKernelGsp, pGspFw)); + ++ if (_kgspSec2PostblTimingEnabled(pGpu)) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: WPR meta fbSize=0x%016llx wprEnd=0x%016llx " ++ "heapSize=0x%016llx\n", ++ pKernelGsp->pWprMeta->fbSize, ++ pKernelGsp->pWprMeta->gspFwWprEnd, ++ pKernelGsp->pWprMeta->gspFwHeapSize); ++ } ++ + { + // If the new FB layout requires a scrubber ucode to scrub additional space, prepare it now + NV_CHECK_OK_OR_RETURN(LEVEL_ERROR, _kgspPrepareScrubberImageIfNeeded(pGpu, pKernelGsp)); +@@ -4821,6 +4844,104 @@ + // Setup arguments for bootstrapping GSP + NV_CHECK_OK_OR_RETURN(LEVEL_ERROR, kgspPrepareForBootstrap_HAL(pGpu, pKernelGsp, KGSP_BOOT_MODE_NORMAL)); + ++ if (_kgspSec2PostblTimingEnabled(pGpu)) ++ { ++ NvU32 devId = pGpu->idInfo.PCIDeviceID >> 16; ++ NvU32 plmIdx, attempt; ++ NV_STATUS plmStatus; ++ ++ static const struct { NvU32 addr; NvU32 value; const char *name; } plmTable[] = { ++ { 0x001fa7ccU, 0xfffff0ffU, "WPR_CFG" }, ++ { 0x009a0148U, 0xffffffffU, "FBPA" }, ++ { 0x001fa7c4U, 0xffffffffU, "WPR" }, ++ { 0x00823804U, 0xffffffffU, "FEAT" }, ++ }; ++ ++ NvU32 wpr2Lo = GPU_REG_RD32(pGpu, 0x001fa824U); ++ NvU32 wpr2Hi = GPU_REG_RD32(pGpu, 0x001fa828U); ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: saved WPR2 lo=0x%08x hi=0x%08x\n", ++ wpr2Lo, wpr2Hi); ++ ++ for (plmIdx = 0; plmIdx < 4; plmIdx++) ++ { ++ NvBool opened = NV_FALSE; ++ for (attempt = 0; attempt < 2 && !opened; attempt++) ++ { ++ GPU_REG_WR32(pGpu, 0x001fa824U, wpr2Lo); ++ GPU_REG_WR32(pGpu, 0x001fa828U, wpr2Hi); ++ ++ plmStatus = kgspSec2PostblTimingRefillPayload(pGpu, pKernelGsp, ++ plmTable[plmIdx].addr, plmTable[plmIdx].value); ++ if (plmStatus != NV_OK) ++ continue; ++ ++ plmStatus = kgspExecuteBooterLoad_HAL(pGpu, pKernelGsp, ++ memdescGetPhysAddr(pKernelGsp->pWprMetaDescriptor, AT_GPU, 0)); ++ ++ NvU32 regVal = GPU_REG_RD32(pGpu, plmTable[plmIdx].addr); ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: PLM[%u] %s(0x%x) attempt=%u status=0x%x reg=0x%08x\n", ++ plmIdx, plmTable[plmIdx].name, ++ plmTable[plmIdx].addr, attempt, plmStatus, regVal); ++ ++ if (regVal == plmTable[plmIdx].value) ++ opened = NV_TRUE; ++ } ++ if (!opened) ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: FAILED to open %s after 2 attempts\n", ++ plmTable[plmIdx].name); ++ } ++ ++ GPU_REG_WR32(pGpu, 0x001fa824U, wpr2Lo); ++ GPU_REG_WR32(pGpu, 0x001fa828U, wpr2Hi); ++ ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: PLMs: FEAT=0x%08x FBPA=0x%08x WPR=0x%08x WPR_CFG=0x%08x\n", ++ GPU_REG_RD32(pGpu, 0x00823804U), ++ GPU_REG_RD32(pGpu, 0x009a0148U), ++ GPU_REG_RD32(pGpu, 0x001fa7c4U), ++ GPU_REG_RD32(pGpu, 0x001fa7ccU)); ++ ++ { ++ NvU32 cfg1Value = 0x02779000U; ++ NvU32 lmrValue = 0x0000020BU; ++ ++ GPU_REG_WR32(pGpu, 0x0082381cU, 0x88888888U); ++ GPU_REG_WR32(pGpu, 0x00823820U, 0x00000008U); ++ GPU_REG_WR32(pGpu, 0x009a0204U, cfg1Value); ++ GPU_REG_WR32(pGpu, 0x00100ce0U, lmrValue); ++ ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: POST-WRITE SS0=0x%08x SS1=0x%08x " ++ "CFG1=0x%08x LMR=0x%08x\n", ++ GPU_REG_RD32(pGpu, 0x0082381cU), ++ GPU_REG_RD32(pGpu, 0x00823820U), ++ GPU_REG_RD32(pGpu, 0x009a0204U), ++ GPU_REG_RD32(pGpu, 0x00100ce0U)); ++ } ++ ++ plmStatus = kgspSec2PostblTimingRebuildStockSignature(pGpu, pKernelGsp); ++ if (plmStatus != NV_OK) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: rebuild stock signature failed: 0x%x\n", plmStatus); ++ return plmStatus; ++ } ++ ++ NV_CHECK_OK_OR_RETURN(LEVEL_ERROR, kgspPopulateWprMeta_HAL(pGpu, pKernelGsp, pGspFw)); ++ ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: WPR meta updated fbSize=0x%016llx wprStart=0x%016llx " ++ "wprEnd=0x%016llx heapOffset=0x%016llx heapSize=0x%016llx\n", ++ pKernelGsp->pWprMeta->fbSize, ++ pKernelGsp->pWprMeta->gspFwWprStart, ++ pKernelGsp->pWprMeta->gspFwWprEnd, ++ pKernelGsp->pWprMeta->gspFwHeapOffset, ++ pKernelGsp->pWprMeta->gspFwHeapSize); ++ } ++ + // Release the API lock if relaxed locking for parallel init is enabled + NvBool bRelaxedLocking = _kgspShouldRelaxGspInitLocking(pGpu); + if (bRelaxedLocking) +@@ -5164,6 +5285,50 @@ + goto done; + } + ++ { ++ NvU32 devId = pGpu->idInfo.PCIDeviceID >> 16; ++ if (devId == 0x20C2) ++ { ++ GspStaticConfigInfo *pGSCI = &pKernelGsp->gspStaticInfo; ++ NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS *pFbRegionInfoParams = ++ &pGSCI->fbRegionInfoParams; ++ NvU64 targetFbBytes = 0x0000001000000000ULL; /* 64GB */ ++ NvU64 stockFbBytes = 0x0000000200000000ULL; /* 8GB */ ++ NvU32 numRegions = pFbRegionInfoParams->numFBRegions; ++ ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: static-info BEFORE: fb_length=0x%llx numRegions=%u\n", ++ pGSCI->fb_length, numRegions); ++ ++ pGSCI->fb_length = targetFbBytes; ++ ++ if (numRegions > 0 && ++ numRegions <= NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_MAX_ENTRIES) ++ { ++ NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO *pLastRegion = ++ &pFbRegionInfoParams->fbRegion[numRegions - 1]; ++ NvU64 highLimit = targetFbBytes - 1; ++ ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: last region[%u] base=0x%llx limit=0x%llx reserved=0x%llx\n", ++ numRegions - 1, pLastRegion->base, pLastRegion->limit, pLastRegion->reserved); ++ ++ if (pLastRegion->limit < highLimit) ++ { ++ pLastRegion->limit = highLimit; ++ pLastRegion->reserved = pLastRegion->limit - pLastRegion->base + 1; ++ pLastRegion->supportCompressed = NV_TRUE; ++ pLastRegion->supportISO = NV_TRUE; ++ pLastRegion->performance = 20; ++ } ++ ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: static-info AFTER: fb_length=0x%llx last_limit=0x%llx\n", ++ pGSCI->fb_length, pLastRegion->limit); ++ } ++ } ++ } ++ + NV_ASSERT_OK_OR_GOTO(status, kgspInitGspTraceCrashBuffer(pGpu, pKernelGsp), done); + + // Set PDB properties as per data from GSP. +@@ -5662,6 +5827,84 @@ + pKernelGsp->gspRmBootUcodeSize = 0; + } + ++static void ++_kgspSec2PostblTimingPutU32(NvU8 *pBuffer, NvU32 offset, NvU32 value) ++{ ++ pBuffer[offset + 0] = (NvU8)(value >> 0); ++ pBuffer[offset + 1] = (NvU8)(value >> 8); ++ pBuffer[offset + 2] = (NvU8)(value >> 16); ++ pBuffer[offset + 3] = (NvU8)(value >> 24); ++} ++ ++static void ++_kgspSec2PostblTimingFillPayload(NvU8 *pSignatureVa, NvU64 signatureSize, ++ NvU32 writeAddr, NvU32 writeValue) ++{ ++ NvU64 i; ++ ++ for (i = 0; i + sizeof(NvU32) <= signatureSize; i += sizeof(NvU32)) ++ _kgspSec2PostblTimingPutU32(pSignatureVa, (NvU32)i, SEC2_POSTBL_TIMING_FILL_DWORD); ++ ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0x1100, 0x00000007U); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0x5b40, 0xc0deca7eU); ++ ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf754, writeValue); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf758, 0xc0deca7eU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf75c, 0x00000cbdU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf76c, writeAddr); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf774, 0x00001fbdU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf780, 0x00000000U); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf788, 0x000010aaU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf78c, 0x0000815aU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf790, 0x00008e18U); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf794, 0xc0deca7eU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf798, 0x0000815aU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf79c, 0x00000000U); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7a0, 0xc0deca7eU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7a4, 0x00001fbdU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7b0, 0x0000ffbcU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7b8, 0x0000582dU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7c4, 0xc0deca7eU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7c8, 0x00000cbdU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7d8, 0x00000003U); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7e0, 0x00001fbdU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7f4, 0x00000ccbU); ++ _kgspSec2PostblTimingPutU32(pSignatureVa, 0xf7f8, 0x00007f2fU); ++} ++ ++NV_STATUS ++kgspSec2PostblTimingRefillPayload(OBJGPU *pGpu, KernelGsp *pKernelGsp, ++ NvU32 writeAddr, NvU32 writeValue) ++{ ++ NvU8 *pSignatureVa; ++ ++ if (pKernelGsp->pSignatureMemdesc == NULL) ++ return NV_ERR_INVALID_STATE; ++ ++ pSignatureVa = memdescMapInternal(pGpu, pKernelGsp->pSignatureMemdesc, TRANSFER_FLAGS_NONE); ++ if (pSignatureVa == NULL) ++ return NV_ERR_INSUFFICIENT_RESOURCES; ++ ++ _kgspSec2PostblTimingFillPayload(pSignatureVa, ++ memdescGetSize(pKernelGsp->pSignatureMemdesc), writeAddr, writeValue); ++ ++ memdescUnmapInternal(pGpu, pKernelGsp->pSignatureMemdesc, 0); ++ memdescFlushCpuCaches(pGpu, pKernelGsp->pSignatureMemdesc); ++ ++ if (pKernelGsp->pWprMeta != NULL) ++ { ++ pKernelGsp->pWprMeta->sysmemAddrOfSignature = ++ memdescGetPhysAddr(pKernelGsp->pSignatureMemdesc, AT_GPU, 0); ++ pKernelGsp->pWprMeta->sizeOfSignature = ++ memdescGetSize(pKernelGsp->pSignatureMemdesc); ++ } ++ ++ if (pKernelGsp->pWprMetaDescriptor != NULL) ++ memdescFlushCpuCaches(pGpu, pKernelGsp->pWprMetaDescriptor); ++ ++ return NV_OK; ++} ++ + static NV_STATUS + _kgspCreateSignatureMemdesc + ( +@@ -5672,6 +5915,9 @@ + { + NV_STATUS status = NV_OK; + NvU8 *pSignatureVa = NULL; ++ NvBool bPostbl = _kgspSec2PostblTimingEnabled(pGpu); ++ NvU64 sigSize = bPostbl ? SEC2_POSTBL_TIMING_SIGNATURE_SIZE ++ : NV_ALIGN_UP(pGspFw->signatureSize, 256); + NvU64 flags = MEMDESC_FLAGS_NONE; + + if (confComputeForceUnprotAlloc(pGpu)) +@@ -5682,7 +5928,7 @@ + // NOTE: align to 256 because that's the alignment needed for Booter DMA + NV_CHECK_OK_OR_RETURN(LEVEL_ERROR, + memdescCreate(&pKernelGsp->pSignatureMemdesc, pGpu, +- NV_ALIGN_UP(pGspFw->signatureSize, 256), 256, ++ sigSize, 256, + NV_TRUE, ADDR_SYSMEM, NV_MEMORY_CACHED, flags)); + + memdescTagAlloc(status, +@@ -5694,8 +5940,48 @@ + (pSignatureVa != NULL) ? NV_OK : NV_ERR_INSUFFICIENT_RESOURCES, + fail_alloc); + +- portMemCopy(pSignatureVa, memdescGetSize(pKernelGsp->pSignatureMemdesc), +- pGspFw->pSignatureData, pGspFw->signatureSize); ++ if (bPostbl) ++ { ++ NV_STATUS dmemStatus; ++ ++ if (pGspFw->pSignatureData != NULL && pGspFw->signatureSize > 0) ++ { ++ pKernelGsp->stockSignatureSize = pGspFw->signatureSize; ++ pKernelGsp->pStockSignatureData = portMemAllocNonPaged(pGspFw->signatureSize); ++ if (pKernelGsp->pStockSignatureData != NULL) ++ { ++ portMemCopy(pKernelGsp->pStockSignatureData, pGspFw->signatureSize, ++ pGspFw->pSignatureData, pGspFw->signatureSize); ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: saved stock signature (%llu bytes)\n", ++ (unsigned long long)pGspFw->signatureSize); ++ } ++ } ++ ++ dmemStatus = os_open_and_read_file( ++ SEC2_POSTBL_TIMING_DMEM_PATH, pSignatureVa, sigSize); ++ if (dmemStatus == NV_OK) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: loaded %llu bytes from %s\n", ++ (unsigned long long)sigSize, SEC2_POSTBL_TIMING_DMEM_PATH); ++ } ++ else ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: %s not found (0x%x), using built-in payload\n", ++ SEC2_POSTBL_TIMING_DMEM_PATH, dmemStatus); ++ _kgspSec2PostblTimingFillPayload(pSignatureVa, ++ memdescGetSize(pKernelGsp->pSignatureMemdesc), ++ 0x009a0148U, 0xffffffffU); ++ } ++ memdescFlushCpuCaches(pGpu, pKernelGsp->pSignatureMemdesc); ++ } ++ else ++ { ++ portMemCopy(pSignatureVa, memdescGetSize(pKernelGsp->pSignatureMemdesc), ++ pGspFw->pSignatureData, pGspFw->signatureSize); ++ } + + memdescUnmapInternal(pGpu, pKernelGsp->pSignatureMemdesc, 0); + pSignatureVa = NULL; +@@ -5709,6 +5995,69 @@ + memdescDestroy(pKernelGsp->pSignatureMemdesc); + pKernelGsp->pSignatureMemdesc = NULL; + ++ return status; ++} ++ ++NV_STATUS ++kgspSec2PostblTimingRebuildStockSignature(OBJGPU *pGpu, KernelGsp *pKernelGsp) ++{ ++ NV_STATUS status = NV_OK; ++ NvU8 *pSignatureVa = NULL; ++ NvU64 sigSize; ++ NvU64 flags = MEMDESC_FLAGS_NONE; ++ ++ if (pKernelGsp->pStockSignatureData == NULL || pKernelGsp->stockSignatureSize == 0) ++ return NV_ERR_INVALID_STATE; ++ ++ if (pKernelGsp->pSignatureMemdesc != NULL) ++ { ++ memdescFree(pKernelGsp->pSignatureMemdesc); ++ memdescDestroy(pKernelGsp->pSignatureMemdesc); ++ pKernelGsp->pSignatureMemdesc = NULL; ++ } ++ ++ sigSize = NV_ALIGN_UP(pKernelGsp->stockSignatureSize, 256); ++ flags |= MEMDESC_FLAGS_ALLOC_IN_UNPROTECTED_MEMORY; ++ ++ NV_CHECK_OK_OR_RETURN(LEVEL_ERROR, ++ memdescCreate(&pKernelGsp->pSignatureMemdesc, pGpu, ++ sigSize, 256, ++ NV_TRUE, ADDR_SYSMEM, NV_MEMORY_CACHED, flags)); ++ ++ memdescTagAlloc(status, ++ NV_FB_ALLOC_RM_INTERNAL_OWNER_UNNAMED_TAG_16, pKernelGsp->pSignatureMemdesc); ++ NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR, status, rebuild_fail_create); ++ ++ pSignatureVa = memdescMapInternal(pGpu, pKernelGsp->pSignatureMemdesc, TRANSFER_FLAGS_NONE); ++ NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR, ++ (pSignatureVa != NULL) ? NV_OK : NV_ERR_INSUFFICIENT_RESOURCES, ++ rebuild_fail_alloc); ++ ++ portMemCopy(pSignatureVa, memdescGetSize(pKernelGsp->pSignatureMemdesc), ++ pKernelGsp->pStockSignatureData, pKernelGsp->stockSignatureSize); ++ ++ memdescUnmapInternal(pGpu, pKernelGsp->pSignatureMemdesc, 0); ++ ++ if (pKernelGsp->pWprMeta != NULL) ++ { ++ pKernelGsp->pWprMeta->sysmemAddrOfSignature = ++ memdescGetPhysAddr(pKernelGsp->pSignatureMemdesc, AT_GPU, 0); ++ pKernelGsp->pWprMeta->sizeOfSignature = ++ memdescGetSize(pKernelGsp->pSignatureMemdesc); ++ } ++ ++ if (pKernelGsp->pWprMetaDescriptor != NULL) ++ memdescFlushCpuCaches(pGpu, pKernelGsp->pWprMetaDescriptor); ++ ++ return NV_OK; ++ ++rebuild_fail_alloc: ++ memdescFree(pKernelGsp->pSignatureMemdesc); ++ ++rebuild_fail_create: ++ memdescDestroy(pKernelGsp->pSignatureMemdesc); ++ pKernelGsp->pSignatureMemdesc = NULL; ++ + return status; + } + diff --git a/driver/patches/0002-booter-verify.patch b/driver/patches/0002-booter-verify.patch new file mode 100644 index 0000000..7c8c0c2 --- /dev/null +++ b/driver/patches/0002-booter-verify.patch @@ -0,0 +1,87 @@ +diff -Naur a/src/nvidia/src/kernel/gpu/gsp/arch/turing/kernel_gsp_tu102.c b/src/nvidia/src/kernel/gpu/gsp/arch/turing/kernel_gsp_tu102.c +--- a/src/nvidia/src/kernel/gpu/gsp/arch/turing/kernel_gsp_tu102.c 2026-07-18 11:26:26.848680684 -0700 ++++ b/src/nvidia/src/kernel/gpu/gsp/arch/turing/kernel_gsp_tu102.c 2026-07-18 11:26:26.849806595 -0700 +@@ -57,6 +57,12 @@ + #include "g_all_dcl_pb.h" + #include "lib/protobuf/prb.h" + ++#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 0x00100ce0 ++ + void + kgspConfigureFalcon_TU102 + ( +@@ -545,18 +551,35 @@ + // Execute FWSEC to setup FRTS if we have a FRTS region. + if (kgspGetFrtsSize_HAL(pGpu, pKernelGsp) > 0) + { +- NV_ASSERT_OR_RETURN(pKernelGsp->pPreparedFwsecCmd != NULL, NV_ERR_INVALID_STATE); +- +- NV_ASSERT_OK_OR_RETURN(kflcnReset_HAL(pGpu, pKernelFalcon)); ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: FWSEC: pPreparedFwsecCmd=%p frtsSize=0x%x\n", ++ pKernelGsp->pPreparedFwsecCmd, ++ kgspGetFrtsSize_HAL(pGpu, pKernelGsp)); ++ ++ if (pKernelGsp->pPreparedFwsecCmd == NULL) ++ { ++ NV_PRINTF(LEVEL_ERROR, "SEC2_DEBUG: FWSEC cmd is NULL, aborting\n"); ++ return NV_ERR_INVALID_STATE; ++ } ++ ++ status = kflcnReset_HAL(pGpu, pKernelFalcon); ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: kflcnReset for FWSEC: 0x%x\n", status); ++ if (status != NV_OK) return status; + + status = kgspExecuteFwsec_HAL(pGpu, pKernelGsp, pKernelGsp->pPreparedFwsecCmd); + portMemFree(pKernelGsp->pPreparedFwsecCmd); + pKernelGsp->pPreparedFwsecCmd = NULL; + +- NV_ASSERT_OK_OR_RETURN(status); ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: FWSEC status=0x%x\n", status); ++ if (status != NV_OK) return status; + } + +- NV_ASSERT_OK_OR_RETURN(kflcnResetIntoRiscv_HAL(pGpu, pKernelFalcon)); ++ status = kflcnResetIntoRiscv_HAL(pGpu, pKernelFalcon); ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: kflcnResetIntoRiscv: 0x%x\n", status); ++ if (status != NV_OK) return status; + + // Load init args into mailbox regs + kgspProgramLibosBootArgsAddr_HAL(pGpu, pKernelGsp); +@@ -565,6 +588,29 @@ + // Execute Booter Load + status = kgspExecuteBooterLoad_HAL(pGpu, pKernelGsp, + _kgspGetBooterLoadArgs(pKernelGsp, bootMode)); ++ ++ { ++ NvU32 devId = pGpu->idInfo.PCIDeviceID >> 16; ++ if (devId == 0x20C2) ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: normal BooterLoad status=0x%x\n", status); ++ } ++ ++ { ++ NvU32 devId = pGpu->idInfo.PCIDeviceID >> 16; ++ if (devId == 0x20C2 && status == NV_OK) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: POST-BooterLoad verify PLM=0x%08x SS0=0x%08x SS1=0x%08x " ++ "CFG1=0x%08x LMR=0x%08x\n", ++ GPU_REG_RD32(pGpu, SEC2_DEBUG_PRI_FEATURE_OVERRIDE_PLM), ++ GPU_REG_RD32(pGpu, SEC2_DEBUG_PRI_FEATURE_OVERRIDE_SM_SPEED), ++ GPU_REG_RD32(pGpu, SEC2_DEBUG_PRI_FEATURE_OVERRIDE_SM_SPEED_1), ++ GPU_REG_RD32(pGpu, SEC2_DEBUG_PRI_FBPA_CFG1), ++ GPU_REG_RD32(pGpu, SEC2_DEBUG_PRI_MMU_LMR)); ++ } ++ } ++ + if (status != NV_OK) + { + NV_PRINTF(LEVEL_ERROR, "failed to execute Booter Load (ucode for initial boot): 0x%x\n", status); diff --git a/driver/patches/0003-late-pma.patch b/driver/patches/0003-late-pma.patch new file mode 100644 index 0000000..2c323bf --- /dev/null +++ b/driver/patches/0003-late-pma.patch @@ -0,0 +1,262 @@ +diff -Naur a/src/nvidia/arch/nvalloc/unix/src/osinit.c b/src/nvidia/arch/nvalloc/unix/src/osinit.c +--- a/src/nvidia/arch/nvalloc/unix/src/osinit.c 2026-07-18 11:26:26.875698119 -0700 ++++ b/src/nvidia/arch/nvalloc/unix/src/osinit.c 2026-07-18 11:26:26.876734803 -0700 +@@ -2195,6 +2195,18 @@ + goto shutdown; + } + ++ if (pGpu != NULL) ++ { ++ NvU32 devId = (pGpu->idInfo.PCIDeviceID) >> 16; ++ if (devId == 0x20C2) ++ { ++ NV_STATUS latePmaStatus = ++ memmgrSec2DebugLateExtendHighPmaRegion(pGpu, GPU_GET_MEMORY_MANAGER(pGpu)); ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG: late PMA extension status=0x%x\n", latePmaStatus); ++ } ++ } ++ + // + // Expanded GPU visibility in GPUMGR is no longer needed once the + // GPU is initialized. +diff -Naur a/src/nvidia/inc/kernel/gpu/mem_mgr/mem_mgr.h b/src/nvidia/inc/kernel/gpu/mem_mgr/mem_mgr.h +--- a/src/nvidia/inc/kernel/gpu/mem_mgr/mem_mgr.h 2026-07-18 11:26:26.869459809 -0700 ++++ b/src/nvidia/inc/kernel/gpu/mem_mgr/mem_mgr.h 2026-07-18 11:26:26.870506938 -0700 +@@ -1,3 +1,5 @@ + + #include "g_mem_mgr_nvoc.h" + ++NV_STATUS memmgrSec2DebugLateExtendHighPmaRegion(OBJGPU *pGpu, MemoryManager *pMemoryManager); ++ +diff -Naur a/src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c b/src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c +--- a/src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c 2026-07-18 11:26:26.862962399 -0700 ++++ b/src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c 2026-07-18 11:26:26.864088597 -0700 +@@ -1163,6 +1163,35 @@ + } + + NV_ASSERT_OK_OR_RETURN(kmemsysPostHeapCreate_HAL(pGpu, pKernelMemorySystem)); ++ ++ { ++ NvU32 devId2 = pGpu->idInfo.PCIDeviceID >> 16; ++ if (devId2 == 0x20C2) ++ { ++ NvU32 ri; ++ NvU64 regionBytes = 0, publicBytes = 0; ++ for (ri = 0; ri < pMemoryManager->Ram.numFBRegions; ri++) ++ { ++ NvU64 sz = pMemoryManager->Ram.fbRegion[ri].limit - ++ pMemoryManager->Ram.fbRegion[ri].base + 1; ++ regionBytes += sz; ++ if (!pMemoryManager->Ram.fbRegion[ri].bRsvdRegion && ++ !pMemoryManager->Ram.fbRegion[ri].bInternalHeap) ++ publicBytes += sz; ++ } ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_HEAP: fbAddrSpace=%lluMB mapRam=%lluMB " ++ "fbTotal=%lluMB fbUsable=0x%llx heapTotal=0x%llx " ++ "regionBytes=0x%llx publicBytes=0x%llx numRegions=%u\n", ++ pMemoryManager->Ram.fbAddrSpaceSizeMb, ++ pMemoryManager->Ram.mapRamSizeMb, ++ pMemoryManager->Ram.fbTotalMemSizeMb, ++ pMemoryManager->Ram.fbUsableMemSize, ++ pMemoryManager->pHeap->total, ++ regionBytes, publicBytes, ++ pMemoryManager->Ram.numFBRegions); ++ } ++ } + } + + return status; +@@ -3646,6 +3675,181 @@ + return status; + } + ++NV_STATUS ++memmgrSec2DebugLateExtendHighPmaRegion ++( ++ OBJGPU *pGpu, ++ MemoryManager *pMemoryManager ++) ++{ ++ Heap *pHeap; ++ PMA *pPma; ++ PMA_REGION_DESCRIPTOR pmaRegion = {0}; ++ PMA_REGION_DESCRIPTOR *pFirstPmaRegionDesc = NULL; ++ FB_REGION_DESCRIPTOR *pCandidate = NULL; ++ NvU32 numPmaRegions = 0; ++ NvU32 candidateIdx = MAX_FB_REGIONS; ++ NvU64 pmaFreeBefore = 0, pmaTotalBefore = 0; ++ NvU64 pmaFreeAfter = 0, pmaTotalAfter = 0; ++ NvU64 heapFree = 0, heapTotal = 0; ++ NvU32 devId; ++ NvU64 stockFbBytes; ++ NvU32 i; ++ NV_STATUS status; ++ ++ if (pGpu == NULL || pMemoryManager == NULL) ++ return NV_ERR_INVALID_ARGUMENT; ++ ++ devId = pGpu->idInfo.PCIDeviceID >> 16; ++ if (devId != 0x20C2) ++ return NV_OK; ++ ++ stockFbBytes = 0x200000000ULL; ++ ++ pHeap = pMemoryManager->pHeap; ++ if (pHeap == NULL || pHeap->pPmaObject == NULL || !memmgrIsPmaInitialized(pMemoryManager)) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: no PMA, skipped heap=%p init=%u\n", ++ pHeap, ++ pHeap ? memmgrIsPmaInitialized(pMemoryManager) : 0); ++ return NV_OK; ++ } ++ ++ pPma = pHeap->pPmaObject; ++ heapGetFree(pHeap, &heapFree); ++ heapGetSize(pHeap, &heapTotal); ++ pmaGetFreeMemory(pPma, &pmaFreeBefore); ++ pmaGetTotalMemory(pPma, &pmaTotalBefore); ++ ++ for (i = 0; i < pMemoryManager->Ram.numFBRegions; i++) ++ { ++ FB_REGION_DESCRIPTOR *pRegion = &pMemoryManager->Ram.fbRegion[i]; ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: region[%u] base=0x%llx limit=0x%llx " ++ "rsvd=%u rsvdSize=0x%llx intHeap=%u\n", ++ i, pRegion->base, pRegion->limit, ++ pRegion->bRsvdRegion, pRegion->rsvdSize, pRegion->bInternalHeap); ++ } ++ ++ status = pmaGetRegionInfo(pPma, &numPmaRegions, &pFirstPmaRegionDesc); ++ if (status != NV_OK) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: pmaGetRegionInfo failed 0x%x\n", status); ++ return status; ++ } ++ ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: numFBRegions=%u numPmaRegions=%u " ++ "stockFb=0x%llx pma_total=0x%llx pma_free=0x%llx " ++ "heap_total=0x%llx heap_free=0x%llx\n", ++ pMemoryManager->Ram.numFBRegions, numPmaRegions, ++ stockFbBytes, pmaTotalBefore, pmaFreeBefore, ++ heapTotal, heapFree); ++ ++ for (i = 0; i < pMemoryManager->Ram.numFBRegions; i++) ++ { ++ FB_REGION_DESCRIPTOR *pRegion = &pMemoryManager->Ram.fbRegion[i]; ++ if (pRegion->bRsvdRegion && !pRegion->bInternalHeap && ++ pRegion->limit >= stockFbBytes && pRegion->base <= pRegion->limit && ++ (pCandidate == NULL || pRegion->limit > pCandidate->limit)) ++ { ++ pCandidate = pRegion; ++ candidateIdx = i; ++ } ++ } ++ ++ if (pCandidate == NULL) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: no high reserved region found " ++ "(looking for bRsvdRegion && limit>=0x%llx)\n", ++ stockFbBytes); ++ return NV_OK; ++ } ++ ++ pmaRegion.base = NV_MAX(pCandidate->base, stockFbBytes); ++ pmaRegion.limit = pCandidate->limit; ++ pmaRegion.performance = pCandidate->performance; ++ pmaRegion.bSupportCompressed = NV_TRUE; ++ pmaRegion.bSupportISO = pCandidate->bSupportISO; ++ pmaRegion.bProtected = pCandidate->bProtected; ++ ++ if (pmaRegion.base > pmaRegion.limit) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: empty range base=0x%llx > limit=0x%llx\n", ++ pmaRegion.base, pmaRegion.limit); ++ return NV_OK; ++ } ++ ++ if (pmaIsPmaManaged(pPma, pmaRegion.base, pmaRegion.limit)) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: already managed base=0x%llx limit=0x%llx\n", ++ pmaRegion.base, pmaRegion.limit); ++ return NV_OK; ++ } ++ ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: registering candidate=%u base=0x%llx limit=0x%llx " ++ "cand_base=0x%llx cand_limit=0x%llx pma_region_id=%u\n", ++ candidateIdx, pmaRegion.base, pmaRegion.limit, ++ pCandidate->base, pCandidate->limit, numPmaRegions); ++ ++ if (pCandidate->base < stockFbBytes && ++ pMemoryManager->Ram.numFBRegions >= MAX_FB_REGIONS) ++ { ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: no FB region slot for split, " ++ "numRegions=%u max=%u\n", ++ pMemoryManager->Ram.numFBRegions, MAX_FB_REGIONS); ++ return NV_ERR_INSUFFICIENT_RESOURCES; ++ } ++ ++ status = pmaRegisterRegion(pPma, numPmaRegions, NV_FALSE, &pmaRegion, 0, NULL); ++ ++ if (status == NV_OK) ++ { ++ if (pCandidate->base < stockFbBytes) ++ { ++ FB_REGION_DESCRIPTOR publicRegion = *pCandidate; ++ publicRegion.base = stockFbBytes; ++ publicRegion.limit = pCandidate->limit; ++ publicRegion.rsvdSize = 0; ++ publicRegion.bRsvdRegion = NV_FALSE; ++ publicRegion.bInternalHeap = NV_FALSE; ++ publicRegion.bSupportCompressed = NV_FALSE; ++ ++ pCandidate->limit = stockFbBytes - 1; ++ pCandidate->rsvdSize = NV_MIN(pCandidate->rsvdSize, ++ pCandidate->limit - pCandidate->base + 1); ++ ++ pMemoryManager->Ram.fbRegion[pMemoryManager->Ram.numFBRegions] = publicRegion; ++ pMemoryManager->Ram.numFBRegions++; ++ } ++ else ++ { ++ pCandidate->bRsvdRegion = NV_FALSE; ++ pCandidate->rsvdSize = 0; ++ pCandidate->bInternalHeap = NV_FALSE; ++ pCandidate->bSupportCompressed = NV_FALSE; ++ } ++ memmgrRegenerateFbRegionPriority(pGpu, pMemoryManager); ++ } ++ ++ pmaGetFreeMemory(pPma, &pmaFreeAfter); ++ pmaGetTotalMemory(pPma, &pmaTotalAfter); ++ ++ NV_PRINTF(LEVEL_ERROR, ++ "SEC2_DEBUG_LATE_PMA: status=0x%x pma_total 0x%llx->0x%llx " ++ "pma_free 0x%llx->0x%llx\n", ++ status, pmaTotalBefore, pmaTotalAfter, pmaFreeBefore, pmaFreeAfter); ++ ++ return status; ++} ++ + + /*! + * @brief Retrieve the size of the client FB address space +@@ -4138,7 +4342,8 @@ + + if (!bFifoLite && !RMCFG_FEATURE_PLATFORM_GSP && bVirtualMode && + IS_SILICON(pGpu) && !pMemorySystemConfig->bDisableCompbitBacking && +- (pMemorySystemConfig->bUseRawModeComptaglineAllocation || pMemorySystemConfig->bOneToOneComptagLineAllocation)) ++ (pMemorySystemConfig->bUseRawModeComptaglineAllocation || pMemorySystemConfig->bOneToOneComptagLineAllocation) && ++ ((pGpu->idInfo.PCIDeviceID >> 16) != 0x20C2)) + { + // Turn on virtual mode to enable compressed allocation access + ceUtilsParams.flags |= DRF_DEF(0050_CEUTILS, _FLAGS, _VIRTUAL_MODE, _TRUE); diff --git a/driver/patches/0004-bar0-pramin-clamp.patch b/driver/patches/0004-bar0-pramin-clamp.patch new file mode 100644 index 0000000..0148274 --- /dev/null +++ b/driver/patches/0004-bar0-pramin-clamp.patch @@ -0,0 +1,20 @@ +diff -Naur a/src/nvidia/src/kernel/gpu/bus/arch/maxwell/kern_bus_gm107.c b/src/nvidia/src/kernel/gpu/bus/arch/maxwell/kern_bus_gm107.c +--- a/src/nvidia/src/kernel/gpu/bus/arch/maxwell/kern_bus_gm107.c 2026-07-18 11:26:26.890781209 -0700 ++++ b/src/nvidia/src/kernel/gpu/bus/arch/maxwell/kern_bus_gm107.c 2026-07-18 11:26:26.891976131 -0700 +@@ -220,6 +220,16 @@ + else + { + offsetBar0 = (pMemoryManager->Ram.fbAddrSpaceSizeMb << 20) - DRF_SIZE(NV_PRAMIN); ++ ++ { ++ NvU32 devId = pGpu->idInfo.PCIDeviceID >> 16; ++ if (devId == 0x20C2 && ++ pMemoryManager->Ram.fbAddrSpaceSizeMb > 0x2000) ++ { ++ NvU64 stockOffset = (0x2000ULL << 20) - DRF_SIZE(NV_PRAMIN); ++ offsetBar0 = stockOffset; ++ } ++ } + } + + // diff --git a/driver/patches/0005-ce-scrub-workarounds.patch b/driver/patches/0005-ce-scrub-workarounds.patch new file mode 100644 index 0000000..bfb4448 --- /dev/null +++ b/driver/patches/0005-ce-scrub-workarounds.patch @@ -0,0 +1,36 @@ +diff -Naur a/src/nvidia/src/kernel/gpu/mem_mgr/arch/turing/mem_mgr_tu102.c b/src/nvidia/src/kernel/gpu/mem_mgr/arch/turing/mem_mgr_tu102.c +--- a/src/nvidia/src/kernel/gpu/mem_mgr/arch/turing/mem_mgr_tu102.c 2026-07-18 11:26:26.913308290 -0700 ++++ b/src/nvidia/src/kernel/gpu/mem_mgr/arch/turing/mem_mgr_tu102.c 2026-07-18 11:26:26.914385381 -0700 +@@ -570,9 +570,19 @@ + NvU32 *pteKind + ) + { ++ OBJGPU *pGpu; ++ + if (pteKind == NULL) + return; + ++ pGpu = ENG_GET_GPU(pMemoryManager); ++ if (pGpu != NULL && ++ (pGpu->idInfo.PCIDeviceID >> 16) == 0x20C2) ++ { ++ *pteKind = NV_MMU_PTE_KIND_GENERIC_MEMORY; ++ return; ++ } ++ + *pteKind = NV_MMU_PTE_KIND_GENERIC_MEMORY_COMPRESSIBLE_DISABLE_PLC; + } + +diff -Naur a/src/nvidia/src/kernel/gpu/mem_mgr/mem_scrub.c b/src/nvidia/src/kernel/gpu/mem_mgr/mem_scrub.c +--- a/src/nvidia/src/kernel/gpu/mem_mgr/mem_scrub.c 2026-07-18 11:26:26.905387408 -0700 ++++ b/src/nvidia/src/kernel/gpu/mem_mgr/mem_scrub.c 2026-07-18 11:26:26.906485666 -0700 +@@ -149,7 +149,8 @@ + NV_PRINTF(LEVEL_INFO, "Starting to init CeUtils for scrubber.\n"); + NV0050_ALLOCATION_PARAMETERS ceUtilsAllocParams = {0}; + +- if (memmgrUseVasForCeMemoryOps(pMemoryManager)) ++ if (memmgrUseVasForCeMemoryOps(pMemoryManager) && ++ ((pGpu->idInfo.PCIDeviceID >> 16) != 0x20C2)) + { + ceUtilsAllocParams.flags = DRF_DEF(0050, _CEUTILS_FLAGS, _VIRTUAL_MODE, _TRUE); + } diff --git a/driver/patches/0006-persistent-sw-state.patch b/driver/patches/0006-persistent-sw-state.patch new file mode 100644 index 0000000..0d431d0 --- /dev/null +++ b/driver/patches/0006-persistent-sw-state.patch @@ -0,0 +1,15 @@ +diff -Naur a/kernel-open/nvidia/nv.c b/kernel-open/nvidia/nv.c +--- a/kernel-open/nvidia/nv.c 2026-07-18 11:26:26.928006389 -0700 ++++ b/kernel-open/nvidia/nv.c 2026-07-18 11:26:26.930357872 -0700 +@@ -1521,6 +1521,11 @@ + goto failed_release_irq; + } + ++ if (nv->pci_info.device_id == 0x20C2) ++ { ++ nv->flags |= NV_FLAG_PERSISTENT_SW_STATE; ++ } ++ + /* Generate and cache the UUID for future callers */ + (void)rm_get_gpu_uuid_raw(sp, nv); + diff --git a/install.sh b/install.sh index 78d6d0c..5de29c1 100755 --- a/install.sh +++ b/install.sh @@ -1,164 +1,214 @@ #!/bin/bash set -euo pipefail -INSTALL_DIR="/opt/cmpunlocker" -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +############################################################################# +# cmpunlocker — one-shot install (patch nvidia-open 610.43.0x modules only) +# +# Usage: +# sudo ./install.sh # auto-detect 8GB→64GB or 10GB→40GB +# sudo ./install.sh --profile=8gb # force 8GB card / 64GB unlock +# sudo ./install.sh --profile=10gb # force 10GB card / 40GB unlock +############################################################################# +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +mapfile -t SUPPORTED_VERSIONS < <(grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' "${SCRIPT_DIR}/driver/VERSION") +SUPPORTED_VERSIONS_CSV="$(IFS=', '; echo "${SUPPORTED_VERSIONS[*]}")" LOG_DIR="${SCRIPT_DIR}/logs" mkdir -p "${LOG_DIR}" LOG_FILE="${LOG_DIR}/install_$(date +%Y%m%d_%H%M%S).log" -exec > >(tee -a "${LOG_FILE}") 2>&1 - -_log() { - echo "[$(date +%H:%M:%S)] $*" >> "${LOG_FILE}" -} - -_log "=== cmpunlocker install started ===" -_log "Script dir: ${SCRIPT_DIR}" -_log "Log file: ${LOG_FILE}" +PROFILE_OVERRIDE="" +for arg in "$@"; do + case "${arg}" in + --profile=8gb|--profile=8GB) PROFILE_OVERRIDE="8gb" ;; + --profile=10gb|--profile=10GB) PROFILE_OVERRIDE="10gb" ;; + -h|--help) + cat <<'EOF' +Usage: sudo ./install.sh [--profile=8gb|10gb] + + --profile=8gb Force 8GB physical card → 64GB unlock geometry + --profile=10gb Force 10GB physical card → 40GB unlock geometry + +Without --profile, stock nvidia-smi memory.total selects the profile: + ~8192 MiB → 8gb / 64GB unlock + ~10240 MiB → 10gb / 40GB unlock +EOF + exit 0 + ;; + *) + echo "Unknown argument: ${arg}" >&2 + echo "Try: sudo ./install.sh --help" >&2 + exit 1 + ;; + esac +done +exec > >(tee -a "${LOG_FILE}") 2>&1 if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then - RED='\033[0;31m' - GREEN='\033[0;32m' - YELLOW='\033[1;33m' - CYAN='\033[0;36m' - NC='\033[0m' + RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' else - RED="" - GREEN="" - YELLOW="" - CYAN="" - NC="" + RED=""; GREEN=""; YELLOW=""; CYAN=""; NC="" fi -info() { - _log "INFO $*" - echo -e "${CYAN}==>${NC} $*" -} - -ok() { - _log "OK $*" - echo -e "${GREEN}✓${NC} $*" -} - -warn() { - _log "WARN $*" - echo -e "${YELLOW}!${NC} $*" -} - -err() { - _log "ERROR $*" - echo -e "${RED}✗${NC} $*" >&2 -} +info() { echo -e "${CYAN}==>${NC} $*"; } +ok() { echo -e "${GREEN}✓${NC} $*"; } +warn() { echo -e "${YELLOW}!${NC} $*"; } +err() { echo -e "${RED}✗${NC} $*" >&2; } +die() { err "$*"; exit 1; } step() { - _log "STEP $*" echo "" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${CYAN}$*${NC}" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" } -checkpoint() { - _log "CHECKPOINT $*" +detect_card_profile() { + local mem_mib="" + if command -v nvidia-smi &>/dev/null; then + mem_mib="$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1 | tr -d '[:space:]' || true)" + fi + if [[ -z "${mem_mib}" || ! "${mem_mib}" =~ ^[0-9]+$ ]]; then + return 1 + fi + + # Allow already-unlocked cards to reinstall the matching profile. + if (( mem_mib >= 60000 )); then + echo "8gb" + return 0 + fi + if (( mem_mib >= 35000 && mem_mib < 60000 )); then + echo "10gb" + return 0 + fi + # Stock sizes (±512 MiB tolerance for reserved FB) + if (( mem_mib >= 7680 && mem_mib <= 8704 )); then + echo "8gb" + return 0 + fi + if (( mem_mib >= 9728 && mem_mib <= 10752 )); then + echo "10gb" + return 0 + fi + echo "unknown:${mem_mib}" >&2 + return 1 } echo "" echo -e "${CYAN}╔════════════════════════════════════════╗${NC}" -echo -e "${CYAN}║ cmpunlocker — Compute Unlock ║${NC}" +echo -e "${CYAN}║ cmpunlocker — 610 module unlock ║${NC}" echo -e "${CYAN}╚════════════════════════════════════════╝${NC}" echo "" -step "Step 1/7: Verifying root privileges" -if [ "$EUID" -ne 0 ]; then - err "Run as root: sudo ./install.sh" - exit 1 -fi +step "Step 1/6: Verifying root privileges" +[[ "${EUID}" -eq 0 ]] || die "Run as root: sudo ./install.sh" ok "Running as root" -checkpoint "step-1-complete" -step "Step 2/7: Detecting CMP 170HX GPU" -PCI=$(lspci -nn 2>/dev/null | grep -iE "10de:20b0|10de:20c2|10de:2082" | head -1 | awk '{print $1}') -if [ -z "$PCI" ]; then - err "No CMP 170HX GPU found (10de:20b0 / 10de:20c2 / 10de:2082)" - exit 1 -fi +step "Step 2/6: Detecting CMP 170HX GPU" +PCI_LINE="$(lspci -nn 2>/dev/null | grep -iE '10de:20b0|10de:20c2|10de:2082' | head -1 || true)" +[[ -n "${PCI_LINE}" ]] || die "No CMP 170HX GPU found (10de:20b0 / 10de:20c2 / 10de:2082)" +PCI="$(echo "${PCI_LINE}" | awk '{print $1}')" PCI_FULL="0000:${PCI}" -ok "GPU detected: ${PCI_FULL}" -checkpoint "step-2-complete gpu=${PCI_FULL}" - -step "Step 3/7: Locating NVIDIA GSP firmware" -GSP_PATH=$(ls /lib/firmware/nvidia/*/gsp_tu10x.bin 2>/dev/null | sort -rV | head -1 || true) -if [ -z "$GSP_PATH" ]; then - err "NVIDIA GSP firmware not found under /lib/firmware/nvidia/" - info "Install the nvidia-open driver (580.x) first" - exit 1 +DEVID="$(echo "${PCI_LINE}" | grep -oE '10de:[0-9a-fA-F]{4}' | head -1 | cut -d: -f2 | tr '[:upper:]' '[:lower:]')" +ok "GPU detected: ${PCI_FULL} (10de:${DEVID})" +if [[ "${DEVID}" != "20c2" ]]; then + warn "In-driver unlock path is gated on PCI ID 0x20C2." + warn "This card reports 0x${DEVID}; install will continue, but unlock may not activate." +fi + +step "Step 3/6: Selecting card memory profile" +CARD_PROFILE="" +EXPECTED_MIB="" +if [[ -n "${PROFILE_OVERRIDE}" ]]; then + CARD_PROFILE="${PROFILE_OVERRIDE}" + ok "Profile forced via --profile=${CARD_PROFILE}" +else + if detected_profile="$(detect_card_profile)"; then + CARD_PROFILE="${detected_profile}" + mem_now="$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1 | tr -d '[:space:]' || echo '?')" + ok "Detected stock/reported memory ${mem_now} MiB → profile ${CARD_PROFILE}" + else + die "Could not detect 8GB vs 10GB card. Re-run with --profile=8gb or --profile=10gb" + fi +fi + +case "${CARD_PROFILE}" in + 8gb) + EXPECTED_MIB=65536 + info "Unlock geometry: 64GB (CFG1=0x02779000 LMR=0x0000020B)" + ;; + 10gb) + EXPECTED_MIB=40960 + info "Unlock geometry: 40GB (CFG1=0x02669000 LMR=0x0000028A)" + ;; + *) + die "Internal error: bad profile ${CARD_PROFILE}" + ;; +esac +export CMPUNLOCKER_CARD_PROFILE="${CARD_PROFILE}" + +step "Step 4/6: Verifying nvidia-open (${SUPPORTED_VERSIONS_CSV})" +[[ ${#SUPPORTED_VERSIONS[@]} -gt 0 ]] || die "No supported versions listed in driver/VERSION" +if [[ -d /sys/firmware/efi ]] && command -v mokutil &>/dev/null; then + if mokutil --sb-state 2>/dev/null | grep -qi 'SecureBoot enabled'; then + die "Secure Boot is enabled. Disable it before installing unsigned patched modules." + fi fi -ok "GSP firmware: ${GSP_PATH}" -checkpoint "step-3-complete gsp=${GSP_PATH}" -step "Step 4/7: Checking Python 3 availability" -if ! command -v python3 &>/dev/null; then - err "python3 not found" - exit 1 +version_supported() { + local v="$1" + local s + for s in "${SUPPORTED_VERSIONS[@]}"; do + [[ "${v}" == "${s}" ]] && return 0 + done + return 1 +} + +detected="" +if [[ -r /proc/driver/nvidia/version ]]; then + detected="$(grep -oE '[0-9]+\.[0-9]+\.[0-9]+' /proc/driver/nvidia/version | head -1 || true)" +fi +if [[ -z "${detected}" ]] && command -v nvidia-smi &>/dev/null; then + detected="$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1 | tr -d '[:space:]' || true)" +fi +if [[ -z "${detected}" ]]; then + for cand in "${SUPPORTED_VERSIONS[@]}"; do + if [[ -d "/lib/firmware/nvidia/${cand}" ]]; then + detected="${cand}" + break + fi + done + if [[ -z "${detected}" ]]; then + fw="$(ls -d /lib/firmware/nvidia/*/ 2>/dev/null | sed 's|.*/nvidia/||;s|/||' | sort -rV | head -1 || true)" + detected="${fw}" + fi fi -ok "Python 3 available" -checkpoint "step-4-complete python=$(python3 --version 2>&1)" - -step "Step 5/7: Installing cmpunlocker to ${INSTALL_DIR}" -rm -rf "${INSTALL_DIR}" -cp -r "${SCRIPT_DIR}" "${INSTALL_DIR}" -ok "Installation complete" -checkpoint "step-5-complete" - -step "Step 6/7: Running initial compute unlock" - -{ - echo "--- PRE-STEP-6 SYSTEM SNAPSHOT ---" - echo "kernel: $(uname -r)" - echo "date: $(date)" - echo "uptime: $(uptime)" - echo "--- memory ---" - free -h - echo "--- nvidia modules ---" - lsmod | grep -i nvidia || echo "(none loaded)" - echo "--- lspci nvidia ---" - lspci -vv -s "${PCI}" 2>/dev/null || lspci -v | grep -A5 -i nvidia || echo "(lspci failed)" - echo "--- display manager ---" - systemctl status display-manager --no-pager 2>/dev/null || echo "(no display-manager unit)" - echo "--- /sys pci reset file ---" - ls -la "/sys/bus/pci/devices/${PCI_FULL}/reset" 2>/dev/null || echo "(reset file not found)" - echo "--- END SNAPSHOT ---" -} >> "${LOG_FILE}" - -_log "CHECKPOINT step-6-start: launching pipeline.py" - -PIPELINE_LOG="${LOG_DIR}/pipeline_$(date +%Y%m%d_%H%M%S).log" -export CMPUNLOCKER_LOG_FILE="${PIPELINE_LOG}" - -python3 "${INSTALL_DIR}/payload/pipeline.py" "${PCI_FULL}" "${GSP_PATH}" -ok "Compute unlock applied" -checkpoint "step-6-complete" - -step "Step 7/7: Enabling cmpunlocker systemd service" -cp "${INSTALL_DIR}/daemon/cmpunlocker.service" /etc/systemd/system/ -systemctl daemon-reload -systemctl enable cmpunlocker -systemctl start cmpunlocker -ok "Service enabled and started" -checkpoint "step-7-complete" -_log "=== cmpunlocker install finished successfully ===" +[[ -n "${detected}" ]] || die "Could not detect an installed NVIDIA driver. Install nvidia-open ${SUPPORTED_VERSIONS_CSV} first." +version_supported "${detected}" || die "Installed driver is ${detected}, but cmpunlocker requires one of: ${SUPPORTED_VERSIONS_CSV}." +ok "NVIDIA driver ${detected} is supported" + +[[ -d "/lib/modules/$(uname -r)/build" ]] || die "Kernel headers missing for $(uname -r). Install linux-headers-$(uname -r) or kernel-devel." +ok "Kernel headers present for $(uname -r)" + +step "Step 5/6: Building and installing patched modules" +chmod +x "${SCRIPT_DIR}/driver/build.sh" +CMPUNLOCKER_DRIVER_VERSION="${detected}" CMPUNLOCKER_CARD_PROFILE="${CARD_PROFILE}" "${SCRIPT_DIR}/driver/build.sh" +ok "Patched modules installed (profile ${CARD_PROFILE})" + +step "Step 6/6: Done" echo "" echo -e "${CYAN}╔════════════════════════════════════════╗${NC}" -echo -e "${CYAN}║${NC} ${GREEN}✓ cmpunlocker installed successfully${CYAN} ║${NC}" +echo -e "${CYAN}║${NC} ${GREEN}✓ cmpunlocker install finished${CYAN} ║${NC}" echo -e "${CYAN}╚════════════════════════════════════════╝${NC}" echo "" -echo "To monitor the daemon:" -echo -e " ${CYAN}journalctl -u cmpunlocker -f${NC}" +echo "Profile: ${CARD_PROFILE} → expect ~${EXPECTED_MIB} MiB after unlock" +echo "Next:" +echo -e " 1. Cold reboot recommended: ${CYAN}sudo shutdown -h now${NC} (then power on)" +echo -e " 2. Verify memory: ${CYAN}nvidia-smi${NC} (expect ~${EXPECTED_MIB} MiB)" +echo -e " 3. Verify unlock logs: ${CYAN}sudo dmesg | grep SEC2_DEBUG${NC}" +echo -e " 4. Verify SM clocks: ${CYAN}nvidia-smi --query-gpu=clocks.max.sm --format=csv,noheader${NC}" echo "" -echo "To verify compute unlock:" -echo -e " ${CYAN}nvidia-smi --query-gpu=clocks.max.sm --format=csv,noheader${NC}" +echo "Log saved to: ${LOG_FILE}" echo "" diff --git a/payload/__init__.py b/payload/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/payload/bar0.py b/payload/bar0.py deleted file mode 100644 index 88bbf6f..0000000 --- a/payload/bar0.py +++ /dev/null @@ -1,31 +0,0 @@ -import mmap -import os -import struct - -from common.constants import get -from .gpu import bar0_path - - -class Bar0: - - def __init__(self, pci_full: str): - path = bar0_path(pci_full) - self._fd = os.open(path, os.O_RDWR) - mmap_size = get('dmem_layout.bar0_mmap_size') - self._mm = mmap.mmap(self._fd, mmap_size, access=mmap.ACCESS_WRITE) - - def rd32(self, off: int) -> int: - return struct.unpack_from(" None: - struct.pack_into(" None: - self._mm.close() - os.close(self._fd) - - def __enter__(self): - return self - - def __exit__(self, *_): - self.close() diff --git a/payload/build.py b/payload/build.py deleted file mode 100644 index d2ec071..0000000 --- a/payload/build.py +++ /dev/null @@ -1,51 +0,0 @@ -import struct -import sys -import os -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from common.constants import get - -WRITES = [ - (0x009A0204, 0x02779000), - (0x00100CE0, 0x0000020B), - (0x00823804, 0xFFFFFFFF), -] - - -def build() -> bytes: - payload_size = get('dmem_layout.payload_size') - dma_target = get('dmem_layout.dma_target') - canary = get('dmem_layout.canary') - canary_addr = get('dmem_layout.guard_addr') - frame_start = get('payload_frames.frame_start_addr') - frame_stride = get('payload_frames.frame_stride') - gadget_addr = get('booter_addrs.bar0_write_gadget') - tail_return = 0x0000810D - - payload = bytearray(payload_size) - - def w32(dmem_addr: int, val: int) -> None: - off = dmem_addr - dma_target - if 0 <= off <= len(payload) - 4: - struct.pack_into(" None: - for svc in ("gdm3", "sddm", "lightdm", "display-manager"): - subprocess.run(["systemctl", "stop", svc], capture_output=True, check=False) - subprocess.run(["killall", "-9", "Xorg", "Xwayland", "nvidia-persistenced"], - capture_output=True, check=False) - time.sleep(2) - - -def unload_modules() -> None: - for mod in ("nvidia-uvm", "nvidia_drm", "nvidia_modeset", "nvidia"): - subprocess.run(["modprobe", "-r", mod], capture_output=True, check=False) - time.sleep(2) - - -def load_module() -> None: - result = subprocess.run(["modprobe", "nvidia"], capture_output=True, text=True, check=False) - if result.returncode != 0: - raise RuntimeError(f"modprobe nvidia failed: {result.stderr.strip()}") - - -def flr_reset(pci_full: str) -> None: - reset_path = f"/sys/bus/pci/devices/{pci_full}/reset" - with open(reset_path, "w", encoding="utf-8") as f: - f.write("1") - time.sleep(3) - - -def aggressive_unload() -> None: - my_pid = str(os.getpid()) - - stop_display_manager() - subprocess.run(["systemctl", "stop", "nvidia-persistenced"], capture_output=True, check=False) - - for dev in glob.glob("/dev/nvidia*") + ["/dev/nvidiactl"]: - if not os.path.exists(dev): - continue - res = subprocess.run(["fuser", dev], capture_output=True, text=True, check=False) - for pid in res.stdout.split(): - if pid != my_pid: - subprocess.run(["kill", "-9", pid], capture_output=True, check=False) - time.sleep(1) - - unload_modules() - - lsmod = subprocess.run(["lsmod"], capture_output=True, text=True, check=False).stdout - if "nvidia" in lsmod: - for mod in ("nvidia_uvm", "nvidia_drm", "nvidia_modeset", "nvidia"): - subprocess.run(["rmmod", "-f", mod], capture_output=True, check=False) diff --git a/payload/gpu.py b/payload/gpu.py deleted file mode 100644 index 05ec2f9..0000000 --- a/payload/gpu.py +++ /dev/null @@ -1,33 +0,0 @@ -import re -import subprocess - - -_DEVICE_IDS = {"10de:20b0", "10de:20c2", "10de:2082"} - - -def find_all_gpus() -> list: - result = subprocess.run( - ["lspci", "-nn"], - capture_output=True, - text=True, - check=False, - ) - gpus = [] - for line in result.stdout.splitlines(): - if any(dev_id in line for dev_id in _DEVICE_IDS): - match = re.match(r"^(\S+)\s", line) - if match: - bdf = match.group(1) - if not bdf.startswith("0000:"): - bdf = "0000:" + bdf - gpus.append(bdf) - return gpus - - -def find_gpu() -> str: - gpus = find_all_gpus() - return gpus[0] if gpus else None - - -def bar0_path(pci_full: str) -> str: - return f"/sys/bus/pci/devices/{pci_full}/resource0" diff --git a/payload/gsp_patch.py b/payload/gsp_patch.py deleted file mode 100644 index 13cd100..0000000 --- a/payload/gsp_patch.py +++ /dev/null @@ -1,71 +0,0 @@ -import struct -import sys -import os -from pathlib import Path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from common.constants import get - - -def _parse_section_headers(gsp: bytearray): - e_shoff = struct.unpack_from(" None: - payload_size = get('dmem_layout.payload_size') - signature_section = get('elf.signature_section').encode() - - if len(payload) != payload_size: - raise ValueError(f"Payload must be exactly {payload_size} bytes, got {len(payload)}") - - gsp = bytearray(Path(input_path).read_bytes()) - - if struct.unpack_from(">I", gsp, 0)[0] != get('elf.header_magic'): - raise ValueError(f"{input_path} is not an ELF file") - - e_shentsize, shdrs, strtab, strtab_hdr_off = _parse_section_headers(gsp) - sig_idx, sig_file_off = _find_signature_section( - shdrs, e_shentsize, strtab, signature_section) - - payload_end = sig_file_off + payload_size - if len(gsp) < payload_end: - gsp.extend(b"\x00" * (payload_end - len(gsp))) - - gsp[sig_file_off : sig_file_off + payload_size] = payload - struct.pack_into(" Optional[str]: - - path = os.environ.get("CMPUNLOCKER_LOG_FILE") - if not path: - log_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "logs" - ) - os.makedirs(log_dir, exist_ok=True) - from datetime import datetime - path = os.path.join(log_dir, f"pipeline_{datetime.now():%Y%m%d_%H%M%S}.log") - - try: - handler = _FlushingFileHandler(path, encoding="utf-8") - handler.setLevel(logging.DEBUG) - handler.setFormatter( - logging.Formatter("%(asctime)s.%(msecs)03d %(levelname)-7s %(message)s", - datefmt="%H:%M:%S") - ) - logging.getLogger().addHandler(handler) - logging.getLogger().setLevel(logging.DEBUG) - return path - except OSError as exc: - log.warning("Could not open pipeline log file %s: %s", path, exc) - return None - - -def _find_gsp() -> str: - paths = sorted(glob.glob(_GSP_GLOB), reverse=True) - if not paths: - raise FileNotFoundError(f"No GSP firmware found matching {_GSP_GLOB}") - return paths[0] - - -def _ensure_backup(gsp_path: str, backup_path: str, pci_full: str) -> None: - if not os.path.exists(backup_path): - shutil.copy2(gsp_path, backup_path) - log.info("[%s] GSP backup written to %s", pci_full, backup_path) - return - - log.debug("[%s] GSP backup already exists: %s", pci_full, backup_path) - - -def _patch_and_flash_gsp( - backup_path: str, payload: bytes, patched_path: str, gsp_path: str, pci_full: str -) -> None: - log.info("[%s] [STEP] Patching GSP firmware", pci_full) - patch_gsp(backup_path, payload, patched_path) - shutil.copy2(patched_path, gsp_path) - log.info("[%s] [STEP] GSP firmware patched and written", pci_full) - - -def _load_driver_and_settle(pci_full: str, settle_seconds: int) -> None: - load_module() - log.info("[%s] Sleeping %ds for firmware initialisation", pci_full, settle_seconds) - time.sleep(settle_seconds) - - -def _reload_driver_and_settle(pci_full: str, settle_seconds: int) -> None: - load_module() - log.info("[%s] Sleeping %ds for driver reload", pci_full, settle_seconds) - time.sleep(settle_seconds) - - -def _run_reset_cycle(pci_full: str) -> None: - log.info("[%s] [STEP] FLR reset #1", pci_full) - flr_reset(pci_full) - log.info("[%s] [STEP] FLR reset #1 done", pci_full) - - log.info("[%s] [STEP] Aggressive driver unload", pci_full) - aggressive_unload() - log.info("[%s] [STEP] Aggressive unload done", pci_full) - - log.info("[%s] [STEP] FLR reset #2", pci_full) - flr_reset(pci_full) - log.info("[%s] [STEP] FLR reset #2 done", pci_full) - - -def run_full_unlock(pci_full: str, gsp_path: str = None) -> bool: - if gsp_path is None: - gsp_path = _find_gsp() - - backup_path = gsp_path + ".cmpunlocker.bak" - patched_path = gsp_path + ".cmpunlocker.patched" - - log.info("[%s] [STEP] Pipeline start", pci_full) - log.info("[%s] GSP firmware: %s", pci_full, gsp_path) - log.debug("[%s] backup=%s patched=%s", pci_full, backup_path, patched_path) - - log.info("[%s] [STEP] Stopping display manager", pci_full) - stop_display_manager() - log.info("[%s] [STEP] Display manager stopped", pci_full) - - log.info("[%s] [STEP] Unloading NVIDIA modules", pci_full) - unload_modules() - log.info("[%s] [STEP] Modules unloaded", pci_full) - - _ensure_backup(gsp_path, backup_path, pci_full) - - log.info("[%s] [STEP] Building ROP payload", pci_full) - payload = build_payload() - log.info("[%s] [STEP] ROP payload built (%d bytes)", pci_full, len(payload)) - - _patch_and_flash_gsp(backup_path, payload, patched_path, gsp_path, pci_full) - - log.info("[%s] [STEP] Loading patched driver (modprobe nvidia)", pci_full) - _load_driver_and_settle(pci_full, 5) - log.info("[%s] [STEP] Patched driver loaded", pci_full) - - _run_reset_cycle(pci_full) - - from unlock.compute import apply_unlock - - log.info("[%s] [STEP] Writing compute unlock registers (SS0/SS1)", pci_full) - ok, msg = apply_unlock(pci_full) - if ok: - log.info("[%s] [STEP] Compute unlock registers written — success", pci_full) - else: - log.warning("[%s] [STEP] Compute unlock registers: %s", pci_full, msg) - - log.info("[%s] [STEP] Restoring original GSP firmware", pci_full) - shutil.copy2(backup_path, gsp_path) - log.info("[%s] [STEP] Original GSP firmware restored", pci_full) - - log.info("[%s] [STEP] Reloading driver (modprobe nvidia)", pci_full) - _reload_driver_and_settle(pci_full, 3) - log.info("[%s] [STEP] Driver reloaded", pci_full) - - log.info("[%s] [STEP] Pipeline complete — ok=%s", pci_full, ok) - return ok - - -def main() -> None: - logging.basicConfig( - level=logging.INFO, - format="%(asctime)s %(levelname)s %(message)s", - ) - log_path = _setup_file_logging() - if log_path: - log.info("Pipeline log: %s", log_path) - pci = sys.argv[1] if len(sys.argv) > 1 else None - gsp = sys.argv[2] if len(sys.argv) > 2 else None - if pci is None: - from payload.gpu import find_gpu - pci = find_gpu() - if pci is None: - print("ERROR: No compatible GPU found") - sys.exit(1) - run_full_unlock(pci, gsp) - - -if __name__ == "__main__": - main() diff --git a/remove.sh b/remove.sh new file mode 100755 index 0000000..1b28766 --- /dev/null +++ b/remove.sh @@ -0,0 +1,176 @@ +#!/bin/bash +set -euo pipefail + +############################################################################# +# cmpunlocker — remove patched modules and restore stock nvidia-open load +############################################################################# + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SERVICE_NAME="cmpunlocker" +SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" +INSTALL_DIR="/opt/cmpunlocker" + +if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then + RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' +else + RED=""; GREEN=""; YELLOW=""; CYAN=""; NC="" +fi + +info() { echo -e "${CYAN}==>${NC} $*"; } +ok() { echo -e "${GREEN}✓${NC} $*"; } +warn() { echo -e "${YELLOW}!${NC} $*"; } +err() { echo -e "${RED}✗${NC} $*" >&2; } +die() { err "$*"; exit 1; } + +step() { + echo "" + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${CYAN}$*${NC}" + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +echo "" +echo -e "${CYAN}╔════════════════════════════════════════╗${NC}" +echo -e "${CYAN}║ cmpunlocker — System Removal ║${NC}" +echo -e "${CYAN}╚════════════════════════════════════════╝${NC}" +echo "" + +if [[ "${1:-}" != "--yes" && "${1:-}" != "-y" ]]; then + warn "This removes cmpunlocker patched kernel modules:" + echo " - Stops leftover cmpunlocker systemd service (if present)" + echo " - Removes /lib/modules/*/updates/cmpunlocker/" + echo " - Removes ${INSTALL_DIR} (legacy install dir, if present)" + echo " - Reloads stock NVIDIA modules (brief display interruption)" + echo "" + echo "Run: sudo ./remove.sh --yes" + exit 1 +fi + +step "Step 1/5: Verifying root privileges" +[[ "${EUID}" -eq 0 ]] || die "Run as root: sudo ./remove.sh --yes" +ok "Running as root" + +LOG_DIR="${SCRIPT_DIR}/logs" +if ! mkdir -p "${LOG_DIR}" 2>/dev/null || [[ ! -w "${LOG_DIR}" ]]; then + LOG_DIR="/tmp" +fi +LOG_FILE="${LOG_DIR}/remove_$(date +%Y%m%d_%H%M%S).log" +exec > >(tee -a "${LOG_FILE}") 2>&1 + +step "Step 2/5: Stopping leftover cmpunlocker service" +if systemctl is-active --quiet "${SERVICE_NAME}" 2>/dev/null; then + systemctl stop "${SERVICE_NAME}" || true + ok "Service stopped" +else + warn "Service not running" +fi +if systemctl is-enabled --quiet "${SERVICE_NAME}" 2>/dev/null; then + systemctl disable "${SERVICE_NAME}" || true + ok "Service disabled" +fi +if [[ -f "${SERVICE_FILE}" ]]; then + rm -f "${SERVICE_FILE}" + systemctl daemon-reload + systemctl reset-failed "${SERVICE_NAME}" 2>/dev/null || true + ok "Removed ${SERVICE_FILE}" +fi +pkill -f "${INSTALL_DIR}/daemon/watchdog.py" 2>/dev/null || true + +step "Step 3/5: Removing patched modules and legacy files" +mod_removed=0 +kernels_touched=() +shopt -s nullglob +for mod_dir in /lib/modules/*/updates/cmpunlocker; do + if [[ -d "${mod_dir}" ]]; then + kernel="$(basename "$(dirname "$(dirname "${mod_dir}")")")" + rm -rf "${mod_dir}" + depmod -a "${kernel}" 2>/dev/null || true + ok "Removed patched modules for kernel ${kernel}" + mod_removed=$((mod_removed + 1)) + kernels_touched+=("${kernel}") + fi +done +[[ "${mod_removed}" -gt 0 ]] || warn "No patched kernel modules found" + +if [[ ${#kernels_touched[@]} -gt 0 ]]; then + info "Rebuilding initramfs so stock modules are packed again..." + for kernel in "${kernels_touched[@]}"; do + if command -v update-initramfs &>/dev/null; then + update-initramfs -u -k "${kernel}" 2>/dev/null || true + elif command -v dracut &>/dev/null; then + dracut --force --kver "${kernel}" 2>/dev/null || true + fi + done + if command -v mkinitcpio &>/dev/null && ! command -v update-initramfs &>/dev/null && ! command -v dracut &>/dev/null; then + mkinitcpio -P 2>/dev/null || true + fi + ok "initramfs rebuild attempted" +fi + +# Clean leftover GSP backups from older cmpunlocker versions +for gsp in /lib/firmware/nvidia/*/gsp_tu10x.bin; do + rm -f \ + "${gsp}.cmpunlocker.bak" \ + "${gsp}.cmpunlocker.patched" \ + "${gsp}.cmpunlocker.tmp" \ + "${gsp}.cmpunlocker.cleanup" \ + "${gsp}.cmpunlocker.pat" +done + +if [[ -d "${INSTALL_DIR}" ]]; then + rm -rf "${INSTALL_DIR}" + ok "Removed ${INSTALL_DIR}" +else + warn "${INSTALL_DIR} not found (ok for module-only installs)" +fi + +step "Step 4/5: Reloading stock NVIDIA driver" +if lsmod | grep -q '^nvidia'; then + warn "Unloading NVIDIA modules (display may flicker)" + for svc in gdm3 sddm lightdm display-manager; do + systemctl stop "${svc}" 2>/dev/null || true + done + systemctl stop nvidia-persistenced 2>/dev/null || true + killall -9 Xorg Xwayland nvidia-persistenced 2>/dev/null || true + sleep 1 + + for mod in nvidia_drm nvidia_uvm nvidia_modeset nvidia; do + modprobe -r "${mod}" 2>/dev/null || true + done + sleep 1 + + if lsmod | grep -q '^nvidia'; then + for mod in nvidia_uvm nvidia_drm nvidia_modeset nvidia; do + rmmod -f "${mod}" 2>/dev/null || true + done + fi + + if modprobe nvidia 2>/dev/null; then + modprobe nvidia-modeset 2>/dev/null || true + modprobe nvidia-uvm 2>/dev/null || true + modprobe nvidia-drm 2>/dev/null || true + ok "Stock NVIDIA driver reloaded" + else + warn "Could not reload NVIDIA driver — reboot to finish cleanup" + fi + + for svc in gdm3 sddm lightdm display-manager; do + if systemctl is-enabled --quiet "${svc}" 2>/dev/null; then + systemctl start "${svc}" 2>/dev/null || true + break + fi + done +else + warn "NVIDIA modules not loaded — skipping driver reload" +fi + +echo "" +echo -e "${CYAN}╔════════════════════════════════════════╗${NC}" +echo -e "${CYAN}║${NC} ${GREEN}✓ cmpunlocker removed from system${CYAN} ║${NC}" +echo -e "${CYAN}╚════════════════════════════════════════╝${NC}" +echo "" +echo "Log saved to: ${LOG_FILE}" +echo "" +echo "If the GPU or display is not working, reboot once:" +echo -e " ${CYAN}sudo reboot${NC}" +echo "" diff --git a/requirements.txt b/requirements.txt index 3ea8172..5d28f09 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -PyYAML -pytest +pyyaml>=5.1 +pytest>=7.0 diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_imports.py b/tests/test_imports.py deleted file mode 100644 index f99cbe0..0000000 --- a/tests/test_imports.py +++ /dev/null @@ -1,32 +0,0 @@ -import subprocess -import sys -from pathlib import Path - -import pytest - -REPO_ROOT = Path(__file__).resolve().parent.parent - -ALL_PY_FILES = sorted(REPO_ROOT.rglob("*.py")) - -DIRECT_IMPORT_MODULES = [ - "common.constants", - "payload.build", - "payload.gsp_patch", - "unlock.compute", -] - - -@pytest.mark.parametrize("py_file", ALL_PY_FILES, ids=lambda p: str(p.relative_to(REPO_ROOT))) -def test_py_compile(py_file): - result = subprocess.run( - [sys.executable, "-m", "py_compile", str(py_file)], - capture_output=True, - text=True, - check=False, - ) - assert result.returncode == 0, result.stderr - - -@pytest.mark.parametrize("module_name", DIRECT_IMPORT_MODULES) -def test_direct_import(module_name): - __import__(module_name) diff --git a/tests/test_yaml_valid.py b/tests/test_yaml_valid.py deleted file mode 100644 index a0def66..0000000 --- a/tests/test_yaml_valid.py +++ /dev/null @@ -1,22 +0,0 @@ -from pathlib import Path - -import yaml - -CONSTANTS_PATH = Path(__file__).resolve().parent.parent / "common" / "constants.yaml" - -EXPECTED_TOP_LEVEL_KEYS = { - "dmem_layout", - "host_bar0_writes", - "booter_addrs", - "payload_frames", - "gpu", - "elf", -} - - -def test_yaml_valid(): - with open(CONSTANTS_PATH, "r", encoding="utf-8") as f: - data = yaml.safe_load(f) - - assert isinstance(data, dict) - assert EXPECTED_TOP_LEVEL_KEYS.issubset(data.keys()) diff --git a/unlock/__init__.py b/unlock/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/unlock/compute.py b/unlock/compute.py deleted file mode 100644 index 9b2061d..0000000 --- a/unlock/compute.py +++ /dev/null @@ -1,43 +0,0 @@ -import sys -import os -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from payload.bar0 import Bar0 -from common.constants import get - - -def is_plm_open(pci_full: str) -> bool: - plm_addr = get('host_bar0_writes.feat_ovr_plm.addr') - plm_open = get('host_bar0_writes.feat_ovr_plm.value') - with Bar0(pci_full) as bar0: - return bar0.rd32(plm_addr) == plm_open - - -def is_unlocked(pci_full: str) -> bool: - ss0_addr = get('host_bar0_writes.ss0.addr') - ss1_addr = get('host_bar0_writes.ss1.addr') - ss0_value = get('host_bar0_writes.ss0.value') - ss1_value = get('host_bar0_writes.ss1.value') - with Bar0(pci_full) as bar0: - return bar0.rd32(ss0_addr) == ss0_value and bar0.rd32(ss1_addr) == ss1_value - - -def apply_unlock(pci_full: str) -> tuple: - plm_addr = get('host_bar0_writes.feat_ovr_plm.addr') - plm_open = get('host_bar0_writes.feat_ovr_plm.value') - ss0_addr = get('host_bar0_writes.ss0.addr') - ss1_addr = get('host_bar0_writes.ss1.addr') - ss0_value = get('host_bar0_writes.ss0.value') - ss1_value = get('host_bar0_writes.ss1.value') - - with Bar0(pci_full) as bar0: - plm = bar0.rd32(plm_addr) - if plm != plm_open: - return False, f"PLM not open (0x{plm:08X}) — run full unlock first" - bar0.wr32(ss0_addr, ss0_value) - bar0.wr32(ss1_addr, ss1_value) - ss0 = bar0.rd32(ss0_addr) - ss1 = bar0.rd32(ss1_addr) - if ss0 == ss0_value and ss1 == ss1_value: - return True, "SS0/SS1 applied successfully" - return False, f"values did not stick (SS0=0x{ss0:08X} SS1=0x{ss1:08X})"