#!/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('> 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()