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/decompile_jump_table_handlers.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/decompile_jump_table_handlers.py')
| -rw-r--r-- | firmware/57X-GSP/570.144/decompile_jump_table_handlers.py | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/firmware/57X-GSP/570.144/decompile_jump_table_handlers.py b/firmware/57X-GSP/570.144/decompile_jump_table_handlers.py new file mode 100644 index 0000000..0038729 --- /dev/null +++ b/firmware/57X-GSP/570.144/decompile_jump_table_handlers.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +""" +Decompile all jump table handlers (MAP, UNMAP, PIN, UNPIN, SCHEDULE, YIELD, NOTIFY, BAR_TRAP) +""" + +import struct +import os + +FWIMAGE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gsp-570.144.fwimage.bin") +OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__)) +BASE_ADDR = 0x04000000 + +# Jump table handlers from 0x059bfeb0 +JUMP_TABLE_HANDLERS = [ + (0x05766ab7, "map_handler_a"), + (0x05778a37, "map_handler_b"), + (0x057669b7, "unmap_handler_a"), + (0x05779937, "unmap_handler_b"), + (0x057664b7, "pin_handler_a"), + (0x0577a3b7, "pin_handler_b"), + (0x057682b7, "unpin_handler_a"), + (0x05779fb7, "unpin_handler_b"), + (0x05767f37, "schedule_handler_a"), + (0x0575aeb7, "schedule_handler_b"), + (0x0575ae37, "yield_handler_a"), + (0x05767337, "yield_handler_b"), + (0x0577c8b7, "notify_handler_a"), + (0x05767837, "notify_handler_b"), + (0x057675b7, "bar_trap_handler_jt"), +] + +REG_NAMES = ['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'] + +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, 's': f'c.mv {REG_NAMES[rd+8]}, {REG_NAMES[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, 's': f'c.addi {REG_NAMES[rd+8]}, {imm}'} + if (inst & 0xF07F) == 0x8067: + rs1 = (inst >> 7) & 0x1F + return {'m': 'c.jalr', 'rs1': rs1, 's': f'c.jalr {REG_NAMES[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, 's': f'c.ld {REG_NAMES[rd+8]}, {off*8}({REG_NAMES[rs1+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, 's': f'c.sd {REG_NAMES[rs2+8]}, {off*8}({REG_NAMES[rs1+8]})'} + return {'m': 'c.unk', 'raw': inst, 's': f'c.0x{inst:04x}'} + +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 + + def R(): return REG_NAMES[rd] + def R1(): return REG_NAMES[rs1] + def R2(): return REG_NAMES[rs2] + + if opcode == 0x13 and funct3 == 0: + imm = (inst >> 20) & 0xFFF + if imm >= 0x800: imm -= 0x1000 + return {'m': 'addi', 'rd': rd, 'rs1': rs1, 'imm': imm, 's': f'{R()} = {R1()} + {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, 's': f'{R()} = *(uint64_t*)({R1()} + {off})'} + if opcode == 0x23 and funct3 == 3: + off = ((inst >> 7) & 0x1F) | ((inst >> 25) & 0x7F) << 5 + return {'m': 'sd', 'rs1': rs1, 'rs2': rs2, 'off': off, 's': f'*(uint64_t*)({R1()} + {off}) = {R2()}'} + 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, 's': f'{R()} = PC+4; goto FUNC_{imm+0:08X}'} + if opcode == 0x67 and funct3 == 0: + imm = (inst >> 20) & 0xFFF + if imm >= 0x800: imm -= 0x1000 + if rd == 0 and imm == 0 and rs1 == 1: + return {'m': 'ret', 's': 'return'} + return {'m': 'jalr', 'rd': rd, 'rs1': rs1, 'imm': imm, 's': f'{R()} = PC+4; goto *({R1()} + {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, 's': f'if ({R1()} {btype} {R2()}) goto PC+{imm}'} + if opcode == 0x37: + imm = (inst >> 12) & 0xFFFFF + return {'m': 'lui', 'rd': rd, 'imm': imm, 's': f'{R()} = 0x{imm:05x}000'} + if opcode == 0x17: + imm = (inst >> 12) & 0xFFFFF + return {'m': 'auipc', 'rd': rd, 'imm': imm, 's': f'{R()} = PC + 0x{imm:05x}000'} + if opcode == 0x33: + ops = {0:'add', 0x20:'sub', 1:'sll', 4:'xor', 5:'srl', 0x25:'sra', 6:'or', 7:'and'} + op = ops.get(funct7, f'arith{funct7}') + return {'m': op, 'rd': rd, 'rs1': rs1, 'rs2': rs2, 's': f'{R()} = {R1()} {op} {R2()}'} + if opcode == 0x0F: return {'m': 'fence', 's': 'fence'} + if opcode == 0x73: return {'m': 'ecall', 's': 'ecall/ebreak'} + return {'m': 'unk', 'raw': inst, 's': f'0x{inst:08x}'} + +def trace_function(data, start_addr, max_bytes=3000): + 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') == 'ret': break + return insts + +def analyze_prologue(insts): + stack = None + ra_reg = None + for inst in insts[:20]: + m = inst.get('m') + if m == 'addi' and inst.get('rd') == 2 and inst.get('rs1') == 2 and inst.get('imm', 0) < -16: + stack = -inst['imm'] + if m in ('addi', 'c.mv') and inst.get('rs1') == 1 and inst.get('imm', 0) == 0 and inst.get('rd', 0) >= 9: + ra_reg = inst['rd'] + if m == 'c.mv' and inst.get('rs2') == 1 and inst.get('rd', 0) >= 8: + ra_reg = inst['rd'] + return stack, ra_reg + +def generate_c(insts, name, stack, ra_reg): + lines = [f"// {name} @ 0x{insts[0]['addr']:08x}", f"// Stack: {stack or 'unknown'} bytes, RA saved in {REG_NAMES[ra_reg] if ra_reg else '?'}", ""] + + for inst in insts: + m = inst.get('m') + addr = inst['addr'] + rel = inst['rel'] + lines.append(f"L_{rel:04x}: // 0x{addr:08x}") + + if m == 'addi': + rd, rs1, imm = inst['rd'], inst['rs1'], inst['imm'] + if rd == 2 and rs1 == 2 and imm < 0: + lines.append(f" sp -= {-imm}; // alloc stack frame ({-imm} bytes)") + elif rd == 8 and rs1 == 2: + lines.append(f" s0 = sp + {imm}; // frame pointer") + elif rs1 == 1 and imm == 0 and rd >= 9: + lines.append(f" {REG_NAMES[rd]} = ra; // save return address") + else: + lines.append(f" {REG_NAMES[rd]} = {REG_NAMES[rs1]} + {imm};") + + elif m == 'ld': + lines.append(f" {REG_NAMES[inst['rd']]} = *(uint64_t*)({REG_NAMES[inst['rs1']]} + {inst['off']});") + + elif m == 'sd': + lines.append(f" *(uint64_t*)({REG_NAMES[inst['rs1']]} + {inst['off']}) = {REG_NAMES[inst['rs2']]};") + + elif m == 'jal': + target = inst['addr'] + inst['imm'] + lines.append(f" ra = 0x{inst['addr']+4:08x};") + lines.append(f" goto FUNC_{target:08X}; // call") + + elif m == 'jalr': + if inst.get('rd') == 0 and inst.get('imm') == 0 and inst.get('rs1') == 1: + lines.append(f" return;") + else: + lines.append(f" {REG_NAMES[inst['rd']]} = 0x{inst['addr']+4:08x}; goto *({REG_NAMES[inst['rs1']]} + {inst['imm']});") + + elif m == 'ret': + lines.append(f" return;") + + elif m in ('beq','bne','blt','bge','bltu','bgeu'): + target = inst['addr'] + inst['imm'] + lines.append(f" if ({REG_NAMES[inst['rs1']]} {m} {REG_NAMES[inst['rs2']]}) goto 0x{target:08x};") + + elif m in ('lui','auipc'): + lines.append(f" {REG_NAMES[inst['rd']]} = {m} 0x{inst['imm']:05x}000;") + + elif m in ('add','sub','sll','xor','srl','sra','or','and'): + lines.append(f" {REG_NAMES[inst['rd']]} = {REG_NAMES[inst['rs1']]} {m} {REG_NAMES[inst['rs2']]};") + + elif m.startswith('c.'): + lines.append(f" {inst['s']};") + + else: + lines.append(f" {inst['s']};") + + return '\n'.join(lines) + +def main(): + data = read_binary(FWIMAGE_PATH) + + for addr, name in JUMP_TABLE_HANDLERS: + print(f"Decompiling {name} @ 0x{addr:08x}...") + insts = trace_function(data, addr, 3000) + stack, ra_reg = analyze_prologue(insts) + c_code = generate_c(insts, name, stack, ra_reg) + + out_file = f"{OUTPUT_DIR}/decompiled_{name}.c" + with open(out_file, 'w') as f: + f.write(c_code) + print(f" -> {out_file} ({len(insts)} insts, stack={stack}, ra_reg={REG_NAMES[ra_reg] if ra_reg else '?'})") + +if __name__ == "__main__": + main() |
