xref: /qemu/hw/vmapple/bdif.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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                                  &sector, sizeof(sector),
163                                  MEMTXATTRS_UNSPECIFIED);
164     if (dma_result != MEMTX_OK) {
165         goto out;
166     }
167     le2cpu_sector(&sector);
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