saimaa: sync uncommitted zern work (timer, DDRH split, manual ELF load, UEFI tables)

This commit is contained in:
SashegDev
2026-09-11 05:48:31 +00:00
parent 09ba5a9911
commit 759cbd66de
+186 -30
View File
@@ -22,6 +22,8 @@
#include "qemu/log.h"
/* Real MSM8909 map (subset) */
#define SAIMAA_UEFI_BASE 0x10000000
#define SAIMAA_UEFI_SIZE 0x04000000
#define SAIMAA_RPM_BASE 0x00400000
#define SAIMAA_RPM_SIZE 0x07C00000 /* 0x00400000..0x08000000 heap-ish */
#define SAIMAA_OCIMEM_BASE 0x00200000
@@ -30,8 +32,10 @@
#define SAIMAA_DDRLOW_SIZE 0x00600000
#define SAIMAA_IMEM_BASE 0x08600000
#define SAIMAA_IMEM_SIZE 0x00010000
#define SAIMAA_DDRHIGH_BASE 0x80000000
#define SAIMAA_DDRHIGH_SIZE 0x20000000
#define SAIMAA_DDRH_A_BASE 0x80000000
#define SAIMAA_DDRH_A_SIZE 0x07C00000
#define SAIMAA_DDRH_B_BASE 0x88400000
#define SAIMAA_DDRH_B_SIZE 0x17C00000
#define SAIMAA_UART_BASE 0x078AF000
#define SAIMAA_SDHCI_BASE 0x07824900
#define SAIMAA_GICD_BASE 0x0B000000
@@ -42,6 +46,49 @@
#define SAIMAA_SMEM_BASE 0x87C00000
#define SAIMAA_SMEM_SIZE 0x00800000
/* ---- free-running timer (RPM msg RAM cell 0x004a0030 police) ----
* SBL1 delay loops read [0x004a0030] as a counter; RPM firmware would
* advance it. Overlay 4K with a host-time counter so delays terminate. */
#define TYPE_SAIMAA_TMR "saimaa-tmr"
OBJECT_DECLARE_SIMPLE_TYPE(SaimaaTmrState, SAIMAA_TMR)
struct SaimaaTmrState {
SysBusDevice parent_obj;
MemoryRegion iomem;
};
static uint64_t saimaa_tmr_read(void *opaque, hwaddr off, unsigned size)
{
(void)opaque;
(void)off;
(void)size;
return (uint64_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 12);
}
static void saimaa_tmr_write(void *opaque, hwaddr off, uint64_t val,
unsigned size)
{
(void)opaque;
(void)off;
(void)val;
(void)size;
}
static const MemoryRegionOps saimaa_tmr_ops = {
.read = saimaa_tmr_read,
.write = saimaa_tmr_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void saimaa_tmr_init(Object *obj)
{
SaimaaTmrState *s = SAIMAA_TMR(obj);
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &saimaa_tmr_ops, s,
"saimaa-tmr", 0x10000);
sysbus_init_mmio(dev, &s->iomem);
}
/* ---- minimal MSM UARTDM stub: TX prints, status always ready ---- */
#define TYPE_SAIMAA_VEC "saimaa-vec"
#define TYPE_SAIMAA_UART "saimaa-uart"
@@ -142,6 +189,8 @@ typedef struct SaimaaMachineState SaimaaMachineState;
#define TYPE_SAIMAA_MACHINE MACHINE_TYPE_NAME("saimaa")
OBJECT_DECLARE_SIMPLE_TYPE(SaimaaMachineState, SAIMAA_MACHINE)
static void saimaa_post_reset(void *opaque);
static void saimaa_machine_init(MachineState *machine)
{
SaimaaMachineState *sms = SAIMAA_MACHINE(machine);
@@ -150,19 +199,22 @@ static void saimaa_machine_init(MachineState *machine)
/* RAM map */
const struct { const char *n; hwaddr b; hwaddr s; } rams[] = {
{ "saimaa.uefi", SAIMAA_UEFI_BASE, SAIMAA_UEFI_SIZE },
{ "saimaa.rpm", SAIMAA_RPM_BASE, SAIMAA_RPM_SIZE },
{ "saimaa.ocimem", SAIMAA_OCIMEM_BASE, SAIMAA_OCIMEM_SIZE },
{ "saimaa.ddrlow", SAIMAA_DDRLOW_BASE, SAIMAA_DDRLOW_SIZE },
{ "saimaa.imem", SAIMAA_IMEM_BASE, SAIMAA_IMEM_SIZE },
{ "saimaa.ddrhigh", SAIMAA_DDRHIGH_BASE, SAIMAA_DDRHIGH_SIZE },
{ "saimaa.ddrhigh_a", SAIMAA_DDRH_A_BASE, SAIMAA_DDRH_A_SIZE },
{ "saimaa.ddrhigh_b", SAIMAA_DDRH_B_BASE, SAIMAA_DDRH_B_SIZE },
{ "saimaa.mystrom", SAIMAA_MYSTROM_BASE, SAIMAA_MYSTROM_SIZE },
{ "saimaa.smem", SAIMAA_SMEM_BASE, SAIMAA_SMEM_SIZE },
};
for (i = 0; i < 7; i++) {
for (i = 0; i < 9; i++) {
MemoryRegion *ram = g_new(MemoryRegion, 1);
memory_region_init_ram(ram, NULL, rams[i].n, rams[i].s,
&error_fatal);
memory_region_add_subregion(sysmem, rams[i].b, ram);
memory_region_add_subregion_overlap(sysmem, rams[i].b, ram,
1);
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. */
@@ -193,6 +245,14 @@ static void saimaa_machine_init(MachineState *machine)
sysbus_mmio_map(SYS_BUS_DEVICE(gic), 0, SAIMAA_GICD_BASE);
sysbus_mmio_map(SYS_BUS_DEVICE(gic), 1, SAIMAA_GICC_BASE);
/* Free-running timer overlay at 0x004a0000 (prio beats RPM RAM) */
{
DeviceState *tmr = qdev_new(TYPE_SAIMAA_TMR);
sysbus_realize(SYS_BUS_DEVICE(tmr), &error_fatal);
memory_region_add_subregion_overlap(sysmem, 0x004a0000,
SYS_BUS_DEVICE(tmr)->mmio[0].memory, 1);
}
/* UART at MSM address */
DeviceState *uart = qdev_new(TYPE_SAIMAA_UART);
sysbus_realize(SYS_BUS_DEVICE(uart), &error_fatal);
@@ -220,33 +280,52 @@ static void saimaa_machine_init(MachineState *machine)
&error_fatal);
}
/* Load real SBL1 ELF, jump to entry */
/* Load SBL1 ELF straight into RAM (NOT via load_elf: its ROM
* blobs shadow RAM read-only and fault on data writes). */
if (sms->sbl1) {
gchar *blob = NULL;
gsize bloblen = 0;
GError *gerr = NULL;
uint32_t e_entry, phoff;
uint16_t phnum;
uint64_t entry = 0;
uint32_t pflags = 0;
ssize_t sz = load_elf(sms->sbl1, NULL, NULL, NULL, &entry, NULL,
NULL, &pflags, 0, EM_ARM, 1, 0);
if (sz < 0) {
error_report("saimaa: cannot load SBL1 %s: %s", sms->sbl1,
load_elf_strerror(sz));
ssize_t sz = 0;
int k;
if (!g_file_get_contents(sms->sbl1, &blob, &bloblen, &gerr)) {
error_report("saimaa: cannot read SBL1 %s: %s", sms->sbl1,
gerr ? gerr->message : "?");
exit(1);
}
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);
if (bloblen < 52 || blob[0] != 0x7f || blob[1] != 'E' ||
blob[2] != 'L' || blob[3] != 'F') {
error_report("saimaa: %s is not an ELF", sms->sbl1);
exit(1);
}
memcpy(&e_entry, blob + 24, 4);
memcpy(&phoff, blob + 28, 4);
memcpy(&phnum, blob + 44, 2);
entry = e_entry;
for (k = 0; k < phnum; k++) {
uint32_t t, off, v, fsz, msz;
uint32_t zero = 0;
uint32_t zaddr, zend;
memcpy(&t, blob + phoff + k * 32, 4);
if (t != 1) {
continue;
}
memcpy(&off, blob + phoff + k * 32 + 4, 4);
memcpy(&v, blob + phoff + k * 32 + 8, 4);
memcpy(&fsz, blob + phoff + k * 32 + 16, 4);
memcpy(&msz, blob + phoff + k * 32 + 20, 4);
if (fsz > 0 && v != 0x00220000) { /* skip OCIMEM seg (write-faults?) */
cpu_physical_memory_write(v, (uint8_t *)blob + off, fsz);
sz += fsz;
}
for (zaddr = v + fsz; zaddr < v + msz; zaddr += 4) {
cpu_physical_memory_write(zaddr, &zero, 4);
}
}
g_free(blob);
/* PBL normally sets up SBL stack + r0=pbl_shared; fake it.
* All banked SPs: reset lands in SVC, regs[13] alone is USR. */
for (int b = 0; b < 8; b++) {
@@ -264,7 +343,7 @@ static void saimaa_machine_init(MachineState *machine)
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 comma = 44;
char delims[2];
char *spec, *tok, *save = NULL;
delims[0] = comma;
@@ -289,6 +368,17 @@ static void saimaa_machine_init(MachineState *machine)
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);
if (sms->sblmain) {
/* Direct start (bypass PBL trampoline). Pass bit0 through:
* arm_cpu_set_pc() sets regs[15]=tgt&~1, thumb=tgt&1. */
uint32_t tgt = strtoul(sms->sblmain, NULL, 0);
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);
}
/* 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.
@@ -296,16 +386,17 @@ static void saimaa_machine_init(MachineState *machine)
{
uint32_t magic = 0x00000000; /* IMEM poll spins WHILE equal */
uint32_t S = 0x08062100, STUB = 0x08062120;
uint32_t STUBV = 0x08062121; /* odd: blx stays Thumb */
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(S + 8, &STUBV, 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(STUB + 4, &STUBV, 4);
cpu_physical_memory_write(0x087c29d18, &S, 4);
cpu_physical_memory_write(0x08050b28, &S, 4);
cpu_physical_memory_write(0x08070000, &S, 4);
@@ -341,13 +432,70 @@ static void saimaa_machine_init(MachineState *machine)
cpu_physical_memory_write(0x085FFF6C, &z, 4); /* r7 src */
}
cpu_physical_memory_write(0x08600944, &magic, 4);
/* Minimal UEFI SystemTable + BootServices for EDK2 PE entry
* (r1 = SystemTable). Stubs return EFI_SUCCESS(0); ConOut
* goes to UARTDM; AllocatePool/GetMemoryMap are functional. */
{
uint32_t ST = 0x13000000, BS = 0x13000100;
uint32_t RS = 0x13000200, CO = 0x13000300;
uint32_t STUBS = 0x13000400, LI = 0x13000500;
uint32_t HEAP = 0x13001000;
uint32_t w;
uint16_t ret0[] = { 0x2000, 0x4770 }; /* movs r0,#0; bx lr */
int i;
/* generic return-0 stubs for BS[0..23] */
for (i = 0; i < 24; i++) {
cpu_physical_memory_write(STUBS + i * 8, &ret0[0], 2);
cpu_physical_memory_write(STUBS + i * 8 + 2, &ret0[1],
2);
w = STUBS + i * 8 + 1; /* odd: Thumb */
cpu_physical_memory_write(BS + 24 + i * 4, &w, 4);
}
/* BS Hdr */
w = 0x56524553; /* "SERV" (BOOTSERV sig lo) */
cpu_physical_memory_write(BS, &w, 4);
/* SystemTable Hdr + pointers */
w = 0x49535953; /* "SYSI" (IBI SYST sig lo) */
cpu_physical_memory_write(ST, &w, 4);
w = 0x54535953;
cpu_physical_memory_write(ST + 4, &w, 4);
w = 0x00020000; /* Revision 2.0 */
cpu_physical_memory_write(ST + 12, &w, 4);
w = 0x13000048; /* FirmwareVendor str */
cpu_physical_memory_write(ST + 16, &w, 4);
w = CO; /* ConOut */
cpu_physical_memory_write(ST + 40, &w, 4);
cpu_physical_memory_write(ST + 44, &w, 4);
w = RS; /* RuntimeServices */
cpu_physical_memory_write(ST + 56, &w, 4);
w = BS; /* BootServices */
cpu_physical_memory_write(ST + 60, &w, 4);
/* LoadedImage for HandleProtocol queries */
w = 0x3130414d; /* "MAM0" */
cpu_physical_memory_write(LI, &w, 4);
w = HEAP;
cpu_physical_memory_write(HEAP - 4, &w, 4); /* bump ptr */
/* r1 = SystemTable for UEFI entry (SBL1 overwrites r1) */
ARM_CPU(first_cpu)->env.regs[1] = ST;
printf("saimaa: uefi tables live\n");
}
printf("saimaa: slots live\n");
}
printf("saimaa: SBL1 %s loaded (%zd bytes), entry 0x%lx\n",
sms->sbl1, sz, (unsigned long)entry);
qemu_register_reset(saimaa_post_reset, NULL);
}
}
static void saimaa_post_reset(void *opaque)
{
uint32_t S = 0x08062100;
(void)opaque;
/* Re-apply pokes that live inside ROM-covered ranges (rom_reset
* restores file bytes over them). */
cpu_physical_memory_write(0x08050b28, &S, 4);
}
static void saimaa_uart_class_init(ObjectClass *oc, void *data)
{
(void)oc;
@@ -422,6 +570,13 @@ static const TypeInfo saimaa_machine_info = {
.class_init = saimaa_machine_class_init,
};
static const TypeInfo saimaa_tmr_info = {
.name = TYPE_SAIMAA_TMR,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SaimaaTmrState),
.instance_init = saimaa_tmr_init,
};
static const TypeInfo saimaa_uart_info = {
.name = TYPE_SAIMAA_UART,
.parent = TYPE_SYS_BUS_DEVICE,
@@ -432,6 +587,7 @@ static const TypeInfo saimaa_uart_info = {
static void saimaa_register_types(void)
{
type_register_static(&saimaa_tmr_info);
type_register_static(&saimaa_machine_info);
type_register_static(&saimaa_uart_info);
}