1 /* 2 * VMApple Backdoor Interface 3 * 4 * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 * 9 * SPDX-License-Identifier: GPL-2.0-or-later 10 */ 11 12 #include "qemu/osdep.h" 13 #include "qemu/units.h" 14 #include "qemu/log.h" 15 #include "qemu/module.h" 16 #include "trace.h" 17 #include "hw/vmapple/vmapple.h" 18 #include "hw/sysbus.h" 19 #include "hw/block/block.h" 20 #include "qapi/error.h" 21 #include "system/block-backend.h" 22 #include "system/dma.h" 23 24 OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF) 25 26 struct VMAppleBdifState { 27 SysBusDevice parent_obj; 28 29 BlockBackend *aux; 30 BlockBackend *root; 31 MemoryRegion mmio; 32 }; 33 34 #define VMAPPLE_BDIF_SIZE 0x00200000 35 36 #define REG_DEVID_MASK 0xffff0000 37 #define DEVID_ROOT 0x00000000 38 #define DEVID_AUX 0x00010000 39 #define DEVID_USB 0x00100000 40 41 #define REG_STATUS 0x0 42 #define REG_STATUS_ACTIVE BIT(0) 43 #define REG_CFG 0x4 44 #define REG_CFG_ACTIVE BIT(1) 45 #define REG_UNK1 0x8 46 #define REG_BUSY 0x10 47 #define REG_BUSY_READY BIT(0) 48 #define REG_UNK2 0x400 49 #define REG_CMD 0x408 50 #define REG_NEXT_DEVICE 0x420 51 #define REG_UNK3 0x434 52 53 typedef struct VblkSector { 54 uint32_t pad; 55 uint32_t pad2; 56 uint32_t sector; 57 uint32_t pad3; 58 } VblkSector; 59 60 typedef struct VblkReqCmd { 61 uint64_t addr; 62 uint32_t len; 63 uint32_t flags; 64 } VblkReqCmd; 65 66 typedef struct VblkReq { 67 VblkReqCmd sector; 68 VblkReqCmd data; 69 VblkReqCmd retval; 70 } VblkReq; 71 72 #define VBLK_DATA_FLAGS_READ 0x00030001 73 #define VBLK_DATA_FLAGS_WRITE 0x00010001 74 75 #define VBLK_RET_SUCCESS 0 76 #define VBLK_RET_FAILED 1 77 78 static uint64_t bdif_read(void *opaque, hwaddr offset, unsigned size) 79 { 80 uint64_t ret = -1; 81 uint64_t devid = offset & REG_DEVID_MASK; 82 83 switch (offset & ~REG_DEVID_MASK) { 84 case REG_STATUS: 85 ret = REG_STATUS_ACTIVE; 86 break; 87 case REG_CFG: 88 ret = REG_CFG_ACTIVE; 89 break; 90 case REG_UNK1: 91 ret = 0x420; 92 break; 93 case REG_BUSY: 94 ret = REG_BUSY_READY; 95 break; 96 case REG_UNK2: 97 ret = 0x1; 98 break; 99 case REG_UNK3: 100 ret = 0x0; 101 break; 102 case REG_NEXT_DEVICE: 103 switch (devid) { 104 case DEVID_ROOT: 105 ret = 0x8000000; 106 break; 107 case DEVID_AUX: 108 ret = 0x10000; 109 break; 110 } 111 break; 112 } 113 114 trace_bdif_read(offset, size, ret); 115 return ret; 116 } 117 118 static void le2cpu_sector(VblkSector *sector) 119 { 120 sector->sector = le32_to_cpu(sector->sector); 121 } 122 123 static void le2cpu_reqcmd(VblkReqCmd *cmd) 124 { 125 cmd->addr = le64_to_cpu(cmd->addr); 126 cmd->len = le32_to_cpu(cmd->len); 127 cmd->flags = le32_to_cpu(cmd->flags); 128 } 129 130 static void le2cpu_req(VblkReq *req) 131 { 132 le2cpu_reqcmd(&req->sector); 133 le2cpu_reqcmd(&req->data); 134 le2cpu_reqcmd(&req->retval); 135 } 136 137 static void vblk_cmd(uint64_t devid, BlockBackend *blk, uint64_t gp_addr, 138 uint64_t static_off) 139 { 140 VblkReq req; 141 VblkSector sector; 142 uint64_t off = 0; 143 g_autofree char *buf = NULL; 144 uint8_t ret = VBLK_RET_FAILED; 145 int r; 146 MemTxResult dma_result; 147 148 dma_result = dma_memory_read(&address_space_memory, gp_addr, 149 &req, sizeof(req), MEMTXATTRS_UNSPECIFIED); 150 if (dma_result != MEMTX_OK) { 151 goto out; 152 } 153 154 le2cpu_req(&req); 155 156 if (req.sector.len != sizeof(sector)) { 157 goto out; 158 } 159 160 /* Read the vblk command */ 161 dma_result = dma_memory_read(&address_space_memory, req.sector.addr, 162 §or, sizeof(sector), 163 MEMTXATTRS_UNSPECIFIED); 164 if (dma_result != MEMTX_OK) { 165 goto out; 166 } 167 le2cpu_sector(§or); 168 169 off = sector.sector * 512ULL + static_off; 170 171 /* Sanity check that we're not allocating bogus sizes */ 172 if (req.data.len > 128 * MiB) { 173 goto out; 174 } 175 176 buf = g_malloc0(req.data.len); 177 switch (req.data.flags) { 178 case VBLK_DATA_FLAGS_READ: 179 r = blk_pread(blk, off, req.data.len, buf, 0); 180 trace_bdif_vblk_read(devid == DEVID_AUX ? "aux" : "root", 181 req.data.addr, off, req.data.len, r); 182 if (r < 0) { 183 goto out; 184 } 185 dma_result = dma_memory_write(&address_space_memory, req.data.addr, buf, 186 req.data.len, MEMTXATTRS_UNSPECIFIED); 187 if (dma_result == MEMTX_OK) { 188 ret = VBLK_RET_SUCCESS; 189 } 190 break; 191 case VBLK_DATA_FLAGS_WRITE: 192 /* Not needed, iBoot only reads */ 193 break; 194 default: 195 break; 196 } 197 198 out: 199 dma_memory_write(&address_space_memory, req.retval.addr, &ret, 1, 200 MEMTXATTRS_UNSPECIFIED); 201 } 202 203 static void bdif_write(void *opaque, hwaddr offset, 204 uint64_t value, unsigned size) 205 { 206 VMAppleBdifState *s = opaque; 207 uint64_t devid = (offset & REG_DEVID_MASK); 208 209 trace_bdif_write(offset, size, value); 210 211 switch (offset & ~REG_DEVID_MASK) { 212 case REG_CMD: 213 switch (devid) { 214 case DEVID_ROOT: 215 vblk_cmd(devid, s->root, value, 0x0); 216 break; 217 case DEVID_AUX: 218 vblk_cmd(devid, s->aux, value, 0x0); 219 break; 220 } 221 break; 222 } 223 } 224 225 static const MemoryRegionOps bdif_ops = { 226 .read = bdif_read, 227 .write = bdif_write, 228 .endianness = DEVICE_NATIVE_ENDIAN, 229 .valid = { 230 .min_access_size = 1, 231 .max_access_size = 8, 232 }, 233 .impl = { 234 .min_access_size = 1, 235 .max_access_size = 8, 236 }, 237 }; 238 239 static void bdif_init(Object *obj) 240 { 241 VMAppleBdifState *s = VMAPPLE_BDIF(obj); 242 243 memory_region_init_io(&s->mmio, obj, &bdif_ops, obj, 244 "VMApple Backdoor Interface", VMAPPLE_BDIF_SIZE); 245 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); 246 } 247 248 static const Property bdif_properties[] = { 249 DEFINE_PROP_DRIVE("aux", VMAppleBdifState, aux), 250 DEFINE_PROP_DRIVE("root", VMAppleBdifState, root), 251 }; 252 253 static void bdif_class_init(ObjectClass *klass, void *data) 254 { 255 DeviceClass *dc = DEVICE_CLASS(klass); 256 257 dc->desc = "VMApple Backdoor Interface"; 258 device_class_set_props(dc, bdif_properties); 259 } 260 261 static const TypeInfo bdif_info = { 262 .name = TYPE_VMAPPLE_BDIF, 263 .parent = TYPE_SYS_BUS_DEVICE, 264 .instance_size = sizeof(VMAppleBdifState), 265 .instance_init = bdif_init, 266 .class_init = bdif_class_init, 267 }; 268 269 static void bdif_register_types(void) 270 { 271 type_register_static(&bdif_info); 272 } 273 274 type_init(bdif_register_types) 275