174 lines
12 KiB
Markdown
174 lines
12 KiB
Markdown
# 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-путь).
|