mirror of https://gitlab.com/qemu-project/qemu
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
6.6 KiB
C
275 lines
6.6 KiB
C
/*
|
|
* VMApple Backdoor Interface
|
|
*
|
|
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
* See the COPYING file in the top-level directory.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/units.h"
|
|
#include "qemu/log.h"
|
|
#include "qemu/module.h"
|
|
#include "trace.h"
|
|
#include "hw/vmapple/vmapple.h"
|
|
#include "hw/sysbus.h"
|
|
#include "hw/block/block.h"
|
|
#include "qapi/error.h"
|
|
#include "system/block-backend.h"
|
|
#include "system/dma.h"
|
|
|
|
OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF)
|
|
|
|
struct VMAppleBdifState {
|
|
SysBusDevice parent_obj;
|
|
|
|
BlockBackend *aux;
|
|
BlockBackend *root;
|
|
MemoryRegion mmio;
|
|
};
|
|
|
|
#define VMAPPLE_BDIF_SIZE 0x00200000
|
|
|
|
#define REG_DEVID_MASK 0xffff0000
|
|
#define DEVID_ROOT 0x00000000
|
|
#define DEVID_AUX 0x00010000
|
|
#define DEVID_USB 0x00100000
|
|
|
|
#define REG_STATUS 0x0
|
|
#define REG_STATUS_ACTIVE BIT(0)
|
|
#define REG_CFG 0x4
|
|
#define REG_CFG_ACTIVE BIT(1)
|
|
#define REG_UNK1 0x8
|
|
#define REG_BUSY 0x10
|
|
#define REG_BUSY_READY BIT(0)
|
|
#define REG_UNK2 0x400
|
|
#define REG_CMD 0x408
|
|
#define REG_NEXT_DEVICE 0x420
|
|
#define REG_UNK3 0x434
|
|
|
|
typedef struct VblkSector {
|
|
uint32_t pad;
|
|
uint32_t pad2;
|
|
uint32_t sector;
|
|
uint32_t pad3;
|
|
} VblkSector;
|
|
|
|
typedef struct VblkReqCmd {
|
|
uint64_t addr;
|
|
uint32_t len;
|
|
uint32_t flags;
|
|
} VblkReqCmd;
|
|
|
|
typedef struct VblkReq {
|
|
VblkReqCmd sector;
|
|
VblkReqCmd data;
|
|
VblkReqCmd retval;
|
|
} VblkReq;
|
|
|
|
#define VBLK_DATA_FLAGS_READ 0x00030001
|
|
#define VBLK_DATA_FLAGS_WRITE 0x00010001
|
|
|
|
#define VBLK_RET_SUCCESS 0
|
|
#define VBLK_RET_FAILED 1
|
|
|
|
static uint64_t bdif_read(void *opaque, hwaddr offset, unsigned size)
|
|
{
|
|
uint64_t ret = -1;
|
|
uint64_t devid = offset & REG_DEVID_MASK;
|
|
|
|
switch (offset & ~REG_DEVID_MASK) {
|
|
case REG_STATUS:
|
|
ret = REG_STATUS_ACTIVE;
|
|
break;
|
|
case REG_CFG:
|
|
ret = REG_CFG_ACTIVE;
|
|
break;
|
|
case REG_UNK1:
|
|
ret = 0x420;
|
|
break;
|
|
case REG_BUSY:
|
|
ret = REG_BUSY_READY;
|
|
break;
|
|
case REG_UNK2:
|
|
ret = 0x1;
|
|
break;
|
|
case REG_UNK3:
|
|
ret = 0x0;
|
|
break;
|
|
case REG_NEXT_DEVICE:
|
|
switch (devid) {
|
|
case DEVID_ROOT:
|
|
ret = 0x8000000;
|
|
break;
|
|
case DEVID_AUX:
|
|
ret = 0x10000;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
trace_bdif_read(offset, size, ret);
|
|
return ret;
|
|
}
|
|
|
|
static void le2cpu_sector(VblkSector *sector)
|
|
{
|
|
sector->sector = le32_to_cpu(sector->sector);
|
|
}
|
|
|
|
static void le2cpu_reqcmd(VblkReqCmd *cmd)
|
|
{
|
|
cmd->addr = le64_to_cpu(cmd->addr);
|
|
cmd->len = le32_to_cpu(cmd->len);
|
|
cmd->flags = le32_to_cpu(cmd->flags);
|
|
}
|
|
|
|
static void le2cpu_req(VblkReq *req)
|
|
{
|
|
le2cpu_reqcmd(&req->sector);
|
|
le2cpu_reqcmd(&req->data);
|
|
le2cpu_reqcmd(&req->retval);
|
|
}
|
|
|
|
static void vblk_cmd(uint64_t devid, BlockBackend *blk, uint64_t gp_addr,
|
|
uint64_t static_off)
|
|
{
|
|
VblkReq req;
|
|
VblkSector sector;
|
|
uint64_t off = 0;
|
|
g_autofree char *buf = NULL;
|
|
uint8_t ret = VBLK_RET_FAILED;
|
|
int r;
|
|
MemTxResult dma_result;
|
|
|
|
dma_result = dma_memory_read(&address_space_memory, gp_addr,
|
|
&req, sizeof(req), MEMTXATTRS_UNSPECIFIED);
|
|
if (dma_result != MEMTX_OK) {
|
|
goto out;
|
|
}
|
|
|
|
le2cpu_req(&req);
|
|
|
|
if (req.sector.len != sizeof(sector)) {
|
|
goto out;
|
|
}
|
|
|
|
/* Read the vblk command */
|
|
dma_result = dma_memory_read(&address_space_memory, req.sector.addr,
|
|
§or, sizeof(sector),
|
|
MEMTXATTRS_UNSPECIFIED);
|
|
if (dma_result != MEMTX_OK) {
|
|
goto out;
|
|
}
|
|
le2cpu_sector(§or);
|
|
|
|
off = sector.sector * 512ULL + static_off;
|
|
|
|
/* Sanity check that we're not allocating bogus sizes */
|
|
if (req.data.len > 128 * MiB) {
|
|
goto out;
|
|
}
|
|
|
|
buf = g_malloc0(req.data.len);
|
|
switch (req.data.flags) {
|
|
case VBLK_DATA_FLAGS_READ:
|
|
r = blk_pread(blk, off, req.data.len, buf, 0);
|
|
trace_bdif_vblk_read(devid == DEVID_AUX ? "aux" : "root",
|
|
req.data.addr, off, req.data.len, r);
|
|
if (r < 0) {
|
|
goto out;
|
|
}
|
|
dma_result = dma_memory_write(&address_space_memory, req.data.addr, buf,
|
|
req.data.len, MEMTXATTRS_UNSPECIFIED);
|
|
if (dma_result == MEMTX_OK) {
|
|
ret = VBLK_RET_SUCCESS;
|
|
}
|
|
break;
|
|
case VBLK_DATA_FLAGS_WRITE:
|
|
/* Not needed, iBoot only reads */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
out:
|
|
dma_memory_write(&address_space_memory, req.retval.addr, &ret, 1,
|
|
MEMTXATTRS_UNSPECIFIED);
|
|
}
|
|
|
|
static void bdif_write(void *opaque, hwaddr offset,
|
|
uint64_t value, unsigned size)
|
|
{
|
|
VMAppleBdifState *s = opaque;
|
|
uint64_t devid = (offset & REG_DEVID_MASK);
|
|
|
|
trace_bdif_write(offset, size, value);
|
|
|
|
switch (offset & ~REG_DEVID_MASK) {
|
|
case REG_CMD:
|
|
switch (devid) {
|
|
case DEVID_ROOT:
|
|
vblk_cmd(devid, s->root, value, 0x0);
|
|
break;
|
|
case DEVID_AUX:
|
|
vblk_cmd(devid, s->aux, value, 0x0);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const MemoryRegionOps bdif_ops = {
|
|
.read = bdif_read,
|
|
.write = bdif_write,
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
.valid = {
|
|
.min_access_size = 1,
|
|
.max_access_size = 8,
|
|
},
|
|
.impl = {
|
|
.min_access_size = 1,
|
|
.max_access_size = 8,
|
|
},
|
|
};
|
|
|
|
static void bdif_init(Object *obj)
|
|
{
|
|
VMAppleBdifState *s = VMAPPLE_BDIF(obj);
|
|
|
|
memory_region_init_io(&s->mmio, obj, &bdif_ops, obj,
|
|
"VMApple Backdoor Interface", VMAPPLE_BDIF_SIZE);
|
|
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
|
}
|
|
|
|
static const Property bdif_properties[] = {
|
|
DEFINE_PROP_DRIVE("aux", VMAppleBdifState, aux),
|
|
DEFINE_PROP_DRIVE("root", VMAppleBdifState, root),
|
|
};
|
|
|
|
static void bdif_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
dc->desc = "VMApple Backdoor Interface";
|
|
device_class_set_props(dc, bdif_properties);
|
|
}
|
|
|
|
static const TypeInfo bdif_info = {
|
|
.name = TYPE_VMAPPLE_BDIF,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(VMAppleBdifState),
|
|
.instance_init = bdif_init,
|
|
.class_init = bdif_class_init,
|
|
};
|
|
|
|
static void bdif_register_types(void)
|
|
{
|
|
type_register_static(&bdif_info);
|
|
}
|
|
|
|
type_init(bdif_register_types)
|