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.
590 lines
20 KiB
C
590 lines
20 KiB
C
/*
|
|
* Apple A6 (S5L8950X) AES accelerator and its two-channel DMA front-end.
|
|
*
|
|
* This models the register protocol used by the dumped A6 SecureROM for
|
|
* Image3 KBAG unwrap and DATA encryption/decryption. The built-in GID slot
|
|
* is populated only from an explicit lab key file; the key is never exposed
|
|
* through MMIO or logs.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "crypto/aes.h"
|
|
#include "hw/misc/s5l8950x-aes.h"
|
|
#include "hw/core/cpu.h"
|
|
#include "hw/core/sysbus.h"
|
|
#include "hw/core/qdev-properties.h"
|
|
#include "qemu/log.h"
|
|
#include "qemu/module.h"
|
|
#include "qemu/units.h"
|
|
#include "system/address-spaces.h"
|
|
#include "system/dma.h"
|
|
#include "target/arm/cpu.h"
|
|
|
|
#define A6_AES_DMA_MMIO_SIZE 0x3000
|
|
#define A6_AES_MMIO_SIZE 0x2000
|
|
#define A6_AES_DMA_CHANNELS 3
|
|
|
|
#define A6_DMA_CONTROL 0x000
|
|
#define A6_DMA_CONFIG 0x004
|
|
#define A6_DMA_TRANSFER_SIZE 0x00c
|
|
#define A6_DMA_DESCRIPTOR 0x014
|
|
#define A6_DMA_CHANNEL_SIZE 0x1000
|
|
|
|
#define A6_AES_CAPABILITIES 0x0000
|
|
#define A6_AES_CONTROL 0x1000
|
|
#define A6_AES_IV_BASE 0x1010
|
|
#define A6_AES_IV_END 0x1020
|
|
#define A6_AES_KEY_BASE 0x1020
|
|
#define A6_AES_KEY_END 0x1040
|
|
|
|
#define A6_AES_CTL_ENCRYPT BIT(16)
|
|
#define A6_AES_CTL_CBC BIT(17)
|
|
#define A6_AES_CTL_KEY_BITS_MASK (3u << 18)
|
|
#define A6_AES_CTL_EXPLICIT_KEY BIT(20)
|
|
#define A6_AES_CTL_KEY_SELECT_MASK (3u << 21)
|
|
|
|
#define A6_AES_KEY_SELECT_GID 1
|
|
#define A6_AES_MAX_TRANSFER (16 * MiB)
|
|
#define A6_IBEC_LOAD_BASE 0xbff00000u
|
|
#define A6_IBEC_MIN_SIZE (256 * KiB)
|
|
#define A6_IBOOT_RESET_VECTOR 0xea00000eu
|
|
|
|
typedef struct S5L8950XIBECHandoff {
|
|
uint8_t *payload;
|
|
uint32_t length;
|
|
} S5L8950XIBECHandoff;
|
|
|
|
struct S5L8950XAESState {
|
|
SysBusDevice parent_obj;
|
|
|
|
MemoryRegion dma_mmio;
|
|
MemoryRegion aes_mmio;
|
|
char *gid_key_path;
|
|
bool force_debug_uarts;
|
|
bool authenticated_ibec_handoff;
|
|
bool ibec_handoff_queued;
|
|
uint8_t gid_key[32];
|
|
bool gid_key_loaded;
|
|
|
|
uint32_t dma_control[A6_AES_DMA_CHANNELS];
|
|
uint32_t dma_config[A6_AES_DMA_CHANNELS];
|
|
uint32_t dma_transfer_size[A6_AES_DMA_CHANNELS];
|
|
uint32_t dma_descriptor[A6_AES_DMA_CHANNELS];
|
|
bool dma_started[A6_AES_DMA_CHANNELS];
|
|
|
|
uint32_t capabilities;
|
|
uint32_t control;
|
|
uint8_t iv[16];
|
|
uint8_t explicit_key[32];
|
|
};
|
|
|
|
static void s5l8950x_aes_handoff_ibec_on_cpu(CPUState *cpu,
|
|
run_on_cpu_data data)
|
|
{
|
|
S5L8950XIBECHandoff *handoff = data.host_ptr;
|
|
|
|
if (address_space_write_rom(&address_space_memory, A6_IBEC_LOAD_BASE,
|
|
MEMTXATTRS_UNSPECIFIED, handoff->payload,
|
|
handoff->length) != MEMTX_OK) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.aes: iBEC handoff write failed at 0x%08x\n",
|
|
A6_IBEC_LOAD_BASE);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* iBSS has already completed the IMG3, ticket, nonce and AES work before
|
|
* this callback is queued. The remaining physical operation is a warm
|
|
* CPU transfer into the linked iBEC address; resetting the whole machine
|
|
* would incorrectly re-enter SecureROM and consume another core-entry
|
|
* slot in its retained SRAM state.
|
|
*/
|
|
cpu_reset(cpu);
|
|
ARM_CPU(cpu)->env.regs[0] = 1;
|
|
cpu->halted = 0;
|
|
cpu_set_pc(cpu, A6_IBEC_LOAD_BASE);
|
|
cpu_exit(cpu);
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"s5l8950x.aes: transferred authenticated iBEC (%u bytes) "
|
|
"to PC=0x%08x\n",
|
|
handoff->length, A6_IBEC_LOAD_BASE);
|
|
|
|
out:
|
|
g_free(handoff->payload);
|
|
g_free(handoff);
|
|
}
|
|
|
|
static void s5l8950x_aes_queue_ibec_handoff(S5L8950XAESState *s,
|
|
const uint8_t *payload,
|
|
uint32_t length)
|
|
{
|
|
S5L8950XIBECHandoff *handoff;
|
|
CPUState *cpu = first_cpu;
|
|
|
|
if (!cpu || s->ibec_handoff_queued || length < A6_IBEC_MIN_SIZE ||
|
|
ldl_le_p(payload) != A6_IBOOT_RESET_VECTOR) {
|
|
return;
|
|
}
|
|
|
|
handoff = g_new(S5L8950XIBECHandoff, 1);
|
|
handoff->payload = g_memdup2(payload, length);
|
|
handoff->length = length;
|
|
s->ibec_handoff_queued = true;
|
|
async_run_on_cpu(cpu, s5l8950x_aes_handoff_ibec_on_cpu,
|
|
RUN_ON_CPU_HOST_PTR(handoff));
|
|
}
|
|
|
|
static bool s5l8950x_aes_enable_bootloader_uarts(uint8_t *image,
|
|
size_t image_length,
|
|
hwaddr load_base)
|
|
{
|
|
static const uint16_t helper_tail[] = {
|
|
0x680a, /* ldr r2, [r1] */
|
|
0x4310, /* orrs r0, r2 */
|
|
0x6008, /* str r0, [r1] */
|
|
0x4770, /* bx lr */
|
|
};
|
|
static const uint8_t movs_r0_3[] = { 0x03, 0x20 };
|
|
|
|
for (size_t offset = 0; offset + 10 <= image_length; offset += 2) {
|
|
uint16_t ldr_literal = lduw_le_p(image + offset);
|
|
size_t i;
|
|
|
|
if ((ldr_literal & 0xff00) != 0x4900) {
|
|
continue;
|
|
}
|
|
for (i = 0; i < ARRAY_SIZE(helper_tail); i++) {
|
|
if (lduw_le_p(image + offset + 2 + i * 2) != helper_tail[i]) {
|
|
break;
|
|
}
|
|
}
|
|
if (i != ARRAY_SIZE(helper_tail)) {
|
|
continue;
|
|
}
|
|
memcpy(image + offset + 4, movs_r0_3, sizeof(movs_r0_3));
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"s5l8950x.uart: forced debug-uarts=3 after native "
|
|
"IMG3 decrypt at 0x%08" HWADDR_PRIx "\n",
|
|
load_base + offset + 4);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool s5l8950x_aes_read_descriptor(uint32_t address,
|
|
hwaddr *buffer,
|
|
uint32_t *length)
|
|
{
|
|
uint8_t descriptor[16];
|
|
|
|
if (!address ||
|
|
dma_memory_read(&address_space_memory, address, descriptor,
|
|
sizeof(descriptor),
|
|
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
|
|
return false;
|
|
}
|
|
/*
|
|
* A6 DMA addresses are 32-bit physical addresses. ldl_le_p() returns a
|
|
* signed target-endian value on this host, so widening it directly to
|
|
* hwaddr sign-extends SRAM addresses such as 0xbfe00b00. Preserve the
|
|
* descriptor's raw 32-bit bit pattern before widening it.
|
|
*/
|
|
*buffer = (uint32_t)ldl_le_p(descriptor + 8);
|
|
*length = ldl_le_p(descriptor + 12);
|
|
return true;
|
|
}
|
|
|
|
static size_t s5l8950x_aes_key_length(uint32_t control)
|
|
{
|
|
switch ((control & A6_AES_CTL_KEY_BITS_MASK) >> 18) {
|
|
case 0:
|
|
return 16;
|
|
case 1:
|
|
return 24;
|
|
case 2:
|
|
return 32;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool s5l8950x_aes_crypt(const uint8_t *source, uint8_t *destination,
|
|
size_t length, const uint8_t *key,
|
|
size_t key_length, const uint8_t iv[16],
|
|
bool cbc, bool encrypt)
|
|
{
|
|
AES_KEY expanded_key;
|
|
uint8_t chain[AES_BLOCK_SIZE];
|
|
uint8_t block[AES_BLOCK_SIZE];
|
|
|
|
if ((encrypt ? AES_set_encrypt_key(key, key_length * 8, &expanded_key) :
|
|
AES_set_decrypt_key(key, key_length * 8, &expanded_key))) {
|
|
return false;
|
|
}
|
|
memcpy(chain, iv, sizeof(chain));
|
|
for (size_t offset = 0; offset < length; offset += AES_BLOCK_SIZE) {
|
|
if (encrypt) {
|
|
for (size_t i = 0; i < AES_BLOCK_SIZE; i++) {
|
|
block[i] = source[offset + i] ^ (cbc ? chain[i] : 0);
|
|
}
|
|
AES_encrypt(block, destination + offset, &expanded_key);
|
|
if (cbc) {
|
|
memcpy(chain, destination + offset, sizeof(chain));
|
|
}
|
|
} else {
|
|
AES_decrypt(source + offset, block, &expanded_key);
|
|
for (size_t i = 0; i < AES_BLOCK_SIZE; i++) {
|
|
destination[offset + i] = block[i] ^ (cbc ? chain[i] : 0);
|
|
}
|
|
if (cbc) {
|
|
memcpy(chain, source + offset, sizeof(chain));
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool s5l8950x_aes_process(S5L8950XAESState *s)
|
|
{
|
|
g_autoptr(GByteArray) source = NULL;
|
|
g_autoptr(GByteArray) destination = NULL;
|
|
const uint8_t *key;
|
|
const char *key_name;
|
|
hwaddr source_address;
|
|
hwaddr destination_address;
|
|
uint32_t source_length;
|
|
uint32_t destination_length;
|
|
uint32_t key_select;
|
|
size_t key_length;
|
|
bool cbc;
|
|
bool encrypt;
|
|
|
|
if (!s5l8950x_aes_read_descriptor(s->dma_descriptor[1],
|
|
&source_address, &source_length) ||
|
|
!s5l8950x_aes_read_descriptor(s->dma_descriptor[2],
|
|
&destination_address,
|
|
&destination_length)) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.aes: invalid DMA descriptor\n");
|
|
return false;
|
|
}
|
|
if (!source_length || source_length != destination_length ||
|
|
source_length > A6_AES_MAX_TRANSFER || source_length % 16) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.aes: invalid DMA lengths %u/%u\n",
|
|
source_length, destination_length);
|
|
return false;
|
|
}
|
|
|
|
key_length = s5l8950x_aes_key_length(s->control);
|
|
if (!key_length) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.aes: unsupported key-size selector\n");
|
|
return false;
|
|
}
|
|
|
|
key_select = (s->control & A6_AES_CTL_KEY_SELECT_MASK) >> 21;
|
|
if (key_select == A6_AES_KEY_SELECT_GID) {
|
|
if (!s->gid_key_loaded || key_length != sizeof(s->gid_key)) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.aes: GID operation requested without "
|
|
"a matching 256-bit lab key\n");
|
|
return false;
|
|
}
|
|
key = s->gid_key;
|
|
key_name = "lab-GID";
|
|
} else if (key_select == 0 &&
|
|
(s->control & A6_AES_CTL_EXPLICIT_KEY)) {
|
|
key = s->explicit_key;
|
|
key_name = "explicit";
|
|
} else {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.aes: unsupported built-in key slot %u\n",
|
|
key_select);
|
|
return false;
|
|
}
|
|
|
|
cbc = (s->control & A6_AES_CTL_CBC) != 0;
|
|
encrypt = (s->control & A6_AES_CTL_ENCRYPT) != 0;
|
|
|
|
source = g_byte_array_sized_new(source_length);
|
|
g_byte_array_set_size(source, source_length);
|
|
destination = g_byte_array_sized_new(destination_length);
|
|
g_byte_array_set_size(destination, destination_length);
|
|
if (dma_memory_read(&address_space_memory, source_address, source->data,
|
|
source_length,
|
|
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.aes: DMA source read failed at 0x%08"
|
|
HWADDR_PRIx "\n", source_address);
|
|
return false;
|
|
}
|
|
|
|
if (!s5l8950x_aes_crypt(source->data, destination->data, source_length,
|
|
key, key_length, s->iv, cbc, encrypt)) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.aes: AES operation failed\n");
|
|
return false;
|
|
}
|
|
if (s->force_debug_uarts && !encrypt &&
|
|
key_select == 0 && (s->control & A6_AES_CTL_EXPLICIT_KEY) &&
|
|
!s5l8950x_aes_enable_bootloader_uarts(destination->data,
|
|
destination_length,
|
|
destination_address)) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.uart: debug_enable_uarts helper not found "
|
|
"after native IMG3 decrypt\n");
|
|
}
|
|
if (dma_memory_write(&address_space_memory, destination_address,
|
|
destination->data, destination_length,
|
|
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"s5l8950x.aes: DMA destination write failed at 0x%08"
|
|
HWADDR_PRIx "\n", destination_address);
|
|
return false;
|
|
}
|
|
if (s->authenticated_ibec_handoff && !encrypt && key_select == 0 &&
|
|
(s->control & A6_AES_CTL_EXPLICIT_KEY)) {
|
|
s5l8950x_aes_queue_ibec_handoff(s, destination->data,
|
|
destination_length);
|
|
}
|
|
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"s5l8950x.aes: %s AES-%zu-%s key=%s bytes=%u "
|
|
"src=0x%08" HWADDR_PRIx " dst=0x%08" HWADDR_PRIx "\n",
|
|
encrypt ? "encrypt" : "decrypt", key_length * 8,
|
|
cbc ? "CBC" : "ECB",
|
|
key_name, source_length, source_address,
|
|
destination_address);
|
|
return true;
|
|
}
|
|
|
|
static uint64_t s5l8950x_aes_dma_read(void *opaque, hwaddr offset,
|
|
unsigned size)
|
|
{
|
|
S5L8950XAESState *s = opaque;
|
|
unsigned channel = offset / A6_DMA_CHANNEL_SIZE;
|
|
hwaddr reg = offset % A6_DMA_CHANNEL_SIZE;
|
|
|
|
if (channel >= A6_AES_DMA_CHANNELS) {
|
|
return 0;
|
|
}
|
|
switch (reg) {
|
|
case A6_DMA_CONTROL:
|
|
return s->dma_control[channel];
|
|
case A6_DMA_CONFIG:
|
|
return s->dma_config[channel];
|
|
case A6_DMA_TRANSFER_SIZE:
|
|
return s->dma_transfer_size[channel];
|
|
case A6_DMA_DESCRIPTOR:
|
|
return s->dma_descriptor[channel];
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void s5l8950x_aes_dma_write(void *opaque, hwaddr offset,
|
|
uint64_t value, unsigned size)
|
|
{
|
|
S5L8950XAESState *s = opaque;
|
|
unsigned channel = offset / A6_DMA_CHANNEL_SIZE;
|
|
hwaddr reg = offset % A6_DMA_CHANNEL_SIZE;
|
|
|
|
if (channel >= A6_AES_DMA_CHANNELS) {
|
|
return;
|
|
}
|
|
switch (reg) {
|
|
case A6_DMA_CONTROL:
|
|
s->dma_control[channel] = value;
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"s5l8950x.aes: DMA%u control=0x%08" PRIx64 "\n",
|
|
channel, value);
|
|
if (value == 2) {
|
|
s->dma_started[channel] = false;
|
|
} else if (channel && (value & 1)) {
|
|
s->dma_started[channel] = true;
|
|
if (s->dma_started[1] && s->dma_started[2]) {
|
|
s5l8950x_aes_process(s);
|
|
s->dma_started[1] = false;
|
|
s->dma_started[2] = false;
|
|
/* Status 0 is idle/complete; SecureROM polls bits 16..17. */
|
|
s->dma_control[1] &= ~(3u << 16);
|
|
s->dma_control[2] &= ~(3u << 16);
|
|
}
|
|
}
|
|
break;
|
|
case A6_DMA_CONFIG:
|
|
s->dma_config[channel] = value;
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"s5l8950x.aes: DMA%u config=0x%08" PRIx64 "\n",
|
|
channel, value);
|
|
break;
|
|
case A6_DMA_TRANSFER_SIZE:
|
|
s->dma_transfer_size[channel] = value;
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"s5l8950x.aes: DMA%u length=0x%08" PRIx64 "\n",
|
|
channel, value);
|
|
break;
|
|
case A6_DMA_DESCRIPTOR:
|
|
s->dma_descriptor[channel] = value;
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"s5l8950x.aes: DMA%u descriptor=0x%08" PRIx64 "\n",
|
|
channel, value);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint64_t s5l8950x_aes_read(void *opaque, hwaddr offset,
|
|
unsigned size)
|
|
{
|
|
S5L8950XAESState *s = opaque;
|
|
|
|
switch (offset) {
|
|
case A6_AES_CAPABILITIES:
|
|
return s->capabilities;
|
|
case A6_AES_CONTROL:
|
|
return s->control;
|
|
case A6_AES_IV_BASE ... A6_AES_IV_END - 4:
|
|
return ldl_le_p(s->iv + offset - A6_AES_IV_BASE);
|
|
case A6_AES_KEY_BASE ... A6_AES_KEY_END - 4:
|
|
/* Explicit key registers are write-only on the real device. */
|
|
return 0;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void s5l8950x_aes_write(void *opaque, hwaddr offset,
|
|
uint64_t value, unsigned size)
|
|
{
|
|
S5L8950XAESState *s = opaque;
|
|
|
|
switch (offset) {
|
|
case A6_AES_CAPABILITIES:
|
|
s->capabilities = value;
|
|
break;
|
|
case A6_AES_CONTROL:
|
|
s->control = value;
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"s5l8950x.aes: control=0x%08" PRIx64 "\n", value);
|
|
break;
|
|
case A6_AES_IV_BASE ... A6_AES_IV_END - 4:
|
|
stl_le_p(s->iv + offset - A6_AES_IV_BASE, value);
|
|
break;
|
|
case A6_AES_KEY_BASE ... A6_AES_KEY_END - 4:
|
|
stl_le_p(s->explicit_key + offset - A6_AES_KEY_BASE, value);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const MemoryRegionOps s5l8950x_aes_dma_ops = {
|
|
.read = s5l8950x_aes_dma_read,
|
|
.write = s5l8950x_aes_dma_write,
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
.valid.min_access_size = 4,
|
|
.valid.max_access_size = 4,
|
|
};
|
|
|
|
static const MemoryRegionOps s5l8950x_aes_ops = {
|
|
.read = s5l8950x_aes_read,
|
|
.write = s5l8950x_aes_write,
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
.valid.min_access_size = 4,
|
|
.valid.max_access_size = 4,
|
|
};
|
|
|
|
static void s5l8950x_aes_reset(DeviceState *dev)
|
|
{
|
|
S5L8950XAESState *s = S5L8950X_AES(dev);
|
|
|
|
memset(s->dma_control, 0, sizeof(s->dma_control));
|
|
memset(s->dma_config, 0, sizeof(s->dma_config));
|
|
memset(s->dma_transfer_size, 0, sizeof(s->dma_transfer_size));
|
|
memset(s->dma_descriptor, 0, sizeof(s->dma_descriptor));
|
|
memset(s->dma_started, 0, sizeof(s->dma_started));
|
|
s->capabilities = 0;
|
|
s->control = 0;
|
|
memset(s->iv, 0, sizeof(s->iv));
|
|
memset(s->explicit_key, 0, sizeof(s->explicit_key));
|
|
s->ibec_handoff_queued = false;
|
|
}
|
|
|
|
static void s5l8950x_aes_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
S5L8950XAESState *s = S5L8950X_AES(dev);
|
|
g_autofree gchar *contents = NULL;
|
|
gsize length = 0;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
if (!s->gid_key_path) {
|
|
return;
|
|
}
|
|
if (!g_file_get_contents(s->gid_key_path, &contents, &length, &error)) {
|
|
error_setg(errp, "could not load A6 lab GID key '%s': %s",
|
|
s->gid_key_path, error->message);
|
|
return;
|
|
}
|
|
if (length != sizeof(s->gid_key)) {
|
|
error_setg(errp, "A6 lab GID key '%s' must be exactly 32 bytes",
|
|
s->gid_key_path);
|
|
return;
|
|
}
|
|
memcpy(s->gid_key, contents, sizeof(s->gid_key));
|
|
s->gid_key_loaded = true;
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"s5l8950x.aes: loaded private 256-bit lab GID key\n");
|
|
}
|
|
|
|
static void s5l8950x_aes_init(Object *object)
|
|
{
|
|
S5L8950XAESState *s = S5L8950X_AES(object);
|
|
SysBusDevice *sbd = SYS_BUS_DEVICE(object);
|
|
|
|
memory_region_init_io(&s->dma_mmio, object, &s5l8950x_aes_dma_ops, s,
|
|
TYPE_S5L8950X_AES ".dma",
|
|
A6_AES_DMA_MMIO_SIZE);
|
|
sysbus_init_mmio(sbd, &s->dma_mmio);
|
|
memory_region_init_io(&s->aes_mmio, object, &s5l8950x_aes_ops, s,
|
|
TYPE_S5L8950X_AES, A6_AES_MMIO_SIZE);
|
|
sysbus_init_mmio(sbd, &s->aes_mmio);
|
|
}
|
|
|
|
static const Property s5l8950x_aes_properties[] = {
|
|
DEFINE_PROP_STRING("gid-key-file", S5L8950XAESState, gid_key_path),
|
|
DEFINE_PROP_BOOL("force-debug-uarts", S5L8950XAESState,
|
|
force_debug_uarts, false),
|
|
DEFINE_PROP_BOOL("authenticated-ibec-handoff", S5L8950XAESState,
|
|
authenticated_ibec_handoff, false),
|
|
};
|
|
|
|
static void s5l8950x_aes_class_init(ObjectClass *klass, const void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
dc->realize = s5l8950x_aes_realize;
|
|
device_class_set_legacy_reset(dc, s5l8950x_aes_reset);
|
|
device_class_set_props(dc, s5l8950x_aes_properties);
|
|
}
|
|
|
|
static const TypeInfo s5l8950x_aes_type_info = {
|
|
.name = TYPE_S5L8950X_AES,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(S5L8950XAESState),
|
|
.instance_init = s5l8950x_aes_init,
|
|
.class_init = s5l8950x_aes_class_init,
|
|
};
|
|
|
|
static void s5l8950x_aes_register_types(void)
|
|
{
|
|
type_register_static(&s5l8950x_aes_type_info);
|
|
}
|
|
|
|
type_init(s5l8950x_aes_register_types)
|