/* * Nokia Lumia 550 (Saimaa, MSM8909) emulation — WIP. * Boots real SBL1 (img0.elf from Emergency Payload) on Cortex-A7. * Most peripherals are trace-stubs; UART prints to stdio. */ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" #include "hw/arm/boot.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/sysbus.h" #include "hw/sd/sdhci.h" #include "hw/sd/sd.h" #include "hw/char/serial.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "elf.h" #include "qom/object.h" #include "cpu.h" #include "qemu/error-report.h" #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 #define SAIMAA_OCIMEM_SIZE 0x00100000 #define SAIMAA_DDRLOW_BASE 0x08000000 #define SAIMAA_DDRLOW_SIZE 0x00600000 #define SAIMAA_IMEM_BASE 0x08600000 #define SAIMAA_IMEM_SIZE 0x00010000 #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 #define SAIMAA_GICC_BASE 0x0B002000 #define SAIMAA_NCPUS 4 #define SAIMAA_MYSTROM_BASE 0x69000000 #define SAIMAA_MYSTROM_SIZE 0x00100000 #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" OBJECT_DECLARE_SIMPLE_TYPE(SaimaaUartState, SAIMAA_UART) struct SaimaaUartState { SysBusDevice parent_obj; MemoryRegion iomem; /* 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 < 400) { ARMCPU *cpu = ARM_CPU(current_cpu); uint64_t pc = cpu ? cpu->env.regs[15] : 0; int th = cpu ? cpu->env.thumb : 0; uint32_t mb0 = 0, mb1 = 0; if (saimaa_vec_n < 3) { cpu_physical_memory_read(0x08062200, &mb0, 4); cpu_physical_memory_read(0x08062204, &mb1, 4); } fprintf(stderr, "saimaa-vec: %s off=0x%x pc=0x%lx", size == 2 ? "fetch?" : "read", (unsigned)off, (unsigned long)pc - (th ? 4 : 8)); if (saimaa_vec_n < 3) { fprintf(stderr, " maze-mailbox: lr=0x%x sp=0x%x", mb0, mb1); } if (cpu) { int b, r; fprintf(stderr, " regs="); for (r = 0; r < 16; r++) { fprintf(stderr, "%x,", cpu->env.regs[r]); } fprintf(stderr, " spsr=0x%x banks13=", cpu->env.spsr); for (b = 0; b < 8; b++) { fprintf(stderr, "%x,", cpu->env.banked_r13[b]); } fprintf(stderr, " banks14="); for (b = 0; b < 8; b++) { fprintf(stderr, "%x,", cpu->env.banked_r14[b]); } } fprintf(stderr, "\n"); saimaa_vec_n++; } /* BKPT at exception vectors: data/prefetch abort fetch traps to gdb */ if (off == 0x8 || off == 0xC || off == 0x10 || off == 0x18 || off == 0x1C) { return 0xE1200070; } 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) { switch (off) { case 0x08: /* UARTDM_SR: pretend TX ready + empty */ return 0x000000A0; case 0x70: /* UARTDM_TF: read returns 0 */ return 0; default: qemu_log_mask(LOG_GUEST_ERROR, "saimaa-uart: unimp read @0x%x\n", (unsigned)off); return 0; } } static void saimaa_uart_write(void *opaque, hwaddr off, uint64_t val, unsigned size) { SaimaaUartState *s = SAIMAA_UART(opaque); (void)s; if (off == 0x70) { /* UARTDM_TF */ putchar(val & 0xff); fflush(stdout); return; } qemu_log_mask(LOG_GUEST_ERROR, "saimaa-uart: unimp write @0x%x = 0x%x\n", (unsigned)off, (unsigned)val); } static const MemoryRegionOps saimaa_uart_ops = { .read = saimaa_uart_read, .write = saimaa_uart_write, .endianness = DEVICE_NATIVE_ENDIAN, }; static void saimaa_uart_init(Object *obj) { SaimaaUartState *s = SAIMAA_UART(obj); SysBusDevice *dev = SYS_BUS_DEVICE(obj); memory_region_init_io(&s->iomem, obj, &saimaa_uart_ops, s, "saimaa-uart", 0x1000); sysbus_init_mmio(dev, &s->iomem); } static void saimaa_uart_realize(DeviceState *dev, Error **errp) { /* backend wired directly in machine init; nothing to check */ } /* ---- machine ---- */ struct SaimaaMachineState { MachineState parent_obj; char *sbl1; char *emmc; 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; #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); MemoryRegion *sysmem = get_system_memory(); int i; /* 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_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 < 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_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. */ 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 */ for (i = 0; i < SAIMAA_NCPUS; i++) { Object *cpuobj = object_new("cortex-a7-" TYPE_ARM_CPU); object_property_set_int(cpuobj, "mp-affinity", i, &error_fatal); if (i > 0) { object_property_set_bool(cpuobj, "start-powered-off", true, &error_fatal); } object_property_set_bool(cpuobj, "realized", true, &error_fatal); } /* GICv2 (minimal: dist + cpuif, IRQs wired later) */ DeviceState *gic = qdev_new("arm_gic"); qdev_prop_set_uint32(gic, "num-cpu", SAIMAA_NCPUS); qdev_prop_set_uint32(gic, "num-irq", 160); sysbus_realize(SYS_BUS_DEVICE(gic), &error_fatal); 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); 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); } /* Low-RAM scratch for SBL1 low-address probes (e.g. [0x3146]). * Real HW has PBL ROM/XPU here; zeros for now (WIP). */ { MemoryRegion *low = g_new(MemoryRegion, 1); memory_region_init_ram(low, NULL, "saimaa.low", 0x1000, &error_fatal); memory_region_add_subregion(sysmem, 0x3000, low); } /* SDHCI + eMMC image */ DeviceState *sdhci = qdev_new(TYPE_SYSBUS_SDHCI); sysbus_realize(SYS_BUS_DEVICE(sdhci), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(sdhci), 0, SAIMAA_SDHCI_BASE); DriveInfo *di = drive_get(IF_SD, 0, 0); if (di) { DeviceState *card = qdev_new(TYPE_SD_CARD); qdev_prop_set_drive(card, "drive", blk_by_legacy_dinfo(di)); qdev_realize_and_unref(card, qdev_get_child_bus(sdhci, "sd-bus"), &error_fatal); } /* 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; 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 (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++) { ARM_CPU(first_cpu)->env.banked_r13[b] = 0x085FFF00; } ARM_CPU(first_cpu)->env.regs[13] = 0x085FFF00; ARM_CPU(first_cpu)->env.regs[0] = 0x00220000; /* fake pbl_shared */ /* sbl_main expects r5 = mem-table pointer (PBL trampoline does * mov r5, r0 before jumping here); point it at pbl_shared too. */ ARM_CPU(first_cpu)->env.regs[5] = 0x00220000; /* sl/fp = SBL1 table bases (PBL trampoline would pop these from its * frame); point at free DDRLOW RAM so helper-built tables persist * instead of vanishing into the null-page trap. */ ARM_CPU(first_cpu)->env.regs[10] = 0x08060000; /* sl */ ARM_CPU(first_cpu)->env.regs[11] = 0x08061000; /* fp */ if (sms->regs) { char comma = 44; 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); 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. * (sp values from cpu traces, b10 image) */ { 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. * STUB = mailbox logger + return-0: records lr/sp at entry * to 0x08062200 for post-mortem, then movs r0,#0; bx lr. */ w = S + 8; cpu_physical_memory_write(S, &w, 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 */ cpu_physical_memory_write(S + 4, &STUB, 4); cpu_physical_memory_write(S + 8, &STUB, 4); { /* from qemu/maze.S: mailbox logger + lr-range guard. * Bad lr (not 0x08xxxxxx) spins with IRQs off instead of * jumping into the void. 36 bytes. */ static const uint8_t mb[] = { 0x42, 0xf2, 0x00, 0x2c, 0xc0, 0xf6, 0x06, 0x0c, 0xcc, 0xf8, 0x00, 0xe0, 0xcc, 0xf8, 0x04, 0xd0, 0x00, 0x20, 0x4f, 0xea, 0x1e, 0x6c, 0xbc, 0xf1, 0x08, 0x0f, 0x00, 0xd1, 0x70, 0x47, 0x72, 0xb6, 0xfd, 0xe7, 0x00, 0x00, }; size_t k; for (k = 0; k < sizeof(mb); k += 4) { uint32_t word = 0; size_t n = sizeof(mb) - k < 4 ? sizeof(mb) - k : 4; __builtin_memcpy(&word, &mb[k], n); cpu_physical_memory_write(STUB + k, &word, 4); } } cpu_physical_memory_write(0x087c29d18, &S, 4); cpu_physical_memory_write(0x08050b28, &S, 4); cpu_physical_memory_write(0x08070000, &S, 4); /* S6 stublet at 0x08062160: rebuild popped frame regs, * return to 0x8013ba5 (b14 redirects 0x8017afc pop here) */ { static const uint8_t s6[] = { 0xbd, 0xe8, 0xfc, 0x47, /* ldmia.w sp!,{r2-r9,sl,lr} */ 0x43, 0xf6, 0xa5, 0x3e, /* movw lr,#0x3ba5 */ 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(0x08062160 + k, &word, 4); } } /* Banner stub at 0x08062170: print r0 string via UARTDM * (b16 redirects 0x802f690 blx here). Built from qemu/banner.S: * push frame, print loop, ldr pc,=0x802f694 (callee noreturn). * 28 bytes total. */ { static const uint8_t bn[] = { 0x10, 0xb5, 0x4f, 0xf2, 0x00, 0x03, 0xc0, 0xf2, 0x8a, 0x73, 0x10, 0xf8, 0x01, 0x1b, 0x09, 0xb1, 0x19, 0x67, 0xfa, 0xe7, 0xdf, 0xf8, 0x00, 0xf0, 0x94, 0xf6, 0x02, 0x08, }; size_t k; for (k = 0; k < sizeof(bn); k += 4) { uint32_t word = 0; size_t n = sizeof(bn) - k < 4 ? sizeof(bn) - k : 4; __builtin_memcpy(&word, &bn[k], n); cpu_physical_memory_write(0x08062170 + k, &word, 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); v = 0x8013ba5; /* S6 ldmia.w @7afc: sp=0x85fff20, pc=[+36] */ cpu_physical_memory_write(0x085FFF44, &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); /* 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; (void)data; } static char *saimaa_get_sbl1(Object *obj, Error **errp) { return g_strdup(SAIMAA_MACHINE(obj)->sbl1); } static void saimaa_set_sbl1(Object *obj, const char *value, Error **errp) { SaimaaMachineState *sms = SAIMAA_MACHINE(obj); g_free(sms->sbl1); sms->sbl1 = g_strdup(value); } static char *saimaa_get_sblmain(Object *obj, Error **errp) { 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) { SaimaaMachineState *sms = SAIMAA_MACHINE(obj); g_free(sms->sblmain); sms->sblmain = g_strdup(value); } static void saimaa_machine_instance_init(Object *obj) { SaimaaMachineState *sms = SAIMAA_MACHINE(obj); sms->sbl1 = NULL; sms->emmc = NULL; sms->sblmain = NULL; sms->regs = NULL; 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, saimaa_set_sblmain); } static void saimaa_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "Nokia Lumia 550 Saimaa (MSM8909) WIP"; mc->init = saimaa_machine_init; mc->max_cpus = SAIMAA_NCPUS; mc->default_cpus = SAIMAA_NCPUS; mc->default_ram_size = 512 * MiB; /* only for -m accounting */ } static const TypeInfo saimaa_machine_info = { .name = TYPE_SAIMAA_MACHINE, .parent = TYPE_MACHINE, .instance_size = sizeof(SaimaaMachineState), .instance_init = saimaa_machine_instance_init, .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, .instance_size = sizeof(SaimaaUartState), .instance_init = saimaa_uart_init, .class_init = saimaa_uart_class_init, }; static void saimaa_register_types(void) { type_register_static(&saimaa_tmr_info); type_register_static(&saimaa_machine_info); type_register_static(&saimaa_uart_info); } type_init(saimaa_register_types)