mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-07 01:33:56 -06:00
hw/vmapple/bdif: Introduce vmapple backdoor interface
The VMApple machine exposes AUX and ROOT block devices (as well as USB OTG emulation) via virtio-pci as well as a special, simple backdoor platform device. This patch implements this backdoor platform device to the best of my understanding. I left out any USB OTG parts; they're only needed for guest recovery and I don't understand the protocol yet. Signed-off-by: Alexander Graf <graf@amazon.com> Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Tested-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-ID: <20241223221645.29911-11-phil@philjordan.eu> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
This commit is contained in:
parent
c960b38955
commit
0179bb3c48
5 changed files with 285 additions and 0 deletions
274
hw/vmapple/bdif.c
Normal file
274
hw/vmapple/bdif.c
Normal file
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* 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)
|
Loading…
Add table
Add a link
Reference in a new issue