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:
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user