#include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/s5l8950x.h" #include "hw/arm/machines-qom.h" #include "hw/core/boards.h" #include "hw/core/loader.h" #include "hw/misc/unimp.h" #include "hw/misc/s5l8950x-aes.h" #include "hw/misc/s5l8950x-pke.h" #include "hw/misc/s5l8950x-sha1.h" #include "hw/core/sysbus.h" #include "system/address-spaces.h" #include "target/arm/cpu.h" #include "qemu/units.h" #include "qom/object.h" #include "hw/arm/boot.h" #include "system/system.h" #include "target/arm/cpregs.h" /* * Swift has implementation-defined CP15 controls which the SecureROM uses * while bringing caches and the memory system up. QEMU does not model * those microarchitectural details, so the matching control is write-ignore. */ static const ARMCPRegInfo s5l8950x_cp_reginfo[] = { { .name = "APPLE_SWIFT_CFG", .cp = 15, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NOP, }, { .name = "APPLE_SWIFT_AUX_CFG", .cp = 15, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 4, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, }, { .name = "APPLE_SWIFT_AUX_CFG2", .cp = 15, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 5, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, }, { .name = "APPLE_SWIFT_AUX_CFG3", .cp = 15, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 7, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, }, }; /* Addresses */ #define S5L8950X_SECUREROM_BASE 0x00000000 #define S5L8950X_SECUREROM_SIZE 0x10000 #define S5L8950X_SRAM_BASE 0x10000000 #define S5L8950X_SRAM_SIZE 0x80000 #define S5L8950X_SDIO_BASE 0x20000000 #define S5L8950X_USB_PHY_BASE 0x36000000 #define S5L8950X_USB_BASE 0x36100000 #define S5L8950X_SHA1_BASE 0x30200000 #define S5L8950X_SPI_BASE 0x3D200000 #define S5L8950X_AES_DMA_BASE 0x37000000 #define S5L8950X_AES_BASE 0x37800000 #define S5L8950X_IIC_BASE 0x3E100000 #define S5L8950X_PKE_BASE 0x33100000 #define S5L8950X_WDT_BASE 0x3E300000 #define S5L8950X_CHIPID_BASE 0x3F500000 #define S5L8950X_TIMER_BASE 0x3F200000 #define S5L8950X_PMGR_BASE 0x3F100000 #define S5L8950X_CLOCK_BASE 0x3F800000 #define S5L8950X_MEMCTL_BASE 0x39500000 #define S5L8950X_AMC_BASE 0x40000000 #define S5L8950X_AIC_BASE 0x3F200000 #define S5L8950X_UART_BASE 0x32B00000 #define S5L8950X_GPIO_BASE 0x3FA00000 #define S5L8950X_DRAM_BASE 0x80000000 static const hwaddr s5l8950x_i2c_base[] = { 0x33200000, 0x33300000, 0x33400000, }; static void s5l8950x_init(Object *obj) { S5L8950XState *s = S5L8950X(obj); object_initialize_child(obj, "cpu", &s->cpu, ARM_CPU_TYPE_NAME("cortex-a9")); define_arm_cp_regs(&s->cpu, s5l8950x_cp_reginfo); } static void s5l8950x_realize(DeviceState *dev, Error **errp) { S5L8950XState *s = S5L8950X(dev); Error *err = NULL; /* Realize CPU */ if (!qdev_realize(DEVICE(&s->cpu), NULL, &err)) { error_propagate(errp, err); return; } /* SRAM */ memory_region_init_ram(&s->sram, OBJECT(s), "s5l8950x.sram", S5L8950X_SRAM_SIZE, &error_fatal); memory_region_add_subregion(get_system_memory(), S5L8950X_SRAM_BASE, &s->sram); /* SecureROM */ memory_region_init_rom(&s->securerom, OBJECT(s), "s5l8950x.securerom", S5L8950X_SECUREROM_SIZE, &error_fatal); memory_region_add_subregion(get_system_memory(), S5L8950X_SECUREROM_BASE, &s->securerom); /* Create unimplemented devices */ create_unimplemented_device("s5l8950x.sdio", S5L8950X_SDIO_BASE, 0x10000); create_unimplemented_device("s5l8950x.spi", S5L8950X_SPI_BASE, 0x1000); create_unimplemented_device("s5l8950x.iic", S5L8950X_IIC_BASE, 0x1000); create_unimplemented_device("s5l8950x.wdt", S5L8950X_WDT_BASE, 0x1000); create_unimplemented_device("s5l8950x.amc", S5L8950X_AMC_BASE, 0x1000); /* Realize custom devices (assumes stubs will be created) */ DeviceState *dev_chipid = qdev_new(TYPE_S5L8950X_CHIPID); s->chipid = SYS_BUS_DEVICE(dev_chipid); sysbus_realize_and_unref(s->chipid, &error_fatal); sysbus_mmio_map(s->chipid, 0, S5L8950X_CHIPID_BASE); DeviceState *dev_aes = qdev_new(TYPE_S5L8950X_AES); s->aes = SYS_BUS_DEVICE(dev_aes); sysbus_realize_and_unref(s->aes, &error_fatal); sysbus_mmio_map(s->aes, 0, S5L8950X_AES_DMA_BASE); sysbus_mmio_map(s->aes, 1, S5L8950X_AES_BASE); DeviceState *dev_pke = qdev_new(TYPE_S5L8950X_PKE); s->pke = SYS_BUS_DEVICE(dev_pke); sysbus_realize_and_unref(s->pke, &error_fatal); sysbus_mmio_map(s->pke, 0, S5L8950X_PKE_BASE); DeviceState *dev_sha1 = qdev_new(TYPE_S5L8950X_SHA1); s->sha1 = SYS_BUS_DEVICE(dev_sha1); sysbus_realize_and_unref(s->sha1, &error_fatal); sysbus_mmio_map(s->sha1, 0, S5L8950X_SHA1_BASE); DeviceState *dev_aic = qdev_new(TYPE_S5L8950X_AIC); s->aic = SYS_BUS_DEVICE(dev_aic); sysbus_realize_and_unref(s->aic, &error_fatal); sysbus_mmio_map(s->aic, 0, S5L8950X_AIC_BASE); sysbus_connect_irq(s->aic, 0, qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ)); sysbus_connect_irq(s->aic, 1, qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ)); DeviceState *dev_uart = qdev_new(TYPE_S5L8950X_UART); qdev_prop_set_chr(dev_uart, "chardev", serial_hd(0)); s->uart = SYS_BUS_DEVICE(dev_uart); sysbus_realize_and_unref(s->uart, &error_fatal); sysbus_mmio_map(s->uart, 0, S5L8950X_UART_BASE); /* iBoot's A6 UART table maps 0x32b00000 to external IRQ 0x44. */ sysbus_connect_irq(s->uart, 0, qdev_get_gpio_in(dev_aic, 0x44)); DeviceState *dev_pmgr = qdev_new(TYPE_S5L8950X_PMGR); s->pmgr = SYS_BUS_DEVICE(dev_pmgr); sysbus_realize_and_unref(s->pmgr, &error_fatal); sysbus_mmio_map(s->pmgr, 0, S5L8950X_PMGR_BASE); DeviceState *dev_clock = qdev_new(TYPE_S5L8950X_CLOCK); s->clock = SYS_BUS_DEVICE(dev_clock); sysbus_realize_and_unref(s->clock, &error_fatal); sysbus_mmio_map(s->clock, 0, S5L8950X_CLOCK_BASE); DeviceState *dev_memctl = qdev_new(TYPE_S5L8950X_MEMCTL); s->memctl = SYS_BUS_DEVICE(dev_memctl); sysbus_realize_and_unref(s->memctl, &error_fatal); sysbus_mmio_map(s->memctl, 0, S5L8950X_MEMCTL_BASE); for (size_t i = 0; i < ARRAY_SIZE(s5l8950x_i2c_base); i++) { DeviceState *dev_i2c = qdev_new(TYPE_S5L8950X_I2C); s->i2c[i] = SYS_BUS_DEVICE(dev_i2c); sysbus_realize_and_unref(s->i2c[i], &error_fatal); sysbus_mmio_map(s->i2c[i], 0, s5l8950x_i2c_base[i]); sysbus_connect_irq(s->i2c[i], 0, qdev_get_gpio_in(dev_aic, 0x46 + i)); } DeviceState *dev_gpio = qdev_new(TYPE_S5L8950X_GPIO); qdev_prop_set_bit(dev_gpio, "force-dfu", true); qdev_prop_set_uint8(dev_gpio, "board-straps", 2); s->gpio = SYS_BUS_DEVICE(dev_gpio); sysbus_realize_and_unref(s->gpio, &error_fatal); sysbus_mmio_map(s->gpio, 0, S5L8950X_GPIO_BASE); DeviceState *dev_usb_phy = qdev_new(TYPE_S5L8950X_USB_PHY); qdev_prop_set_bit(dev_usb_phy, "cable-connected", true); s->usb_phy = SYS_BUS_DEVICE(dev_usb_phy); sysbus_realize_and_unref(s->usb_phy, &error_fatal); sysbus_mmio_map(s->usb_phy, 0, S5L8950X_USB_PHY_BASE); DeviceState *dev_usb_otg = qdev_new(TYPE_S5L8950X_USB_OTG); s->usb_otg = SYS_BUS_DEVICE(dev_usb_otg); sysbus_realize_and_unref(s->usb_otg, &error_fatal); sysbus_mmio_map(s->usb_otg, 0, S5L8950X_USB_BASE); sysbus_connect_irq(s->usb_otg, 0, qdev_get_gpio_in(dev_aic, 11)); } static void s5l8950x_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = s5l8950x_realize; } static const TypeInfo s5l8950x_type_info = { .name = TYPE_S5L8950X, .parent = TYPE_DEVICE, .instance_size = sizeof(S5L8950XState), .instance_init = s5l8950x_init, .class_init = s5l8950x_class_init, }; static void iphone5_machine_init(MachineState *machine) { S5L8950XState *s; s = S5L8950X(object_new(TYPE_S5L8950X)); object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); qdev_realize(DEVICE(s), NULL, &error_fatal); /* Map DRAM */ memory_region_add_subregion(get_system_memory(), S5L8950X_DRAM_BASE, machine->ram); /* Load BIOS (SecureROM) */ if (machine->firmware) { load_image_targphys(machine->firmware, S5L8950X_SECUREROM_BASE, S5L8950X_SECUREROM_SIZE, &error_fatal); } } static void iphone5_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "Apple iPhone 5 (S5L8950X / A6)"; mc->init = iphone5_machine_init; mc->default_ram_size = 1 * GiB; mc->default_ram_id = "dram"; mc->ignore_memory_transaction_failures = true; } static const TypeInfo iphone5_machine_type_info = { .name = MACHINE_TYPE_NAME("iphone5"), .parent = TYPE_MACHINE, .class_init = iphone5_machine_class_init, .interfaces = arm_machine_interfaces, }; static void s5l8950x_register_types(void) { type_register_static(&s5l8950x_type_info); type_register_static(&iphone5_machine_type_info); } type_init(s5l8950x_register_types)