958 lines
38 KiB
C
958 lines
38 KiB
C
/*
|
|
* 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
|
|
/* Ends before peripheral window 0x07800000 (SDC/USB/UART live there;
|
|
* a bigger region shadows MMIO with silent RAM!). */
|
|
#define SAIMAA_RPM_SIZE 0x07400000 /* 0x00400000..0x07800000 */
|
|
#define SAIMAA_OCIMEM_BASE 0x00200000
|
|
#define SAIMAA_OCIMEM_SIZE 0x00100000
|
|
#define SAIMAA_DDRLOW_BASE 0x08000000
|
|
#define SAIMAA_DDRLOW_SIZE 0x00600000
|
|
/* High DDRLOW for our stubs (SBL heap reuses 0x0806xxxx) */
|
|
#define SAIMAA_DDRHI_BASE 0x08610000
|
|
#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_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)
|
|
{
|
|
/* 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)off;
|
|
(void)size;
|
|
return ++tick;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/* Stubs region handle for set_readonly after scaffold implant. */
|
|
static MemoryRegion *saimaa_stubs_mr;
|
|
|
|
/* DDRLOW stub trap [0x08006B80,0x08006CE0): serves the scaffolded bytes
|
|
* (S/maze/S6/banner/S7/S9/S10/S16 stubs) on read/fetch, ignores SBL
|
|
* heap-clobber writes. Uni parity: deferred-restore guard in sbl_uni.py
|
|
* (wider: [0x080068C0,0x08006CE0), 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 0x00000160
|
|
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 ---- */
|
|
#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)
|
|
{
|
|
/* Maze blob mirrored at [0,18) 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[18] = {
|
|
0x00, 0x20, 0x4f, 0xea, 0x1e, 0x6c, 0xbc, 0xf1,
|
|
0x08, 0x0f, 0x00, 0xd1, 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);
|
|
uint64_t pc = cpu ? cpu->env.regs[15] : 0;
|
|
int th = cpu ? cpu->env.thumb : 0;
|
|
uint32_t dfar = 0, ifsr = 0, dfsr = 0;
|
|
if (cpu) {
|
|
/* CP15 DFAR/IFSR/DFSR for abort diagnosis */
|
|
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",
|
|
size == 2 ? "fetch?" : "read",
|
|
(unsigned)off, (unsigned long)pc - (th ? 4 : 8));
|
|
fprintf(stderr, " DFAR=0x%x IFSR=0x%x DFSR=0x%x", dfar, ifsr,
|
|
dfsr);
|
|
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 */
|
|
static int uart_n;
|
|
if (uart_n < 64) {
|
|
fprintf(stderr, "saimaa-uart: TX 0x%02x '%c'\n",
|
|
(unsigned)(val & 0xff),
|
|
(val & 0xff) >= 32 && (val & 0xff) < 127 ?
|
|
(char)(val & 0xff) : '.');
|
|
uart_n++;
|
|
}
|
|
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;
|
|
|
|
setvbuf(stdout, NULL, _IONBF, 0); /* UART stub uses putchar; no lost logs */
|
|
|
|
/* 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.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.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 < ARRAY_SIZE(rams); 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.stubs")) {
|
|
saimaa_stubs_mr = 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 */
|
|
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;
|
|
/* 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 */
|
|
/* 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 {
|
|
/* 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):
|
|
* 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 = 0x08006B80, STUB = 0x08006B8C; /* dead PBL-entry code */
|
|
uint32_t STUBV = 0x08006B8D; /* 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 = minimal logger + return-0 (movs r0,#0; bx lr);
|
|
* mailbox at 0x08006BD0 records last (lr,sp). */
|
|
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.
|
|
* NOTE: ODD (STUBV) — blx to even would switch to ARM! */
|
|
cpu_physical_memory_write(S + 4, &STUBV, 4);
|
|
cpu_physical_memory_write(S + 8, &STUBV, 4);
|
|
{
|
|
/* Minimal maze: movs r0,#0; bx lr (4B). No mailbox, no
|
|
* guard — vec trap already reports fault state. Less
|
|
* surface for SBL heap clobbers. */
|
|
static const uint8_t mb[] = {
|
|
0x00, 0x20, 0x4f, 0xea, 0x1e, 0x6c, 0xbc, 0xf1,
|
|
0x08, 0x0f, 0x00, 0xd1, 0x70, 0x47, 0x72, 0xb6,
|
|
0xfd, 0xe7,
|
|
};
|
|
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 0x08006B9E: 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(0x08006B9E + k, &word, 4);
|
|
}
|
|
}
|
|
/* Banner stub at 0x08006BAC: 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, 0x21, 0x21, 0x19, 0x67, 0x10, 0xf8,
|
|
0x01, 0x1b, 0x09, 0xb1, 0x19, 0x67, 0xfa, 0xe7,
|
|
0x4f, 0xf2, 0x94, 0x6e, 0xc0, 0xf6, 0x02, 0x0e,
|
|
0x70, 0x47,
|
|
};
|
|
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(0x08006BAC + k, &word, 4);
|
|
}
|
|
}
|
|
/* PBL stub at 0x080068C0 (52B, from qemu/pbl.S): BLX to SBL1
|
|
* entry, then canonical regs + jump to sbl_main. Used when
|
|
* sblmain prop is unset (default): faithful PBL call flow. */
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
/* 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);
|
|
}
|
|
/* HW-ready bit (b25): [0x073A100] bit2 (SBL read-only poll). */
|
|
{
|
|
uint32_t v = 0x00000004;
|
|
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 (b25, 32B @0x08006964, ARM): blx-to-padding
|
|
* becomes movw/movt ip (=S16) + ACK peripheral bit1
|
|
* ([0x073A100], literal pool after code) + bx lr.
|
|
* PBL would place the real ARM helper here. */
|
|
{
|
|
static const uint8_t s12[] = {
|
|
0xc1, 0xcc, 0x06, 0xe3, 0x00, 0xc8, 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 (b25, 18B @0x08006CC0): helper-pop chain exit.
|
|
* restore r7 (peripheral base); jump to poll resumption
|
|
* 0x8007779 via ip. Entered via B66 bx ip (ip preset by S12). */
|
|
{
|
|
static const uint8_t s16[] = {
|
|
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;
|
|
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");
|
|
/* 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",
|
|
sms->sbl1, sz, (unsigned long)entry);
|
|
qemu_register_reset(saimaa_post_reset, NULL);
|
|
}
|
|
}
|
|
|
|
static void saimaa_post_reset(void *opaque)
|
|
{
|
|
uint32_t S = 0x08006B80;
|
|
(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)
|