diff options
| author | Open-IOV <dev@open-iov.org> | 2026-09-23 05:44:08 -0400 |
|---|---|---|
| committer | Open-IOV <dev@open-iov.org> | 2026-09-23 05:46:43 -0400 |
| commit | 68cdd7728a7cc6715bbbff7decfe2b11c3bdf558 (patch) | |
| tree | 3dc8ce2e6af313a97ec6852f5c663a9a93665d2e /firmware/57X-GSP/570.144/trace_all_subsystems.py | |
| download | open-iov-68cdd7728a7cc6715bbbff7decfe2b11c3bdf558.tar.gz open-iov-68cdd7728a7cc6715bbbff7decfe2b11c3bdf558.zip | |
Open-IOV: initial public release
Open-IOV virtualizes NVIDIA GPUs so that unmodified guest drivers run with
real GPU functionality - CUDA first, 3D and video to follow - on unmodified
hosts, without a proprietary vGPU license and without patching any driver,
firmware or hypervisor.
Core principle: emulate the interface, pass through the work.
kernel/ open-iov-vfio.ko - host VFIO mdev driver: virtual GPU device
(PCI config space, BAR shadows, MSI-X), GSP boot emulation,
GSP-RM message-queue/RPC emulation, memory model (GPU-VA to
guest-PA discovery) and the semaphore-completion service that
advances guest UVM progress.
userspace/ libiov.so management API and examples.
tools/ guest diagnostics, firmware analysis wrappers, RM probes.
scripts/ stack manager, VM harness, build/load/test automation.
config/ hardware/version profiles (the portability surface).
docs/ documentation tree - start at docs/README.md.
wiki/ long-form firmware and protocol deep dives.
Status: initialization works end to end for a stock 570.144 Linux guest on
Turing (TU104): driver probe, nvidia-smi, the full GSP-RM control battery,
object creation and the early CUDA/UVM start-up path. GPU work forwarding -
the step that makes CUDA kernels execute on real hardware - is the active
work item; see docs/04-implementation/01-known-knowns-and-unknowns.md.
License: BSD-3-Clause for the project; GPL-2.0 for kernel modules that link
GPL-only kernel interfaces. See LICENSE and docs/09-reference/legal-and-licensing.md.
Diffstat (limited to 'firmware/57X-GSP/570.144/trace_all_subsystems.py')
| -rw-r--r-- | firmware/57X-GSP/570.144/trace_all_subsystems.py | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/firmware/57X-GSP/570.144/trace_all_subsystems.py b/firmware/57X-GSP/570.144/trace_all_subsystems.py new file mode 100644 index 0000000..e004791 --- /dev/null +++ b/firmware/57X-GSP/570.144/trace_all_subsystems.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python3 +""" +Trace all subsystems: WPR, Migration, Primary Dispatcher sub-commands +""" + +import struct +import os + +FWIMAGE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gsp-570.144.fwimage.bin") +BASE_ADDR = 0x04000000 + +def read_binary(path): + with open(path, 'rb') as f: + return f.read() + +def decode_inst(data, offset): + if offset + 4 > len(data): return None, 0 + first16 = struct.unpack('<H', data[offset:offset+2])[0] + if (first16 & 0x3) != 0x3: + return decode_compressed(first16), 2 + inst = struct.unpack('<I', data[offset:offset+4])[0] + return decode_32bit(inst), 4 + +def decode_compressed(inst): + if (inst & 0xF003) == 0x8002: + rd = (inst >> 7) & 0x7; rs2 = (inst >> 10) & 0x7 + return {'m': 'c.mv', 'rd': rd+8, 'rs2': rs2+8} + if (inst & 0xE003) == 0x6001: + rd = (inst >> 7) & 0x7 + imm = ((inst >> 2) & 0x3F) | ((inst >> 12) & 0x1) << 6 + if imm & 0x40: imm -= 0x80 + return {'m': 'c.addi', 'rd': rd+8, 'imm': imm} + if (inst & 0xF07F) == 0x8067: + rs1 = (inst >> 7) & 0x1F + return {'m': 'c.jalr', 'rs1': rs1} + if (inst & 0xE003) == 0x6003: + rd = (inst >> 10) & 0x7; rs1 = (inst >> 7) & 0x7 + off = ((inst >> 2) & 0x1F) | ((inst >> 13) & 0x3) << 5 + return {'m': 'c.ld', 'rd': rd+8, 'rs1': rs1+8, 'off': off*8} + if (inst & 0xE003) == 0xE000: + rs1 = (inst >> 7) & 0x7; rs2 = (inst >> 10) & 0x7 + off = ((inst >> 2) & 0x1F) | ((inst >> 13) & 0x3) << 5 + return {'m': 'c.sd', 'rs1': rs1+8, 'rs2': rs2+8, 'off': off*8} + return {'m': 'c.unk', 'raw': inst} + +def decode_32bit(inst): + opcode = inst & 0x7F + funct3 = (inst >> 12) & 0x7 + rd = (inst >> 7) & 0x1F + rs1 = (inst >> 15) & 0x1F + rs2 = (inst >> 20) & 0x1F + funct7 = (inst >> 25) & 0x7F + if opcode == 0x13 and funct3 == 0: + imm = (inst >> 20) & 0xFFF + if imm >= 0x800: imm -= 0x1000 + return {'m': 'addi', 'rd': rd, 'rs1': rs1, 'imm': imm} + if opcode == 0x03 and funct3 == 3: + off = (inst >> 20) & 0xFFF + if off >= 0x800: off -= 0x1000 + return {'m': 'ld', 'rd': rd, 'rs1': rs1, 'off': off} + if opcode == 0x23 and funct3 == 3: + off = ((inst >> 7) & 0x1F) | ((inst >> 25) & 0x7F) << 5 + return {'m': 'sd', 'rs1': rs1, 'rs2': rs2, 'off': off} + if opcode == 0x6F: + imm = ((inst >> 31) & 0x1) << 20 | ((inst >> 12) & 0xFF) << 12 | ((inst >> 20) & 0x1) << 11 | ((inst >> 21) & 0x3FF) << 1 + if imm & 0x100000: imm -= 0x200000 + return {'m': 'jal', 'rd': rd, 'imm': imm} + if opcode == 0x67 and funct3 == 0: + imm = (inst >> 20) & 0xFFF + if imm >= 0x800: imm -= 0x1000 + return {'m': 'jalr', 'rd': rd, 'rs1': rs1, 'imm': imm} + if opcode == 0x63: + imm = ((inst >> 31) & 0x1) << 12 | ((inst >> 7) & 0x1) << 11 | ((inst >> 25) & 0x3F) << 5 | ((inst >> 8) & 0xF) << 1 + if imm & 0x1000: imm -= 0x2000 + btype = ['beq','bne','?','?','blt','bge','bltu','bgeu'][funct3] + return {'m': btype, 'rs1': rs1, 'rs2': rs2, 'imm': imm} + if opcode == 0x37: + imm = (inst >> 12) & 0xFFFFF + return {'m': 'lui', 'rd': rd, 'imm': imm} + if opcode == 0x17: + imm = (inst >> 12) & 0xFFFFF + return {'m': 'auipc', 'rd': rd, 'imm': imm} + if opcode == 0x33: + ops = {0:'add', 0x20:'sub', 1:'sll', 4:'xor', 5:'srl', 0x25:'sra', 6:'or', 7:'and'} + return {'m': ops.get(funct7, f'arith{funct7}'), 'rd': rd, 'rs1': rs1, 'rs2': rs2} + return {'m': 'unk', 'raw': inst} + +def trace_function(data, start_addr, max_bytes=2000): + offset = start_addr - BASE_ADDR + insts = [] + i = 0 + while i < max_bytes: + idx = offset + i + if idx >= len(data): break + inst, size = decode_inst(data, idx) + if inst is None: i += 2; continue + inst['addr'] = BASE_ADDR + idx + inst['rel'] = i + insts.append(inst) + i += size + if inst.get('m') in ('ret', 'jalr') and inst.get('rd') == 0 and inst.get('imm', 0) == 0 and inst.get('rs1', 1) == 1: + break + return insts + +def find_string_refs(data, strings): + """Find all references to given strings in the binary""" + results = {} + for s in strings: + s_bytes = s.encode('ascii') + results[s] = [] + # Search for string in data + for i in range(len(data) - len(s_bytes)): + if data[i:i+len(s_bytes)] == s_bytes: + results[s].append(BASE_ADDR + i) + return results + +def find_string_refs_via_loads(data, string_addrs): + """Find code that loads addresses of strings""" + results = {} + for s, addrs in string_addrs.items(): + results[s] = [] + for str_addr in addrs: + # Look for auipc/addi pairs that compute this address + for i in range(0, len(data) - 4, 2): + if i + 4 <= len(data): + inst = struct.unpack('<I', data[i:i+4])[0] + opcode = inst & 0x7F + if opcode == 0x17: # auipc + rd = (inst >> 7) & 0x1F + imm = (inst >> 12) & 0xFFFFF + pc = BASE_ADDR + i + base = (pc + (imm << 12)) & ~0xFFF + # Check next instruction for addi + if i + 8 <= len(data): + inst2 = struct.unpack('<I', data[i+4:i+8])[0] + if (inst2 & 0x7F) == 0x13 and ((inst2 >> 12) & 0x7) == 0: + rd2 = (inst2 >> 7) & 0x1F + rs1 = (inst2 >> 15) & 0x1F + imm2 = (inst2 >> 20) & 0xFFF + if imm2 >= 0x800: imm2 -= 0x1000 + if rd == rd2 and rs1 == rd: + computed = base + imm2 + if computed == str_addr: + results[s].append(BASE_ADDR + i) + return results + +data = read_binary(FWIMAGE_PATH) + +# ============================================================ +# 1. TRACE WPR SUBSYSTEM +# ============================================================ +print("=" * 60) +print("1. WPR SUBSYSTEM - String References") +print("=" * 60) + +wpr_strings = [ + "WPR_WRITE_COMPLETE", "WPR_READ_COMPLETE", + "WPR_WRITE_TIMEOUT", "WPR_READ_TIMEOUT", + "NV_WPR_WRITE_COMPLETE", "NV_WPR_READ_COMPLETE", + "NV_WPR_WRITE_TIMEOUT", "NV_WPR_READ_TIMEOUT" +] + +wpr_locs = find_string_refs(data, wpr_strings) +for s, addrs in wpr_locs.items(): + if addrs: + print(f"\n{s}:") + for addr in addrs: + print(f" String at 0x{addr:08x}") + +# Find code referencing these strings +wpr_refs = find_string_refs_via_loads(data, wpr_locs) +for s, addrs in wpr_refs.items(): + if addrs: + print(f"\nCode referencing {s}:") + for addr in addrs[:5]: + print(f" 0x{addr:08x}") + +# ============================================================ +# 2. TRACE BAR SUBSYSTEM +# ============================================================ +print("\n" + "=" * 60) +print("2. BAR SUBSYSTEM - String References") +print("=" * 60) + +bar_strings = ["BAR1", "Bar1Rest", "ForceBarAccess", "Bar1Mapping", "Bar1Aperture", "Bar1Size"] +bar_locs = find_string_refs(data, bar_strings) +for s, addrs in bar_locs.items(): + if addrs: + print(f"\n{s}:") + for addr in addrs: + print(f" String at 0x{addr:08x}") + +bar_refs = find_string_refs_via_loads(data, bar_locs) +for s, addrs in bar_refs.items(): + if addrs: + print(f"\nCode referencing {s}:") + for addr in addrs[:5]: + print(f" 0x{addr:08x}") + +# ============================================================ +# 3. DECODE PRIMARY DISPATCHER SUB-COMMANDS (Jump Table at 0x057a3ad8) +# ============================================================ +print("\n" + "=" * 60) +print("3. PRIMARY DISPATCHER SUB-COMMANDS (0x057a3ad8)") +print("=" * 60) + +def decode_jal_target(inst, pc): + imm = ((inst >> 31) & 0x1) << 20 | ((inst >> 12) & 0xFF) << 12 | ((inst >> 20) & 0x1) << 11 | ((inst >> 21) & 0x3FF) << 1 + if imm & 0x100000: imm -= 0x200000 + return pc + imm + +jt_base = 0x057a3ad8 +for i in range(32): + entry_addr = jt_base + i * 8 + offset = entry_addr - BASE_ADDR + if offset + 8 <= len(data): + addi_inst = struct.unpack('<I', data[offset:offset+4])[0] + jal_inst = struct.unpack('<I', data[offset+4:offset+8])[0] + target = decode_jal_target(jal_inst, entry_addr + 8) + print(f" Subcmd {i:2d}: 0x{entry_addr:08x} -> jal 0x{target:08x}") + +# ============================================================ +# 4. TRACE MIGRATION (RPC 0x0B) IN DISPATCHERS +# ============================================================ +print("\n" + "=" * 60) +print("4. MIGRATION (RPC 0x0B) HANDLERS") +print("=" * 60) + +# Primary dispatcher - look for RPC 0x0B branches +print("\nPrimary Dispatcher (0x057a1ac0) MIGRATE branches:") +offset = 0x57a1ac0 - BASE_ADDR +for i in range(0, 3000, 2): + idx = offset + i + if idx + 4 <= len(data): + inst = struct.unpack('<I', data[idx:idx+4])[0] + opcode = inst & 0x7F + if opcode == 0x63: # branch + funct3 = (inst >> 12) & 0x7 + rs1 = (inst >> 15) & 0x1F + rs2 = (inst >> 20) & 0x1F + imm = ((inst >> 31) & 0x1) << 12 | ((inst >> 7) & 0x1) << 11 | ((inst >> 25) & 0x3F) << 5 | ((inst >> 8) & 0xF) << 1 + if imm & 0x1000: imm -= 0x2000 + target = BASE_ADDR + idx + imm + # Check for nearby addi with imm=11 (MIGRATE) + for back in range(2, 20, 2): + if idx - back >= 0: + prev = struct.unpack('<I', data[idx-back:idx-back+4])[0] + if (prev & 0x7F) == 0x13 and ((prev >> 12) & 0x7) == 0: + prev_imm = (prev >> 20) & 0xFFF + if prev_imm >= 0x800: prev_imm -= 0x1000 + if prev_imm == 11: + btype = ['beq','bne','?','?','blt','bge','bltu','bgeu'][funct3] + print(f" 0x{BASE_ADDR+idx:08x}: {btype} x{rs1}, x{rs2}, 0x{target:08x} (RPC=0x0B)") + +# Secondary dispatcher +print("\nSecondary Dispatcher (0x04ed39dc) MIGRATE branches:") +offset = 0xed39dc +for i in range(0, 5000, 2): + idx = offset + i + if idx + 4 <= len(data): + inst = struct.unpack('<I', data[idx:idx+4])[0] + opcode = inst & 0x7F + if opcode == 0x63: + funct3 = (inst >> 12) & 0x7 + rs1 = (inst >> 15) & 0x1F + rs2 = (inst >> 20) & 0x1F + imm = ((inst >> 31) & 0x1) << 12 | ((inst >> 7) & 0x1) << 11 | ((inst >> 25) & 0x3F) << 5 | ((inst >> 8) & 0xF) << 1 + if imm & 0x1000: imm -= 0x2000 + target = BASE_ADDR + idx + imm + for back in range(2, 20, 2): + if idx - back >= 0: + prev = struct.unpack('<I', data[idx-back:idx-back+4])[0] + if (prev & 0x7F) == 0x13 and ((prev >> 12) & 0x7) == 0: + prev_imm = (prev >> 20) & 0xFFF + if prev_imm >= 0x800: prev_imm -= 0x1000 + if prev_imm == 11: + btype = ['beq','bne','?','?','blt','bge','bltu','bgeu'][funct3] + print(f" 0x{BASE_ADDR+idx:08x}: {btype} x{rs1}, x{rs2}, 0x{target:08x} (RPC=0x0B)") + |
