diff --git a/docs/sbl-bringup.md b/docs/sbl-bringup.md index ea1627d..8c2b819 100644 --- a/docs/sbl-bringup.md +++ b/docs/sbl-bringup.md @@ -91,3 +91,15 @@ - b23: 2M инстр. без падений (sp здоров), sbl_main идёт дальше (`0x80164C4`-subtree → прыжок в OCIMEM-нули `0x222F84` — фронт PBL-данных). QEMU-паритет: R15=0x223000, sp=0x85FFF00, lr=0x80164CB — там же. + +## b24: world-switch stub, clock S11 (2026-09-12) +- Диспетчер (`0x80164C4`-subtree) зовёт ARM jump-table `0x8007158` + (`ldr pc`), та — в OCIMEM-трамплин world-switch `0x221EF8` + (ARM: save regs, `cps MON`, `mrc`, `ldr sp/pc` из PBL-таблиц + `[0x221F6C]=0x223620`/`[0x221F70]=0x223930` → вызов TZ; TZ не загружен). + Вход получается в Thumb → b24-стаб (4B `movs r0,#0; bx lr`): + возврат 0 в диспетчер (TZ deferred). Перезаписывает голову ARM'а + (восстановимо из ELF). +- Дальше: clock-fn `0x800E960` (udiv-подсчёты) с голым выходом + `pop{r4,pc}` (без push, tail-chain через NOP-идиому) → S11 (b24): + вход → `bx lr` (lr = брошенный-но-валидный `0x80076CD`). diff --git a/qemu/saimaa.c b/qemu/saimaa.c index 4d62a50..0585bcf 100644 --- a/qemu/saimaa.c +++ b/qemu/saimaa.c @@ -698,6 +698,13 @@ static void saimaa_machine_init(MachineState *machine) uint32_t v = 0x08006B8D; cpu_physical_memory_write(0x080528D0, &v, 4); } + /* PBL world-switch Thumb-stub (b24, 4B @0x00221EF8): + * movs r0,#0; bx lr (return 0 to dispatcher, TZ deferred). + * Overwrites ARM trampoline head (recoverable from ELF). */ + { + uint32_t v = 0x47702000; + cpu_physical_memory_write(0x00221EF8, &v, 4); + } { uint32_t fill = 0x08006B8D; uint32_t a; diff --git a/tools/sbl_patch.py b/tools/sbl_patch.py index b8663f5..6cd5a5e 100644 --- a/tools/sbl_patch.py +++ b/tools/sbl_patch.py @@ -874,6 +874,87 @@ def main(): save(d, 'img0_b23.elf') + # b24 = b23 + S11 (clock-fn naked-exit fix): + # 0x800E988 pop{r4,pc} has no matching push (naked tail-chain worker: + # bl 0x8006684-idiom tail-jumps to the clock fn, abandoning lr). + # Each dispatcher pass eats 0x8+ ([sp] -> wild 0x902FC506). + # S11 turns the exit into bx lr (lr = abandoned-but-valid resumption + # 0x80076CD/0x80076D3 in the caller). 2B patch, like S8. + 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'), + + (0x8017AFC, 'bde8fc87', 'eef74fb8'), + + (0x802F690, '9847fee7', 'c046c046'), + + (0x801671C, '9847fee7', 'c046c046'), + + (0x8013BAC, 'f8bd0a4a', 'f3f730b8'), + + (0x801B6BE, 'bde8f081', 'ebf7c3ba'), + + (0x8016720, '10bd0000', 'f0f7b2ba'), + + ]: + + assert hx(d, va) == exp, (hex(va), hx(d, va)) + + set4(d, va, new) + + # Full bl idiom at 0x8006682 (bl + pop, 6B) -> NOPs (see b18). + o = off(0x8006682) + + assert d[o:o + 6].hex() == '00f001f807bd', hx(d, 0x8006682, 6) + + d[o:o + 6] = bytes.fromhex('c046c046c046') + + 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') + + # Logger S8 (see b21). + o = off(0x8034810) + + assert d[o:o + 4].hex() == 'bde8fc87', hx(d, 0x8034810) + + d[o:o + 2] = bytes.fromhex('7047') + + # Clock S11: pop{r4,pc} (2B 10bd) -> bx lr (2B 7047). + o = off(0x800E988) + + assert d[o:o + 2].hex() == '10bd', hx(d, 0x800E988, 2) + + d[o:o + 2] = bytes.fromhex('7047') + + save(d, 'img0_b24.elf') + diff --git a/tools/sbl_uni.py b/tools/sbl_uni.py index 92fec15..c0e36f7 100644 --- a/tools/sbl_uni.py +++ b/tools/sbl_uni.py @@ -158,7 +158,11 @@ def main(): inside = _elo <= addr < _ehi if inside and not _inrng[_i]: if len(elog) < 20000: - elog.append((count[0], _prev[0], addr)) + try: + _th = (mu.reg_read(_CPSR) >> 5) & 1 + except Exception: + _th = -1 + elog.append((count[0], _prev[0], addr, _th)) _inrng[_i] = inside _prev[0] = addr if calls is not None and size in (2, 4): @@ -331,6 +335,12 @@ def main(): # PBL version-table fabrication (b23): [0x080528D0] = STUBV so the # rollback query's blx calls maze (returns 0 = versions OK). W32(0x080528D0, STUBV) + # PBL world-switch Thumb-stub (b24): the ARM trampoline at 0x221EF8 + # is entered in Thumb (mode confusion at dispatch); real ARM would + # save regs/CPS to MON and call TZ (not loaded yet). Stub returns 0 + # to the dispatcher via stale lr (TZ bringup deferred). + # NOTE: overwrites 4B of real ARM trampoline (recoverable from ELF). + mu.mem_write(0x00221EF8, bytes([0x00, 0x20, 0x70, 0x47])) # stack zone fill (STUBV odd) mu.mem_write(0x085F0000, struct.pack(" {cur:#x}") - for n, prev, cur in elog[-10:]: - print(f" LAST insn#{n} {prev:#x} -> {cur:#x}") + for n, prev, cur, th in elog[:10]: + print(f" FIRST insn#{n} {prev:#x} -> {cur:#x}{'t' if th == 1 else ('a' if th == 0 else '?')}") + for n, prev, cur, th in elog[-10:]: + print(f" LAST insn#{n} {prev:#x} -> {cur:#x}{'t' if th == 1 else ('a' if th == 0 else '?')}") if _splog is not None: print(f"sp-highs: {len(_splog)}") for n, pc, sp in _splog: