summaryrefslogtreecommitdiffstats
path: root/firmware/57X-GSP/570.144/decompile_functions.py
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/57X-GSP/570.144/decompile_functions.py')
-rw-r--r--firmware/57X-GSP/570.144/decompile_functions.py311
1 files changed, 311 insertions, 0 deletions
diff --git a/firmware/57X-GSP/570.144/decompile_functions.py b/firmware/57X-GSP/570.144/decompile_functions.py
new file mode 100644
index 0000000..6e27d05
--- /dev/null
+++ b/firmware/57X-GSP/570.144/decompile_functions.py
@@ -0,0 +1,311 @@
+#!/usr/bin/env python3
+"""
+Python-based decompilation of key GSP firmware functions.
+Uses our decoded instruction stream to generate pseudo-C.
+"""
+
+import struct
+import json
+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
+
+# Key functions to decompile
+TARGET_FUNCTIONS = [
+ (0x057a1ac0, "rpc_dispatcher_primary"),
+ (0x04ed39dc, "rpc_dispatcher_secondary"),
+ (0x04cc58f8, "rpc_dispatcher_tertiary"),
+ (0x05723ef4, "rpc_dispatcher_quaternary"),
+ (0x04cb8189, "rpc_bar_trap_handler"),
+ (0x04009000, "entry_point"),
+ (0x057e94ec, "rpc_alloc_handler"),
+ (0x04d00a18, "rpc_alloc_handler_2"),
+ (0x051efbf4, "rpc_schedule_handler"),
+ (0x04efeac8, "top_call_target"),
+]
+
+def read_binary(path):
+ with open(path, 'rb') as f:
+ return f.read()
+
+def decode_instruction(data, offset):
+ """Decode instruction at offset, return (inst_dict, size)"""
+ 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, offset), 2
+ if offset + 4 <= len(data):
+ inst = struct.unpack('<I', data[offset:offset+4])[0]
+ return decode_32bit(inst, offset), 4
+ return None, 0
+
+def decode_compressed(inst, offset):
+ if (inst & 0xF003) == 0x8002:
+ rd = (inst >> 7) & 0x7; rs2 = (inst >> 10) & 0x7
+ return {'mnemonic': 'c.mv', 'rd': rd+8, 'rs2': rs2+8, 'str': f'c.mv s{rd}, s{rs2}'}
+ 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, 'str': f'c.addi s{rd}, {imm}'}
+ if (inst & 0xF07F) == 0x8067:
+ rs1 = (inst >> 7) & 0x1F
+ return {'mnemonic': 'c.jalr', 'rs1': rs1, 'str': f'c.jalr x{rs1}'}
+ if (inst & 0xE003) == 0x6003:
+ rd = (inst >> 10) & 0x7; rs1 = (inst >> 7) & 0x7
+ off = ((inst >> 2) & 0x1F) | ((inst >> 13) & 0x3) << 5
+ return {'mnemonic': 'c.ld', 'rd': rd+8, 'rs1': rs1+8, 'offset': off*8, 'str': f'c.ld s{rd}, {off*8}(s{rs1})'}
+ if (inst & 0xE003) == 0xE000:
+ rs1 = (inst >> 7) & 0x7; rs2 = (inst >> 10) & 0x7
+ off = ((inst >> 2) & 0x1F) | ((inst >> 13) & 0x3) << 5
+ return {'mnemonic': 'c.sd', 'rs1': rs1+8, 'rs2': rs2+8, 'offset': off*8, 'str': f'c.sd s{rs2}, {off*8}(s{rs1})'}
+ return {'mnemonic': 'c.unknown', 'raw': inst, 'str': f'c.0x{inst:04x}'}
+
+def decode_32bit(inst, offset):
+ opcode = inst & 0x7F
+ funct3 = (inst >> 12) & 0x7
+ rd = (inst >> 7) & 0x1F
+ rs1 = (inst >> 15) & 0x1F
+ rs2 = (inst >> 20) & 0x1F
+ reg_name = 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}'
+
+ def fmt_imm(imm): return str(imm) if imm >= 0 else str(imm)
+
+ 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, 'str': f'addi {reg_name(rd)}, {reg_name(rs1)}, {imm}'}
+
+ if opcode == 0x23 and funct3 == 3: # sd
+ off = ((inst >> 7) & 0x1F) | ((inst >> 25) & 0x7F) << 5
+ return {'mnemonic': 'sd', 'rs1': rs1, 'rs2': rs2, 'offset': off, 'str': f'sd {reg_name(rs2)}, {off}({reg_name(rs1)})'}
+
+ if opcode == 0x03 and funct3 == 3: # ld
+ off = (inst >> 20) & 0xFFF
+ if off >= 0x800: off -= 0x1000
+ return {'mnemonic': 'ld', 'rd': rd, 'rs1': rs1, 'offset': off, 'str': f'ld {reg_name(rd)}, {off}({reg_name(rs1)})'}
+
+ 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
+ target = BASE_ADDR + offset + imm
+ return {'mnemonic': 'jal', 'rd': rd, 'imm': imm, 'target': target, 'str': f'jal {reg_name(rd)}, 0x{target:08x}'}
+
+ 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, 'str': f'jalr {reg_name(rd)}, {imm}({reg_name(rs1)})'}
+
+ 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
+ btype = ['beq','bne','?','?','blt','bge','bltu','bgeu'][funct3]
+ return {'mnemonic': btype, 'rs1': rs1, 'rs2': rs2, 'imm': imm, 'target': target, 'str': f'{btype} {reg_name(rs1)}, {reg_name(rs2)}, 0x{target:08x}'}
+
+ if opcode == 0x17: # auipc
+ imm = (inst >> 12) & 0xFFFFF
+ return {'mnemonic': 'auipc', 'rd': rd, 'imm': imm, 'str': f'auipc {reg_name(rd)}, 0x{imm:05x}'}
+
+ if opcode == 0x37: # lui
+ imm = (inst >> 12) & 0xFFFFF
+ return {'mnemonic': 'lui', 'rd': rd, 'imm': imm, 'str': f'lui {reg_name(rd)}, 0x{imm:05x}'}
+
+ if opcode == 0x33: # arithmetic
+ funct7 = (inst >> 25) & 0x7F
+ 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 {'mnemonic': op, 'rd': rd, 'rs1': rs1, 'rs2': rs2, 'str': f'{op} {reg_name(rd)}, {reg_name(rs1)}, {reg_name(rs2)}'}
+
+ if opcode == 0x0F: # fence
+ return {'mnemonic': 'fence', 'str': 'fence'}
+
+ if opcode == 0x73: # system
+ return {'mnemonic': 'ecall/ebreak', 'str': 'ecall/ebreak'}
+
+ return {'mnemonic': 'unknown', 'raw': inst, 'str': f'0x{inst:08x}'}
+
+def trace_function(data, start_offset, max_bytes=2000):
+ """Trace a function and return instruction list"""
+ instructions = []
+ i = 0
+ while i < max_bytes:
+ offset = start_offset + i
+ if offset >= len(data): break
+ inst_info, size = decode_instruction(data, offset)
+ if inst_info is None:
+ i += 2; continue
+ inst_info['offset'] = BASE_ADDR + offset
+ inst_info['rel_offset'] = i
+ instructions.append(inst_info)
+ i += size
+
+ # Stop at return
+ if inst_info['mnemonic'] in ('jalr', 'c.jalr') and inst_info.get('rd', 1) == 0 and inst_info.get('imm', 0) == 0 and inst_info.get('rs1', 1) == 1:
+ break
+ return instructions
+
+def analyze_function(instructions, func_name):
+ """Analyze instructions for patterns"""
+ analysis = {
+ 'name': func_name,
+ 'stack_size': None,
+ 'ra_save_reg': None,
+ 'ra_restore_reg': None,
+ 'calls': [],
+ 'rpc_refs': [],
+ 'strings': [],
+ }
+
+ RPC_TYPES = {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'}
+
+ for inst in instructions:
+ mnemonic = inst.get('mnemonic', '')
+
+ # Prologue
+ if mnemonic == 'addi' and inst.get('rd') == 2 and inst.get('rs1') == 2 and inst.get('imm', 0) < -16:
+ analysis['stack_size'] = -inst['imm']
+
+ # Save ra
+ if mnemonic == 'addi' and inst.get('rs1') == 1 and inst.get('imm') == 0 and 9 <= inst.get('rd', 0) <= 18:
+ if analysis['ra_save_reg'] is None:
+ analysis['ra_save_reg'] = inst['rd']
+ if mnemonic == 'c.mv' and inst.get('rs2') == 1 and 8 <= inst.get('rd', 0) <= 15:
+ if analysis['ra_save_reg'] is None:
+ analysis['ra_save_reg'] = inst['rd']
+
+ # Restore ra
+ if mnemonic == 'addi' and inst.get('rd') == 1 and inst.get('imm') == 0 and 9 <= inst.get('rs1', 0) <= 18:
+ analysis['ra_restore_reg'] = inst['rs1']
+ if mnemonic == 'c.mv' and inst.get('rd') == 1 and 8 <= inst.get('rs2', 0) <= 15:
+ analysis['ra_restore_reg'] = inst['rs2']
+
+ # Calls
+ if mnemonic == 'jal':
+ analysis['calls'].append(inst.get('target'))
+
+ # RPC type refs
+ if mnemonic in ('addi', 'andi', 'ori') and inst.get('imm') in RPC_TYPES:
+ analysis['rpc_refs'].append((inst['rel_offset'], RPC_TYPES[inst['imm']]))
+
+ if mnemonic in ('beq','bne','blt','bge','bltu','bgeu') and inst.get('rs1') != 0:
+ # Could be comparing register with RPC type
+ pass
+
+ return analysis
+
+def generate_pseudo_c(instructions, func_name, analysis):
+ """Generate pseudo-C from instructions"""
+ lines = [f"// {func_name}", f"// Stack: {analysis['stack_size'] or 'unknown'} bytes", f"// RA save: s{analysis['ra_save_reg']-8 if analysis['ra_save_reg'] else '?'}", ""]
+
+ # Track stack slots
+ stack_vars = {}
+ var_counter = 0
+
+ for inst in instructions:
+ mnemonic = inst.get('mnemonic', '')
+ rel = inst['rel_offset']
+ addr = inst['offset']
+
+ # Add label for branch targets
+ lines.append(f"L_{rel:04x}: // 0x{addr:08x}")
+
+ if mnemonic == 'addi':
+ rd = inst['rd']; rs1 = inst['rs1']; imm = inst['imm']
+ if rd == 2 and rs1 == 2 and imm < 0:
+ lines.append(f" sp = sp + {imm}; // allocate 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" s{rd-8} = ra; // save return address")
+ elif rd == 1 and imm == 0 and rs1 >= 9:
+ lines.append(f" ra = s{rs1-8}; // restore return address")
+ else:
+ regs = ['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']
+ lines.append(f" {regs[rd]} = {regs[rs1]} + {imm};")
+
+ elif mnemonic == 'sd':
+ lines.append(f" *(uint64_t*)({inst['rs1']} + {inst['offset']}) = {['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'][inst['rs2']]};")
+
+ elif mnemonic == 'ld':
+ lines.append(f" {['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'][inst['rd']]} = *(uint64_t*)({['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'][inst['rs1']]} + {inst['offset']});")
+
+ elif mnemonic == 'jal':
+ target = inst.get('target', 0)
+ lines.append(f" ra = 0x{addr+4:08x}; // return address")
+ lines.append(f" goto 0x{target:08x}; // call FUNC_{target:08X}")
+
+ elif mnemonic == 'jalr':
+ if inst.get('rd') == 0 and inst.get('imm') == 0 and inst.get('rs1') == 1:
+ lines.append(f" return; // ret")
+ else:
+ lines.append(f" ra = 0x{addr+4:08x}; goto *({['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'][inst['rs1']]} + {inst['imm']});")
+
+ elif mnemonic in ('beq','bne','blt','bge','bltu','bgeu'):
+ target = inst.get('target', 0)
+ regs = ['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']
+ lines.append(f" if ({regs[inst['rs1']]} {mnemonic} {regs[inst['rs2']]}) goto 0x{target:08x};")
+
+ elif mnemonic in ('auipc', 'lui'):
+ lines.append(f" {['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'][inst['rd']]} = {mnemonic} 0x{inst['imm']:05x};")
+
+ elif mnemonic in ('add','sub','sll','xor','srl','sra','or','and'):
+ regs = ['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']
+ lines.append(f" {regs[inst['rd']]} = {regs[inst['rs1']]} {mnemonic} {regs[inst['rs2']]};")
+
+ elif mnemonic.startswith('c.'):
+ lines.append(f" {inst['str']};")
+
+ else:
+ lines.append(f" {inst['str']};")
+
+ return '\n'.join(lines)
+
+def main():
+ print("Reading firmware...")
+ data = read_binary(FWIMAGE_PATH)
+ print(f"Binary size: {len(data)} bytes")
+
+ all_results = {}
+
+ for addr, name in TARGET_FUNCTIONS:
+ offset = addr - BASE_ADDR
+ if offset < 0 or offset >= len(data):
+ print(f"Skipping {name} at 0x{addr:08x} - out of bounds")
+ continue
+
+ print(f"\nDecompiling {name} at 0x{addr:08x}...")
+ instructions = trace_function(data, offset, max_bytes=2000)
+ analysis = analyze_function(instructions, name)
+ pseudo_c = generate_pseudo_c(instructions, name, analysis)
+
+ # Save
+ filename = f"{OUTPUT_DIR}/decompiled_{name}.c"
+ with open(filename, 'w') as f:
+ f.write(pseudo_c)
+ print(f" Saved to {filename}")
+
+ # Print summary
+ print(f" Instructions: {len(instructions)}")
+ print(f" Stack: {analysis['stack_size']}")
+ print(f" RA save: s{analysis['ra_save_reg']-8 if analysis['ra_save_reg'] else '?'}")
+ print(f" RA restore: s{analysis['ra_restore_reg']-8 if analysis['ra_restore_reg'] else '?'}")
+ print(f" Calls: {len(analysis['calls'])}")
+ print(f" RPC refs: {analysis['rpc_refs'][:10]}")
+
+ all_results[name] = {
+ 'address': f"0x{addr:08x}",
+ 'instruction_count': len(instructions),
+ 'analysis': analysis,
+ }
+
+ # Save summary
+ with open(f"{OUTPUT_DIR}/decompilation_summary.json", 'w') as f:
+ json.dump(all_results, f, indent=2)
+ print(f"\nSummary saved to {OUTPUT_DIR}/decompilation_summary.json")
+
+if __name__ == "__main__":
+ main()