From 68cdd7728a7cc6715bbbff7decfe2b11c3bdf558 Mon Sep 17 00:00:00 2001 From: Open-IOV Date: Wed, 23 Sep 2026 05:44:08 -0400 Subject: 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. --- firmware/57X-GSP/570.144/trace_all_subsystems.py | 279 +++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 firmware/57X-GSP/570.144/trace_all_subsystems.py (limited to 'firmware/57X-GSP/570.144/trace_all_subsystems.py') 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('> 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('> 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('> 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(' 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('> 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('> 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('> 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('> 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)") + -- cgit v1.2.3