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
bdif_read(void * opaque,hwaddr offset,unsigned size)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
le2cpu_sector(VblkSector * sector)118 static void le2cpu_sector(VblkSector *sector)
119 {
120 sector->sector = le32_to_cpu(sector->sector);
121 }
122
le2cpu_reqcmd(VblkReqCmd * cmd)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
le2cpu_req(VblkReq * req)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
vblk_cmd(uint64_t devid,BlockBackend * blk,uint64_t gp_addr,uint64_t static_off)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
bdif_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)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
bdif_init(Object * obj)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
bdif_class_init(ObjectClass * klass,const void * data)253 static void bdif_class_init(ObjectClass *klass, const 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
bdif_register_types(void)269 static void bdif_register_types(void)
270 {
271 type_register_static(&bdif_info);
272 }
273
274 type_init(bdif_register_types)
275