# 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 0x8018a78`→`0x8018a7c`). - 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 (`f8bd0a4a` → `f3f730b8`, `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 (`pop` → `bx 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`. ## b30: epilogue blx (2026-09-13) - sbl_main дошёл до `0x8016320` (logger-ish, грузит магию "SBL1"), тот зовёт shared-эпилог `0x8007280` (Thumb!) через `blx`-imm (чётный → ARM → fault на coprocessor-мусоре). Патч в глобальной BLX2BL (`blx` → `bl`, как остальные). r7="SBL1", r0=SMEM — читает настоящие таблицы! ## b31: logger-epilogue NOP (2026-09-13) - `0x8016320` зовёт эпилог `0x8007280`, чей pop читает fill/STUBV → maze → stale-lr в logger-mid → снова blx эпилог (голая взаимная рекурсия). Вызов скипается (NOP NOP); sbl_main идёт дальше `0x802F6B0` в свежий код. ## b32: logger-tail b.w (2026-09-13) - 9-й вызывающий shared-эпилога (`0x8016342 blx`, пропущен статикой): точный `b.w` (флип съезжает +2). r7="SBL1" магика из настоящих таблиц. ## b33: partition wrap-check (2026-09-13) - Валидатор партиций (`adds; blo success`, line 0x1FD) ассёртит: r0+r4 не враппаются (PBL-таблицы нулевые, r4 stale). `blo` → `b` (2B). TODO(MainOS): настоящие PBL/SMEM partition tables. ## b34: hang neutralize (2026-09-13) - Assert-hang `0x8018458` (`b .`) достигается напрямую через maze-stale-lr (мимо патченного wrap-check). `b .` → `b success` (2B, в success-путь). ## b35: sbl_mc hangs (2026-09-13) - Мёртвая assert-fn `sbl_mc.c:320` (`0x802F9A0-b.`, `0x802F9B2-b.`) достигается accident'ом (pop-garbage). Оба hang'а → S19 (рестарт post-logger init, bounded table work). 4B `b.w` каждый. - Урок: полный IMEM-poison ломает IMEM-стек (счётчики читают 0xFFFFFFFF вместо свежих нулей → underflow → wild-pop). Только tripwire 4B на входе дрейфа `[0x8600000,0x8600004)`. ## b36: assert-entry to S19 (2026-09-13) - sbl_mc assert-fn входит mid-way (`0x802F9A2`), минуя S19-hang'и. Вход тоже в S19 (ест ldr + половину movw). ## b37: S20 clock-dispatcher router (2026-09-13) - Голый clock-диспетчер (`0x800EB80`, без пушей) снимает `pop.w 0x30` за проход → марш sp к верху IMEM. S20 (34B @0x08006D38): `[sp+0x2C]` валиден → исходный pop; иначе r0=sl + re-run (идемпотентная математика). Эпилог `pop.w` (4B) → `b.w S20`. ## b38: S21 table-walker router (2026-09-13) - Голый table-walker (`0x8007916`, mid-входы без пушей) снимает `pop.w 0x28`, читает saved-FP (`0x85FFEB0`) как pc → ARM fill-дрейф (STM-clobbers!). S21 (32B @0x08006D5A): `[sp+0x24]` валиден → исходный pop; иначе `b.w S19`. Эпилог `pop.w` (4B) → `b.w S21`. ## b37: S20 clock-dispatcher router (2026-09-13) - Голый clock-диспетчер (`0x800EB80`, без пушей) снимает `pop.w 0x30` за проход → марш sp к верху IMEM. S20 (34B @0x08006D38): `[sp+0x2C]` валиден → исходный pop; иначе r0=sl + re-run (идемпотентная математика). Эпилог `pop.w` (4B) → `b.w S20`. ## maze v2 (2026-09-13) - Maze с чётным lr уходил в ARM-дрейф (STM-clobbers + IMEM fault). Maze v2 (24B): возврат только при odd Thumb lr из DDR, иначе hang громко. Залез на голову S6 (`[B9E,BA4)`, S6 dormant). Vec-maze тоже v2. ## b39: clock-store NOP (2026-09-14) - Голый clock-leaf `0x800E986 str r1,[r3]` никогда не ставит r3 (stale stacked return addr, видели `0x8007975`); запись `0xffff` ложится на живой код, падает следующий fetch там. Периферийная запись, в эмуляции никто не читает (поллы на scaffolded `[0x73A100]`). NOP 2B→2B, как b17-stm. Поймано watchlow-фильтром. ## b39 long runs: table phases + leak-sink (2026-09-14) - b39 детерминирован (повторы 1:1 по prog-pc). После clock-фаз: 5-entry table-loop 0x8007932 (флаг [r5+0x69], r5=pbl_shared), math-хелпер 0x8006684 (udiv/mls + str [r7+0x110]), arith 0x800E940 (udiv-интерполяция), аксессоры 0x80079D8/0x8007A3A (strb/strh/str по r5-моде 1..4), poll-цепочки ubfx#10 (bit10 дает 1 = ready). - sbl_uni: prog-принт каждые 2M (pc-сэмпл). 2M S12-check, 5M table r4=1, 20M dispatch r4/r5=table-ptrs, 50M S-wrapper, 80M 0x800ECxx (r5=вычисленное), 100M снова table-loop r4=0 (ВНЕШНИЙ re-pass). - Утечка sp: S12-stub не снимает фрейм враппера (8B/вызов, около 58B/Kinsn; раньше считался harmless). К 100M sp=0x807ABF0 (около 500KB до дна DDRLOW). Фикс: гэп 07800000-08000000 (минус UARTDM-страница) замаплен в uni как leak-sink (+8M runway). QEMU не тронут (в гэпе периферия; короткие прогоны не достают). UART все еще пуст. ## b40: S12-wrapper pop restore (2026-09-14) - 105M-фолт раскрыт ловушкой записи в текст: push.w на 0x80078DC писал по sp=0x802F688 (стек в тексте sbl_main), далее fetch по затертым байтам. Корень: b25 заменил pop r4,pc на bx ip во враппере S12 (0x8007B66), каждый вызов сиротил 8B фрейма (около 58B/Kinsn); к 105M sp съехал, pop.w с восстановлением sp/pc (0x800EB94) взял чужой stacked-lr. b40 вернул pop (2B), r0=0 снова по контракту. - Побочка: ранний clock-путь теперь идет честно (раньше диверт его пропускал): IMEM-стек, чтение за верхом IMEM. Uni не мапил DDRHI, QEMU мапит: добавлен паритет (0x08610000, 16M-64K). b40 на 2M чист, sp живет в DDRHI, unmapped 0. Дефолт run-sbl теперь b40. ## b41: desert retarget EB90->EB0A (2026-09-14) - Вызов пустыни шел в середину функции (мимо push.w 48B пролога), эпилог pop.w снимал чужое (+48B), S20-rerun возвращал в середину: ловушка +100B/Kinsn. b41 зовет пролог, фрейм балансируется, пустыня возвращается к вызывающему. sbl_main дошел до 0x802F652 (SMEM write), пошли фазы строк SBL1, lr в sbl_main. - Creep остался (~320B/K, равномерный): --sphist показал хвост враппера S12 (+8B x27/K) и дерево mid-pop. QEMU b41 чист 120с. ## b42/b43: frameless откачен (2026-09-14) - b42 убрал push+pop враппера: creep исчез в ноль на 2M, но хвост bx lr зациклился сам на себя (lr=0x8007B65): push хранит возврат вызывающего, убирать нельзя. Откат. - PBL-контракт вместо этого: S12-keep (валидный pushed lr) идет в хвост (парный pop, баланс), S12-default (мусор) дивертит в poll-resumption без визита в хвост (блоб 100->132B, uni+QEMU). Диагностика: --sphist (дельты sp по pc), --tpop (куда уходит pop). ## b43 длинные прогоны - статус init 2026-09-15 - Сделано: прог-принты каждые 200K pc/sp/регистры, ловушка записи в текст, --sphist дельты sp по pc, --tpop адреса ухода pop, sbl_dis.py дизассемблер диапазонов без elftools. - 105M-фолт b39 раскрыт: сиротский фрейм S12 гнал sp в текст, push.w затер sbl_main. b40 вернул pop, b41 перенаправил вызов пустыни на пролог и закрыл S20-ловушку, b42-безкаркас откачен как вис, b43 разделил S12 на keep/divert по PBL-контракту. - Состояние: b43 идет 380M без фолтов и зависаний, sbl_main доходит до глубоких вызовов 0x802F938, строки SBL1, таблицы pbl_shared в работе. UART пока пуст: init табличный и медленный. - Что дает: зацикливания от наших стабов закрыты, каждый прогон упирается в настоящий код прошивки. Дальше SDHCI и UEFI. - Creep 300B/Kinsn вверх остается, закрыт маппингами DDRHI-ext как runway. QEMU-паритет держится на b43. ## logstream --logport 2026-09-15 - Харнесс умеет отдавать лог в TCP: --logport PORT слушает 0.0.0.0, tee stdout всем клиентам, accept в prog-блоке, медленные отваливаются. Читать: nc HOST PORT. Строки с новыми строками как в stdout.