Compare commits
55 Commits
f449b56b05
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8aef555346 | |||
| d3e2efe7ba | |||
| 3d011f2994 | |||
| 785eb934e6 | |||
| 4fcb316c99 | |||
| 8ce95238e0 | |||
| f46e4e94fe | |||
| 9187bdcff0 | |||
| 5362f0a6f8 | |||
| 503c334b2e | |||
| 2a3bdcd72d | |||
| d0a62a86c8 | |||
| 66b1721b7a | |||
| ae9ad07370 | |||
| 308e8aa2af | |||
| 110a60562c | |||
| 8cd7c85771 | |||
| dcfdeef917 | |||
| b5671fbbd7 | |||
| b7de1d80e0 | |||
| 0863405819 | |||
| 757e74fb4f | |||
| 68fab44137 | |||
| 47e78ee42f | |||
| 80ee6d1bf3 | |||
| 9cd903c34d | |||
| 8b1eaeba18 | |||
| 13b314e9cf | |||
| c875e8712f | |||
| 15eeba929d | |||
| d2d180feac | |||
| b06f009dd0 | |||
| 95b98054b3 | |||
| 9c45d06ed2 | |||
| f2510065ea | |||
| c29860618f | |||
| 8d05bebc0c | |||
| 615574fa6f | |||
| 8d5657d5a2 | |||
| 6de5a47b34 | |||
| 4bd9997412 | |||
| 9e63dfc258 | |||
| 1e271b6d53 | |||
| 62f32be733 | |||
| 049f98122b | |||
| 67539f7351 | |||
| 22ea0dd0f1 | |||
| 74e4ead780 | |||
| 4ff0511da1 | |||
| 444a39d8d0 | |||
| 6875e8bada | |||
| 124e9f5e72 | |||
| 2087cba49c | |||
| 40943bbd51 | |||
| fda762acc8 |
@@ -41,3 +41,168 @@
|
|||||||
1. Добить баннер (см. свежий in_asm-хвост).
|
1. Добить баннер (см. свежий in_asm-хвост).
|
||||||
2. Заменить стаблеты настоящим PBL (нужны дампы с живого тела!).
|
2. Заменить стаблеты настоящим PBL (нужны дампы с живого тела!).
|
||||||
3. UEFI (img3, EDK2) — отдельный фронт после SBL1.
|
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.
|
||||||
|
|||||||
+3
-9
@@ -1,20 +1,14 @@
|
|||||||
.syntax unified
|
.syntax unified
|
||||||
.cpu cortex-a7
|
.cpu cortex-a7
|
||||||
.thumb
|
.thumb
|
||||||
@ maze entry logger: record lr/sp to mailbox 0x08006964, return 0.
|
@ maze entry: return 0. Guard: bad lr (high byte != 0x08) spins
|
||||||
@ Guard: if lr is outside known-good SBL/stub ranges, spin with
|
@ with IRQs off instead of jumping into the void.
|
||||||
@ interrupts disabled instead of jumping into the void (post-mortem
|
|
||||||
@ via vec trap regs).
|
|
||||||
.text
|
.text
|
||||||
.global maze_stub
|
.global maze_stub
|
||||||
maze_stub:
|
maze_stub:
|
||||||
movw r12, #:lower16:0x08006964
|
|
||||||
movt r12, #:upper16:0x08006964
|
|
||||||
str lr, [r12]
|
|
||||||
str sp, [r12, #4]
|
|
||||||
movs r0, #0
|
movs r0, #0
|
||||||
lsr r12, lr, #24
|
lsr r12, lr, #24
|
||||||
cmp r12, #0x08
|
cmp r12, #8
|
||||||
bne maze_hang
|
bne maze_hang
|
||||||
bx lr
|
bx lr
|
||||||
maze_hang:
|
maze_hang:
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
.syntax unified
|
||||||
|
.cpu cortex-a7
|
||||||
|
.thumb
|
||||||
|
@ Minimal PBL stub (position-independent): BLX to SBL1 entry (seg1),
|
||||||
|
@ then canonical regs + jump to sbl_main. Mirrors PBL call-then-jump.
|
||||||
|
.text
|
||||||
|
.global pbl_stub
|
||||||
|
pbl_stub:
|
||||||
|
push {r4-r7, lr}
|
||||||
|
movw r12, #0x6b81
|
||||||
|
movt r12, #0x800
|
||||||
|
blx r12 @ SBL1 entry (odd: stay Thumb)
|
||||||
|
movw r0, #0x0000
|
||||||
|
movt r0, #0x0022 @ r0 = pbl_shared 0x00220000
|
||||||
|
mov r5, r0
|
||||||
|
movw sl, #0x0000
|
||||||
|
movt sl, #0x0806 @ sl = table base 0x08060000
|
||||||
|
movw fp, #0x1000
|
||||||
|
movt fp, #0x0806 @ fp = 0x08061000
|
||||||
|
movw lr, #0xf63f
|
||||||
|
movt lr, #0x0802 @ lr = sbl_main odd (noreturn marker)
|
||||||
|
bx lr
|
||||||
Executable
+25
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# run-sbl.sh — запуск SBL1 на машине saimaa (пути постоянные, не /tmp).
|
||||||
|
# Использование: ./qemu/run-sbl.sh [bXX] [extra-qemu-args...]
|
||||||
|
# bXX — вариант патча (по умолчанию b17)
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
QEMU=~/qemu-src/qemu-8.2.2/build/qemu-system-arm
|
||||||
|
B=${1:-b38}
|
||||||
|
IMG=fw/img/emmc-real.img
|
||||||
|
if [ ! -f "$IMG" ]; then
|
||||||
|
echo "no $IMG — сначала: python3 tools/ffu_extract.py fw/dl/RM1127_059X5R5_orig.ffu --emmc-user fw/img/user-real.bin"
|
||||||
|
echo "и: python3 tools/mkimage.py --user fw/img/user-real.bin $IMG"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift 2>/dev/null || true
|
||||||
|
# timeout для тестовых прогонов (0 = без ограничения)
|
||||||
|
T=${T:-15}
|
||||||
|
if [ "$T" != "0" ]; then
|
||||||
|
exec timeout "$T" "$QEMU" -M saimaa,sbl1="fw/img/patch/img0_${B}.elf",sblmain=0x802f63f \
|
||||||
|
-m 512 -display none -monitor none -serial file:uart.log \
|
||||||
|
-drive file="$IMG",if=sd,format=raw "$@" 2>&1
|
||||||
|
fi
|
||||||
|
exec "$QEMU" -M saimaa,sbl1="fw/img/patch/img0_${B}.elf",sblmain=0x802f63f \
|
||||||
|
-m 512 -display none -monitor none -serial file:uart.log \
|
||||||
|
-drive file="$IMG",if=sd,format=raw "$@" 2>&1
|
||||||
+400
-49
@@ -35,6 +35,10 @@
|
|||||||
/* High DDRLOW for our stubs (SBL heap reuses 0x0806xxxx) */
|
/* High DDRLOW for our stubs (SBL heap reuses 0x0806xxxx) */
|
||||||
#define SAIMAA_DDRHI_BASE 0x08610000
|
#define SAIMAA_DDRHI_BASE 0x08610000
|
||||||
#define SAIMAA_DDRHI_SIZE 0x00FF0000 /* ..0x09600000 */
|
#define SAIMAA_DDRHI_SIZE 0x00FF0000 /* ..0x09600000 */
|
||||||
|
/* Stubs carve-out inside DDRHI (SBL heap overwrites plain RAM):
|
||||||
|
* maze+S6+banner+mailbox live here, set read-only after implant. */
|
||||||
|
#define SAIMAA_STUBS_BASE 0x08800000
|
||||||
|
#define SAIMAA_STUBS_SIZE 0x00000300
|
||||||
#define SAIMAA_IMEM_BASE 0x08600000
|
#define SAIMAA_IMEM_BASE 0x08600000
|
||||||
#define SAIMAA_IMEM_SIZE 0x00010000
|
#define SAIMAA_IMEM_SIZE 0x00010000
|
||||||
#define SAIMAA_DDRH_A_BASE 0x80000000
|
#define SAIMAA_DDRH_A_BASE 0x80000000
|
||||||
@@ -64,10 +68,13 @@ struct SaimaaTmrState {
|
|||||||
|
|
||||||
static uint64_t saimaa_tmr_read(void *opaque, hwaddr off, unsigned size)
|
static uint64_t saimaa_tmr_read(void *opaque, hwaddr off, unsigned size)
|
||||||
{
|
{
|
||||||
|
/* Deterministic tick (+1/read): advances delay loops but keeps runs
|
||||||
|
* reproducible (qemu_clock would diverge run to run). */
|
||||||
|
static uint64_t tick;
|
||||||
(void)opaque;
|
(void)opaque;
|
||||||
(void)off;
|
(void)off;
|
||||||
(void)size;
|
(void)size;
|
||||||
return (uint64_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 12);
|
return ++tick;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void saimaa_tmr_write(void *opaque, hwaddr off, uint64_t val,
|
static void saimaa_tmr_write(void *opaque, hwaddr off, uint64_t val,
|
||||||
@@ -94,6 +101,57 @@ static void saimaa_tmr_init(Object *obj)
|
|||||||
sysbus_init_mmio(dev, &s->iomem);
|
sysbus_init_mmio(dev, &s->iomem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stubs region handle for set_readonly after scaffold implant. */
|
||||||
|
static MemoryRegion *saimaa_stubs_mr;
|
||||||
|
|
||||||
|
/* DDRLOW stub trap [0x08006B80,0x08006D80): serves the scaffolded bytes
|
||||||
|
* (S/maze/S6/banner/S7/S9/S10/S16/S17/S18/S20/S21 stubs) on read/fetch,
|
||||||
|
* ignores SBL heap-clobber writes. Uni parity: deferred-restore guard
|
||||||
|
* in sbl_uni.py (wider: [0x080068C0,0x08006D80), also covers PBL stub).
|
||||||
|
* Snapshot is taken AFTER ELF load + all stub writes (readback).
|
||||||
|
* PBL stub [0x080068C0,0x080068F4) and the ELF-text gap below stay plain
|
||||||
|
* RAM: PBL runs once (if at all), gap holds real hot SBL code. */
|
||||||
|
#define SAIMAA_DSTUB_BASE 0x08006B80
|
||||||
|
#define SAIMAA_DSTUB_SIZE 0x00000200
|
||||||
|
static uint8_t saimaa_dstub_mem[SAIMAA_DSTUB_SIZE];
|
||||||
|
|
||||||
|
static uint64_t saimaa_dstub_read(void *opaque, hwaddr off, unsigned size)
|
||||||
|
{
|
||||||
|
uint64_t v = 0;
|
||||||
|
unsigned i;
|
||||||
|
(void)opaque;
|
||||||
|
if (off >= SAIMAA_DSTUB_SIZE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (off + size > SAIMAA_DSTUB_SIZE) {
|
||||||
|
size = SAIMAA_DSTUB_SIZE - off;
|
||||||
|
}
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
v |= (uint64_t)saimaa_dstub_mem[off + i] << (8 * i);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void saimaa_dstub_write(void *opaque, hwaddr off, uint64_t val,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
static int n = 0;
|
||||||
|
(void)opaque;
|
||||||
|
(void)val;
|
||||||
|
(void)size;
|
||||||
|
if (n < 8) {
|
||||||
|
fprintf(stderr, "saimaa-dstub: ignored heap write @0x%x\n",
|
||||||
|
SAIMAA_DSTUB_BASE + (unsigned)off);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps saimaa_dstub_ops = {
|
||||||
|
.read = saimaa_dstub_read,
|
||||||
|
.write = saimaa_dstub_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
/* ---- minimal MSM UARTDM stub: TX prints, status always ready ---- */
|
/* ---- minimal MSM UARTDM stub: TX prints, status always ready ---- */
|
||||||
#define TYPE_SAIMAA_VEC "saimaa-vec"
|
#define TYPE_SAIMAA_VEC "saimaa-vec"
|
||||||
#define TYPE_SAIMAA_UART "saimaa-uart"
|
#define TYPE_SAIMAA_UART "saimaa-uart"
|
||||||
@@ -109,21 +167,46 @@ struct SaimaaUartState {
|
|||||||
static int saimaa_vec_n;
|
static int saimaa_vec_n;
|
||||||
static uint64_t saimaa_vec_read(void *opaque, hwaddr off, unsigned size)
|
static uint64_t saimaa_vec_read(void *opaque, hwaddr off, unsigned size)
|
||||||
{
|
{
|
||||||
if (saimaa_vec_n < 400) {
|
/* Maze blob v2 mirrored at [0,24) for THUMB fetches: null-jump
|
||||||
|
* fetches (bx r0=0, pop 0, ldr pc,=0) execute the maze and return
|
||||||
|
* via lr — uni parity (hook_null -> STUB). ARM-mode fetches (real
|
||||||
|
* exception entry) keep BKPT vectors below. Data probes elsewhere
|
||||||
|
* read 0. */
|
||||||
|
static const uint8_t maze[24] = {
|
||||||
|
0x00, 0x20, 0x4f, 0xea, 0x1e, 0x6c, 0xbc, 0xf1,
|
||||||
|
0x08, 0x0f, 0x03, 0xd1, 0x1e, 0xf0, 0x01, 0x0f,
|
||||||
|
0x00, 0xd0, 0x70, 0x47, 0x72, 0xb6, 0xfd, 0xe7,
|
||||||
|
};
|
||||||
|
{
|
||||||
|
ARMCPU *cpu = ARM_CPU(current_cpu);
|
||||||
|
if (cpu && cpu->env.thumb && off < sizeof(maze)) {
|
||||||
|
uint64_t v = 0;
|
||||||
|
unsigned i, n = size;
|
||||||
|
if (off + n > sizeof(maze)) {
|
||||||
|
n = sizeof(maze) - off;
|
||||||
|
}
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
v |= (uint64_t)maze[off + i] << (8 * i);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
} if (saimaa_vec_n < 400) {
|
||||||
ARMCPU *cpu = ARM_CPU(current_cpu);
|
ARMCPU *cpu = ARM_CPU(current_cpu);
|
||||||
uint64_t pc = cpu ? cpu->env.regs[15] : 0;
|
uint64_t pc = cpu ? cpu->env.regs[15] : 0;
|
||||||
int th = cpu ? cpu->env.thumb : 0;
|
int th = cpu ? cpu->env.thumb : 0;
|
||||||
uint32_t mb0 = 0, mb1 = 0;
|
uint32_t dfar = 0, ifsr = 0, dfsr = 0;
|
||||||
if (saimaa_vec_n < 3) {
|
if (cpu) {
|
||||||
cpu_physical_memory_read(0x08006964, &mb0, 4);
|
/* CP15 DFAR/IFSR/DFSR for abort diagnosis */
|
||||||
cpu_physical_memory_read(0x08006968, &mb1, 4);
|
dfar = cpu->env.cp15.dfar_s;
|
||||||
|
ifsr = (uint32_t)cpu->env.cp15.ifsr_s;
|
||||||
|
dfsr = (uint32_t)cpu->env.cp15.dfsr_s;
|
||||||
|
fprintf(stderr, " SCTLR=0x%lx", (unsigned long)cpu->env.cp15.sctlr_s);
|
||||||
}
|
}
|
||||||
fprintf(stderr, "saimaa-vec: %s off=0x%x pc=0x%lx",
|
fprintf(stderr, "saimaa-vec: %s off=0x%x pc=0x%lx",
|
||||||
size == 2 ? "fetch?" : "read",
|
size == 2 ? "fetch?" : "read",
|
||||||
(unsigned)off, (unsigned long)pc - (th ? 4 : 8));
|
(unsigned)off, (unsigned long)pc - (th ? 4 : 8));
|
||||||
if (saimaa_vec_n < 3) {
|
fprintf(stderr, " DFAR=0x%x IFSR=0x%x DFSR=0x%x", dfar, ifsr,
|
||||||
fprintf(stderr, " maze-mailbox: lr=0x%x sp=0x%x", mb0, mb1);
|
dfsr);
|
||||||
}
|
|
||||||
if (cpu) {
|
if (cpu) {
|
||||||
int b, r;
|
int b, r;
|
||||||
fprintf(stderr, " regs=");
|
fprintf(stderr, " regs=");
|
||||||
@@ -248,19 +331,27 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
{ "saimaa.rpm", SAIMAA_RPM_BASE, SAIMAA_RPM_SIZE },
|
{ "saimaa.rpm", SAIMAA_RPM_BASE, SAIMAA_RPM_SIZE },
|
||||||
{ "saimaa.ocimem", SAIMAA_OCIMEM_BASE, SAIMAA_OCIMEM_SIZE },
|
{ "saimaa.ocimem", SAIMAA_OCIMEM_BASE, SAIMAA_OCIMEM_SIZE },
|
||||||
{ "saimaa.ddrlow", SAIMAA_DDRLOW_BASE, SAIMAA_DDRLOW_SIZE },
|
{ "saimaa.ddrlow", SAIMAA_DDRLOW_BASE, SAIMAA_DDRLOW_SIZE },
|
||||||
{ "saimaa.ddrhi", SAIMAA_DDRHI_BASE, SAIMAA_DDRHI_SIZE },
|
{ "saimaa.ddrhi_lo", SAIMAA_DDRHI_BASE,
|
||||||
|
SAIMAA_STUBS_BASE - SAIMAA_DDRHI_BASE },
|
||||||
|
{ "saimaa.stubs", SAIMAA_STUBS_BASE, SAIMAA_STUBS_SIZE },
|
||||||
|
{ "saimaa.ddrhi_hi", SAIMAA_STUBS_BASE + SAIMAA_STUBS_SIZE,
|
||||||
|
SAIMAA_DDRHI_BASE + SAIMAA_DDRHI_SIZE -
|
||||||
|
(SAIMAA_STUBS_BASE + SAIMAA_STUBS_SIZE) },
|
||||||
{ "saimaa.imem", SAIMAA_IMEM_BASE, SAIMAA_IMEM_SIZE },
|
{ "saimaa.imem", SAIMAA_IMEM_BASE, SAIMAA_IMEM_SIZE },
|
||||||
{ "saimaa.ddrhigh_a", SAIMAA_DDRH_A_BASE, SAIMAA_DDRH_A_SIZE },
|
{ "saimaa.ddrhigh_a", SAIMAA_DDRH_A_BASE, SAIMAA_DDRH_A_SIZE },
|
||||||
{ "saimaa.ddrhigh_b", SAIMAA_DDRH_B_BASE, SAIMAA_DDRH_B_SIZE },
|
{ "saimaa.ddrhigh_b", SAIMAA_DDRH_B_BASE, SAIMAA_DDRH_B_SIZE },
|
||||||
{ "saimaa.mystrom", SAIMAA_MYSTROM_BASE, SAIMAA_MYSTROM_SIZE },
|
{ "saimaa.mystrom", SAIMAA_MYSTROM_BASE, SAIMAA_MYSTROM_SIZE },
|
||||||
{ "saimaa.smem", SAIMAA_SMEM_BASE, SAIMAA_SMEM_SIZE },
|
{ "saimaa.smem", SAIMAA_SMEM_BASE, SAIMAA_SMEM_SIZE },
|
||||||
};
|
};
|
||||||
for (i = 0; i < 10; i++) {
|
for (i = 0; i < ARRAY_SIZE(rams); i++) {
|
||||||
MemoryRegion *ram = g_new(MemoryRegion, 1);
|
MemoryRegion *ram = g_new(MemoryRegion, 1);
|
||||||
memory_region_init_ram(ram, NULL, rams[i].n, rams[i].s,
|
memory_region_init_ram(ram, NULL, rams[i].n, rams[i].s,
|
||||||
&error_fatal);
|
&error_fatal);
|
||||||
memory_region_add_subregion_overlap(sysmem, rams[i].b, ram,
|
memory_region_add_subregion_overlap(sysmem, rams[i].b, ram,
|
||||||
1);
|
1);
|
||||||
|
if (!strcmp(rams[i].n, "saimaa.stubs")) {
|
||||||
|
saimaa_stubs_mr = ram;
|
||||||
|
}
|
||||||
if (!strcmp(rams[i].n, "saimaa.mystrom")) {
|
if (!strcmp(rams[i].n, "saimaa.mystrom")) {
|
||||||
/* Mystery handler ROM: ARM mov r0,#0; bx lr repeating,
|
/* Mystery handler ROM: ARM mov r0,#0; bx lr repeating,
|
||||||
* so unknown blx targets return 0 instead of faulting. */
|
* so unknown blx targets return 0 instead of faulting. */
|
||||||
@@ -387,6 +478,12 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
ARM_CPU(first_cpu)->env.banked_r13[b] = 0x085FFF00;
|
ARM_CPU(first_cpu)->env.banked_r13[b] = 0x085FFF00;
|
||||||
}
|
}
|
||||||
ARM_CPU(first_cpu)->env.regs[13] = 0x085FFF00;
|
ARM_CPU(first_cpu)->env.regs[13] = 0x085FFF00;
|
||||||
|
/* Stale lr (never bl'd on synthetic paths) returns into maze
|
||||||
|
* (returns 0 = SBL "empty" convention) instead of jumping to 0. */
|
||||||
|
ARM_CPU(first_cpu)->env.regs[14] = 0x08006B8D;
|
||||||
|
for (int b = 0; b < 8; b++) {
|
||||||
|
ARM_CPU(first_cpu)->env.banked_r14[b] = 0x08006B8D;
|
||||||
|
}
|
||||||
ARM_CPU(first_cpu)->env.regs[0] = 0x00220000; /* fake pbl_shared */
|
ARM_CPU(first_cpu)->env.regs[0] = 0x00220000; /* fake pbl_shared */
|
||||||
/* sbl_main expects r5 = mem-table pointer (PBL trampoline does
|
/* sbl_main expects r5 = mem-table pointer (PBL trampoline does
|
||||||
* mov r5, r0 before jumping here); point it at pbl_shared too. */
|
* mov r5, r0 before jumping here); point it at pbl_shared too. */
|
||||||
@@ -432,7 +529,10 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
tgt & ~1u, (tgt & 1) ? " thumb" : " arm",
|
tgt & ~1u, (tgt & 1) ? " thumb" : " arm",
|
||||||
(unsigned long)entry);
|
(unsigned long)entry);
|
||||||
} else {
|
} else {
|
||||||
cpu_set_pc(first_cpu, entry);
|
/* Default: start at PBL stub (faithful call-then-jump flow) */
|
||||||
|
cpu_set_pc(first_cpu, 0x080068C1);
|
||||||
|
printf("saimaa: PBL-stub start -> entry 0x%lx then sbl_main\n",
|
||||||
|
(unsigned long)entry);
|
||||||
}
|
}
|
||||||
/* Boot scaffolding for SBL1 BringUp (WIP stub-driven PBL):
|
/* Boot scaffolding for SBL1 BringUp (WIP stub-driven PBL):
|
||||||
* static continuation slots for shared-epilogue pops.
|
* static continuation slots for shared-epilogue pops.
|
||||||
@@ -440,30 +540,31 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
* (sp values from cpu traces, b10 image) */
|
* (sp values from cpu traces, b10 image) */
|
||||||
{
|
{
|
||||||
uint32_t magic = 0x00000000; /* IMEM poll spins WHILE equal */
|
uint32_t magic = 0x00000000; /* IMEM poll spins WHILE equal */
|
||||||
uint32_t S = 0x08006900, STUB = 0x08006910; /* in-code zeros, safe from heap */
|
uint32_t S = 0x08006B80, STUB = 0x08006B8C; /* dead PBL-entry code */
|
||||||
uint32_t STUBV = 0x08006911; /* odd: blx stays Thumb */
|
uint32_t STUBV = 0x08006B8D; /* odd: blx stays Thumb */
|
||||||
uint32_t w;
|
uint32_t w;
|
||||||
uint16_t h0 = 0x2000, h1 = 0x4770; /* movs r0,#0; bx lr */
|
uint16_t h0 = 0x2000, h1 = 0x4770; /* movs r0,#0; bx lr */
|
||||||
uint32_t v;
|
uint32_t v;
|
||||||
/* pointer maze in owned RAM for SBL1 struct chains.
|
/* pointer maze in owned RAM for SBL1 struct chains.
|
||||||
* STUB = mailbox logger + return-0: records lr/sp at entry
|
* STUB = minimal logger + return-0 (movs r0,#0; bx lr);
|
||||||
* to 0x08006964 for post-mortem, then movs r0,#0; bx lr. */
|
* mailbox at 0x08006BD0 records last (lr,sp). */
|
||||||
w = S + 8;
|
w = S + 8;
|
||||||
cpu_physical_memory_write(S, &w, 4);
|
cpu_physical_memory_write(S, &w, 4);
|
||||||
/* [struct+4] is another fn ptr (e.g. 0x801b72e ldr r3,[r0,#4]
|
/* [struct+4] is another fn ptr (e.g. 0x801b72e ldr r3,[r0,#4]
|
||||||
* with r0 = maze S via POOLS redirect); point at STUB too */
|
* with r0 = maze S via POOLS redirect); point at STUB too.
|
||||||
cpu_physical_memory_write(S + 4, &STUB, 4);
|
* NOTE: ODD (STUBV) — blx to even would switch to ARM! */
|
||||||
cpu_physical_memory_write(S + 8, &STUB, 4);
|
cpu_physical_memory_write(S + 4, &STUBV, 4);
|
||||||
|
cpu_physical_memory_write(S + 8, &STUBV, 4);
|
||||||
{
|
{
|
||||||
/* from qemu/maze.S: mailbox logger + lr-range guard.
|
/* Maze v2 (24B @STUB): movs r0,#0; return 0 via lr ONLY
|
||||||
* Bad lr (not 0x08xxxxxx) spins with IRQs off instead of
|
* if lr is odd Thumb in DDR (top 8); even lr (stale
|
||||||
* jumping into the void. 36 bytes. */
|
* mid-fn raw addr!) would ARM-drift (STM-clobbers +
|
||||||
|
* IMEM fault), so hang loud instead. Overlaps S6 head
|
||||||
|
* ([B9E,BA4), S6 dormant, documented below). */
|
||||||
static const uint8_t mb[] = {
|
static const uint8_t mb[] = {
|
||||||
0x46, 0xf6, 0x64, 0x1c, 0xc0, 0xf6, 0x00, 0x0c,
|
|
||||||
0xcc, 0xf8, 0x00, 0xe0, 0xcc, 0xf8, 0x04, 0xd0,
|
|
||||||
0x00, 0x20, 0x4f, 0xea, 0x1e, 0x6c, 0xbc, 0xf1,
|
0x00, 0x20, 0x4f, 0xea, 0x1e, 0x6c, 0xbc, 0xf1,
|
||||||
0x08, 0x0f, 0x00, 0xd1, 0x70, 0x47, 0x72, 0xb6,
|
0x08, 0x0f, 0x03, 0xd1, 0x1e, 0xf0, 0x01, 0x0f,
|
||||||
0xfd, 0xe7, 0x00, 0x00,
|
0x00, 0xd0, 0x70, 0x47, 0x72, 0xb6, 0xfd, 0xe7,
|
||||||
};
|
};
|
||||||
size_t k;
|
size_t k;
|
||||||
for (k = 0; k < sizeof(mb); k += 4) {
|
for (k = 0; k < sizeof(mb); k += 4) {
|
||||||
@@ -476,24 +577,12 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
cpu_physical_memory_write(0x087c29d18, &S, 4);
|
cpu_physical_memory_write(0x087c29d18, &S, 4);
|
||||||
cpu_physical_memory_write(0x08050b28, &S, 4);
|
cpu_physical_memory_write(0x08050b28, &S, 4);
|
||||||
cpu_physical_memory_write(0x08070000, &S, 4);
|
cpu_physical_memory_write(0x08070000, &S, 4);
|
||||||
/* S6 stublet at 0x08006934: rebuild popped frame regs,
|
/* S6 stublet DISABLED (maze v2 overlaps its head [B9E,BA4);
|
||||||
* return to 0x8013ba5 (b14 redirects 0x8017afc pop here) */
|
* S6 dormant, never entered (b14 goes to 0x8005B9E).
|
||||||
{
|
* Bytes kept for reference (would live at 0x08006B9E):
|
||||||
static const uint8_t s6[] = {
|
* ldmia.w sp!,{r2-r9,sl,lr}; movw lr,#0x3ba5;
|
||||||
0xbd, 0xe8, 0xfc, 0x47, /* ldmia.w sp!,{r2-r9,sl,lr} */
|
* movt lr,#0x801; bx lr. */
|
||||||
0x43, 0xf6, 0xa5, 0x3e, /* movw lr,#0x3ba5 */
|
/* Banner stub at 0x08006BAC: print r0 string via UARTDM
|
||||||
0xc0, 0xf6, 0x01, 0x0e, /* movt lr,#0x801 */
|
|
||||||
0x70, 0x47, /* bx lr */
|
|
||||||
};
|
|
||||||
size_t k;
|
|
||||||
for (k = 0; k < sizeof(s6); k += 4) {
|
|
||||||
uint32_t word = 0;
|
|
||||||
size_t n = sizeof(s6) - k < 4 ? sizeof(s6) - k : 4;
|
|
||||||
__builtin_memcpy(&word, &s6[k], n);
|
|
||||||
cpu_physical_memory_write(0x08006934 + k, &word, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Banner stub at 0x08006942: print r0 string via UARTDM
|
|
||||||
* (b16 redirects 0x802f690 blx here). Built from qemu/banner.S:
|
* (b16 redirects 0x802f690 blx here). Built from qemu/banner.S:
|
||||||
* push frame, print loop, ldr pc,=0x802f694 (callee noreturn).
|
* push frame, print loop, ldr pc,=0x802f694 (callee noreturn).
|
||||||
* 28 bytes total. */
|
* 28 bytes total. */
|
||||||
@@ -510,18 +599,263 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
uint32_t word = 0;
|
uint32_t word = 0;
|
||||||
size_t n = sizeof(bn) - k < 4 ? sizeof(bn) - k : 4;
|
size_t n = sizeof(bn) - k < 4 ? sizeof(bn) - k : 4;
|
||||||
__builtin_memcpy(&word, &bn[k], n);
|
__builtin_memcpy(&word, &bn[k], n);
|
||||||
cpu_physical_memory_write(0x08006942 + k, &word, 4);
|
cpu_physical_memory_write(0x08006BAC + k, &word, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* stack zone defaults to plain-stub addr: every register
|
/* PBL stub at 0x080068C0 (52B, from qemu/pbl.S): BLX to SBL1
|
||||||
* popped from untouched stack becomes callable (returns 0). */
|
* entry, then canonical regs + jump to sbl_main. Used when
|
||||||
|
* sblmain prop is unset (default): faithful PBL call flow. */
|
||||||
{
|
{
|
||||||
uint32_t fill = 0x08006911;
|
static const uint8_t pbl[] = {
|
||||||
|
0xf0, 0xb5, 0x46, 0xf6, 0x81, 0x3c, 0xc0, 0xf6,
|
||||||
|
0x00, 0x0c, 0xe0, 0x47, 0x40, 0xf2, 0x00, 0x00,
|
||||||
|
0xc0, 0xf2, 0x22, 0x00, 0x05, 0x46, 0x40, 0xf2,
|
||||||
|
0x00, 0x0a, 0xc0, 0xf6, 0x06, 0x0a, 0x41, 0xf2,
|
||||||
|
0x00, 0x0b, 0xc0, 0xf6, 0x06, 0x0b, 0x4f, 0xf2,
|
||||||
|
0x3f, 0x6e, 0xc0, 0xf6, 0x02, 0x0e, 0x70, 0x47,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(pbl); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(pbl) - k < 4 ? sizeof(pbl) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &pbl[k], n);
|
||||||
|
cpu_physical_memory_write(0x080068C0 + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* S7 stublet at 0x08006C10 (52B, b18): delay-epilogue exit
|
||||||
|
* router. cmp r4,#0xff: iterate (r4<=0xff -> 0x8013ba5) or
|
||||||
|
* done (restore r4/r5/sl/fp, return to sbl_main 0x802f681).
|
||||||
|
* Assembled with keystone (see docs/sbl-bringup.md). */
|
||||||
|
{
|
||||||
|
static const uint8_t s7[] = {
|
||||||
|
0xff, 0x2c, 0x13, 0xd9, 0x40, 0xf2, 0x00, 0x04,
|
||||||
|
0xc0, 0xf2, 0x22, 0x04, 0x40, 0xf2, 0x00, 0x05,
|
||||||
|
0xc0, 0xf2, 0x22, 0x05, 0x40, 0xf2, 0x00, 0x0a,
|
||||||
|
0xc0, 0xf6, 0x06, 0x0a, 0x41, 0xf2, 0x00, 0x0b,
|
||||||
|
0xc0, 0xf6, 0x06, 0x0b, 0xdf, 0xf8, 0x00, 0xf0,
|
||||||
|
0x81, 0xf6, 0x02, 0x08, 0xdf, 0xf8, 0x00, 0xf0,
|
||||||
|
0xa5, 0x3b, 0x01, 0x08,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(s7); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(s7) - k < 4 ? sizeof(s7) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &s7[k], n);
|
||||||
|
cpu_physical_memory_write(0x08006C10 + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* S9 stublet at 0x08006C48 (62B, b22): labyrinth-epilogue
|
||||||
|
* exit router (0x801B6BE pop.w -> b.w S9). Empty frames ->
|
||||||
|
* restore r0/r4/r5/sl/fp, reset sp to 0x085FFF00, jump to
|
||||||
|
* sbl_main 0x802f69d via ip. Assembled with keystone. */
|
||||||
|
{
|
||||||
|
static const uint8_t s9[] = {
|
||||||
|
0x05, 0x9b, 0x0b, 0xb1, 0xbd, 0xe8, 0xf0, 0x81,
|
||||||
|
0x00, 0x20, 0x40, 0xf2, 0x00, 0x04, 0xc0, 0xf2,
|
||||||
|
0x22, 0x04, 0x40, 0xf2, 0x00, 0x05, 0xc0, 0xf2,
|
||||||
|
0x22, 0x05, 0x40, 0xf2, 0x00, 0x0a, 0xc0, 0xf6,
|
||||||
|
0x06, 0x0a, 0x41, 0xf2, 0x00, 0x0b, 0xc0, 0xf6,
|
||||||
|
0x06, 0x0b, 0x4f, 0xf6, 0x00, 0x73, 0xc0, 0xf6,
|
||||||
|
0x5f, 0x03, 0x9d, 0x46, 0x4f, 0xf2, 0x9d, 0x6c,
|
||||||
|
0xc0, 0xf6, 0x02, 0x0c, 0x60, 0x47,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(s9); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(s9) - k < 4 ? sizeof(s9) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &s9[k], n);
|
||||||
|
cpu_physical_memory_write(0x08006C48 + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* S10 stublet at 0x08006C88 (10B, b23): rollback-exit router.
|
||||||
|
* movw/movt ip + bx ip to sbl_main 0x802f6a3. */
|
||||||
|
{
|
||||||
|
static const uint8_t s10[] = {
|
||||||
|
0x4f, 0xf2, 0xa3, 0x6c, 0xc0, 0xf6, 0x02, 0x0c,
|
||||||
|
0x60, 0x47,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(s10); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(s10) - k < 4 ? sizeof(s10) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &s10[k], n);
|
||||||
|
cpu_physical_memory_write(0x08006C88 + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* S18 stublet at 0x08006D22 (10B, b29): poll-epilogue router.
|
||||||
|
* movw/movt ip + bx ip to sbl_main 0x802f6a9. */
|
||||||
|
{
|
||||||
|
static const uint8_t s18[] = {
|
||||||
|
0x4f, 0xf2, 0xa9, 0x6c, 0xc0, 0xf6, 0x02, 0x0c,
|
||||||
|
0x60, 0x47,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(s18); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(s18) - k < 4 ? sizeof(s18) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &s18[k], n);
|
||||||
|
cpu_physical_memory_write(0x08006D22 + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* S21 stublet at 0x08006D5A (32B, b38): table-walker router.
|
||||||
|
* [sp+0x24] valid-code? => original pop.w (legit return);
|
||||||
|
* else b.w S19 (sbl_main post-logger restart). */
|
||||||
|
{
|
||||||
|
static const uint8_t s21[] = {
|
||||||
|
0x09, 0x9b, 0x13, 0xf0, 0x01, 0x0f, 0x09, 0xd0,
|
||||||
|
0x1a, 0x0e, 0x08, 0x2a, 0x01, 0xd1, 0xbd, 0xe8,
|
||||||
|
0xf0, 0x9f, 0x1a, 0x0f, 0x08, 0x2a, 0x01, 0xd1,
|
||||||
|
0xbd, 0xe8, 0xf0, 0x9f, 0xff, 0xf7, 0xd9, 0xbf,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(s21); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(s21) - k < 4 ? sizeof(s21) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &s21[k], n);
|
||||||
|
cpu_physical_memory_write(0x08006D5A + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* S20 stublet at 0x08006D38 (34B, b37): clock-dispatcher
|
||||||
|
* router. [sp+0x2C] valid-code? => original pop.w (legit);
|
||||||
|
* else r0=sl + b.w dispatcher re-run (idempotent math). */
|
||||||
|
{
|
||||||
|
static const uint8_t s20[] = {
|
||||||
|
0x0b, 0x9b, 0x13, 0xf0, 0x01, 0x0f, 0x09, 0xd0,
|
||||||
|
0x1a, 0x0e, 0x08, 0x2a, 0x01, 0xd1, 0xbd, 0xe8,
|
||||||
|
0xfe, 0x8f, 0x1a, 0x0f, 0x08, 0x2a, 0x01, 0xd1,
|
||||||
|
0xbd, 0xe8, 0xfe, 0x8f, 0x50, 0x46, 0x07, 0xf0,
|
||||||
|
0x13, 0xbf,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(s20); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(s20) - k < 4 ? sizeof(s20) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &s20[k], n);
|
||||||
|
cpu_physical_memory_write(0x08006D38 + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* S17 safe-logger (50B @0x08006CF0, b26): print r1-string
|
||||||
|
* via UARTDM-TF iff r1 in rodata [0x804B800,0x8058000);
|
||||||
|
* pop-return (no leak). Replaces S8 silence with logs. */
|
||||||
|
{
|
||||||
|
static const uint8_t s17[] = {
|
||||||
|
0x4b, 0xf6, 0x00, 0x03, 0xc0, 0xf6, 0x04, 0x03,
|
||||||
|
0x99, 0x42, 0x11, 0xd3, 0x48, 0xf2, 0x00, 0x03,
|
||||||
|
0xc0, 0xf6, 0x05, 0x03, 0x99, 0x42, 0x0b, 0xd2,
|
||||||
|
0x10, 0xb5, 0x4f, 0xf2, 0x00, 0x03, 0xc0, 0xf2,
|
||||||
|
0x8a, 0x73, 0x0c, 0x46, 0x14, 0xf8, 0x01, 0x0b,
|
||||||
|
0x08, 0xb1, 0x18, 0x67, 0xfa, 0xe7, 0x10, 0xbd,
|
||||||
|
0x70, 0x47,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(s17); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(s17) - k < 4 ? sizeof(s17) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &s17[k], n);
|
||||||
|
cpu_physical_memory_write(0x08006CF0 + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* PBL version-table fabrication (b23): [0x080528D0] = STUBV
|
||||||
|
* so the rollback query's blx calls maze (0 = versions OK). */
|
||||||
|
{
|
||||||
|
uint32_t v = 0x08006B8D;
|
||||||
|
cpu_physical_memory_write(0x080528D0, &v, 4);
|
||||||
|
}
|
||||||
|
/* PBL dispatcher-table fabrication (b33, moved b37): retarget
|
||||||
|
* literal [0x80164F0] (image: hash blob!) to scratch struct
|
||||||
|
* at 0x08024000 (OCIMEM, out of stack-march paths!) with
|
||||||
|
* [0x18]=2 (ready -> dispatch). Was IMEM 0x08600200
|
||||||
|
* (self-executed when sp marched there!). */
|
||||||
|
{
|
||||||
|
uint32_t v = 0x08024000;
|
||||||
|
cpu_physical_memory_write(0x08024000, &v, 4);
|
||||||
|
v = 0x00000002;
|
||||||
|
cpu_physical_memory_write(0x08024014, &v, 4);
|
||||||
|
v = 0x08024000;
|
||||||
|
cpu_physical_memory_write(0x080164F0, &v, 4);
|
||||||
|
}
|
||||||
|
/* HW-ready bits (b25/b26): [0x073A100] bits 0,1,2 (SBL
|
||||||
|
* read-only polls: bit2 poll#1, bit1 poll#2, bit0 gate). */
|
||||||
|
{
|
||||||
|
uint32_t v = 0x00000007;
|
||||||
|
cpu_physical_memory_write(0x073A100, &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);
|
||||||
|
}
|
||||||
|
/* S12-stub (b27, 100B @0x08006964, ARM): blx-to-padding
|
||||||
|
* becomes smart dispatcher: ldr ip,[sp,#4] (=pushed lr);
|
||||||
|
* ip==STUBV/even/non-code => ip=poll resumption 0x8007779;
|
||||||
|
* else keep pushed-lr (NO add-sp!); restore r7;
|
||||||
|
* ACK peripheral bit1 ([literal 0x073A100]); bx lr.
|
||||||
|
* PBL would place the real ARM helper here. */
|
||||||
|
{
|
||||||
|
static const uint8_t s12[] = {
|
||||||
|
0x04, 0xc0, 0x9d, 0xe5, 0x8d, 0x3b, 0x06, 0xe3,
|
||||||
|
0x00, 0x38, 0x40, 0xe3, 0x03, 0x00, 0x5c, 0xe1,
|
||||||
|
0x09, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x1c, 0xe3,
|
||||||
|
0x07, 0x00, 0x00, 0x0a, 0x2c, 0x3c, 0xa0, 0xe1,
|
||||||
|
0x08, 0x00, 0x53, 0xe3, 0x00, 0x00, 0x00, 0x1a,
|
||||||
|
0x05, 0x00, 0x00, 0xea, 0x2c, 0x3e, 0xa0, 0xe1,
|
||||||
|
0x08, 0x00, 0x53, 0xe3, 0x00, 0x00, 0x00, 0x1a,
|
||||||
|
0x01, 0x00, 0x00, 0xea, 0x79, 0xc7, 0x07, 0xe3,
|
||||||
|
0x00, 0xc8, 0x40, 0xe3, 0x00, 0x70, 0x0a, 0xe3,
|
||||||
|
0x73, 0x70, 0x40, 0xe3, 0x0c, 0x30, 0x9f, 0xe5,
|
||||||
|
0x00, 0x20, 0x93, 0xe5, 0x02, 0x20, 0x82, 0xe3,
|
||||||
|
0x00, 0x20, 0x83, 0xe5, 0x1e, 0xff, 0x2f, 0xe1,
|
||||||
|
0x00, 0xa1, 0x73, 0x00,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(s12); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(s12) - k < 4 ? sizeof(s12) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &s12[k], n);
|
||||||
|
cpu_physical_memory_write(0x08006964 + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* S16-stublet (b27, 64B @0x08006CC0): smart return for the
|
||||||
|
* B66 bx-ip chain. ip==STUBV (fill/maze, never legit) =>
|
||||||
|
* default; ip odd + top 8 => add sp,#8 (pop the helper
|
||||||
|
* frame B66 skipped!) + bx ip (legit pushed-lr, un-leaks);
|
||||||
|
* else default (restore r7, jump poll resumption 0x8007779
|
||||||
|
* via ip). */
|
||||||
|
{
|
||||||
|
static const uint8_t s16[] = {
|
||||||
|
0xdd, 0xf8, 0x04, 0xc0, 0x46, 0xf6, 0x8d, 0x33,
|
||||||
|
0xc0, 0xf6, 0x00, 0x03, 0x9c, 0x45, 0x0e, 0xd0,
|
||||||
|
0x1c, 0xf0, 0x01, 0x0f, 0x0b, 0xd0, 0x5f, 0xea,
|
||||||
|
0x1c, 0x63, 0x08, 0x2b, 0x01, 0xd1, 0x02, 0xb0,
|
||||||
|
0x60, 0x47, 0x5f, 0xea, 0x1c, 0x73, 0x08, 0x2b,
|
||||||
|
0x01, 0xd1, 0x02, 0xb0, 0x60, 0x47, 0x4a, 0xf2,
|
||||||
|
0x00, 0x07, 0xc0, 0xf2, 0x73, 0x07, 0x47, 0xf2,
|
||||||
|
0x79, 0x7c, 0xc0, 0xf6, 0x00, 0x0c, 0x60, 0x47,
|
||||||
|
};
|
||||||
|
size_t k;
|
||||||
|
for (k = 0; k < sizeof(s16); k += 4) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
size_t n = sizeof(s16) - k < 4 ? sizeof(s16) - k : 4;
|
||||||
|
__builtin_memcpy(&word, &s16[k], n);
|
||||||
|
cpu_physical_memory_write(0x08006CC0 + k, &word, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32_t fill = 0x08006B8D;
|
||||||
uint32_t a;
|
uint32_t a;
|
||||||
for (a = 0x085F0000; a < 0x08600000; a += 4) {
|
for (a = 0x085F0000; a < 0x08600000; a += 4) {
|
||||||
cpu_physical_memory_write(a, &fill, 4);
|
cpu_physical_memory_write(a, &fill, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* IMEM tripwire [0x8600000,0x8600004): drift-sink trap.
|
||||||
|
* Must stay 4B (wider poison corrupts IMEM-stack reads). */
|
||||||
|
{
|
||||||
|
uint32_t p = 0xFFFFFFFF;
|
||||||
|
cpu_physical_memory_write(0x08600000, &p, 4);
|
||||||
|
}
|
||||||
/* pop-site slots -> stublets (sp values from cpu traces) */
|
/* pop-site slots -> stublets (sp values from cpu traces) */
|
||||||
v = 0x802f65d; /* S1 pop10 @fef0: resume sbl_main */
|
v = 0x802f65d; /* S1 pop10 @fef0: resume sbl_main */
|
||||||
cpu_physical_memory_write(0x085FFF14, &v, 4);
|
cpu_physical_memory_write(0x085FFF14, &v, 4);
|
||||||
@@ -595,6 +929,23 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
printf("saimaa: uefi tables live\n");
|
printf("saimaa: uefi tables live\n");
|
||||||
}
|
}
|
||||||
printf("saimaa: slots live\n");
|
printf("saimaa: slots live\n");
|
||||||
|
/* DDRLOW stub trap overlay: snapshot ELF+stubs, serve on
|
||||||
|
* read/fetch, ignore heap writes (uni parity). Must come
|
||||||
|
* AFTER all stub writes above. Priority 2 > RAMs' 1. */
|
||||||
|
cpu_physical_memory_read(SAIMAA_DSTUB_BASE, saimaa_dstub_mem,
|
||||||
|
SAIMAA_DSTUB_SIZE);
|
||||||
|
{
|
||||||
|
MemoryRegion *dstub = g_new(MemoryRegion, 1);
|
||||||
|
memory_region_init_io(dstub, NULL, &saimaa_dstub_ops,
|
||||||
|
NULL, "saimaa-dstub",
|
||||||
|
SAIMAA_DSTUB_SIZE);
|
||||||
|
memory_region_add_subregion_overlap(sysmem,
|
||||||
|
SAIMAA_DSTUB_BASE,
|
||||||
|
dstub, 2);
|
||||||
|
}
|
||||||
|
/* Freeze stubs: any SBL write here now faults loudly
|
||||||
|
* (data abort with DFAR) instead of silent corruption. */
|
||||||
|
memory_region_set_readonly(saimaa_stubs_mr, true);
|
||||||
}
|
}
|
||||||
printf("saimaa: SBL1 %s loaded (%zd bytes), entry 0x%lx\n",
|
printf("saimaa: SBL1 %s loaded (%zd bytes), entry 0x%lx\n",
|
||||||
sms->sbl1, sz, (unsigned long)entry);
|
sms->sbl1, sz, (unsigned long)entry);
|
||||||
@@ -604,7 +955,7 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
|
|
||||||
static void saimaa_post_reset(void *opaque)
|
static void saimaa_post_reset(void *opaque)
|
||||||
{
|
{
|
||||||
uint32_t S = 0x08006900;
|
uint32_t S = 0x08006B80;
|
||||||
(void)opaque;
|
(void)opaque;
|
||||||
/* Re-apply pokes that live inside ROM-covered ranges (rom_reset
|
/* Re-apply pokes that live inside ROM-covered ranges (rom_reset
|
||||||
* restores file bytes over them). */
|
* restores file bytes over them). */
|
||||||
|
|||||||
+2173
-5
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,707 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""sbl_uni.py — быстрый harness SBL1 на Unicorn (без пересборки QEMU).
|
||||||
|
Карта RAM как в saimaa; ELF-сегменты; UARTDM/TF в stdout; MMIO->0.
|
||||||
|
Использование:
|
||||||
|
./tools/sbl_uni.py fw/img/patch/img0_b17.elf --thumb 0x802f63f [--regs r0=0x220000,...]
|
||||||
|
[--fill 0x085F0000:0x10000:0x08006831] [--poke 0xADDR=0xVAL] [--limit 1000000]
|
||||||
|
Печатает: выполнено инстр., последний PC, fault-адрес, UART.
|
||||||
|
"""
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from unicorn import (Uc, UC_ARCH_ARM, UC_MODE_THUMB, UC_HOOK_MEM_UNMAPPED,
|
||||||
|
UC_HOOK_MEM_WRITE, UC_HOOK_CODE)
|
||||||
|
from unicorn.arm_const import (UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2,
|
||||||
|
UC_ARM_REG_R3, UC_ARM_REG_R4, UC_ARM_REG_R5,
|
||||||
|
UC_ARM_REG_R6, UC_ARM_REG_R7, UC_ARM_REG_SP,
|
||||||
|
UC_ARM_REG_LR, UC_ARM_REG_PC,
|
||||||
|
UC_ARM_REG_CPSR)
|
||||||
|
_REGS = (UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3,
|
||||||
|
UC_ARM_REG_R4, UC_ARM_REG_R5, UC_ARM_REG_R6, UC_ARM_REG_R7)
|
||||||
|
|
||||||
|
RAMS = [
|
||||||
|
(0x00000000, 0x00001000), # zero page (QEMU-compat: [0] writes succeed)
|
||||||
|
(0x00200000, 0x00100000), # OCIMEM
|
||||||
|
(0x00400000, 0x07400000), # RPM range (heap-ish)
|
||||||
|
(0x08000000, 0x00600000), # DDRLOW
|
||||||
|
(0x08600000, 0x00010000), # IMEM
|
||||||
|
(0x80000000, 0x07C00000), # DDRHIGH_A
|
||||||
|
(0x87C00000, 0x00800000), # SMEM(+SBL BSS)
|
||||||
|
(0x078AF000, 0x00001000), # UARTDM page
|
||||||
|
(0x13000000, 0x00002000), # UEFI tables (already above, keep)
|
||||||
|
]
|
||||||
|
UART_TF = 0x078AF070
|
||||||
|
UART_SR = 0x078AF008
|
||||||
|
|
||||||
|
|
||||||
|
def load_elf(mu, path):
|
||||||
|
d = open(path, "rb").read()
|
||||||
|
assert d[:4] == b"\x7fELF"
|
||||||
|
entry = struct.unpack("<I", d[24:28])[0]
|
||||||
|
phoff = struct.unpack("<I", d[28:32])[0]
|
||||||
|
phnum = struct.unpack("<H", d[44:46])[0]
|
||||||
|
for k in range(phnum):
|
||||||
|
p = d[phoff + k * 32:phoff + (k + 1) * 32]
|
||||||
|
t, off, v, pa, fsz, msz, fl, al = struct.unpack("<IIIIIIII", p)
|
||||||
|
if t != 1:
|
||||||
|
continue
|
||||||
|
if fsz and v != 0x00220000: # QEMU parity: skip OCIMEM seg file-bytes
|
||||||
|
mu.mem_write(v, d[off:off + fsz])
|
||||||
|
for z in range(v + fsz, v + msz, 4):
|
||||||
|
mu.mem_write(z, b"\x00\x00\x00\x00")
|
||||||
|
return entry
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
path = sys.argv[1]
|
||||||
|
thumb = int(sys.argv[sys.argv.index("--thumb") + 1], 0)
|
||||||
|
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
|
||||||
|
for base, size in RAMS:
|
||||||
|
mu.mem_map(base, size)
|
||||||
|
uart_out = []
|
||||||
|
unmapped = {"n": 0, "first": []}
|
||||||
|
|
||||||
|
def hook_unmapped(mu, access, addr, size, value, data):
|
||||||
|
unmapped["n"] += 1
|
||||||
|
if len(unmapped["first"]) < 5:
|
||||||
|
unmapped["first"].append((hex(addr), size, access))
|
||||||
|
return False
|
||||||
|
|
||||||
|
mu.hook_add(UC_HOOK_MEM_UNMAPPED, hook_unmapped)
|
||||||
|
# SR reads return TX-ready
|
||||||
|
mu.mem_write(UART_SR, struct.pack("<I", 0xA0))
|
||||||
|
mu.mem_write(0x13000000, struct.pack("<II", 0x49535953, 0x54535953))
|
||||||
|
|
||||||
|
def hook_uw(mu, access, addr, size, value, data):
|
||||||
|
if addr == UART_TF:
|
||||||
|
uart_out.append(chr(value & 0xFF))
|
||||||
|
|
||||||
|
mu.hook_add(UC_HOOK_MEM_WRITE, hook_uw, begin=UART_TF, end=UART_TF)
|
||||||
|
entry = load_elf(mu, path)
|
||||||
|
for r in range(13):
|
||||||
|
mu.reg_write(UC_ARM_REG_R0 + r, 0)
|
||||||
|
if "--regs" in sys.argv:
|
||||||
|
for kv in sys.argv[sys.argv.index("--regs") + 1].split(","):
|
||||||
|
r, v = kv.split("=")
|
||||||
|
mu.reg_write(UC_ARM_REG_R0 + int(r[1:]), int(v, 0))
|
||||||
|
mu.reg_write(UC_ARM_REG_SP, 0x085FFF00)
|
||||||
|
from unicorn.arm_const import UC_ARM_REG_LR as _LR
|
||||||
|
mu.reg_write(_LR, 0x08006B8D) # QEMU parity: stale lr -> maze
|
||||||
|
mu.reg_write(UC_ARM_REG_CPSR, 0x13)
|
||||||
|
if "--fill" in sys.argv:
|
||||||
|
for spec in sys.argv[sys.argv.index("--fill") + 1].split(","):
|
||||||
|
a, sz, v = spec.split(":")
|
||||||
|
a, sz, v = int(a, 0), int(sz, 0), int(v, 0)
|
||||||
|
mu.mem_write(a, struct.pack("<I", v) * (sz // 4))
|
||||||
|
if "--poke" in sys.argv:
|
||||||
|
for spec in sys.argv[sys.argv.index("--poke") + 1].split(","):
|
||||||
|
a, v = spec.split("=")
|
||||||
|
mu.mem_write(int(a, 0), struct.pack("<I", int(v, 0)))
|
||||||
|
count = [0]
|
||||||
|
last = [0]
|
||||||
|
from collections import deque
|
||||||
|
hist = deque(maxlen=20000)
|
||||||
|
# sled detector: sequential runs (pc == prev+2/4) longer than THRESH
|
||||||
|
# are zero-drifts; log run start (the jump/fall that entered it).
|
||||||
|
_sled_start = [0]
|
||||||
|
_sled_n = [0]
|
||||||
|
_sled_log = []
|
||||||
|
_SLED_TH = 100
|
||||||
|
calls = [] if "--calltrace" in sys.argv else None
|
||||||
|
rets = [] if "--retrace" in sys.argv else None
|
||||||
|
qlog = [] if "--querylog" in sys.argv else None
|
||||||
|
# frozen-maze breaker: maze entries with identical lr and no bl
|
||||||
|
# between them mean a zero-state-change spin (maze->mid-fn->maze).
|
||||||
|
# After K, redirect once to poll resumption (fresh bls unfreeze lr).
|
||||||
|
_fmlr = [None]
|
||||||
|
_fmct = [0]
|
||||||
|
_FMK = 50
|
||||||
|
_FMPC = 0x8007779
|
||||||
|
_traps = []
|
||||||
|
_trap_n = [0]
|
||||||
|
if "--trap" in sys.argv:
|
||||||
|
_traps = [int(x, 0) for x in sys.argv[sys.argv.index("--trap") + 1].split(",")]
|
||||||
|
if "--trapn" in sys.argv:
|
||||||
|
_trap_n = [int(sys.argv[sys.argv.index("--trapn") + 1])]
|
||||||
|
_spmax = [0x085FFF00]
|
||||||
|
_splog = [] if "--spytrack" in sys.argv else None
|
||||||
|
elog = [] if "--entrylog" in sys.argv else None
|
||||||
|
_prev = [0]
|
||||||
|
_eranges = []
|
||||||
|
_inrng = []
|
||||||
|
if "--entrylog" in sys.argv:
|
||||||
|
for _spec in sys.argv[sys.argv.index("--entrylog") + 1].split(","):
|
||||||
|
_lo, _sz = _spec.split(":")
|
||||||
|
_lo, _sz = int(_lo, 0), int(_sz, 0)
|
||||||
|
_eranges.append((_lo, _lo + _sz))
|
||||||
|
_inrng.append(False)
|
||||||
|
from unicorn.arm_const import UC_ARM_REG_CPSR as _CPSR
|
||||||
|
|
||||||
|
def hook_code(mu, addr, size, data):
|
||||||
|
count[0] += 1
|
||||||
|
last[0] = addr
|
||||||
|
_pp = _prev[0]
|
||||||
|
_prev[0] = addr
|
||||||
|
if addr == _pp + 2 or addr == _pp + 4:
|
||||||
|
if _sled_n[0] == 0:
|
||||||
|
_sled_start[0] = _pp
|
||||||
|
_sled_n[0] += 1
|
||||||
|
else:
|
||||||
|
if _sled_n[0] >= _SLED_TH and len(_sled_log) < 20:
|
||||||
|
_sled_log.append((count[0], _sled_start[0], _pp, _sled_n[0]))
|
||||||
|
_sled_n[0] = 0
|
||||||
|
if 0x08006B8C <= addr < 0x08006B9E:
|
||||||
|
try:
|
||||||
|
_lr = mu.reg_read(_LR)
|
||||||
|
except Exception:
|
||||||
|
_lr = None
|
||||||
|
if _lr == _fmlr[0]:
|
||||||
|
_fmct[0] += 1
|
||||||
|
if _fmct[0] >= _FMK:
|
||||||
|
_fmct[0] = 0
|
||||||
|
_fmlr[0] = None
|
||||||
|
mu.reg_write(_PC, _FMPC)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
_fmlr[0] = _lr
|
||||||
|
_fmct[0] = 0
|
||||||
|
elif _fmlr[0] is not None:
|
||||||
|
# any non-maze fetch: reset only on bl (lr change means
|
||||||
|
# fresh calls; other insns keep the freeze assessment)
|
||||||
|
pass
|
||||||
|
if _splog is not None:
|
||||||
|
try:
|
||||||
|
sp = mu.reg_read(UC_ARM_REG_SP)
|
||||||
|
except Exception:
|
||||||
|
sp = 0
|
||||||
|
if sp > _spmax[0] and len(_splog) < 40:
|
||||||
|
_spmax[0] = sp
|
||||||
|
_splog.append((count[0], addr, sp))
|
||||||
|
if _dirty[0]:
|
||||||
|
# deferred restore: MEM_WRITE hooks fire BEFORE the write lands,
|
||||||
|
# so in-hook restore gets overwritten by the instruction itself
|
||||||
|
_inww[0] = True
|
||||||
|
try:
|
||||||
|
for a, b in _dirty[1]:
|
||||||
|
mu.mem_write(a, stub_snap[a - STUB_LO:b - STUB_LO])
|
||||||
|
finally:
|
||||||
|
_inww[0] = False
|
||||||
|
_dirty[0] = False
|
||||||
|
del _dirty[1][:]
|
||||||
|
try:
|
||||||
|
thumb = (mu.reg_read(_CPSR) >> 5) & 1
|
||||||
|
except Exception:
|
||||||
|
thumb = 1
|
||||||
|
hist.append((addr, thumb))
|
||||||
|
if _traps and addr in _traps:
|
||||||
|
try:
|
||||||
|
vals = [mu.reg_read(_REGS[i]) for i in range(8)]
|
||||||
|
except Exception:
|
||||||
|
vals = []
|
||||||
|
print(f"TRAP {addr:#x} insn#{count[0]} r0-r7={[hex(x) for x in vals]} sp={mu.reg_read(UC_ARM_REG_SP):#x} lr={mu.reg_read(_LR):#x}")
|
||||||
|
try:
|
||||||
|
print(f" [73A100]={bytes(mu.mem_read(0x073A100, 4)).hex()}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" [73A100] unreadable: {e}")
|
||||||
|
try:
|
||||||
|
from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB, CS_MODE_ARM
|
||||||
|
md_t = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
|
||||||
|
md_a = Cs(CS_ARCH_ARM, CS_MODE_ARM)
|
||||||
|
for _pc, _th in list(hist)[-60:]:
|
||||||
|
try:
|
||||||
|
_b = _pc & ~1
|
||||||
|
_c = mu.mem_read(_b, 8 if _th else 12)
|
||||||
|
for _ins in (md_t if _th else md_a).disasm(_c, _b):
|
||||||
|
print(f" hist {_b:#x}{'t' if _th else 'a'}: {_ins.mnemonic} {_ins.op_str}")
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
print(f" hist {_pc:#x}: <unreadable>")
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
_trap_n[0] -= 1
|
||||||
|
if _trap_n[0] <= 0 and "--trapn" in sys.argv:
|
||||||
|
mu.emu_stop()
|
||||||
|
return
|
||||||
|
if "--trapn" not in sys.argv:
|
||||||
|
mu.emu_stop()
|
||||||
|
return
|
||||||
|
if qlog is not None and addr in (0x8017EA4, 0x8017C60):
|
||||||
|
try:
|
||||||
|
vals = [mu.reg_read(_REGS[i]) for i in range(8)]
|
||||||
|
except Exception:
|
||||||
|
vals = []
|
||||||
|
qlog.append((addr, vals,
|
||||||
|
mu.reg_read(UC_ARM_REG_SP),
|
||||||
|
mu.reg_read(UC_ARM_REG_LR)))
|
||||||
|
if elog is not None:
|
||||||
|
for _i, (_elo, _ehi) in enumerate(_eranges):
|
||||||
|
inside = _elo <= addr < _ehi
|
||||||
|
if inside and not _inrng[_i]:
|
||||||
|
if len(elog) < 20000:
|
||||||
|
try:
|
||||||
|
_th = (mu.reg_read(_CPSR) >> 5) & 1
|
||||||
|
except Exception:
|
||||||
|
_th = -1
|
||||||
|
elog.append((count[0], _prev[0], addr, _th))
|
||||||
|
if not inside and _inrng[_i]:
|
||||||
|
if len(elog) < 20000:
|
||||||
|
elog.append((count[0], _prev[0], -addr, 2))
|
||||||
|
_inrng[_i] = inside
|
||||||
|
if calls is not None and size in (2, 4):
|
||||||
|
try:
|
||||||
|
if size == 2:
|
||||||
|
h = struct.unpack("<H", mu.mem_read(addr & ~1, 2))[0]
|
||||||
|
is_blx = (h & 0xFF80) == 0x4780
|
||||||
|
rm = (h >> 3) & 7 if is_blx else -1
|
||||||
|
# bx Rm (jump, no link): 010001110 Rm:000
|
||||||
|
is_bx = (h & 0xFF80) == 0x4700
|
||||||
|
if is_bx:
|
||||||
|
bxrm = (h >> 3) & 15
|
||||||
|
try:
|
||||||
|
if bxrm < 8:
|
||||||
|
bxt = mu.reg_read(_REGS[bxrm])
|
||||||
|
elif bxrm == 14:
|
||||||
|
bxt = mu.reg_read(_LR)
|
||||||
|
else:
|
||||||
|
bxt = -1
|
||||||
|
except Exception:
|
||||||
|
bxt = -1
|
||||||
|
if len(calls) < 8000:
|
||||||
|
calls.append((addr, 100 + bxrm, bxt))
|
||||||
|
# pop {..,pc} (0xBD00+): log stacked target
|
||||||
|
if calls is not None and (h & 0xFF00) == 0xBD00:
|
||||||
|
try:
|
||||||
|
_sp = mu.reg_read(UC_ARM_REG_SP)
|
||||||
|
_tgt = struct.unpack("<I", mu.mem_read(_sp, 4))[0]
|
||||||
|
except Exception:
|
||||||
|
_tgt = -1
|
||||||
|
if len(calls) < 12000:
|
||||||
|
calls.append((addr, 300, _tgt))
|
||||||
|
if rets is not None and 0x8017E00 <= addr < 0x8018000:
|
||||||
|
# pop {..,pc} (0xBD00+) or bx lr (0x4770)
|
||||||
|
if h == 0x4770 or (h & 0xFF00) == 0xBD00:
|
||||||
|
try:
|
||||||
|
rv = mu.reg_read(_REGS[0])
|
||||||
|
except Exception:
|
||||||
|
rv = -1
|
||||||
|
if len(rets) < 200:
|
||||||
|
rets.append((addr, rv))
|
||||||
|
else:
|
||||||
|
w = struct.unpack("<I", mu.mem_read(addr & ~3, 4))[0]
|
||||||
|
# ARM bx/blx Rm: cond(4) 0001001011111111111100x1 Rm(4)
|
||||||
|
is_blx = (w & 0x0FFFFFF0) in (0x012FFF10, 0x012FFF30)
|
||||||
|
rm = w & 15 if is_blx else -1
|
||||||
|
tgt = -1
|
||||||
|
if not is_blx and calls is not None:
|
||||||
|
# Thumb32 BL: h0=11110S+imm10, h1=1111x+J+imm11
|
||||||
|
h0 = w & 0xFFFF
|
||||||
|
h1 = (w >> 16) & 0xFFFF
|
||||||
|
if (h0 & 0xF800) == 0xF000 and (h1 & 0xE000) == 0xE000:
|
||||||
|
S = (h0 >> 10) & 1
|
||||||
|
J1 = (h1 >> 13) & 1
|
||||||
|
J2 = (h1 >> 11) & 1
|
||||||
|
I1 = 1 - (J1 ^ S)
|
||||||
|
I2 = 1 - (J2 ^ S)
|
||||||
|
off = (I1 << 23) | (I2 << 22) | ((h0 & 0x3FF) << 12) | ((h1 & 0x7FF) << 1)
|
||||||
|
if S:
|
||||||
|
off -= 0x1000000
|
||||||
|
if len(calls) < 8000:
|
||||||
|
calls.append((addr, 200, (addr + 4 + off) & 0xFFFFFFFF))
|
||||||
|
if rets is not None and 0x8017E00 <= addr < 0x8018000:
|
||||||
|
# pop.w {...,pc}: 0xE8BDxxxx with bit15 set
|
||||||
|
if (w & 0xFFFF8000) == 0xE8BD8000:
|
||||||
|
try:
|
||||||
|
rv = mu.reg_read(_REGS[0])
|
||||||
|
except Exception:
|
||||||
|
rv = -1
|
||||||
|
if len(rets) < 200:
|
||||||
|
rets.append((addr, rv))
|
||||||
|
if is_blx:
|
||||||
|
try:
|
||||||
|
tgt = mu.reg_read(_REGS[rm])
|
||||||
|
except Exception:
|
||||||
|
tgt = -1
|
||||||
|
if len(calls) < 4000:
|
||||||
|
calls.append((addr, rm, tgt))
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
|
||||||
|
mu.hook_add(UC_HOOK_CODE, hook_code)
|
||||||
|
# null-fetch redirect: branch-to-0 lands in maze (movs r0,#0; bx lr),
|
||||||
|
# immune to [0]-writes that clobber a static maze copy
|
||||||
|
from unicorn.arm_const import UC_ARM_REG_PC as _PC
|
||||||
|
_nulllog = []
|
||||||
|
_halt_on_null = "--haltonnull" in sys.argv
|
||||||
|
|
||||||
|
def hook_null(mu, addr, size, data):
|
||||||
|
if len(_nulllog) < 10:
|
||||||
|
try:
|
||||||
|
r = [mu.reg_read(_REGS[i]) for i in range(4)]
|
||||||
|
lr = mu.reg_read(_LR)
|
||||||
|
sp = mu.reg_read(UC_ARM_REG_SP)
|
||||||
|
except Exception:
|
||||||
|
r, lr, sp = [], -1, -1
|
||||||
|
# walk back: find the branch source from hist
|
||||||
|
_nulllog.append((count[0], r, lr, sp))
|
||||||
|
if _halt_on_null and len(_nulllog) == 1:
|
||||||
|
return False
|
||||||
|
mu.reg_write(_PC, STUB + 1)
|
||||||
|
|
||||||
|
mu.hook_add(UC_HOOK_CODE, hook_null, begin=0x0, end=0xFFF)
|
||||||
|
if "--watchcode" in sys.argv:
|
||||||
|
from unicorn.arm_const import UC_ARM_REG_PC as _PC2
|
||||||
|
wlog = []
|
||||||
|
|
||||||
|
def hook_ww(mu, access, addr, size, value, data):
|
||||||
|
if len(wlog) < 30:
|
||||||
|
try:
|
||||||
|
pc = mu.reg_read(_PC2)
|
||||||
|
except Exception:
|
||||||
|
pc = -1
|
||||||
|
wlog.append((pc, addr, size, value))
|
||||||
|
|
||||||
|
mu.hook_add(UC_HOOK_MEM_WRITE, hook_ww,
|
||||||
|
begin=0x8005800, end=0x8039254)
|
||||||
|
import atexit
|
||||||
|
|
||||||
|
def _dump():
|
||||||
|
print(f"code-writes: {len(wlog)}")
|
||||||
|
for pc, a, sz, v in wlog[:30]:
|
||||||
|
print(f" pc={pc:#x} [{a:#x}] sz={sz} val={v:#x}")
|
||||||
|
atexit.register(_dump)
|
||||||
|
# --- machine scaffold parity (saimaa.c values, verbatim) ---
|
||||||
|
def W32(a, v):
|
||||||
|
mu.mem_write(a, struct.pack("<I", v & 0xFFFFFFFF))
|
||||||
|
S, STUB, STUBV = 0x08006B80, 0x08006B8C, 0x08006B8D
|
||||||
|
W32(S, S + 8)
|
||||||
|
W32(S + 4, STUBV)
|
||||||
|
W32(S + 8, STUBV)
|
||||||
|
# maze blob (24B @STUB): movs r0,#0; return 0 via lr ONLY if lr is
|
||||||
|
# odd Thumb in DDR (top 8); even lr (stale mid-fn raw addr!) would
|
||||||
|
# ARM-drift (STM-clobbers + IMEM fault), so hang loud instead.
|
||||||
|
# Overlaps S6 head ([B9E,BA4), S6 dormant, documented).
|
||||||
|
mu.mem_write(STUB, bytes([
|
||||||
|
0x00, 0x20, 0x4f, 0xea, 0x1e, 0x6c, 0xbc, 0xf1,
|
||||||
|
0x08, 0x0f, 0x03, 0xd1, 0x1e, 0xf0, 0x01, 0x0f,
|
||||||
|
0x00, 0xd0, 0x70, 0x47, 0x72, 0xb6, 0xfd, 0xe7]))
|
||||||
|
print("maze-rb:", bytes(mu.mem_read(STUB, 24)).hex())
|
||||||
|
for a in (0x087c29d18, 0x08050b28, 0x08070000):
|
||||||
|
W32(a, S)
|
||||||
|
# S6 stublet DISABLED (maze v2 overlaps its head; S6 dormant,
|
||||||
|
# never entered (b14 goes to 0x8005B9E, not here). Bytes kept for
|
||||||
|
# reference (would live at 0x08006B9E):
|
||||||
|
# mu.mem_write(0x08006B9E, bytes([
|
||||||
|
# 0xbd, 0xe8, 0xfc, 0x47, 0x43, 0xf6, 0xa5, 0x3e,
|
||||||
|
# 0xc0, 0xf6, 0x01, 0x0e, 0x70, 0x47]))
|
||||||
|
# banner stub (34B @0x08006BAC, prints r0 via UARTDM)
|
||||||
|
mu.mem_write(0x08006BAC, bytes([
|
||||||
|
0x10, 0xb5, 0x4f, 0xf2, 0x00, 0x03, 0xc0, 0xf2,
|
||||||
|
0x8a, 0x73, 0x21, 0x21, 0x19, 0x67, 0x10, 0xf8,
|
||||||
|
0x01, 0x1b, 0x09, 0xb1, 0x19, 0x67, 0xfa, 0xe7,
|
||||||
|
0x4f, 0xf2, 0x94, 0x6e, 0xc0, 0xf6, 0x02, 0x0e,
|
||||||
|
0x70, 0x47]))
|
||||||
|
# PBL stub (52B @0x080068C0)
|
||||||
|
mu.mem_write(0x080068C0, bytes([
|
||||||
|
0xf0, 0xb5, 0x46, 0xf6, 0x81, 0x3c, 0xc0, 0xf6,
|
||||||
|
0x00, 0x0c, 0xe0, 0x47, 0x40, 0xf2, 0x00, 0x00,
|
||||||
|
0xc0, 0xf2, 0x22, 0x00, 0x05, 0x46, 0x40, 0xf2,
|
||||||
|
0x00, 0x0a, 0xc0, 0xf6, 0x06, 0x0a, 0x41, 0xf2,
|
||||||
|
0x00, 0x0b, 0xc0, 0xf6, 0x06, 0x0b, 0x4f, 0xf2,
|
||||||
|
0x3f, 0x6e, 0xc0, 0xf6, 0x02, 0x0e, 0x70, 0x47,
|
||||||
|
0x00, 0x00, 0x00, 0x00]))
|
||||||
|
# S7 stublet (52B @0x08006C10, b18): delay-epilogue exit router.
|
||||||
|
# cmp r4,#0xff: iterate (r4<=0xff -> restart 0x8013ba5) or done
|
||||||
|
# (restore r4/r5/sl/fp, return to sbl_main 0x802f681).
|
||||||
|
mu.mem_write(0x08006C10, bytes.fromhex(
|
||||||
|
"ff2c13d940f20004c0f2220440f20005c0f2220540f2000a"
|
||||||
|
"c0f6060a41f2000bc0f6060bdff800f081f60208dff800f0"
|
||||||
|
"a53b0108"))
|
||||||
|
# S9 stublet (62B @0x08006C48, b22): labyrinth-epilogue exit router.
|
||||||
|
# ldr r3,[sp,#0x14]: nonzero -> original pop.w (legit return);
|
||||||
|
# zero (empty scaffold frames) -> restore r0/r4/r5/sl/fp, reset sp
|
||||||
|
# to 0x085FFF00 (fresh STUBV-fill stack), jump to sbl_main 0x802f69d
|
||||||
|
# via ip (no literal loads).
|
||||||
|
mu.mem_write(0x08006C48, bytes.fromhex(
|
||||||
|
"059b0bb1bde8f081002040f20004c0f2220440f20005c0f2"
|
||||||
|
"220540f2000ac0f6060a41f2000bc0f6060b4ff60073c0f6"
|
||||||
|
"5f039d464ff29d6cc0f6020c6047"))
|
||||||
|
# S10 stublet (10B @0x08006C88, b23): rollback-exit router.
|
||||||
|
# movw/movt ip + bx ip to sbl_main 0x802f6a3.
|
||||||
|
mu.mem_write(0x08006C88, bytes.fromhex("4ff2a36cc0f6020c6047"))
|
||||||
|
# S18 stublet (10B @0x08006D22, b29): poll-epilogue router.
|
||||||
|
# movw/movt ip + bx ip to sbl_main 0x802f6a9 (post-dispatcher).
|
||||||
|
mu.mem_write(0x08006D22, bytes.fromhex("4ff2a96cc0f6020c6047"))
|
||||||
|
# S21 stublet (32B @0x08006D5A, b38): table-walker router.
|
||||||
|
# [sp+0x24] valid-code? => original pop.w (legit return);
|
||||||
|
# else b.w S19 (sbl_main post-logger restart, bounded table work).
|
||||||
|
mu.mem_write(0x08006D5A, bytes.fromhex(
|
||||||
|
"099b13f0010f09d01a0e082a01d1bde8f09f1a0f082a"
|
||||||
|
"01d1bde8f09ffff7d9bf"))
|
||||||
|
# S20 stublet (34B @0x08006D38, b37): clock-dispatcher router.
|
||||||
|
# [sp+0x2C] valid-code? => original pop.w (legit return);
|
||||||
|
# else r0=sl (PBL tables) + b.w dispatcher re-run (idempotent math).
|
||||||
|
mu.mem_write(0x08006D38, bytes.fromhex(
|
||||||
|
"0b9b13f0010f09d01a0e082a01d1bde8fe8f1a0f082a"
|
||||||
|
"01d1bde8fe8f504607f013bf"))
|
||||||
|
# S17 safe-logger (50B @0x08006CF0, b26): print r1-string via
|
||||||
|
# UARTDM-TF only if r1 in rodata [0x804B800,0x8058000); pop-return.
|
||||||
|
mu.mem_write(0x08006CF0, bytes.fromhex(
|
||||||
|
"4bf60003c0f60403994211d348f20003c0f605039942"
|
||||||
|
"0bd210b54ff20003c0f28a730c4614f8010b08b11867fae710bd7047"))
|
||||||
|
# PBL version-table fabrication (b23): [0x080528D0] = STUBV so the
|
||||||
|
# rollback query's blx calls maze (returns 0 = versions OK).
|
||||||
|
W32(0x080528D0, STUBV)
|
||||||
|
# PBL dispatcher-table fabrication (b33): [0x80164F0] (literal pool,
|
||||||
|
# image default points at hash blob!) retargeted to scratch struct
|
||||||
|
# at 0x08024000 (OCIMEM, out of stack-march paths!) with [0x18]=2
|
||||||
|
# (ready-flag -> world-switch dispatch; TZ deferred via OCIMEM stub).
|
||||||
|
W32(0x08024000, 0x08024000)
|
||||||
|
W32(0x08024014, 0x00000002)
|
||||||
|
W32(0x080164F0, 0x08024000)
|
||||||
|
# HW-ready bit fabrication (b25): [0x073A100] bits 0,1,2 = peripheral
|
||||||
|
# status done-bits (poll#1 checks bit2 (lsls#29), poll#2 bit1 (lsls#30),
|
||||||
|
# post-poll gate bit0 (lsls#31)). SBL only reads it, never writes.
|
||||||
|
W32(0x073A100, 0x00000007)
|
||||||
|
# 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]))
|
||||||
|
# S12-stub (b27, 100B @0x08006964, ARM): blx-to-padding becomes
|
||||||
|
# smart dispatcher: ldr ip,[sp,#4] (=pushed lr); ip==STUBV/even/
|
||||||
|
# non-code => ip=poll resumption 0x8007779; else keep pushed-lr
|
||||||
|
# (NO add-sp: marches on mid-entries! un-leak dropped, helper-leak
|
||||||
|
# down is harmless); restore r7; ACK peripheral bit1; bx lr.
|
||||||
|
# PBL would place the real ARM helper here.
|
||||||
|
mu.mem_write(0x08006964, bytes.fromhex(
|
||||||
|
"04c09de58d3b06e3003840e303005ce10900000a0100"
|
||||||
|
"1ce30700000a2c3ca0e1080053e30000001a050000ea"
|
||||||
|
"2c3ea0e1080053e30000001a010000ea79c707e300c8"
|
||||||
|
"40e300700ae3737040e30c309fe5002093e5022082e3"
|
||||||
|
"002083e51eff2fe100a17300"))
|
||||||
|
# S16-stublet (b27, 64B @0x08006CC0): smart return for B66 bx ip.
|
||||||
|
# ip==STUBV (fill/maze, never a legit return) => default;
|
||||||
|
# ip odd + top 8 (code) => add sp,#8 (pop the helper frame B66
|
||||||
|
# skipped!) + bx ip (legit pushed-lr return, un-leaks the stack);
|
||||||
|
# else default (restore r7, jump poll resumption 0x8007779).
|
||||||
|
mu.mem_write(0x08006CC0, bytes.fromhex(
|
||||||
|
"ddf804c046f68d33c0f600039c450ed01cf0010f0bd0"
|
||||||
|
"5fea1c63082b01d102b060475fea1c73082b01d102b0"
|
||||||
|
"60474af20007c0f2730747f2797cc0f6000c6047"))
|
||||||
|
# stack zone fill (STUBV odd)
|
||||||
|
mu.mem_write(0x085F0000, struct.pack("<I", STUBV) * (0x10000 // 4))
|
||||||
|
# IMEM tripwire [0x8600000,0x8600004): drift-sink trap (UDF).
|
||||||
|
# Fill-drift falls through here sequentially; faults LOUDLY at
|
||||||
|
# entry. Must stay 4B: wider poison corrupts IMEM-stack reads
|
||||||
|
# (SBL frames live at 0x860000C+, counters underflow on 0xFF!).
|
||||||
|
mu.mem_write(0x08600000, b"\xff\xff\xff\xff")
|
||||||
|
# pop-site slots (sp values from traces)
|
||||||
|
for a, v in [(0x085FFF14, 0x802f65d), (0x085FFF1C, 0x802f665),
|
||||||
|
(0x085FFF44, 0x8013ba5), (0x085FFF5C, 0x802f681),
|
||||||
|
(0x085FFF74, 0x8016183)]:
|
||||||
|
W32(a, v)
|
||||||
|
W32(0x085FFF58, 0)
|
||||||
|
W32(0x085FFF34, 0)
|
||||||
|
W32(0x085FFF54, 0)
|
||||||
|
W32(0x085FFF6C, 0)
|
||||||
|
W32(0x08600944, 0) # IMEM poll magic
|
||||||
|
# minimal UEFI tables (ST/BS/RS/CO + ret0 stubs)
|
||||||
|
ST, BS, RS, CO, STUBS, LI, HEAP = (0x13000000, 0x13000100, 0x13000200,
|
||||||
|
0x13000300, 0x13000400, 0x13000500,
|
||||||
|
0x13001000)
|
||||||
|
for i in range(24):
|
||||||
|
mu.mem_write(STUBS + i * 8, bytes([0x00, 0x20, 0x70, 0x47,
|
||||||
|
0x00, 0x00, 0x00, 0x00]))
|
||||||
|
W32(BS + 24 + i * 4, STUBS + i * 8 + 1)
|
||||||
|
W32(BS, 0x56524553)
|
||||||
|
W32(ST, 0x49535953)
|
||||||
|
W32(ST + 4, 0x54535953)
|
||||||
|
W32(ST + 12, 0x00020000)
|
||||||
|
W32(ST + 16, 0x13000048)
|
||||||
|
W32(ST + 40, CO)
|
||||||
|
W32(ST + 44, CO)
|
||||||
|
W32(ST + 56, RS)
|
||||||
|
W32(ST + 60, BS)
|
||||||
|
W32(LI, 0x3130414d)
|
||||||
|
W32(HEAP - 4, HEAP)
|
||||||
|
# NOTE: no vendor string bytes (QEMU leaves zeros at 0x13000048)
|
||||||
|
# map UEFI table region (already in RAMS? add 0x13000000)
|
||||||
|
# --- end parity ---
|
||||||
|
# stub range is readonly on HW/QEMU: ignore SBL heap-clobber writes,
|
||||||
|
# keep maze/stublets intact (restore-on-write with reentrancy guard)
|
||||||
|
STUB_LO, STUB_HI = 0x080068C0, 0x08006D80 # +S7/S9/S10/S16/S17/S18/S20/S21
|
||||||
|
stub_snap = bytes(mu.mem_read(STUB_LO, STUB_HI - STUB_LO))
|
||||||
|
_inww = [False]
|
||||||
|
_swn = [0]
|
||||||
|
_swlog = []
|
||||||
|
_dirty = [False, []]
|
||||||
|
print("stub-snap:", stub_snap[:24].hex())
|
||||||
|
print("stub-snap-maze:", stub_snap[0x2CC:0x2CC + 18].hex())
|
||||||
|
|
||||||
|
def hook_stubw(mu, access, addr, size, value, data):
|
||||||
|
if _inww[0]:
|
||||||
|
return
|
||||||
|
if addr + size <= STUB_LO or addr >= STUB_HI:
|
||||||
|
return
|
||||||
|
_swn[0] += 1
|
||||||
|
if len(_swlog) < 8:
|
||||||
|
try:
|
||||||
|
_pc = mu.reg_read(_PC)
|
||||||
|
except Exception:
|
||||||
|
_pc = -1
|
||||||
|
_swlog.append((_pc, addr, size))
|
||||||
|
# record for deferred restore (see hook_code); in-hook restore
|
||||||
|
# would be overwritten by the faulting instruction itself
|
||||||
|
a = max(addr, STUB_LO)
|
||||||
|
b = min(addr + size, STUB_HI)
|
||||||
|
if b > a and len(_dirty[1]) < 16:
|
||||||
|
_dirty[1].append((a, b))
|
||||||
|
_dirty[0] = True
|
||||||
|
|
||||||
|
#hook all DDRLOW, filter in python (catches writes starting below LO)
|
||||||
|
mu.hook_add(UC_HOOK_MEM_WRITE, hook_stubw,
|
||||||
|
begin=0x08000000, end=0x085FFFFF)
|
||||||
|
if "--watchmaze" in sys.argv:
|
||||||
|
from unicorn.arm_const import UC_ARM_REG_PC as _PC3
|
||||||
|
mzlog = []
|
||||||
|
|
||||||
|
def hook_mz(mu, access, addr, size, value, data):
|
||||||
|
if len(mzlog) < 40:
|
||||||
|
try:
|
||||||
|
pc = mu.reg_read(_PC3)
|
||||||
|
except Exception:
|
||||||
|
pc = -1
|
||||||
|
mzlog.append((pc, addr, size, value))
|
||||||
|
|
||||||
|
mu.hook_add(UC_HOOK_MEM_WRITE, hook_mz,
|
||||||
|
begin=0x08006B8C, end=0x08006B9D)
|
||||||
|
import atexit as _ax
|
||||||
|
|
||||||
|
def _dmz():
|
||||||
|
print(f"maze-writes: {len(mzlog)}")
|
||||||
|
for pc, a, sz, v in mzlog[:40]:
|
||||||
|
print(f" pc={pc:#x} [{a:#x}] sz={sz} val={v:#x}")
|
||||||
|
_ax.register(_dmz)
|
||||||
|
if "--watchlow" in sys.argv:
|
||||||
|
_spec = sys.argv[sys.argv.index("--watchlow") + 1].split(":")
|
||||||
|
_wlo, _whi = int(_spec[0], 0), int(_spec[0], 0) + int(_spec[1], 0)
|
||||||
|
from unicorn.arm_const import UC_ARM_REG_PC as _PC4
|
||||||
|
wlolog = []
|
||||||
|
|
||||||
|
def hook_wlo(mu, access, addr, size, value, data):
|
||||||
|
if len(wlolog) < 60:
|
||||||
|
try:
|
||||||
|
pc = mu.reg_read(_PC4)
|
||||||
|
except Exception:
|
||||||
|
pc = -1
|
||||||
|
wlolog.append((pc, addr, size, value))
|
||||||
|
|
||||||
|
mu.hook_add(UC_HOOK_MEM_WRITE, hook_wlo,
|
||||||
|
begin=0x00200000, end=0x085FFFFF)
|
||||||
|
import atexit as _ax2
|
||||||
|
|
||||||
|
def _dwlo():
|
||||||
|
inn = [(pc, a, sz, v) for pc, a, sz, v in wlolog
|
||||||
|
if not (a + sz <= _wlo or a >= _whi)]
|
||||||
|
print(f"low-writes-in-range: {len(inn)}")
|
||||||
|
for pc, a, sz, v in inn[:60]:
|
||||||
|
print(f" pc={pc:#x} [{a:#x}] sz={sz} val={v:#x}")
|
||||||
|
_ax2.register(_dwlo)
|
||||||
|
limit = 2000000
|
||||||
|
if "--limit" in sys.argv:
|
||||||
|
limit = int(sys.argv[sys.argv.index("--limit") + 1])
|
||||||
|
try:
|
||||||
|
mu.emu_start(thumb | 1, 0xFFFFFFFF, 0, limit)
|
||||||
|
except Exception as e:
|
||||||
|
print("EMU-EXC:", str(e)[:120])
|
||||||
|
print("insns:", count[0], "last-pc:", hex(last[0]))
|
||||||
|
print("unmapped:", unmapped["n"], unmapped["first"][:4])
|
||||||
|
print("regs:",
|
||||||
|
" ".join(f"r{i}={mu.reg_read(_REGS[i]):#x}" for i in range(8)),
|
||||||
|
f"sp={mu.reg_read(UC_ARM_REG_SP):#x}",
|
||||||
|
f"lr={mu.reg_read(UC_ARM_REG_LR):#x}",
|
||||||
|
f"pc={mu.reg_read(UC_ARM_REG_PC):#x}")
|
||||||
|
try:
|
||||||
|
from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB, CS_MODE_ARM
|
||||||
|
md_t = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
|
||||||
|
md_a = Cs(CS_ARCH_ARM, CS_MODE_ARM)
|
||||||
|
real = [(pc, th) for pc, th in hist
|
||||||
|
if not (0 <= (pc & ~1) < 0x1000)][-48:]
|
||||||
|
for pc, thumb in real:
|
||||||
|
try:
|
||||||
|
base = pc & ~1
|
||||||
|
code = mu.mem_read(base, 8 if thumb else 12)
|
||||||
|
md = md_t if thumb else md_a
|
||||||
|
for ins in md.disasm(code, base):
|
||||||
|
print(f" {base:#x}{'t' if thumb else 'a'}: {ins.mnemonic} {ins.op_str}")
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
print(f" {pc:#x}: <unreadable>")
|
||||||
|
except ImportError:
|
||||||
|
print("hist:", [hex(pc) for pc, _ in list(hist)[-12:]])
|
||||||
|
if _sled_log or _sled_n[0] >= _SLED_TH:
|
||||||
|
if _sled_n[0] >= _SLED_TH:
|
||||||
|
_sled_log.append((count[0], _sled_start[0], last[0], _sled_n[0]))
|
||||||
|
print(f"sleds: {len(_sled_log)}")
|
||||||
|
for n, s, e, c in _sled_log:
|
||||||
|
print(f" insn#{n} [{s:#x}..{e:#x}] len={c}")
|
||||||
|
if elog is not None:
|
||||||
|
print(f"entries: {len(elog)}")
|
||||||
|
for n, prev, cur, th in elog[:10]:
|
||||||
|
if cur < 0:
|
||||||
|
print(f" FIRST EXIT insn#{n} {prev:#x} -> {(-cur):#x}")
|
||||||
|
else:
|
||||||
|
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:]:
|
||||||
|
if cur < 0:
|
||||||
|
print(f" LAST EXIT insn#{n} {prev:#x} -> {(-cur):#x}")
|
||||||
|
else:
|
||||||
|
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:
|
||||||
|
print(f" insn#{n} {pc:#x} sp={sp:#x}")
|
||||||
|
if _nulllog:
|
||||||
|
print(f"null-entries: {len(_nulllog)}")
|
||||||
|
for n, r, lr, sp in _nulllog:
|
||||||
|
print(f" insn#{n} r0-r3={[hex(x) for x in r]} lr={lr:#x} sp={sp:#x}")
|
||||||
|
if qlog is not None:
|
||||||
|
print(f"querylog: {len(qlog)}")
|
||||||
|
for pc, r, sp, lr in qlog[:20]:
|
||||||
|
print(f" {pc:#x} r0-r7={[hex(x) for x in r]} sp={sp:#x} lr={lr:#x}")
|
||||||
|
if rets is not None:
|
||||||
|
print(f"rets-in-range: {len(rets)}")
|
||||||
|
for pc, rv in rets[-20:]:
|
||||||
|
print(f" ret {pc:#x} r0={rv:#x}" if rv >= 0 else f" ret {pc:#x} r0=?")
|
||||||
|
if calls is not None:
|
||||||
|
print(f"blx-calls: {len(calls)}")
|
||||||
|
bls = [(pc, tgt) for pc, rm, tgt in calls if rm == 200]
|
||||||
|
print(f"bl-immed: {len(bls)}")
|
||||||
|
for pc, tgt in bls[-30:]:
|
||||||
|
print(f" {pc:#x}: bl #{tgt:#x}")
|
||||||
|
pops = [(pc, tgt) for pc, rm, tgt in calls if rm == 300]
|
||||||
|
print(f"pop-pc: {len(pops)}")
|
||||||
|
for pc, tgt in pops[:15]:
|
||||||
|
print(f" FIRST {pc:#x}: pop-> {tgt:#x}" if tgt >= 0 else f" FIRST {pc:#x}: pop-> ?")
|
||||||
|
for pc, tgt in pops[-15:]:
|
||||||
|
print(f" LAST {pc:#x}: pop-> {tgt:#x}" if tgt >= 0 else f" LAST {pc:#x}: pop-> ?")
|
||||||
|
for pc, rm, tgt in calls[-12:]:
|
||||||
|
if rm == 200:
|
||||||
|
continue
|
||||||
|
tag = f"bx r{rm - 100}" if rm >= 100 else f"blx r{rm}"
|
||||||
|
print(f" {pc:#x}: {tag} -> {tgt:#x}" if tgt >= 0 else f" {pc:#x}: {tag} -> ?")
|
||||||
|
if "--dumpmem" in sys.argv:
|
||||||
|
for spec in sys.argv[sys.argv.index("--dumpmem") + 1].split(","):
|
||||||
|
a, sz = spec.split(":")
|
||||||
|
a, sz = int(a, 0), int(sz, 0)
|
||||||
|
print(f"mem[{a:#x}+{sz:#x}]:")
|
||||||
|
print(bytes(mu.mem_read(a, sz)).hex())
|
||||||
|
print("UART:", "".join(uart_out)[:160])
|
||||||
|
print(f"stubguard-hits: {_swn[0]}")
|
||||||
|
for _pc, _a, _s in _swlog:
|
||||||
|
print(f" guarded pc={_pc:#x} [{_a:#x}] sz={_s}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user