246 lines
7.8 KiB
C
246 lines
7.8 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_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_DDRHIGH_BASE 0x80000000
|
|
#define SAIMAA_DDRHIGH_SIZE 0x20000000
|
|
#define SAIMAA_UART_BASE 0x078AF000
|
|
#define SAIMAA_SDHCI_BASE 0x07824900
|
|
#define SAIMAA_GICD_BASE 0x0B000000
|
|
#define SAIMAA_GICC_BASE 0x0B002000
|
|
#define SAIMAA_NCPUS 4
|
|
|
|
/* ---- minimal MSM UARTDM stub: TX prints, status always ready ---- */
|
|
#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 */
|
|
};
|
|
|
|
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;
|
|
};
|
|
typedef struct SaimaaMachineState SaimaaMachineState;
|
|
|
|
#define TYPE_SAIMAA_MACHINE MACHINE_TYPE_NAME("saimaa")
|
|
OBJECT_DECLARE_SIMPLE_TYPE(SaimaaMachineState, SAIMAA_MACHINE)
|
|
|
|
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.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 },
|
|
};
|
|
for (i = 0; i < 4; 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);
|
|
}
|
|
|
|
/* 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);
|
|
|
|
/* 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);
|
|
|
|
/* 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 real SBL1 ELF, jump to entry */
|
|
if (sms->sbl1) {
|
|
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));
|
|
exit(1);
|
|
}
|
|
cpu_set_pc(first_cpu, entry);
|
|
printf("saimaa: SBL1 %s loaded (%zd bytes), entry 0x%lx\n",
|
|
sms->sbl1, sz, (unsigned long)entry);
|
|
}
|
|
}
|
|
|
|
static Property saimaa_uart_props[] = {
|
|
DEFINE_PROP_CHR("chardev", SaimaaUartState, chr),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void saimaa_uart_class_init(ObjectClass *oc, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
|
dc->realize = saimaa_uart_realize;
|
|
device_class_set_props(dc, saimaa_uart_props);
|
|
}
|
|
|
|
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 void saimaa_machine_instance_init(Object *obj)
|
|
{
|
|
SaimaaMachineState *sms = SAIMAA_MACHINE(obj);
|
|
sms->sbl1 = NULL;
|
|
sms->emmc = NULL;
|
|
object_property_add_str(obj, "sbl1", saimaa_get_sbl1, saimaa_set_sbl1);
|
|
}
|
|
|
|
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_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_machine_info);
|
|
type_register_static(&saimaa_uart_info);
|
|
}
|
|
type_init(saimaa_register_types)
|