VBIOS backup: precise failure diagnosis plus opt-in unload retry

save_bios now distinguishes nvflash driver-loaded refusal from a
missing sysfs rom node (ROM BAR not exposed) vs enable denial,
and appends host lockdown/Secure Boot state to the note. New
opt-in last resort: unload NVIDIA modules, retry nvflash --save,
always reload (y/N prompt default n, or --bios-unload). Docs
(flow, flags, safety, AGENTS.md invariant) updated to match.
This commit is contained in:
2026-10-15 00:30:02 +02:00
parent bfadeafb72
commit 294fb4a3c4
4 changed files with 115 additions and 18 deletions
+105 -14
View File
@@ -23,6 +23,7 @@ REPOS = {
VERIFY_SH = HARNESS_DIR / "cmpunlocker" / "verify.sh"
INSTALL_SH = HARNESS_DIR / "cmpunlocker" / "install.sh"
GPU_IDS = ("20c2", "2082")
NVIDIA_MODULES = ("nvidia_uvm", "nvidia_drm", "nvidia_modeset", "nvidia")
VULKAN_TEST_SECONDS = 300
SPEED_TO_GEN = {"2.5GT/s": 1, "5GT/s": 2, "8GT/s": 3, "16GT/s": 4, "32GT/s": 5, "64GT/s": 6}
@@ -642,25 +643,25 @@ def save_bios(nf, out_path, args):
if getattr(args, "skip_bios", False):
result["note"] = "bios backup skipped (--skip-bios)"
return result
notes = []
if nf:
r = as_root([nf, "--save", str(out_path)], timeout=90)
if r["rc"] == 0 and out_path.exists() and out_path.stat().st_size > 0:
ok_save, note = try_nvflash_save(nf, out_path)
if ok_save:
data = out_path.read_bytes()
if data.strip(b"\x00") == b"":
result["note"] = "nvflash wrote an all-zero image"
else:
result.update(method="nvflash", path=str(out_path), size=len(data),
sha256=hashlib.sha256(data).hexdigest(), ok=True)
return result
else:
result["note"] = f"nvflash --save failed rc={r['rc']}: {(r['err'] or r['out'])[-200:]}"
result.update(method="nvflash", path=str(out_path), size=len(data),
sha256=hashlib.sha256(data).hexdigest(), ok=True)
return result
notes.append(note)
else:
result["note"] = "nvflash unavailable"
notes.append("nvflash unavailable")
for bdf in detect_gpus():
rom = sysfs(bdf) / "rom"
if not rom.exists():
notes.append(f"no sysfs rom node for {bdf} (ROM BAR not exposed - fallback unavailable)")
continue
if as_root(["sh", "-c", f"echo 1 > {rom}"], timeout=15)["rc"] != 0:
result["note"] += " | sysfs rom enable failed"
notes.append(f"sysfs rom enable denied for {bdf}")
continue
data = read_file(rom, binary=True)
as_root(["sh", "-c", f"echo 0 > {rom}"], timeout=15)
@@ -668,12 +669,102 @@ def save_bios(nf, out_path, args):
out_path.write_bytes(data)
result.update(method="sysfs-rom", path=str(out_path), size=len(data),
sha256=hashlib.sha256(data).hexdigest(), ok=True)
result["note"] = (result["note"] + " | saved via sysfs rom").strip(" |")
return result
result["note"] += " | sysfs rom empty/all-zero"
notes.append(f"sysfs rom empty/all-zero for {bdf}")
if any("no sysfs rom node" in n for n in notes):
notes.append(f"host lockdown={lockdown_state()} secureboot={secure_boot_state()}")
result["note"] = " | ".join(notes)
# Opt-in last resort: unload the driver, retry nvflash --save, always reload.
if nf and any("driver loaded" in n for n in notes):
if getattr(args, "bios_unload", False) or (
sys.stdin.isatty()
and ask("Unload NVIDIA driver and retry nvflash --save now? (y/N)",
"n").lower().startswith("y")):
result["note"] += " | " + retry_save_unloaded(nf, out_path, result)
else:
result["note"] += (" | skipped driver-unload retry (use --bios-unload; "
"local console may freeze while the driver is out)")
return result
def try_nvflash_save(nf, out_path):
"""One nvflash --save attempt. Returns (ok, note)."""
try:
if out_path.exists():
out_path.unlink()
except OSError:
pass
r = as_root([nf, "--save", str(out_path)], timeout=90)
text = r["out"] + r["err"]
if r["rc"] == 0 and out_path.exists() and out_path.stat().st_size > 0:
data = out_path.read_bytes()
if data.strip(b"\x00") == b"":
return False, "nvflash wrote an all-zero image"
return True, ""
if "unload" in text.lower() and "kernel driver" in text.lower():
return False, (f"nvflash --save refused: NVIDIA driver loaded (rc={r['rc']}) - "
"unload it first or use --bios-unload")
return False, f"nvflash --save failed rc={r['rc']}: {text[-200:].strip()}"
def retry_save_unloaded(nf, out_path, result):
"""Unload NVIDIA modules, retry nvflash --save, always reload. Returns note."""
warn("unloading NVIDIA driver - local display may freeze until reload")
_, unload_log = unload_nvidia_modules()
try:
ok_save, note = try_nvflash_save(nf, out_path)
if ok_save:
data = out_path.read_bytes()
result.update(method="nvflash-unload", path=str(out_path), size=len(data),
sha256=hashlib.sha256(data).hexdigest(), ok=True)
return "saved via nvflash after driver unload"
return f"nvflash --save still failed after unload: {note}"
finally:
ok_reload, reload_log = load_nvidia_modules()
for line in unload_log + reload_log:
info(line)
if ok_reload and which("nvidia-smi"):
ok("NVIDIA driver reloaded")
else:
err("NVIDIA driver reload incomplete - reboot the host")
def unload_nvidia_modules():
"""Best-effort rmmod of the NVIDIA stack. Returns (all_ok, log_lines)."""
log, all_ok = [], True
for mod in NVIDIA_MODULES:
r = as_root(["rmmod", mod], timeout=60)
log.append(f"rmmod {mod}: rc={r['rc']} {(r['err'] or r['out']).strip()[:160]}")
if r["rc"] != 0:
all_ok = False
return all_ok, log
def load_nvidia_modules():
"""Reload the NVIDIA stack in reverse order. Returns (all_ok, log_lines)."""
log, all_ok = [], True
for mod in reversed(NVIDIA_MODULES):
r = as_root(["modprobe", mod], timeout=120)
log.append(f"modprobe {mod}: rc={r['rc']} {(r['err'] or r['out']).strip()[:160]}")
if r["rc"] != 0:
all_ok = False
return all_ok, log
def lockdown_state():
data = read_file("/sys/kernel/security/lockdown")
return data.strip() if data else "lockdown file unreadable"
def secure_boot_state():
if not which("mokutil"):
return "mokutil not installed"
r = run(["mokutil", "--sb-state"], timeout=15)
lines = (r["out"] or r["err"]).strip().splitlines()
return lines[0].strip() if lines else "mokutil gave no output"
# ---------------------------------------------------------------- nvidia / verify
def nvidia_smi_info():