diff --git a/common/constants.yaml b/common/constants.yaml index 63ca972..e3ee258 100644 --- a/common/constants.yaml +++ b/common/constants.yaml @@ -6,6 +6,10 @@ dmem_layout: 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 diff --git a/install.sh b/install.sh old mode 100644 new mode 100755 index e4bfbfe..78d6d0c --- a/install.sh +++ b/install.sh @@ -4,6 +4,21 @@ set -euo pipefail INSTALL_DIR="/opt/cmpunlocker" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +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}" + + if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then RED='\033[0;31m' GREEN='\033[0;32m' @@ -19,28 +34,37 @@ else 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 } step() { + _log "STEP $*" echo "" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${CYAN}$*${NC}" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" } +checkpoint() { + _log "CHECKPOINT $*" +} + echo "" echo -e "${CYAN}╔════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ cmpunlocker — Compute Unlock ║${NC}" @@ -53,6 +77,7 @@ if [ "$EUID" -ne 0 ]; then exit 1 fi 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}') @@ -62,6 +87,7 @@ if [ -z "$PCI" ]; then fi 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) @@ -71,6 +97,7 @@ if [ -z "$GSP_PATH" ]; then exit 1 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 @@ -78,15 +105,42 @@ if ! command -v python3 &>/dev/null; then exit 1 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/ @@ -94,6 +148,8 @@ systemctl daemon-reload systemctl enable cmpunlocker systemctl start cmpunlocker ok "Service enabled and started" +checkpoint "step-7-complete" +_log "=== cmpunlocker install finished successfully ===" echo "" echo -e "${CYAN}╔════════════════════════════════════════╗${NC}" diff --git a/payload/pipeline.py b/payload/pipeline.py index dca4104..39458a0 100644 --- a/payload/pipeline.py +++ b/payload/pipeline.py @@ -4,6 +4,7 @@ import os import shutil import sys import time +from typing import Optional sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -18,6 +19,44 @@ log = logging.getLogger(__name__) _GSP_GLOB = "/lib/firmware/nvidia/*/gsp_tu10x.bin" +class _FlushingFileHandler(logging.FileHandler): + + def emit(self, record): + super().emit(record) + self.flush() + # Best-effort fsync so the kernel doesn't buffer the write. + try: + os.fsync(self.stream.fileno()) + except OSError: + pass + + +def _setup_file_logging() -> 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: @@ -25,6 +64,50 @@ def _find_gsp() -> str: 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() @@ -32,54 +115,50 @@ def run_full_unlock(pci_full: str, gsp_path: str = None) -> bool: backup_path = gsp_path + ".cmpunlocker.bak" patched_path = gsp_path + ".cmpunlocker.patched" - log.info("[%s] Starting full unlock pipeline", pci_full) + 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] Stopping display manager and unloading modules", pci_full) + 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) - 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) + _ensure_backup(gsp_path, backup_path, pci_full) - log.info("[%s] Building ROP payload", 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)) - log.info("[%s] Injecting payload into GSP firmware", pci_full) - patch_gsp(backup_path, payload, patched_path) - shutil.copy2(patched_path, gsp_path) + _patch_and_flash_gsp(backup_path, payload, patched_path, gsp_path, pci_full) - log.info("[%s] Loading patched driver", pci_full) - load_module() - time.sleep(5) + 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) - log.info("[%s] FLR reset #1", pci_full) - flr_reset(pci_full) - - log.info("[%s] Aggressive driver unload", pci_full) - aggressive_unload() - - log.info("[%s] FLR reset #2", pci_full) - flr_reset(pci_full) + _run_reset_cycle(pci_full) from unlock.compute import apply_unlock - log.info("[%s] Applying compute unlock", pci_full) + log.info("[%s] [STEP] Writing compute unlock registers (SS0/SS1)", pci_full) ok, msg = apply_unlock(pci_full) if ok: - log.info("[%s] Compute unlock succeeded", pci_full) + log.info("[%s] [STEP] Compute unlock registers written — success", pci_full) else: - log.warning("[%s] Compute unlock: %s", pci_full, msg) + log.warning("[%s] [STEP] Compute unlock registers: %s", pci_full, msg) - log.info("[%s] Restoring original GSP firmware", pci_full) + 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] Reloading driver", pci_full) - load_module() - time.sleep(3) + 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] Pipeline complete — ok=%s", pci_full, ok) + log.info("[%s] [STEP] Pipeline complete — ok=%s", pci_full, ok) return ok @@ -88,6 +167,9 @@ def main() -> None: 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: