summaryrefslogtreecommitdiffstats
path: root/firmware/57X-GSP/570.144/trace_dispatchers.py
diff options
context:
space:
mode:
authorOpen-IOV <dev@open-iov.org>2026-09-23 05:44:08 -0400
committerOpen-IOV <dev@open-iov.org>2026-09-23 05:46:43 -0400
commit68cdd7728a7cc6715bbbff7decfe2b11c3bdf558 (patch)
tree3dc8ce2e6af313a97ec6852f5c663a9a93665d2e /firmware/57X-GSP/570.144/trace_dispatchers.py
downloadopen-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_dispatchers.py')
-rw-r--r--firmware/57X-GSP/570.144/trace_dispatchers.py162
1 files changed, 162 insertions, 0 deletions
diff --git a/firmware/57X-GSP/570.144/trace_dispatchers.py b/firmware/57X-GSP/570.144/trace_dispatchers.py
new file mode 100644
index 0000000..c5a066d
--- /dev/null
+++ b/firmware/57X-GSP/570.144/trace_dispatchers.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python3
+"""
+Trace dispatcher switch cases to find specific RPC handler functions.
+"""
+
+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_instruction(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
+ if offset + 4 <= len(data):
+ inst = struct.unpack('<I', data[offset:offset+4])[0]
+ return decode_32bit(inst), 4
+ return None, 0
+
+def decode_compressed(inst):
+ if (inst & 0xF003) == 0x8002:
+ rd = (inst >> 7) & 0x7; rs2 = (inst >> 10) & 0x7
+ return {'mnemonic': '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 {'mnemonic': 'c.addi', 'rd': rd+8, 'imm': imm}
+ if (inst & 0xF07F) == 0x8067:
+ rs1 = (inst >> 7) & 0x1F
+ return {'mnemonic': 'c.jalr', 'rs1': rs1}
+ return {'mnemonic': 'c.unknown', '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
+ reg = lambda r: ['x0','ra','sp','gp','tp','t0','t1','t2','s0','s1','a0','a1','a2','a3','a4','a5','a6','a7','s2','s3','s4','s5','s6','s7','s8','s9','s10','s11','t3','t4','t5','t6'][r] if r < 32 else f'x{r}'
+
+ if opcode == 0x63: # branch
+ 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 + offset + imm if 'offset' in locals() else 0
+ btype = ['beq','bne','?','?','blt','bge','bltu','bgeu'][funct3]
+ return {'mnemonic': btype, 'rs1': rs1, 'rs2': rs2, 'imm': imm, 'type': 'branch'}
+ if opcode == 0x6F: # jal
+ imm = ((inst >> 31) & 0x1) << 20 | ((inst >> 12) & 0xFF) << 12 | ((inst >> 20) & 0x1) << 11 | ((inst >> 21) & 0x3FF) << 1
+ if imm & 0x100000: imm -= 0x200000
+ return {'mnemonic': 'jal', 'rd': rd, 'imm': imm, 'type': 'call'}
+ if opcode == 0x67 and funct3 == 0: # jalr
+ imm = (inst >> 20) & 0xFFF
+ if imm >= 0x800: imm -= 0x1000
+ return {'mnemonic': 'jalr', 'rd': rd, 'rs1': rs1, 'imm': imm, 'type': 'call_indirect'}
+ if opcode == 0x13 and funct3 == 0: # addi
+ imm = (inst >> 20) & 0xFFF
+ if imm >= 0x800: imm -= 0x1000
+ return {'mnemonic': 'addi', 'rd': rd, 'rs1': rs1, 'imm': imm}
+ return {'mnemonic': 'other', 'raw': inst}
+
+def trace_dispatcher(data, start_addr, max_bytes=3000):
+ """Trace a dispatcher function and extract switch case targets."""
+ offset = start_addr - BASE_ADDR
+ cases = [] # (rpc_type, target_addr)
+ current_rpc = None
+
+ i = 0
+ while i < max_bytes:
+ idx = offset + i
+ if idx >= len(data): break
+ inst, size = decode_instruction(data, idx)
+ if inst is None: i += 2; continue
+
+ inst['offset'] = BASE_ADDR + idx
+ mnemonic = inst.get('mnemonic', '')
+
+ # Track RPC type being compared (in register)
+ if mnemonic == 'addi' and inst.get('rd') != 0 and inst.get('imm') in [1,2,3,4,5,6,7,8,9,10,11,0xFF]:
+ current_rpc = inst['imm']
+
+ # Branch instructions - check if comparing with RPC type
+ if mnemonic in ('beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu'):
+ target = BASE_ADDR + idx + inst.get('imm', 0)
+ if current_rpc is not None:
+ cases.append({
+ 'rpc_type': current_rpc,
+ 'branch_type': mnemonic,
+ 'branch_addr': BASE_ADDR + idx,
+ 'target': target,
+ 'taken': mnemonic == 'beq' # beq = equal = match
+ })
+
+ # Indirect calls (jalr) - likely handler calls
+ if mnemonic == 'jalr' and inst.get('rd') == 1: # ra = handler
+ # The rs1 register holds the handler address
+ pass
+
+ i += size
+
+ return cases
+
+def find_jalr_targets(data, start_addr, max_bytes=3000):
+ """Find jalr instructions that call handlers."""
+ offset = start_addr - BASE_ADDR
+ targets = []
+
+ i = 0
+ while i < max_bytes:
+ idx = offset + i
+ if idx >= len(data): break
+ inst, size = decode_instruction(data, idx)
+ if inst is None: i += 2; continue
+
+ if inst.get('mnemonic') == 'jalr':
+ # Try to find where rs1 gets its value (look backwards)
+ targets.append({
+ 'addr': BASE_ADDR + idx,
+ 'rs1': inst.get('rs1'),
+ 'rd': inst.get('rd'),
+ })
+ i += size
+
+ return targets
+
+def main():
+ data = read_binary(FWIMAGE_PATH)
+
+ # Trace primary dispatcher at 0x057a1ac0
+ print("=== Tracing Primary Dispatcher (0x057a1ac0) ===")
+ cases = trace_dispatcher(data, 0x057a1ac0, 3000)
+ for c in cases:
+ rpc_names = {1:'ALLOC',2:'FREE',3:'MAP',4:'UNMAP',5:'PIN',6:'UNPIN',7:'SCHEDULE',8:'YIELD',9:'NOTIFY',10:'BAR_TRAP',11:'MIGRATE',0xFF:'DEBUG'}
+ print(f" RPC {rpc_names.get(c['rpc_type'], c['rpc_type'])} (0x{c['rpc_type']:02x}): {c['branch_type']} at 0x{c['branch_addr']:08x} -> 0x{c['target']:08x} (taken={c['taken']})")
+
+ print("\n=== Finding jalr targets in Primary Dispatcher ===")
+ targets = find_jalr_targets(data, 0x057a1ac0, 3000)
+ for t in targets:
+ print(f" jalr at 0x{t['addr']:08x}: rs1=x{t['rs1']}, rd=x{t['rd']}")
+
+ # Trace secondary dispatcher at 0x04ed39dc
+ print("\n=== Tracing Secondary Dispatcher (0x04ed39dc) ===")
+ cases = trace_dispatcher(data, 0x04ed39dc, 5000)
+ for c in cases[:30]:
+ rpc_names = {1:'ALLOC',2:'FREE',3:'MAP',4:'UNMAP',5:'PIN',6:'UNPIN',7:'SCHEDULE',8:'YIELD',9:'NOTIFY',10:'BAR_TRAP',11:'MIGRATE',0xFF:'DEBUG'}
+ print(f" RPC {rpc_names.get(c['rpc_type'], c['rpc_type'])} (0x{c['rpc_type']:02x}): {c['branch_type']} at 0x{c['branch_addr']:08x} -> 0x{c['target']:08x} (taken={c['taken']})")
+
+ # Trace tertiary dispatcher at 0x04cc58f8
+ print("\n=== Tracing Tertiary Dispatcher (0x04cc58f8) ===")
+ cases = trace_dispatcher(data, 0x04cc58f8, 3000)
+ for c in cases[:30]:
+ rpc_names = {1:'ALLOC',2:'FREE',3:'MAP',4:'UNMAP',5:'PIN',6:'UNPIN',7:'SCHEDULE',8:'YIELD',9:'NOTIFY',10:'BAR_TRAP',11:'MIGRATE',0xFF:'DEBUG'}
+ print(f" RPC {rpc_names.get(c['rpc_type'], c['rpc_type'])} (0x{c['rpc_type']:02x}): {c['branch_type']} at 0x{c['branch_addr']:08x} -> 0x{c['target']:08x} (taken={c['taken']})")
+
+if __name__ == "__main__":
+ main()