master
m 285 lines 8.46 KB
Raw
1 /*
2 * QEMU Apple ParavirtualizedGraphics.framework device, MMIO (arm64) variant
3 *
4 * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 *
8 * ParavirtualizedGraphics.framework is a set of libraries that macOS provides
9 * which implements 3d graphics passthrough to the host as well as a
10 * proprietary guest communication channel to drive it. This device model
11 * implements support to drive that library from within QEMU as an MMIO-based
12 * system device for macOS on arm64 VMs.
13 */
14
15 #include "qemu/osdep.h"
16 #include "qemu/log.h"
17 #include "qemu/aio-wait.h"
18 #include "hw/core/sysbus.h"
19 #include "hw/core/irq.h"
20 #include "apple-gfx.h"
21 #include "trace.h"
22
23 #import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
24
25 OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXMMIOState, APPLE_GFX_MMIO)
26
27 /*
28 * ParavirtualizedGraphics.Framework only ships header files for the PCI
29 * variant which does not include IOSFC descriptors and host devices. We add
30 * their definitions here so that we can also work with the ARM version.
31 */
32 typedef bool(^IOSFCRaiseInterrupt)(uint32_t vector);
33 typedef bool(^IOSFCUnmapMemory)(void *, void *, void *, void *, void *, void *);
34 typedef bool(^IOSFCMapMemory)(uint64_t phys, uint64_t len, bool ro, void **va,
35 void *, void *);
36
37 @interface PGDeviceDescriptor (IOSurfaceMapper)
38 @property (readwrite, nonatomic) bool usingIOSurfaceMapper;
39 @end
40
41 @interface PGIOSurfaceHostDeviceDescriptor : NSObject
42 -(PGIOSurfaceHostDeviceDescriptor *)init;
43 @property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory;
44 @property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory;
45 @property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt;
46 @end
47
48 @interface PGIOSurfaceHostDevice : NSObject
49 -(instancetype)initWithDescriptor:(PGIOSurfaceHostDeviceDescriptor *)desc;
50 -(uint32_t)mmioReadAtOffset:(size_t)offset;
51 -(void)mmioWriteAtOffset:(size_t)offset value:(uint32_t)value;
52 @end
53
54 struct AppleGFXMapSurfaceMemoryJob;
55 struct AppleGFXMMIOState {
56 SysBusDevice parent_obj;
57
58 AppleGFXState common;
59
60 qemu_irq irq_gfx;
61 qemu_irq irq_iosfc;
62 MemoryRegion iomem_iosfc;
63 PGIOSurfaceHostDevice *pgiosfc;
64 };
65
66 typedef struct AppleGFXMMIOJob {
67 AppleGFXMMIOState *state;
68 uint64_t offset;
69 uint64_t value;
70 bool completed;
71 } AppleGFXMMIOJob;
72
73 static void iosfc_do_read(void *opaque)
74 {
75 AppleGFXMMIOJob *job = opaque;
76 job->value = [job->state->pgiosfc mmioReadAtOffset:job->offset];
77 qatomic_set(&job->completed, true);
78 aio_wait_kick();
79 }
80
81 static uint64_t iosfc_read(void *opaque, hwaddr offset, unsigned size)
82 {
83 AppleGFXMMIOJob job = {
84 .state = opaque,
85 .offset = offset,
86 .completed = false,
87 };
88 dispatch_queue_t queue =
89 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
90
91 dispatch_async_f(queue, &job, iosfc_do_read);
92 AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
93
94 trace_apple_gfx_mmio_iosfc_read(offset, job.value);
95 return job.value;
96 }
97
98 static void iosfc_do_write(void *opaque)
99 {
100 AppleGFXMMIOJob *job = opaque;
101 [job->state->pgiosfc mmioWriteAtOffset:job->offset value:job->value];
102 qatomic_set(&job->completed, true);
103 aio_wait_kick();
104 }
105
106 static void iosfc_write(void *opaque, hwaddr offset, uint64_t val,
107 unsigned size)
108 {
109 AppleGFXMMIOJob job = {
110 .state = opaque,
111 .offset = offset,
112 .value = val,
113 .completed = false,
114 };
115 dispatch_queue_t queue =
116 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
117
118 dispatch_async_f(queue, &job, iosfc_do_write);
119 AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
120
121 trace_apple_gfx_mmio_iosfc_write(offset, val);
122 }
123
124 static const MemoryRegionOps apple_iosfc_ops = {
125 .read = iosfc_read,
126 .write = iosfc_write,
127 .endianness = DEVICE_LITTLE_ENDIAN,
128 .valid = {
129 .min_access_size = 4,
130 .max_access_size = 8,
131 },
132 .impl = {
133 .min_access_size = 4,
134 .max_access_size = 8,
135 },
136 };
137
138 static void raise_irq_bh(void *opaque)
139 {
140 qemu_irq *irq = opaque;
141
142 qemu_irq_pulse(*irq);
143 }
144
145 static void *apple_gfx_mmio_map_surface_memory(uint64_t guest_physical_address,
146 uint64_t length, bool read_only)
147 {
148 void *mem;
149 MemoryRegion *region = NULL;
150
151 RCU_READ_LOCK_GUARD();
152 mem = apple_gfx_host_ptr_for_gpa_range(guest_physical_address,
153 length, read_only, &region);
154 if (mem) {
155 memory_region_ref(region);
156 }
157 return mem;
158 }
159
160 static bool apple_gfx_mmio_unmap_surface_memory(void *ptr)
161 {
162 MemoryRegion *region;
163 ram_addr_t offset = 0;
164
165 RCU_READ_LOCK_GUARD();
166 region = memory_region_from_host(ptr, &offset);
167 if (!region) {
168 qemu_log_mask(LOG_GUEST_ERROR,
169 "%s: memory at %p to be unmapped not found.\n",
170 __func__, ptr);
171 return false;
172 }
173
174 trace_apple_gfx_iosfc_unmap_memory_region(ptr, region);
175 memory_region_unref(region);
176 return true;
177 }
178
179 static PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device(
180 AppleGFXMMIOState *s)
181 {
182 PGIOSurfaceHostDeviceDescriptor *iosfc_desc =
183 [PGIOSurfaceHostDeviceDescriptor new];
184 PGIOSurfaceHostDevice *iosfc_host_dev;
185
186 iosfc_desc.mapMemory =
187 ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
188 *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
189
190 trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
191
192 return *va != NULL;
193 };
194
195 iosfc_desc.unmapMemory =
196 ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
197 return apple_gfx_mmio_unmap_surface_memory(va);
198 };
199
200 iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) {
201 trace_apple_gfx_iosfc_raise_irq(vector);
202 aio_bh_schedule_oneshot(qemu_get_aio_context(),
203 raise_irq_bh, &s->irq_iosfc);
204 return true;
205 };
206
207 iosfc_host_dev =
208 [[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc];
209 [iosfc_desc release];
210 return iosfc_host_dev;
211 }
212
213 static void apple_gfx_mmio_realize(DeviceState *dev, Error **errp)
214 {
215 @autoreleasepool {
216 AppleGFXMMIOState *s = APPLE_GFX_MMIO(dev);
217 PGDeviceDescriptor *desc = [PGDeviceDescriptor new];
218
219 desc.raiseInterrupt = ^(uint32_t vector) {
220 trace_apple_gfx_raise_irq(vector);
221 aio_bh_schedule_oneshot(qemu_get_aio_context(),
222 raise_irq_bh, &s->irq_gfx);
223 };
224
225 desc.usingIOSurfaceMapper = true;
226 s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
227
228 if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) {
229 [s->pgiosfc release];
230 s->pgiosfc = nil;
231 }
232
233 [desc release];
234 desc = nil;
235 }
236 }
237
238 static void apple_gfx_mmio_init(Object *obj)
239 {
240 AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj);
241
242 apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_MMIO);
243
244 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->common.iomem_gfx);
245 memory_region_init_io(&s->iomem_iosfc, obj, &apple_iosfc_ops, s,
246 TYPE_APPLE_GFX_MMIO, 0x10000);
247 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem_iosfc);
248 sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_gfx);
249 sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_iosfc);
250 }
251
252 static void apple_gfx_mmio_reset(Object *obj, ResetType type)
253 {
254 AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj);
255 [s->common.pgdev reset];
256 }
257
258 static const Property apple_gfx_mmio_properties[] = {
259 DEFINE_PROP_ARRAY("display-modes", AppleGFXMMIOState,
260 common.num_display_modes, common.display_modes,
261 qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode),
262 };
263
264 static void apple_gfx_mmio_class_init(ObjectClass *klass, const void *data)
265 {
266 DeviceClass *dc = DEVICE_CLASS(klass);
267 ResettableClass *rc = RESETTABLE_CLASS(klass);
268
269 rc->phases.hold = apple_gfx_mmio_reset;
270 dc->hotpluggable = false;
271 dc->realize = apple_gfx_mmio_realize;
272
273 device_class_set_props(dc, apple_gfx_mmio_properties);
274 }
275
276 static const TypeInfo apple_gfx_mmio_types[] = {
277 {
278 .name = TYPE_APPLE_GFX_MMIO,
279 .parent = TYPE_SYS_BUS_DEVICE,
280 .instance_size = sizeof(AppleGFXMMIOState),
281 .class_init = apple_gfx_mmio_class_init,
282 .instance_init = apple_gfx_mmio_init,
283 }
284 };
285 DEFINE_TYPES(apple_gfx_mmio_types)