Files
Yaya48 5d9a60a926 hw/arm: add authenticated A6 IMG3 boot lab
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.
2026-09-01 09:51:49 -07:00

131 lines
3.7 KiB
C

/*
* Apple S5L8950X DRAM controller training/status model.
*
* iBEC programs the timing registers and then waits for the controller and
* PHY training state in register zero. QEMU's RAM is already usable, so the
* training phases complete synchronously while ordinary register values are
* retained for debugger inspection.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/arm/s5l8950x.h"
#include "hw/core/sysbus.h"
#include "qom/object.h"
#define S5L8950X_MEMCTL_REGION_SIZE 0x1000
#define S5L8950X_MEMCTL_NUM_REGS \
(S5L8950X_MEMCTL_REGION_SIZE / sizeof(uint32_t))
/* Controller ready, PHY ready and all low training stages complete. */
#define S5L8950X_MEMCTL_READY 0x801003ffu
OBJECT_DECLARE_SIMPLE_TYPE(S5L8950XMemctlState, S5L8950X_MEMCTL)
struct S5L8950XMemctlState {
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t regs[S5L8950X_MEMCTL_NUM_REGS];
};
static uint64_t s5l8950x_memctl_read(void *opaque, hwaddr offset,
unsigned size)
{
S5L8950XMemctlState *s = opaque;
if (offset == 0) {
uint32_t status = s->regs[0] | S5L8950X_MEMCTL_READY;
/*
* The 0b101 command starts a training phase whose lane bits are
* active-low. iBEC clears the command before waiting for them to
* return to the completed state.
*/
if ((s->regs[0x14 / 4] & 5) == 5) {
status &= ~0x000003f0u;
}
/*
* Register 0x08 bit 31 requests a controller state transition. The
* corresponding completion state is exposed in status bit 10 and is
* cleared again when iBEC drops the request.
*/
if (s->regs[0x08 / 4] & BIT(31)) {
status |= BIT(10);
} else {
status &= ~BIT(10);
}
return status;
}
/*
* The PHY command/status block starts at 0x34. Its status register at
* 0x44 reports both command-complete stages once the programmed timing
* values have been accepted.
*/
if (offset == 0x44) {
return s->regs[offset / sizeof(uint32_t)] | 0x00500000u;
}
return s->regs[offset / sizeof(uint32_t)];
}
static void s5l8950x_memctl_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
S5L8950XMemctlState *s = opaque;
s->regs[offset / sizeof(uint32_t)] = value;
}
static const MemoryRegionOps s5l8950x_memctl_ops = {
.read = s5l8950x_memctl_read,
.write = s5l8950x_memctl_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static void s5l8950x_memctl_reset(DeviceState *dev)
{
S5L8950XMemctlState *s = S5L8950X_MEMCTL(dev);
memset(s->regs, 0, sizeof(s->regs));
}
static void s5l8950x_memctl_init(Object *obj)
{
S5L8950XMemctlState *s = S5L8950X_MEMCTL(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &s5l8950x_memctl_ops, s,
TYPE_S5L8950X_MEMCTL,
S5L8950X_MEMCTL_REGION_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
}
static void s5l8950x_memctl_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
device_class_set_legacy_reset(dc, s5l8950x_memctl_reset);
}
static const TypeInfo s5l8950x_memctl_info = {
.name = TYPE_S5L8950X_MEMCTL,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(S5L8950XMemctlState),
.instance_init = s5l8950x_memctl_init,
.class_init = s5l8950x_memctl_class_init,
};
static void s5l8950x_memctl_register_types(void)
{
type_register_static(&s5l8950x_memctl_info);
}
type_init(s5l8950x_memctl_register_types)