ago

ago / check_gpc.py

Last active 1 month ago

Like 0

ago revised this gist 1 month ago · f49b12e

1 file changed

check_gpc.py renamed to ga100_check_gpc.py

File renamed without changes

ago revised this gist 1 month ago · 9a02566

1 file changed, 2 insertions, 1 deletion

check_gpc.py
@@ -3,6 +3,7 @@
3 3
4 4 Never writes to BAR0. Requires root to mmap /sys/bus/pci/devices/<bdf>/resource0.
5 5 """
6 +
6 7 import mmap
7 8 import os
8 9 import struct
@@ -10,7 +11,7 @@ import subprocess
10 11 import sys
11 12
12 13 REG_GPC_DISABLE = 0x00820350
13 - REG_GPC_DEFECTIVE = 0x008205c4
14 + REG_GPC_DEFECTIVE = 0x008205C4
14 15 PAGE = 0x1000
15 16
16 17

ago revised this gist 1 month ago · 352cd5d

1 file changed, 86 insertions

check_gpc.py (file created)
@@ -0,0 +1,86 @@
1 + #!/usr/bin/env python3
2 + """Read-only BAR0 probe for GA100 GPC floorsweep fuses (OPT_GPC_DISABLE / OPT_GPC_DEFECTIVE).
3 +
4 + Never writes to BAR0. Requires root to mmap /sys/bus/pci/devices/<bdf>/resource0.
5 + """
6 + import mmap
7 + import os
8 + import struct
9 + import subprocess
10 + import sys
11 +
12 + REG_GPC_DISABLE = 0x00820350
13 + REG_GPC_DEFECTIVE = 0x008205c4
14 + PAGE = 0x1000
15 +
16 +
17 + def find_ga100_cards(pattern="GA100"):
18 + """Return [(bdf, label)] for PCI devices whose lspci name matches pattern."""
19 + try:
20 + out = subprocess.check_output(["lspci", "-D"], text=True)
21 + except FileNotFoundError:
22 + sys.exit("lspci not found; install pciutils or pass BDFs another way")
23 + except subprocess.CalledProcessError as e:
24 + sys.exit(f"lspci failed: {e}")
25 + cards = []
26 + for line in out.splitlines():
27 + if pattern in line:
28 + bdf = line.split(None, 1)[0]
29 + cards.append((bdf, bdf.split(":")[1]))
30 + return cards
31 +
32 +
33 + def read_reg(bdf, addr):
34 + path = f"/sys/bus/pci/devices/{bdf}/resource0"
35 + if not os.path.exists(path):
36 + raise OSError(f"no BAR0 (resource0 missing, device has no MMIO?)")
37 + if os.path.getsize(path) <= addr:
38 + raise OSError(f"BAR0 too small for offset 0x{addr:x}")
39 + fd = os.open(path, os.O_RDONLY | os.O_SYNC)
40 + try:
41 + base = addr - (addr % PAGE)
42 + off = addr - base
43 + m = mmap.mmap(fd, PAGE, mmap.MAP_SHARED, mmap.PROT_READ, offset=base)
44 + try:
45 + return struct.unpack_from("<I", m, off)[0]
46 + finally:
47 + m.close()
48 + finally:
49 + os.close(fd)
50 +
51 +
52 + def bits_set(mask, width=8):
53 + return [i for i in range(width) if mask & (1 << i)]
54 +
55 +
56 + def main():
57 + if os.geteuid() != 0:
58 + sys.exit("must run as root (mmaps BAR0 via /sys/.../resource0)")
59 +
60 + cards = find_ga100_cards()
61 + if not cards:
62 + sys.exit("no GA100 cards found (lspci name match)")
63 + print(f"Detected GA100 card(s): {', '.join(b for b, _ in cards)}\n")
64 +
65 + print(f"{'Card':<6} {'Disabled clusters':<20} {'Marked defective':<18} {'Disabled but not defective'}")
66 + for bdf, label in cards:
67 + try:
68 + disable = read_reg(bdf, REG_GPC_DISABLE)
69 + defective = read_reg(bdf, REG_GPC_DEFECTIVE)
70 + except OSError as e:
71 + print(f"{label:<6} ERROR reading {bdf}: {e}")
72 + continue
73 +
74 + disabled_bits = set(bits_set(disable))
75 + defective_bits = set(bits_set(defective))
76 + not_defective_bits = disabled_bits - defective_bits
77 +
78 + def fmt(bits):
79 + return ", ".join(str(b) for b in sorted(bits)) if bits else "none"
80 +
81 + print(f"{label:<6} {fmt(disabled_bits):<20} {fmt(defective_bits):<18} {fmt(not_defective_bits)}")
82 + print(f" (raw: OPT_GPC_DISABLE=0x{disable:08x} OPT_GPC_DEFECTIVE=0x{defective:08x})")
83 +
84 +
85 + if __name__ == "__main__":
86 + main()