Files
lumia-emulator/docs/sbl-bringup.md
T
2026-09-13 12:23:15 +00:00

10 KiB
Raw Blame History

SBL1 BringUp — статус (2026-09-07)

Цель: выполнить оригинальный SBL1 (RM-1127) в QEMU до баннера SBL1, Start на UART.

Что работает

  • Машина saimaa (qemu/saimaa.c, QEMU 8.2.2 на zern): Cortex-A7×4, DDRLOW/OCIMEM/IMEM/SMEM/RPM/mystrom, UARTDM-стаб, SDHCI+eMMC 8GiB, VFP/NEON вкл (CPACR+NSACR+FPEXC), null-page trap, fault-tolerant векторы.
  • eMMC-образ с реальной GPT RM-1127 (33 раздела, tools/mkimage.py).
  • FFU-разбор (tools/ffu_parse.py, RKH/MSM/OEM/PlatformID, GPT33).
  • .edp-нарезка ELF (tools/edp_carve.py, 10 образов, fw/sbl/ в .gitignore).
  • tools/sbl_patch.py — воспроизводимо генерирует img0_bl..b10.elf из img0.elf.
  • Стаблеты компенсации pop (qemu/stublets.S → 0x08062000): чинят SP, повторы попадают в те же слоты. Слоты в saimaa_machine_init.
  • Указательный maze (0x08062100) + scratch (0x08070000) для struct-цепочек.
  • Трассировка: -d in_asm,cpu,guest_errors,int + GDB (только чтение! continue в batch виснет — использовать interrupt+detach).

Найденное

  • sbl_main (Thumb) = 0x802f63e, баннер SBL1, Start зовётся из 0x802f6ae через bl 0x8016320. Poll IMEM 0x08600944 != 0x53444247 (выходит при 0).
  • В .edp-копии вызовы blx imm ведут в Thumb-код (на HW тоже; QEMU уходит в ARM → патчим в bl, см. sbl_patch.py). Часть вызовов off-by-2/4 (таблица 0x8013aa8, bl 0x8018a780x8018a7c).
  • FFU-копия SBL1 — другая сборка тех же исходников (структура та же, bl вместо blx, полные фреймы). Используется как эталон.
  • PBL-контекст (r4-r12) фабрикуется машиной; таблицы собираются частично.
  • Текущий фронтир (b10 + canonical scaffold): ~20 уровней init, падеж в районе 0x80079xx (див switch по мусорным флагам) — см. свежий трейс.

Запуск

./tools/sbl_patch.py fw/sbl/img0.elf /tmp/p
~/qemu-src/qemu-8.2.2/build/qemu-system-arm \
  -M saimaa,sbl1=/tmp/p/img0_b10.elf,sblmain=0x802f63f -m 512 \
  -display none -monitor none -serial file:/tmp/uart.log \
  -drive file=/tmp/emmc-test.img,if=sd,format=raw

TODO

  1. Добить баннер (см. свежий in_asm-хвост).
  2. Заменить стаблеты настоящим PBL (нужны дампы с живого тела!).
  3. UEFI (img3, EDK2) — отдельный фронт после SBL1.

b18: S7 и цикл delay-helper (2026-09-12)

Механизм зацикливания (b17, доказан Unicorn-harness tools/sbl_uni.py):

  • sbl_main → delay-функция 0x8013B78 (256-итераций table-walk, bl 0x8017AF4 каждую итерацию) → b14 (0x8017AFC: b.w 0x8005B9E) → helper 0x8005BB0 → выход ldr pc,[sp],#0x14 (0x8005BCA) → S3-слот (0x8013BA5, рестарт).
  • Эпилог delay (0x8013BAC: pop {r3-r7,pc}) снимает 0x18 без push; каждый S3-рестарт съедает 0x18 живого стека. Маршрут: слоты → fill (STUBV → pop pc = maze) → pristine IMEM (нули → null-jump → hook_null → maze → stale lr) → переполнение IMEM (0x8610008).
  • Heap SBL (stm в 0x8006684) затирает maze/S6/banner (0, 0x7a, STUBV). QEMU: dstub-overlay [0x08006B80,0x08006C50) (read = снапшот ELF+стабы, write = игнор). Uni: deferred-restore guard (MEM_WRITE-хук срабатывает ДО записи, restore надо делать в hook_code!).
  • Maze-blob: movs r0,#0; lsr ip,lr,#24; cmp #8; bne hang; bx lr (возврат 0 только для lr из DDRLOW 0x08..., иначе hang). Фикс b18: патч BAC (f8bd0a4af3f730b8, b.w S7) + S7-стаблет (52B @ 0x08006C10): cmp r4,#0xff → iterate (0x8013BA5) или done (restore r4/r5/sl/fp, возврат в sbl_main 0x802f681). BAE-путь ([r7]==2) принесён в жертву (r7=0, недостижим). Фронтир b18: sbl_main возвращается, новый код 0x801Bxxx (strb [r7,#2]-паттерн, b17-NOP на 0x801B71E), падеж pop'ом пустого стека у верха IMEM — следующий фронт.

