Model the A6 crypto, interrupt, USB, and platform blocks needed to boot SecureROM through iBSS into iBEC Recovery. Add local lab identity, IMG3, and APTicket tooling, patched macOS recovery utilities, UART and GDB access, and English end-user documentation.
280 lines
9.3 KiB
C
280 lines
9.3 KiB
C
#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)
|