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

145 lines
4.3 KiB
C

#include "qemu/osdep.h"
#include "hw/core/sysbus.h"
#include "qemu/log.h"
#include "qom/object.h"
#define TYPE_S5L8950X_PMGR "s5l8950x-pmgr"
OBJECT_DECLARE_SIMPLE_TYPE(S5L8950XPmgrState, S5L8950X_PMGR)
#define PMGR_REGION_SIZE 0x10000
#define PMGR_NUM_REGS (PMGR_REGION_SIZE / 4)
#define PMGR_GATE_BASE 0x1000
#define PMGR_GATE_COUNT 0x50
#define PMGR_GATE_END (PMGR_GATE_BASE + PMGR_GATE_COUNT * sizeof(uint32_t))
#define PMGR_DOMAIN_CTRL_STRIDE 0x18
#define PMGR_DOMAIN_COUNT 9
#define PMGR_DOMAIN_STATUS_BASE 0x2010
#define PMGR_SPECIAL_DOMAIN_CTRL 0x100
#define PMGR_SPECIAL_DOMAIN_STATUS 0x2034
#define PMGR_DOMAIN_ACTIVE BIT(30)
#define PMGR_TRANSITION_DONE BIT(29)
#define PMGR_PLL0_CTRL 0x60
#define PMGR_PLL_LOCKED BIT(29)
struct S5L8950XPmgrState {
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t regs[PMGR_NUM_REGS];
};
static uint64_t s5l8950x_pmgr_read(void *opaque, hwaddr offset, unsigned size)
{
S5L8950XPmgrState *s = opaque;
uint32_t idx = offset / 4;
if (idx < PMGR_NUM_REGS) {
return s->regs[idx];
}
return 0;
}
static void s5l8950x_pmgr_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
S5L8950XPmgrState *s = opaque;
uint32_t idx = offset / 4;
if (idx < PMGR_NUM_REGS) {
uint32_t reg = (uint32_t)val;
/*
* Gate registers expose the requested state in bits [3:0] and the
* hardware-acknowledged state in bits [7:4]. Clocks settle
* immediately in this model, so mirror the request on every write.
*/
if (offset >= PMGR_GATE_BASE && offset < PMGR_GATE_END) {
reg = (reg & ~0xf0u) | ((reg & 0xfu) << 4);
}
if (offset == PMGR_PLL0_CTRL) {
reg |= PMGR_PLL_LOCKED;
}
/* Power-domain transitions also complete immediately. */
if (offset < PMGR_DOMAIN_CTRL_STRIDE * PMGR_DOMAIN_COUNT &&
offset % PMGR_DOMAIN_CTRL_STRIDE == 0) {
uint32_t domain = offset / PMGR_DOMAIN_CTRL_STRIDE;
uint32_t status = (PMGR_DOMAIN_STATUS_BASE / 4) + domain;
if (reg & BIT(31)) {
reg |= PMGR_TRANSITION_DONE;
}
s->regs[status] = deposit32(s->regs[status], 30, 1,
!!(reg & PMGR_DOMAIN_ACTIVE));
} else if (offset == PMGR_SPECIAL_DOMAIN_CTRL) {
uint32_t status = PMGR_SPECIAL_DOMAIN_STATUS / 4;
if (reg & BIT(31)) {
reg |= PMGR_TRANSITION_DONE;
}
s->regs[status] = deposit32(s->regs[status], 30, 1,
!!(reg & PMGR_DOMAIN_ACTIVE));
}
s->regs[idx] = reg;
} else {
qemu_log_mask(LOG_UNIMP, "%s: Out of bounds write to offset 0x%" HWADDR_PRIx
" with value 0x%" PRIx64 "\n", __func__, offset, val);
}
}
static const MemoryRegionOps s5l8950x_pmgr_ops = {
.read = s5l8950x_pmgr_read,
.write = s5l8950x_pmgr_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static void s5l8950x_pmgr_reset(DeviceState *dev)
{
S5L8950XPmgrState *s = S5L8950X_PMGR(dev);
int i;
/* Initialize all registers to 0xF (all clocks enabled) as reasonable defaults */
for (i = 0; i < PMGR_NUM_REGS; i++) {
s->regs[i] = 0xF;
}
for (i = PMGR_GATE_BASE / 4; i < PMGR_GATE_END / 4; i++) {
s->regs[i] = 0xFF;
}
}
static void s5l8950x_pmgr_init(Object *obj)
{
S5L8950XPmgrState *s = S5L8950X_PMGR(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &s5l8950x_pmgr_ops, s,
TYPE_S5L8950X_PMGR, PMGR_REGION_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
}
static void s5l8950x_pmgr_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
device_class_set_legacy_reset(dc, s5l8950x_pmgr_reset);
}
static const TypeInfo s5l8950x_pmgr_info = {
.name = TYPE_S5L8950X_PMGR,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(S5L8950XPmgrState),
.instance_init = s5l8950x_pmgr_init,
.class_init = s5l8950x_pmgr_class_init,
};
static void s5l8950x_pmgr_register_types(void)
{
type_register_static(&s5l8950x_pmgr_info);
}
type_init(s5l8950x_pmgr_register_types)