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_dispatchers.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_dispatchers.py')
| -rw-r--r-- | firmware/57X-GSP/570.144/trace_dispatchers.py | 162 |
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() |
