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