#!/usr/bin/env python3 """sbl_patch.py — reproduce all WIP file patches for SBL1 bring-up. Usage: ./tools/sbl_patch.py fw/sbl/img0.elf out-dir/ Produces img0_bl.elf, img0_b2..b8.elf (cumulative variants, see STEPS). Each step is documented with WHY (all WIP, to be replaced by real PBL). """ import os import sys SEG_OFF, SEG_VA = 0x3000, 0x8005800 BLX2BL = { # blx-imm -> bl (stay Thumb): set bit12 of 2nd halfword 0x802F658: ('d8f7c2ed', 'd8f7c2fd'), 0x802F660: ('d8f702ec', 'd8f702fc'), } NOPS = { # call -> NOP NOP (skip unmodellable init/assert; r0/r5 ignored) 0x802F664: 'e4f77efc', # bl walker (result ignored by caller) 0x802F672: 'e6f7bbfa', # bl 0x8015bec (assert-hang) 0x802F678: None, # bl 0x8018a78, see REDIRECT (installer runs!) 0x801B71E: '9847fee7', # blx r3 + b.n self (terminal assert, 4B) 0x8016184: 'fdf790fc', # bl 0x8013aa8 (subtree nopped, see b8) } REDIRECT = { # bl target off-by-N fixes (verified by disasm + QEMU traces): 0x8013AA8: ('07f026be', '07f027be'), # jump-table case0: +2 0x802F678: ('e9f7fef9', 'e9f700fa'), # bl 0x8018a78 -> 0x8018a7c (+4) } POOLS = { # literal pool words redirected to owned scratch (orig struct garbage): 0x801B76C: ('280b0508', '00000708'), # -> 0x08070000 } def off(va): return SEG_OFF + (va - SEG_VA) def hx(d, va, n=4): return d[off(va):off(va) + n].hex() def set4(d, va, hexbytes): d[off(va):off(va) + 4] = bytes.fromhex(hexbytes) def main(): src, outdir = sys.argv[1], sys.argv[2] os.makedirs(outdir, exist_ok=True) base = bytearray(open(src, 'rb').read()) def save(d, name): p = os.path.join(outdir, name) open(p, 'wb').write(bytes(d)) print('wrote', p, len(d)) d = bytearray(base) for va, (a, b) in BLX2BL.items(): assert hx(d, va) == a, (hex(va), hx(d, va)) set4(d, va, b) save(d, 'img0_bl.elf') # b2 = bl + walker NOP o = off(0x802F664) assert d[o:o + 4].hex() == 'e4f77efc' d[o:o + 4] = bytes.fromhex('c046c046') save(d, 'img0_b2.elf') # b3 = b2 + 0x8015bec NOP o = off(0x802F672) assert d[o:o + 4].hex() == 'e6f7bbfa', hx(d, 0x802F672) d[o:o + 4] = bytes.fromhex('c046c046') save(d, 'img0_b3.elf') # b4 = b3 + 0x8018a78 NOP o = off(0x802F678) assert d[o:o + 4].hex() == 'e9f7fef9', hx(d, 0x802F678) d[o:o + 4] = bytes.fromhex('c046c046') save(d, 'img0_b4.elf') # b5 = b4 + jump-table +2 o = off(0x8013AA8) assert d[o:o + 4].hex() == '07f026be' d[o:o + 4] = bytes.fromhex('07f027be') save(d, 'img0_b5.elf') # b6 = b5 + terminal assert NOP (4B) o = off(0x801B71E) assert d[o:o + 4].hex() == '9847fee7' d[o:o + 4] = bytes.fromhex('c046c046') save(d, 'img0_b6.elf') # b7 = b6 + redirect bl to installer entry (undoes b4 NOP) o = off(0x802F678) assert d[o:o + 4].hex() == 'c046c046' d[o:o + 4] = bytes.fromhex('e9f700fa') save(d, 'img0_b7.elf') # b8 = b7 + subtree NOP + pool redirect o = off(0x8016184) assert d[o:o + 4].hex() == 'fdf790fc', hx(d, 0x8016184) d[o:o + 4] = bytes.fromhex('c046c046') o = off(0x801B76C) assert d[o:o + 4].hex() == '280b0508' d[o:o + 4] = bytes.fromhex('00000708') save(d, 'img0_b8.elf') # b9 = b8 + hole-call NOP (EDL-dead padding slide at 0x80068FC) o = off(0x8008396) assert d[o:o + 4].hex() == 'fef780f9', hx(d, 0x8008396) d[o:o + 4] = bytes.fromhex('c046c046') save(d, 'img0_b9.elf') # b10 = b9 with installer-bl reverted to NOP (S2 lands mid-NOPs safely, # falls into the 0x8013b7c call; installer skipped, structs via maze) o = off(0x802F678) assert d[o:o + 4].hex() == 'e9f700fa', hx(d, 0x802F678) d[o:o + 4] = bytes.fromhex('c046c046') save(d, 'img0_b10.elf') # b11 = b10 with installer-bl restored (redirect, installer runs again) o = off(0x802F678) assert d[o:o + 4].hex() == 'c046c046' d[o:o + 4] = bytes.fromhex('e9f700fa') save(d, 'img0_b11.elf') # b12: b10-variant with jump-table entry0 NOP (fall into entry1/case-1); # rebuilt from b10 base (installer stays skipped) d = bytearray(base) for va, (a, b) in BLX2BL.items(): set4(d, va, b) for va, exp, new in [ (0x802F664, 'e4f77efc', 'c046c046'), (0x802F672, 'e6f7bbfa', 'c046c046'), (0x802F678, 'e9f7fef9', 'c046c046'), (0x801B71E, '9847fee7', 'c046c046'), (0x8008396, 'fef780f9', 'c046c046'), (0x8016184, 'fdf790fc', 'c046c046'), (0x801B76C, '280b0508', '00000708'), ]: assert hx(d, va) == exp, (hex(va), hx(d, va)) set4(d, va, new) o = off(0x8013AA8) assert d[o:o + 4].hex() == '07f026be', hx(d, 0x8013AA8) d[o:o + 4] = bytes.fromhex('c046c046') save(d, 'img0_b12.elf') # b15 = b14 + force table-loop count (0x8013ba6 ldrh r0,[r7,#4] -> # movs r0,#0xff): exit pop reads clobbered slot (->0); looping builds # tables via helpers instead. 2-byte patch. d = bytearray(base) for va, (a, b) in BLX2BL.items(): set4(d, va, b) for va, exp, new in [ (0x802F664, 'e4f77efc', 'c046c046'), (0x802F672, 'e6f7bbfa', 'c046c046'), (0x802F678, 'e9f7fef9', 'c046c046'), (0x801B71E, '9847fee7', 'c046c046'), (0x8008396, 'fef780f9', 'c046c046'), (0x8016184, 'fdf790fc', 'c046c046'), (0x801B76C, '280b0508', '00000708'), (0x8006684, '01f807bd', '46c046c0'), (0x8017AFC, 'bde8fc87', '4af020bb'), ]: assert hx(d, va) == exp, (hex(va), hx(d, va)) set4(d, va, new) o = off(0x8013AA8) assert d[o:o + 4].hex() == '07f026be', hx(d, 0x8013AA8) d[o:o + 4] = bytes.fromhex('c046c046') o = off(0x8013BA6) assert d[o:o + 2].hex() == 'b888', (hex(0x8013BA6), hx(d, 0x8013BA6, 2)) d[o:o + 2] = bytes.fromhex('ff20') save(d, 'img0_b15.elf') # b14 = b13 + redirect S6 pop (0x8017afc ldmia.w sp!,{r2-r9,sl,pc}) # to S6 stublet at 0x08062140 (pc slot clobbered to 0 at runtime) d = bytearray(base) for va, (a, b) in BLX2BL.items(): set4(d, va, b) for va, exp, new in [ (0x802F664, 'e4f77efc', 'c046c046'), (0x802F672, 'e6f7bbfa', 'c046c046'), (0x802F678, 'e9f7fef9', 'c046c046'), (0x801B71E, '9847fee7', 'c046c046'), (0x8008396, 'fef780f9', 'c046c046'), (0x8016184, 'fdf790fc', 'c046c046'), (0x801B76C, '280b0508', '00000708'), (0x8006684, '01f807bd', '46c046c0'), (0x8017AFC, 'bde8fc87', '4af020bb'), ]: assert hx(d, va) == exp, (hex(va), hx(d, va)) set4(d, va, new) o = off(0x8013AA8) assert d[o:o + 4].hex() == '07f026be', hx(d, 0x8013AA8) d[o:o + 4] = bytes.fromhex('c046c046') save(d, 'img0_b14.elf') # b13 = b12 + NOP strb.w fp,[r1,#-7]! at 0x8006684 (helper stores # fp into struct r1, but r1 arrives NULL from 0x8007e82 chain; # store to [0-7]=0xfffffff9 faults; struct unrecoverable here) d = bytearray(base) for va, (a, b) in BLX2BL.items(): set4(d, va, b) for va, exp, new in [ (0x802F664, 'e4f77efc', 'c046c046'), (0x802F672, 'e6f7bbfa', 'c046c046'), (0x802F678, 'e9f7fef9', 'c046c046'), (0x801B71E, '9847fee7', 'c046c046'), (0x8008396, 'fef780f9', 'c046c046'), (0x8016184, 'fdf790fc', 'c046c046'), (0x801B76C, '280b0508', '00000708'), (0x8006684, '01f807bd', '46c046c0'), ]: assert hx(d, va) == exp, (hex(va), hx(d, va)) set4(d, va, new) o = off(0x8013AA8) assert d[o:o + 4].hex() == '07f026be', hx(d, 0x8013AA8) d[o:o + 4] = bytes.fromhex('c046c046') save(d, 'img0_b13.elf') if __name__ == '__main__': main()