b18 parity (QEMU == uni, 2026-09-12)

  • QEMU поправлен до паритета: dstub-overlay + vec-maze для Thumb (null-fetch исполняет maze и возвращается) + убран висячий префикс bl (bogus odd store -> alignment fault, DFAR=0x8006B8D).
  • QEMU b18 доходит до того же фронтира, что uni b18 (38961 инстр.): 0x801Bxxx, r1=0x80522A4, r2/r3=STUBV, lr=0x801B73F, sp марширует к верху IMEM. Смерти чуть различаются по таймингу (uni: read 0x8610000; QEMU: null-jump → maze-hang c lr-top 0x80), регион тот же.
  • Следующий фронт (оба): лабиринт 0x801B6xx (логгер, r7=0/r4=0, pop пустого стека). Кандидаты: S8-стаблет, таблица логгера.

b21-b23: логгер, S9, S10, rollback (2026-09-12)

  • Логгер 0x8034810 = голый pop.w 0x28 без push (жрал стек) → S8: вход → bx lr (b21). Возврат игнорируется (callers ставят r0=0).
  • Лабиринт 0x801B6A0: счётчик [r4]++ + pop.w на пустых фреймах (r4/r7=STUBV из fill) → S9 (b22): [sp+0x14] ненулевой → исходный pop; ноль → restore r0/r4/r5/sl/fp + reset sp=0x085FFF00 + прыжок в sbl_main 0x802f69d через ip (без литералов).
  • Rollback boot_rollback_version.c:580 (query всегда 0x302E) → NOP NOP (b22, возврат игнорируется). Query зовёт *table[0x080528D0] (нули) → фабрикация: poke STUBV (maze вернёт 0 = версии OK) (b23).
  • Rollback выходил боком в helper-mid (pop{r4,pc} без push) → S10 (b23): movw/movt ip + bx ip в sbl_main 0x802f6a3.
  • 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).
  • Poll [0x73A100] (lsls#29+bpl = проверка bit2!): SBL только читает → фабрикация bit2=1 (b24 scaffold, обе стороны).
  • blx #0x8006964 в файловые нули паддинга (PBL scratch) → S12v2 (b25 scaffold, ARM! т.к. blx-imm на чётный): preset ip=S16 + ACK bit1 [0x73A100] (side effect для poll#2) + bx lr. Литерал [0x8006980].
  • Helper-pop 0x8007B66 читает scratch (счётчики 0-4 вместо pushed lr, фреймы съедены null-циклами) → S16v2 (b25 smart): B66 (popbx ip, 2B) → S16 проверяет ip (odd + top 8 = legit pushed-lr → bx ip; иначе default: restore r7, прыжок 0x8007779). S12v3 грузит ip из [sp+4] (pushed lr) + ACK bit1. B66-патч в ELF (b25), S12/S16 в scaffold.

b26: S17 safe-logger (2026-09-13)

  • S8 (bx lr) гасил весь init-лог (UART пуст by design) вслепую. S17 (50B @0x08006CF0): печать r1-строки в UARTDM-TF, только если r1 в rodata [0x804B800,0x8058000) (wild-указатели скипаются), затем pop-return (без leak'а, возврат игнорируется). Логгер 0x8034810 (pop.w 4B) → b.w S17. Видимость прогресса вместо счёта инстр.

b27: poll re-init skip (2026-09-13)

  • Poll-fn 0x8007748: cbz r6 (r6=2 константа caller'а, никогда 0) → вечный re-init (movs r4,#0, table loop) вместо poll#2-direct. Патч 2B→2B в b. Биты 0,1,2 [0x73A100] pref-set (scaffold).
  • S16v3: add sp,#8 перед bx ip (снимает helper-фрейм, пропущенный B66 — un-leak стека вниз).
  • S16v4: отбраковка ip==STUBV (fill/maze — никогда legit return) → default вместо возврата в maze (разрыв maze-B66 петли).
  • S12v4 (scaffold, без ELF): smart dispatcher в самом S12 (проверка pushed-lr + ACK + возврат) — S16 больше не нужен в цепочке (остаётся как fallback).
  • S12v5: +restore r7 (peripheral base, S16 dormant) прямо в S12.

b28: poll-epilogue march kill (2026-09-13)

  • Poll-fn входит mid-way (без prologue-push), выходит через add sp,#0x1C + pop.w 0x14 (+0x30/проход, пушей нет) → марш вверх через fill в IMEM-нули → fault на верху. add sp → NOP (2B, b28). Pop читает helper-фреймы (pushed-lr, валидные возвраты).

b29: S18 poll-epilogue router (2026-09-13)

  • b28-NOP убрал только +0x1C; pop.w 0x14 без пушей всё равно марширует. S18 (10B @0x08006D22): слепой прыжок в sbl_main 0x802F6A9 (post-dispatcher). Эпилог pop.w (4B) → b.w S18.