merge zern/master: take zern saimaa.c + sbl_mains (b12 bringup ahead)
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
# 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.
|
||||||
+178
-11
@@ -22,6 +22,8 @@
|
|||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
|
|
||||||
/* Real MSM8909 map (subset) */
|
/* Real MSM8909 map (subset) */
|
||||||
|
#define SAIMAA_RPM_BASE 0x00400000
|
||||||
|
#define SAIMAA_RPM_SIZE 0x07C00000 /* 0x00400000..0x08000000 heap-ish */
|
||||||
#define SAIMAA_OCIMEM_BASE 0x00200000
|
#define SAIMAA_OCIMEM_BASE 0x00200000
|
||||||
#define SAIMAA_OCIMEM_SIZE 0x00100000
|
#define SAIMAA_OCIMEM_SIZE 0x00100000
|
||||||
#define SAIMAA_DDRLOW_BASE 0x08000000
|
#define SAIMAA_DDRLOW_BASE 0x08000000
|
||||||
@@ -35,8 +37,13 @@
|
|||||||
#define SAIMAA_GICD_BASE 0x0B000000
|
#define SAIMAA_GICD_BASE 0x0B000000
|
||||||
#define SAIMAA_GICC_BASE 0x0B002000
|
#define SAIMAA_GICC_BASE 0x0B002000
|
||||||
#define SAIMAA_NCPUS 4
|
#define SAIMAA_NCPUS 4
|
||||||
|
#define SAIMAA_MYSTROM_BASE 0x69000000
|
||||||
|
#define SAIMAA_MYSTROM_SIZE 0x00100000
|
||||||
|
#define SAIMAA_SMEM_BASE 0x87C00000
|
||||||
|
#define SAIMAA_SMEM_SIZE 0x00800000
|
||||||
|
|
||||||
/* ---- 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_UART "saimaa-uart"
|
#define TYPE_SAIMAA_UART "saimaa-uart"
|
||||||
OBJECT_DECLARE_SIMPLE_TYPE(SaimaaUartState, SAIMAA_UART)
|
OBJECT_DECLARE_SIMPLE_TYPE(SaimaaUartState, SAIMAA_UART)
|
||||||
|
|
||||||
@@ -46,6 +53,33 @@ struct SaimaaUartState {
|
|||||||
/* WIP: plain host stdout, no chardev dance */
|
/* WIP: plain host stdout, no chardev dance */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Low vectors / null-page trap: log faulting PC, return 0. */
|
||||||
|
static int saimaa_vec_n;
|
||||||
|
static uint64_t saimaa_vec_read(void *opaque, hwaddr off, unsigned size)
|
||||||
|
{
|
||||||
|
if (saimaa_vec_n < 24) {
|
||||||
|
ARMCPU *cpu = ARM_CPU(current_cpu);
|
||||||
|
uint64_t pc = cpu ? cpu->env.regs[15] : 0;
|
||||||
|
int th = cpu ? cpu->env.thumb : 0;
|
||||||
|
fprintf(stderr, "saimaa-vec: %s off=0x%x pc=0x%lx\n",
|
||||||
|
size == 2 ? "fetch?" : "read",
|
||||||
|
(unsigned)off, (unsigned long)pc - (th ? 4 : 8));
|
||||||
|
saimaa_vec_n++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void saimaa_vec_write(void *opaque, hwaddr off, uint64_t val,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps saimaa_vec_ops = {
|
||||||
|
.read = saimaa_vec_read,
|
||||||
|
.write = saimaa_vec_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
static uint64_t saimaa_uart_read(void *opaque, hwaddr off, unsigned size)
|
static uint64_t saimaa_uart_read(void *opaque, hwaddr off, unsigned size)
|
||||||
{
|
{
|
||||||
switch (off) {
|
switch (off) {
|
||||||
@@ -101,6 +135,7 @@ struct SaimaaMachineState {
|
|||||||
char *sbl1;
|
char *sbl1;
|
||||||
char *emmc;
|
char *emmc;
|
||||||
char *sblmain; /* hex: popped-PC override for PBL trampoline */
|
char *sblmain; /* hex: popped-PC override for PBL trampoline */
|
||||||
|
char *regs; /* PBL reg overrides: N=V,N=V (N=r1,r4,r6-r9,sl,fp) */
|
||||||
};
|
};
|
||||||
typedef struct SaimaaMachineState SaimaaMachineState;
|
typedef struct SaimaaMachineState SaimaaMachineState;
|
||||||
|
|
||||||
@@ -115,16 +150,28 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
|
|
||||||
/* RAM map */
|
/* RAM map */
|
||||||
const struct { const char *n; hwaddr b; hwaddr s; } rams[] = {
|
const struct { const char *n; hwaddr b; hwaddr s; } rams[] = {
|
||||||
|
{ "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.imem", SAIMAA_IMEM_BASE, SAIMAA_IMEM_SIZE },
|
{ "saimaa.imem", SAIMAA_IMEM_BASE, SAIMAA_IMEM_SIZE },
|
||||||
{ "saimaa.ddrhigh", SAIMAA_DDRHIGH_BASE, SAIMAA_DDRHIGH_SIZE },
|
{ "saimaa.ddrhigh", SAIMAA_DDRHIGH_BASE, SAIMAA_DDRHIGH_SIZE },
|
||||||
|
{ "saimaa.mystrom", SAIMAA_MYSTROM_BASE, SAIMAA_MYSTROM_SIZE },
|
||||||
|
{ "saimaa.smem", SAIMAA_SMEM_BASE, SAIMAA_SMEM_SIZE },
|
||||||
};
|
};
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 7; 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(sysmem, rams[i].b, ram);
|
memory_region_add_subregion(sysmem, rams[i].b, ram);
|
||||||
|
if (!strcmp(rams[i].n, "saimaa.mystrom")) {
|
||||||
|
/* Mystery handler ROM: ARM mov r0,#0; bx lr repeating,
|
||||||
|
* so unknown blx targets return 0 instead of faulting. */
|
||||||
|
uint32_t *p = memory_region_get_ram_ptr(ram);
|
||||||
|
size_t k, n = SAIMAA_MYSTROM_SIZE / 4;
|
||||||
|
for (k = 0; k < n; k++) {
|
||||||
|
p[k] = 0xe12fff1e; /* bx lr: any 4-aligned entry returns */
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CPUs: 4x Cortex-A7, only cpu0 boots */
|
/* CPUs: 4x Cortex-A7, only cpu0 boots */
|
||||||
@@ -151,6 +198,16 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
sysbus_realize(SYS_BUS_DEVICE(uart), &error_fatal);
|
sysbus_realize(SYS_BUS_DEVICE(uart), &error_fatal);
|
||||||
sysbus_mmio_map(SYS_BUS_DEVICE(uart), 0, SAIMAA_UART_BASE);
|
sysbus_mmio_map(SYS_BUS_DEVICE(uart), 0, SAIMAA_UART_BASE);
|
||||||
|
|
||||||
|
/* Null-page trap at 0x0 (vectors live here; PBL normally maps ROM).
|
||||||
|
* Returns 0 for reads (data-0 invariant for SBL1 probing), logs the
|
||||||
|
* first faulting PCs for diagnosis. */
|
||||||
|
{
|
||||||
|
MemoryRegion *vec = g_new(MemoryRegion, 1);
|
||||||
|
memory_region_init_io(vec, NULL, &saimaa_vec_ops, NULL,
|
||||||
|
"saimaa-vec", 0x1000);
|
||||||
|
memory_region_add_subregion(sysmem, 0x0, vec);
|
||||||
|
}
|
||||||
|
|
||||||
/* SDHCI + eMMC image */
|
/* SDHCI + eMMC image */
|
||||||
DeviceState *sdhci = qdev_new(TYPE_SYSBUS_SDHCI);
|
DeviceState *sdhci = qdev_new(TYPE_SYSBUS_SDHCI);
|
||||||
sysbus_realize(SYS_BUS_DEVICE(sdhci), &error_fatal);
|
sysbus_realize(SYS_BUS_DEVICE(sdhci), &error_fatal);
|
||||||
@@ -174,7 +231,22 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
load_elf_strerror(sz));
|
load_elf_strerror(sz));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
cpu_set_pc(first_cpu, entry);
|
if (sms->sblmain) {
|
||||||
|
/*
|
||||||
|
* Direct start at sbl_main: skip the PBL trampoline at
|
||||||
|
* entry (it BLs into zero padding and pop-zeroes r0-r12).
|
||||||
|
* Odd address = Thumb code -> set env.thumb (CPSR T).
|
||||||
|
*/
|
||||||
|
uint32_t tgt = strtoul(sms->sblmain, NULL, 0);
|
||||||
|
/* NOTE: pass bit0 through: arm_cpu_set_pc() sets
|
||||||
|
* regs[15]=tgt&~1 and env.thumb=tgt&1 itself. */
|
||||||
|
cpu_set_pc(first_cpu, tgt);
|
||||||
|
printf("saimaa: direct start -> 0x%x%s (entry 0x%lx skipped)\n",
|
||||||
|
tgt & ~1u, (tgt & 1) ? " thumb" : " arm",
|
||||||
|
(unsigned long)entry);
|
||||||
|
} else {
|
||||||
|
cpu_set_pc(first_cpu, entry);
|
||||||
|
}
|
||||||
/* PBL normally sets up SBL stack + r0=pbl_shared; fake it.
|
/* PBL normally sets up SBL stack + r0=pbl_shared; fake it.
|
||||||
* All banked SPs: reset lands in SVC, regs[13] alone is USR. */
|
* All banked SPs: reset lands in SVC, regs[13] alone is USR. */
|
||||||
for (int b = 0; b < 8; b++) {
|
for (int b = 0; b < 8; b++) {
|
||||||
@@ -182,14 +254,94 @@ static void saimaa_machine_init(MachineState *machine)
|
|||||||
}
|
}
|
||||||
ARM_CPU(first_cpu)->env.regs[13] = 0x085FFF00;
|
ARM_CPU(first_cpu)->env.regs[13] = 0x085FFF00;
|
||||||
ARM_CPU(first_cpu)->env.regs[0] = 0x00220000; /* fake pbl_shared */
|
ARM_CPU(first_cpu)->env.regs[0] = 0x00220000; /* fake pbl_shared */
|
||||||
if (sms->sblmain) {
|
/* sbl_main expects r5 = mem-table pointer (PBL trampoline does
|
||||||
/* PBL trampoline pops pc from stack top; override it */
|
* mov r5, r0 before jumping here); point it at pbl_shared too. */
|
||||||
uint32_t tgt = strtoul(sms->sblmain, NULL, 0);
|
ARM_CPU(first_cpu)->env.regs[5] = 0x00220000;
|
||||||
uint32_t zero = 0;
|
|
||||||
cpu_physical_memory_write(0x085FFF00, &zero, 4);
|
/* sl/fp = SBL1 table bases (PBL trampoline would pop these from its
|
||||||
/* trampoline pops r0-r12,pc: pc slot is 14th word */
|
* frame); point at free DDRLOW RAM so helper-built tables persist
|
||||||
cpu_physical_memory_write(0x085FFF00 + 13 * 4, &tgt, 4);
|
* instead of vanishing into the null-page trap. */
|
||||||
printf("saimaa: stack-pc override -> 0x%x\n", tgt);
|
ARM_CPU(first_cpu)->env.regs[10] = 0x08060000; /* sl */
|
||||||
|
ARM_CPU(first_cpu)->env.regs[11] = 0x08061000; /* fp */
|
||||||
|
if (sms->regs) {
|
||||||
|
char comma = 59;
|
||||||
|
char delims[2];
|
||||||
|
char *spec, *tok, *save = NULL;
|
||||||
|
delims[0] = comma;
|
||||||
|
delims[1] = 0;
|
||||||
|
spec = g_strdup(sms->regs);
|
||||||
|
for (tok = strtok_r(spec, delims, &save); tok;
|
||||||
|
tok = strtok_r(NULL, delims, &save)) {
|
||||||
|
char *eq = strchr(tok, 61);
|
||||||
|
if (eq) {
|
||||||
|
int n = strtoul(tok, NULL, 0);
|
||||||
|
*eq = 0;
|
||||||
|
eq++;
|
||||||
|
if (n >= 0 && n < 16) {
|
||||||
|
ARM_CPU(first_cpu)->env.regs[n] =
|
||||||
|
strtoul(eq, NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free(spec);
|
||||||
|
}
|
||||||
|
/* PBL enables VFP/NEON (CPACR CP10+CP11, FPEXC.EN) before SBL1. */
|
||||||
|
ARM_CPU(first_cpu)->env.cp15.cpacr_el1 = 0x00f00000;
|
||||||
|
ARM_CPU(first_cpu)->env.cp15.nsacr |= (3u << 10);
|
||||||
|
ARM_CPU(first_cpu)->env.vfp.xregs[ARM_VFP_FPEXC] |= (1u << 30);
|
||||||
|
/* Boot scaffolding for SBL1 BringUp (WIP stub-driven PBL):
|
||||||
|
* static continuation slots for shared-epilogue pops.
|
||||||
|
* sp advances linearly (no compensation); one slot per pop site.
|
||||||
|
* (sp values from cpu traces, b10 image) */
|
||||||
|
{
|
||||||
|
uint32_t magic = 0x00000000; /* IMEM poll spins WHILE equal */
|
||||||
|
uint32_t S = 0x08062100, STUB = 0x08062120;
|
||||||
|
uint32_t w;
|
||||||
|
uint16_t h0 = 0x2000, h1 = 0x4770; /* movs r0,#0; bx lr */
|
||||||
|
uint32_t v;
|
||||||
|
/* pointer maze in owned RAM for SBL1 struct chains */
|
||||||
|
w = S + 8;
|
||||||
|
cpu_physical_memory_write(S, &w, 4);
|
||||||
|
cpu_physical_memory_write(S + 8, &STUB, 4);
|
||||||
|
cpu_physical_memory_write(STUB, &h0, 2);
|
||||||
|
cpu_physical_memory_write(STUB + 2, &h1, 2);
|
||||||
|
cpu_physical_memory_write(STUB + 4, &STUB, 4);
|
||||||
|
cpu_physical_memory_write(0x087c29d18, &S, 4);
|
||||||
|
cpu_physical_memory_write(0x08050b28, &S, 4);
|
||||||
|
cpu_physical_memory_write(0x08070000, &S, 4);
|
||||||
|
/* stack zone defaults to plain-stub addr: every register
|
||||||
|
* popped from untouched stack becomes callable (returns 0). */
|
||||||
|
{
|
||||||
|
uint32_t fill = 0x08062121;
|
||||||
|
uint32_t a;
|
||||||
|
for (a = 0x085F0000; a < 0x08600000; a += 4) {
|
||||||
|
cpu_physical_memory_write(a, &fill, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* pop-site slots -> stublets (sp values from cpu traces) */
|
||||||
|
v = 0x802f65d; /* S1 pop10 @fef0: resume sbl_main */
|
||||||
|
cpu_physical_memory_write(0x085FFF14, &v, 4);
|
||||||
|
v = 0x802f665; /* S2 pop2 @ff18: walker site (NOP) */
|
||||||
|
cpu_physical_memory_write(0x085FFF1C, &v, 4);
|
||||||
|
v = 0x8013ba5; /* S3 pop10 @ff20: 0x8013b7c loop */
|
||||||
|
cpu_physical_memory_write(0x085FFF44, &v, 4);
|
||||||
|
v = 0x802f681; /* S4 pop6 @ff48: back to sbl_main */
|
||||||
|
cpu_physical_memory_write(0x085FFF5C, &v, 4);
|
||||||
|
v = 0x8016183; /* S5 pop10 @ff50: 0x8013ae4 caller */
|
||||||
|
cpu_physical_memory_write(0x085FFF74, &v, 4);
|
||||||
|
{ /* words that popped regs must read as 0 (not stub-fill) */
|
||||||
|
uint32_t z = 0;
|
||||||
|
cpu_physical_memory_write(0x085FFF58, &z, 4); /* r7 src */
|
||||||
|
}
|
||||||
|
{ /* installer-path slots + zero words */
|
||||||
|
uint32_t z = 0;
|
||||||
|
uint32_t z2 = 0;
|
||||||
|
cpu_physical_memory_write(0x085FFF34, &z2, 4); /* S3 r7 */
|
||||||
|
cpu_physical_memory_write(0x085FFF54, &z2, 4);
|
||||||
|
cpu_physical_memory_write(0x085FFF6C, &z, 4); /* r7 src */
|
||||||
|
}
|
||||||
|
cpu_physical_memory_write(0x08600944, &magic, 4);
|
||||||
|
printf("saimaa: slots live\n");
|
||||||
}
|
}
|
||||||
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);
|
||||||
@@ -219,6 +371,18 @@ static char *saimaa_get_sblmain(Object *obj, Error **errp)
|
|||||||
return g_strdup(SAIMAA_MACHINE(obj)->sblmain);
|
return g_strdup(SAIMAA_MACHINE(obj)->sblmain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *saimaa_get_regs(Object *obj, Error **errp)
|
||||||
|
{
|
||||||
|
return g_strdup(SAIMAA_MACHINE(obj)->regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void saimaa_set_regs(Object *obj, const char *value, Error **errp)
|
||||||
|
{
|
||||||
|
SaimaaMachineState *sms = SAIMAA_MACHINE(obj);
|
||||||
|
g_free(sms->regs);
|
||||||
|
sms->regs = g_strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
static void saimaa_set_sblmain(Object *obj, const char *value, Error **errp)
|
static void saimaa_set_sblmain(Object *obj, const char *value, Error **errp)
|
||||||
{
|
{
|
||||||
SaimaaMachineState *sms = SAIMAA_MACHINE(obj);
|
SaimaaMachineState *sms = SAIMAA_MACHINE(obj);
|
||||||
@@ -232,7 +396,10 @@ static void saimaa_machine_instance_init(Object *obj)
|
|||||||
sms->sbl1 = NULL;
|
sms->sbl1 = NULL;
|
||||||
sms->emmc = NULL;
|
sms->emmc = NULL;
|
||||||
sms->sblmain = NULL;
|
sms->sblmain = NULL;
|
||||||
|
sms->regs = NULL;
|
||||||
object_property_add_str(obj, "sbl1", saimaa_get_sbl1, saimaa_set_sbl1);
|
object_property_add_str(obj, "sbl1", saimaa_get_sbl1, saimaa_set_sbl1);
|
||||||
|
object_property_add_str(obj, "regs", saimaa_get_regs,
|
||||||
|
saimaa_set_regs);
|
||||||
object_property_add_str(obj, "sblmain", saimaa_get_sblmain,
|
object_property_add_str(obj, "sblmain", saimaa_get_sblmain,
|
||||||
saimaa_set_sblmain);
|
saimaa_set_sblmain);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
.syntax unified
|
||||||
|
.cpu cortex-a7
|
||||||
|
.thumb
|
||||||
|
@ SBL1 pop-compensation stublets for saimaa (v3: slots hold STUBLET
|
||||||
|
@ addresses, never raw CONTS; S4<->S5 ping-pong stage each other).
|
||||||
|
.text
|
||||||
|
.global stub10_a, stub2, stub10_b, stub6_a, stub10_c, stub6_b, stub8
|
||||||
|
stub10_a: @ N=10, jump CONT=0x802f65d
|
||||||
|
push {r4-r7}
|
||||||
|
push {r8-r11,ip,lr}
|
||||||
|
ldr ip, =stub10_a
|
||||||
|
str ip, [sp, #36]
|
||||||
|
ldr pc, =0x802f65d
|
||||||
|
stub2: @ N=2, jump CONT=0x802f665
|
||||||
|
push {r4-r5}
|
||||||
|
ldr ip, =stub2
|
||||||
|
str ip, [sp, #4]
|
||||||
|
ldr pc, =0x802f665
|
||||||
|
stub10_b: @ N=10, jump CONT=0x8013ba5
|
||||||
|
push {r4-r7}
|
||||||
|
push {r8-r11,ip,lr}
|
||||||
|
ldr ip, =stub10_b
|
||||||
|
str ip, [sp, #36]
|
||||||
|
ldr pc, =0x8013ba5
|
||||||
|
stub6_a: @ N=6, jump CONT=0x802f681,
|
||||||
|
@ stage stub10_c for upcoming S5
|
||||||
|
push {r4-r7,lr}
|
||||||
|
push {r0}
|
||||||
|
ldr ip, =stub10_c
|
||||||
|
str ip, [sp, #20]
|
||||||
|
ldr pc, =0x802f681
|
||||||
|
stub10_c: @ N=10, jump CONT=0x8016183,
|
||||||
|
@ restage stub6_a for future S4
|
||||||
|
push {r4-r7}
|
||||||
|
push {r8-r11,ip,lr}
|
||||||
|
ldr ip, =stub6_a
|
||||||
|
str ip, [sp, #36]
|
||||||
|
ldr pc, =0x8016183
|
||||||
|
stub6_b: @ N=6, jump CONT=0x8018a8d
|
||||||
|
push {r4-r7,lr}
|
||||||
|
push {r0}
|
||||||
|
ldr ip, =stub6_b
|
||||||
|
str ip, [sp, #20]
|
||||||
|
ldr pc, =0x8018a8d
|
||||||
|
stub8: @ N=8, jump CONT=0x802f67d
|
||||||
|
push {r4-r7,lr}
|
||||||
|
push {r0-r2}
|
||||||
|
ldr ip, =stub8
|
||||||
|
str ip, [sp, #28]
|
||||||
|
ldr pc, =0x802f67d
|
||||||
+1
-1
@@ -21,7 +21,7 @@ for off, v, sz in segs:
|
|||||||
code = d[off:off + sz]
|
code = d[off:off + sz]
|
||||||
for j in range(0, len(code) - 4, 4):
|
for j in range(0, len(code) - 4, 4):
|
||||||
x = struct.unpack("<I", code[j:j + 4])[0]
|
x = struct.unpack("<I", code[j:j + 4])[0]
|
||||||
if (x & 0xFFFF0800) == 0xE92D0800: # push ... r11?/lr frame
|
if (x & 0xFFFF0000) == 0xE92D0000: # push ... r11?/lr frame
|
||||||
# count BLs in next 2KB
|
# count BLs in next 2KB
|
||||||
bls = 0
|
bls = 0
|
||||||
for q in range(j, min(j + 2048, len(code) - 4), 4):
|
for q in range(j, min(j + 2048, len(code) - 4), 4):
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""sbl_patch.py — reproduce all WIP file patches for SBL1 bring-up.
|
||||||
|
Usage: ./tools/sbl_patch.py fw/sbl/img0.elf out-dir/
|
||||||
|
Produces img0_bl.elf, img0_b2..b8.elf (cumulative variants, see STEPS).
|
||||||
|
Each step is documented with WHY (all WIP, to be replaced by real PBL).
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SEG_OFF, SEG_VA = 0x3000, 0x8005800
|
||||||
|
|
||||||
|
BLX2BL = { # blx-imm -> bl (stay Thumb): set bit12 of 2nd halfword
|
||||||
|
0x802F658: ('d8f7c2ed', 'd8f7c2fd'),
|
||||||
|
0x802F660: ('d8f702ec', 'd8f702fc'),
|
||||||
|
}
|
||||||
|
NOPS = { # call -> NOP NOP (skip unmodellable init/assert; r0/r5 ignored)
|
||||||
|
0x802F664: 'e4f77efc', # bl walker (result ignored by caller)
|
||||||
|
0x802F672: 'e6f7bbfa', # bl 0x8015bec (assert-hang)
|
||||||
|
0x802F678: None, # bl 0x8018a78, see REDIRECT (installer runs!)
|
||||||
|
0x801B71E: '9847fee7', # blx r3 + b.n self (terminal assert, 4B)
|
||||||
|
0x8016184: 'fdf790fc', # bl 0x8013aa8 (subtree nopped, see b8)
|
||||||
|
}
|
||||||
|
REDIRECT = {
|
||||||
|
# bl target off-by-N fixes (verified by disasm + QEMU traces):
|
||||||
|
0x8013AA8: ('07f026be', '07f027be'), # jump-table case0: +2
|
||||||
|
0x802F678: ('e9f7fef9', 'e9f700fa'), # bl 0x8018a78 -> 0x8018a7c (+4)
|
||||||
|
}
|
||||||
|
POOLS = {
|
||||||
|
# literal pool words redirected to owned scratch (orig struct garbage):
|
||||||
|
0x801B76C: ('280b0508', '00000708'), # -> 0x08070000
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def off(va):
|
||||||
|
return SEG_OFF + (va - SEG_VA)
|
||||||
|
|
||||||
|
|
||||||
|
def hx(d, va, n=4):
|
||||||
|
return d[off(va):off(va) + n].hex()
|
||||||
|
|
||||||
|
|
||||||
|
def set4(d, va, hexbytes):
|
||||||
|
d[off(va):off(va) + 4] = bytes.fromhex(hexbytes)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
src, outdir = sys.argv[1], sys.argv[2]
|
||||||
|
os.makedirs(outdir, exist_ok=True)
|
||||||
|
base = bytearray(open(src, 'rb').read())
|
||||||
|
|
||||||
|
def save(d, name):
|
||||||
|
p = os.path.join(outdir, name)
|
||||||
|
open(p, 'wb').write(bytes(d))
|
||||||
|
print('wrote', p, len(d))
|
||||||
|
|
||||||
|
d = bytearray(base)
|
||||||
|
for va, (a, b) in BLX2BL.items():
|
||||||
|
assert hx(d, va) == a, (hex(va), hx(d, va))
|
||||||
|
set4(d, va, b)
|
||||||
|
save(d, 'img0_bl.elf')
|
||||||
|
|
||||||
|
# b2 = bl + walker NOP
|
||||||
|
o = off(0x802F664)
|
||||||
|
assert d[o:o + 4].hex() == 'e4f77efc'
|
||||||
|
d[o:o + 4] = bytes.fromhex('c046c046')
|
||||||
|
save(d, 'img0_b2.elf')
|
||||||
|
# b3 = b2 + 0x8015bec NOP
|
||||||
|
o = off(0x802F672)
|
||||||
|
assert d[o:o + 4].hex() == 'e6f7bbfa', hx(d, 0x802F672)
|
||||||
|
d[o:o + 4] = bytes.fromhex('c046c046')
|
||||||
|
save(d, 'img0_b3.elf')
|
||||||
|
# b4 = b3 + 0x8018a78 NOP
|
||||||
|
o = off(0x802F678)
|
||||||
|
assert d[o:o + 4].hex() == 'e9f7fef9', hx(d, 0x802F678)
|
||||||
|
d[o:o + 4] = bytes.fromhex('c046c046')
|
||||||
|
save(d, 'img0_b4.elf')
|
||||||
|
# b5 = b4 + jump-table +2
|
||||||
|
o = off(0x8013AA8)
|
||||||
|
assert d[o:o + 4].hex() == '07f026be'
|
||||||
|
d[o:o + 4] = bytes.fromhex('07f027be')
|
||||||
|
save(d, 'img0_b5.elf')
|
||||||
|
# b6 = b5 + terminal assert NOP (4B)
|
||||||
|
o = off(0x801B71E)
|
||||||
|
assert d[o:o + 4].hex() == '9847fee7'
|
||||||
|
d[o:o + 4] = bytes.fromhex('c046c046')
|
||||||
|
save(d, 'img0_b6.elf')
|
||||||
|
# b7 = b6 + redirect bl to installer entry (undoes b4 NOP)
|
||||||
|
o = off(0x802F678)
|
||||||
|
assert d[o:o + 4].hex() == 'c046c046'
|
||||||
|
d[o:o + 4] = bytes.fromhex('e9f700fa')
|
||||||
|
save(d, 'img0_b7.elf')
|
||||||
|
# b8 = b7 + subtree NOP + pool redirect
|
||||||
|
o = off(0x8016184)
|
||||||
|
assert d[o:o + 4].hex() == 'fdf790fc', hx(d, 0x8016184)
|
||||||
|
d[o:o + 4] = bytes.fromhex('c046c046')
|
||||||
|
o = off(0x801B76C)
|
||||||
|
assert d[o:o + 4].hex() == '280b0508'
|
||||||
|
d[o:o + 4] = bytes.fromhex('00000708')
|
||||||
|
save(d, 'img0_b8.elf')
|
||||||
|
# b9 = b8 + hole-call NOP (EDL-dead padding slide at 0x80068FC)
|
||||||
|
o = off(0x8008396)
|
||||||
|
assert d[o:o + 4].hex() == 'fef780f9', hx(d, 0x8008396)
|
||||||
|
d[o:o + 4] = bytes.fromhex('c046c046')
|
||||||
|
save(d, 'img0_b9.elf')
|
||||||
|
# b10 = b9 with installer-bl reverted to NOP (S2 lands mid-NOPs safely,
|
||||||
|
# falls into the 0x8013b7c call; installer skipped, structs via maze)
|
||||||
|
o = off(0x802F678)
|
||||||
|
assert d[o:o + 4].hex() == 'e9f700fa', hx(d, 0x802F678)
|
||||||
|
d[o:o + 4] = bytes.fromhex('c046c046')
|
||||||
|
save(d, 'img0_b10.elf')
|
||||||
|
# b11 = b10 with installer-bl restored (redirect, installer runs again)
|
||||||
|
o = off(0x802F678)
|
||||||
|
assert d[o:o + 4].hex() == 'c046c046'
|
||||||
|
d[o:o + 4] = bytes.fromhex('e9f700fa')
|
||||||
|
save(d, 'img0_b11.elf')
|
||||||
|
# b12: b10-variant with jump-table entry0 NOP (fall into entry1/case-1);
|
||||||
|
# rebuilt from b10 base (installer stays skipped)
|
||||||
|
d = bytearray(base)
|
||||||
|
for va, (a, b) in BLX2BL.items():
|
||||||
|
set4(d, va, b)
|
||||||
|
for va, exp, new in [
|
||||||
|
(0x802F664, 'e4f77efc', 'c046c046'),
|
||||||
|
(0x802F672, 'e6f7bbfa', 'c046c046'),
|
||||||
|
(0x802F678, 'e9f7fef9', 'c046c046'),
|
||||||
|
(0x801B71E, '9847fee7', 'c046c046'),
|
||||||
|
(0x8008396, 'fef780f9', 'c046c046'),
|
||||||
|
(0x8016184, 'fdf790fc', 'c046c046'),
|
||||||
|
(0x801B76C, '280b0508', '00000708'),
|
||||||
|
]:
|
||||||
|
assert hx(d, va) == exp, (hex(va), hx(d, va))
|
||||||
|
set4(d, va, new)
|
||||||
|
o = off(0x8013AA8)
|
||||||
|
assert d[o:o + 4].hex() == '07f026be', hx(d, 0x8013AA8)
|
||||||
|
d[o:o + 4] = bytes.fromhex('c046c046')
|
||||||
|
save(d, 'img0_b12.elf')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import struct
|
||||||
|
|
||||||
|
M = '/root/qemu-src/qemu-8.2.2/hw/arm/saimaa.c'
|
||||||
|
src = open(M).read()
|
||||||
|
|
||||||
|
start = src.find(' /* Boot scaffolding for SBL1 BringUp')
|
||||||
|
assert start > 0, 'scaffold start not found'
|
||||||
|
end_marker = 'printf("saimaa: SBL1 %s loaded'
|
||||||
|
end = src.find(end_marker, start)
|
||||||
|
assert end > 0, 'scaffold end not found'
|
||||||
|
line_start = src.rfind('\n', 0, end) + 1
|
||||||
|
|
||||||
|
blob = open('/tmp/stubs3.bin', 'rb').read()
|
||||||
|
print('stub blob len', len(blob))
|
||||||
|
c_array = ', '.join('0x%02x' % b for b in blob)
|
||||||
|
|
||||||
|
S10_A = '0x08062001'
|
||||||
|
S2 = '0x08062013'
|
||||||
|
S10_B = '0x08062021'
|
||||||
|
S6_A = '0x08062033'
|
||||||
|
|
||||||
|
new_block = ''' /* Boot scaffolding for SBL1 BringUp (WIP stub-driven PBL):
|
||||||
|
* pop-compensation stublets at 0x08062000 pin sp and route onward,
|
||||||
|
* so repeated pops always hit valid slots. */
|
||||||
|
{
|
||||||
|
static const unsigned char stub_blob[] = { %s };
|
||||||
|
uint32_t magic = 0x00000000; /* IMEM poll spins WHILE equal */
|
||||||
|
uint32_t S = 0x08062100, STUB = 0x08062120;
|
||||||
|
uint32_t w;
|
||||||
|
uint16_t h0 = 0x2000, h1 = 0x4770; /* movs r0,#0; bx lr */
|
||||||
|
size_t k;
|
||||||
|
uint32_t v;
|
||||||
|
for (k = 0; k < sizeof(stub_blob); k += 4) {
|
||||||
|
uint32_t word;
|
||||||
|
__builtin_memcpy(&word, &stub_blob[k], 4);
|
||||||
|
cpu_physical_memory_write(0x08062000 + k, &word, 4);
|
||||||
|
}
|
||||||
|
/* pointer maze in owned RAM for SBL1 struct chains */
|
||||||
|
w = S + 8;
|
||||||
|
cpu_physical_memory_write(S, &w, 4);
|
||||||
|
cpu_physical_memory_write(S + 8, &STUB, 4);
|
||||||
|
cpu_physical_memory_write(STUB, &h0, 2);
|
||||||
|
cpu_physical_memory_write(STUB + 2, &h1, 2);
|
||||||
|
cpu_physical_memory_write(0x087c29d18, &S, 4);
|
||||||
|
cpu_physical_memory_write(0x08050b28, &S, 4);
|
||||||
|
cpu_physical_memory_write(0x08070000, &S, 4);
|
||||||
|
/* pop-site slots -> stublets (sp values from cpu traces) */
|
||||||
|
v = %s; /* S1 pop10 */
|
||||||
|
cpu_physical_memory_write(0x085FFF14, &v, 4);
|
||||||
|
v = %s; /* S2 pop2 */
|
||||||
|
cpu_physical_memory_write(0x085FFF1C, &v, 4);
|
||||||
|
v = %s; /* S3 pop10 */
|
||||||
|
cpu_physical_memory_write(0x085FFF44, &v, 4);
|
||||||
|
cpu_physical_memory_write(0x085FFF7C, &v, 4);
|
||||||
|
v = %s; /* S4 pop6 (stublet chains S5) */
|
||||||
|
cpu_physical_memory_write(0x085FFF34, &v, 4);
|
||||||
|
cpu_physical_memory_write(0x08600944, &magic, 4);
|
||||||
|
printf("saimaa: stub-scaffold live\\n");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
''' % (c_array, S10_A, S2, S10_B, S6_A)
|
||||||
|
|
||||||
|
src = src[:start] + new_block + src[line_start:]
|
||||||
|
open(M, 'w').write(src)
|
||||||
|
print('CANONICAL SCAFFOLD v2 OK')
|
||||||
Reference in New Issue
Block a user