File renamed without changes
ago / check_gpc.py
Last active 1 month ago
| @@ -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 | ||
| @@ -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() | |