diff --git a/qemu/saimaa.c b/qemu/saimaa.c index abcaa95..2e51525 100644 --- a/qemu/saimaa.c +++ b/qemu/saimaa.c @@ -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); }