1
<meta>
2
This patch series has been through months of review and
3
refinement. It now has end-to-end Reviewed-by: tags and
4
all code patches but one have Tested-by: tags. No significant
5
issues have been found via review for some weeks.
6
7
The patch set creates two new subsystems:
8
hw/display/apple-gfx
9
hw/vmapple
10
so it doesn't fall within the responsibility of existing
11
maintainers. How do we proceed to get this merged now that
12
10.0 development is open?
13
</meta>
14
15
1
This patch set introduces a new ARM and macOS HVF specific machine type
16
This patch set introduces a new ARM and macOS HVF specific machine type
2
called "vmapple", as well as a family of display devices based on the
17
called "vmapple", as well as a family of display devices based on the
3
ParavirtualizedGraphics.framework in macOS. One of the display adapter
18
ParavirtualizedGraphics.framework in macOS. One of the display adapter
4
variants, apple-gfx-mmio, is required for the new machine type, while
19
variants, apple-gfx-mmio, is required for the new machine type, while
5
apple-gfx-pci can be used to enable 3D graphics acceleration with x86-64
20
apple-gfx-pci can be used to enable 3D graphics acceleration with x86-64
...
...
31
hosts only because ParavirtualizedGraphics.framework is a black box
46
hosts only because ParavirtualizedGraphics.framework is a black box
32
implementing most of the logic behind the apple-gfx device.)
47
implementing most of the logic behind the apple-gfx device.)
33
* PCI devices use legacy IRQs, not MSI/MSI-X. As far as I can tell,
48
* PCI devices use legacy IRQs, not MSI/MSI-X. As far as I can tell,
34
we'd need to include the GICv3 ITS, but it's unclear to me what
49
we'd need to include the GICv3 ITS, but it's unclear to me what
35
exactly needs wiring up.
50
exactly needs wiring up.
36
* Due to lack of MSI(-X), event delivery from USB devices to the guest
51
* Due to a quirk (bug?) in the macOS XHCI driver when MSI-X is not
37
macOS isn't working correctly. My current conclusion is that the
52
available, correct functioning of the USB controller (and thus
38
OS's XHCI driver simply was never designed to work with legacy IRQs.
53
keyboard/tablet) requires a small workaround in the XHCI controller
39
The upshot is that keyboard and mouse/tablet input is very laggy.
54
device. This is part of another patch series:
40
The solution would be to implement MSI(-X) support or figure out how
55
https://patchew.org/QEMU/20241208191646.64857-1-phil@philjordan.eu/
41
to make hcd-xhci-sysbus work with the macOS guest, if at all possible.
42
(EHCI and UHCI/OHCI controllers are not an option as the VMAPPLE
43
guest kernel does not include drivers for these.)
44
* The guest OS must first be provisioned using Virtualization.framework;
56
* The guest OS must first be provisioned using Virtualization.framework;
45
the disk images can subsequently be used in Qemu. (See docs.)
57
the disk images can subsequently be used in Qemu. (See docs.)
46
58
47
The apple-gfx device can be used independently from the vmapple machine
59
The apple-gfx device can be used independently from the vmapple machine
48
type, at least in the PCI variant. It mainly targets x86-64 macOS guests
60
type, at least in the PCI variant. It mainly targets x86-64 macOS guests
...
...
192
* 16/16 (NEW/RFC vmapple/virtio-blk): Proposed change to replace type hierarchy
204
* 16/16 (NEW/RFC vmapple/virtio-blk): Proposed change to replace type hierarchy
193
with a variant property. This seems cleaner and less confusing than the
205
with a variant property. This seems cleaner and less confusing than the
194
original approach to me, but I'm not sure if it warrants creation of a new
206
original approach to me, but I'm not sure if it warrants creation of a new
195
QAPI enum and property type definition.
207
QAPI enum and property type definition.
196
208
209
v9 -> v10:
210
211
* 01/15 (ui & main loop): Added comments to qemu_main declaration and GTK.
212
* 02/15 (apple-gfx): Reworked the way frame rendering code is threaded to use
213
BHs for sections requiring BQL.
214
* 02/15 (apple-gfx): Fixed ./configure error on non-macOS platforms.
215
* 10/15 (vmapple/aes): Code style and comment improvements.
216
* 12/15 (vmapple/cfg): Slightly tidier error reporting for overlong property
217
values.
218
* 13/15 (vmapple/virtio-blk): Folded v9 patch 16/16 into this one, changing
219
the device type design to provide a single device type with a variant
220
     property instead of 2 different subtypes for aux and root volumes.
221
* 15/15 (vmapple machine type): Documentation fixup for changed virtio-blk
222
device type; small improvements to shell commands in documentation;
223
improved propagation of errors during cfg device instantiation.
224
225
v10 -> v11:
226
227
* 01/15 (ui & main loop): Simplified main.c, better comments & commit message
228
* 02/15 (apple-gfx): Give each PV display instance a unique serial number.
229
* 02 & 03/15 (apple-gfx, -pci): Formatting/style tweaks
230
* 15/15 (vmapple machine type): Improvements to shell code in docs
231
232
v11 -> v12:
233
234
* 01/15 (ui & main loop): More precise wording of code comments.
235
* 02/15 (apple-gfx): Fixed memory management regressions introduced in v10;
236
improved error handling; various more conmetic code adjustments
237
* 09/15 (GPEX): Fixed uses of deleted GPEX_NUM_IRQS constant that have been
238
added to QEMU since this patch was originally written.
239
240
v12 -> v13:
241
242
* 15/15 (vmapple machine type): Bumped the machine type version from 9.2
243
to 10.0.
244
* All patches in the series now have been positively reviewed and received
245
corresponding reviewed-by tags.
246
247
v13 -> v14:
248
249
* 6/15 (hw/vmapple directory): Changed myself from reviewer
250
to maintainer, as that seemed appropriate at this point.
251
* 15/15 (vmapple machine type): Gate creation of XHCI and
252
USB HID devices behind if (defaults_enabled()).
253
197
Alexander Graf (9):
254
Alexander Graf (9):
198
hw: Add vmapple subdir
255
hw: Add vmapple subdir
199
hw/misc/pvpanic: Add MMIO interface
256
hw/misc/pvpanic: Add MMIO interface
200
hvf: arm: Ignore writes to CNTP_CTL_EL0
257
hvf: arm: Ignore writes to CNTP_CTL_EL0
201
gpex: Allow more than 4 legacy IRQs
258
gpex: Allow more than 4 legacy IRQs
202
hw/vmapple/aes: Introduce aes engine
259
hw/vmapple/aes: Introduce aes engine
203
hw/vmapple/bdif: Introduce vmapple backdoor interface
260
hw/vmapple/bdif: Introduce vmapple backdoor interface
204
hw/vmapple/cfg: Introduce vmapple cfg region
261
hw/vmapple/cfg: Introduce vmapple cfg region
205
hw/vmapple/virtio-blk: Add support for apple virtio-blk
262
hw/vmapple/virtio-blk: Add support for apple virtio-blk
206
hw/vmapple/vmapple: Add vmapple machine type
263
hw/vmapple/vmapple: Add vmapple machine type
207
264
208
Phil Dennis-Jordan (7):
265
Phil Dennis-Jordan (6):
209
ui & main loop: Redesign of system-specific main thread event handling
266
ui & main loop: Redesign of system-specific main thread event handling
210
hw/display/apple-gfx: Introduce ParavirtualizedGraphics.Framework
267
hw/display/apple-gfx: Introduce ParavirtualizedGraphics.Framework
211
support
268
support
212
hw/display/apple-gfx: Adds PCI implementation
269
hw/display/apple-gfx: Adds PCI implementation
213
hw/display/apple-gfx: Adds configurable mode list
270
hw/display/apple-gfx: Adds configurable mode list
214
MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF
271
MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF
215
hw/block/virtio-blk: Replaces request free function with g_free
272
hw/block/virtio-blk: Replaces request free function with g_free
216
hw/vmapple/virtio-blk: Replace variant types with property on base
217
273
218
MAINTAINERS | 15 +
274
MAINTAINERS | 15 +
219
contrib/vmapple/uuid.sh | 9 +
275
contrib/vmapple/uuid.sh | 9 +
220
docs/system/arm/vmapple.rst | 63 ++
276
docs/system/arm/vmapple.rst | 63 ++
221
docs/system/target-arm.rst | 1 +
277
docs/system/target-arm.rst | 1 +
222
hw/Kconfig | 1 +
278
hw/Kconfig | 1 +
223
hw/arm/sbsa-ref.c | 2 +-
279
hw/arm/sbsa-ref.c | 2 +-
224
hw/arm/virt.c | 2 +-
280
hw/arm/virt.c | 2 +-
225
hw/block/virtio-blk.c | 58 +-
281
hw/block/virtio-blk.c | 58 +-
226
hw/core/qdev-properties-system.c | 8 +
282
hw/core/qdev-properties-system.c | 8 +
227
hw/display/Kconfig | 13 +
283
hw/display/Kconfig | 13 +
228
hw/display/apple-gfx-mmio.m | 289 ++++++++++
284
hw/display/apple-gfx-mmio.m | 289 +++++++++
229
hw/display/apple-gfx-pci.m | 157 +++++
285
hw/display/apple-gfx-pci.m | 157 +++++
230
hw/display/apple-gfx.h | 76 +++
286
hw/display/apple-gfx.h | 77 +++
231
hw/display/apple-gfx.m | 861 ++++++++++++++++++++++++++++
287
hw/display/apple-gfx.m | 880 ++++++++++++++++++++++++++++
232
hw/display/meson.build | 5 +
288
hw/display/meson.build | 7 +
233
hw/display/trace-events | 30 +
289
hw/display/trace-events | 30 +
234
hw/i386/microvm.c | 2 +-
290
hw/i386/microvm.c | 2 +-
235
hw/loongarch/virt.c | 2 +-
291
hw/loongarch/virt.c | 12 +-
236
hw/meson.build | 1 +
292
hw/meson.build | 1 +
237
hw/mips/loongson3_virt.c | 2 +-
293
hw/mips/loongson3_virt.c | 2 +-
238
hw/misc/Kconfig | 4 +
294
hw/misc/Kconfig | 4 +
239
hw/misc/meson.build | 1 +
295
hw/misc/meson.build | 1 +
240
hw/misc/pvpanic-mmio.c | 61 ++
296
hw/misc/pvpanic-mmio.c | 61 ++
241
hw/openrisc/virt.c | 12 +-
297
hw/openrisc/virt.c | 12 +-
242
hw/pci-host/gpex.c | 43 +-
298
hw/pci-host/gpex.c | 43 +-
243
hw/riscv/virt.c | 12 +-
299
hw/riscv/virt.c | 12 +-
244
hw/vmapple/Kconfig | 32 ++
300
hw/vmapple/Kconfig | 32 +
245
hw/vmapple/aes.c | 582 +++++++++++++++++++
301
hw/vmapple/aes.c | 581 ++++++++++++++++++
246
hw/vmapple/bdif.c | 275 +++++++++
302
hw/vmapple/bdif.c | 275 +++++++++
247
hw/vmapple/cfg.c | 196 +++++++
303
hw/vmapple/cfg.c | 196 +++++++
248
hw/vmapple/meson.build | 5 +
304
hw/vmapple/meson.build | 5 +
249
hw/vmapple/trace-events | 21 +
305
hw/vmapple/trace-events | 21 +
250
hw/vmapple/trace.h | 1 +
306
hw/vmapple/trace.h | 1 +
251
hw/vmapple/virtio-blk.c | 201 +++++++
307
hw/vmapple/virtio-blk.c | 205 +++++++
252
hw/vmapple/vmapple.c | 639 +++++++++++++++++++++
308
hw/vmapple/vmapple.c | 648 ++++++++++++++++++++
309
hw/xen/xen-pvh-common.c | 2 +-
253
hw/xtensa/virt.c | 2 +-
310
hw/xtensa/virt.c | 2 +-
254
include/hw/misc/pvpanic.h | 1 +
311
include/hw/misc/pvpanic.h | 1 +
255
include/hw/pci-host/gpex.h | 7 +-
312
include/hw/pci-host/gpex.h | 7 +-
256
include/hw/pci/pci_ids.h | 1 +
313
include/hw/pci/pci_ids.h | 1 +
257
include/hw/qdev-properties-system.h | 6 +
314
include/hw/qdev-properties-system.h | 5 +
258
include/hw/virtio/virtio-blk.h | 11 +-
315
include/hw/virtio/virtio-blk.h | 11 +-
259
include/hw/vmapple/vmapple.h | 23 +
316
include/hw/vmapple/vmapple.h | 23 +
260
include/qemu-main.h | 3 +-
317
include/qemu-main.h | 14 +-
261
include/qemu/cutils.h | 15 +
318
include/qemu/cutils.h | 15 +
262
include/qemu/typedefs.h | 1 +
263
meson.build | 5 +
319
meson.build | 5 +
264
qapi/virtio.json | 14 +
320
qapi/virtio.json | 14 +
265
system/main.c | 50 +-
321
system/main.c | 37 +-
266
target/arm/hvf/hvf.c | 9 +
322
target/arm/hvf/hvf.c | 9 +
267
ui/cocoa.m | 54 +-
323
ui/cocoa.m | 54 +-
268
ui/gtk.c | 3 +
324
ui/gtk.c | 4 +
269
ui/sdl2.c | 4 +
325
ui/sdl2.c | 4 +
270
util/hexdump.c | 18 +
326
util/hexdump.c | 18 +
271
53 files changed, 3803 insertions(+), 106 deletions(-)
327
53 files changed, 3842 insertions(+), 110 deletions(-)
272
create mode 100755 contrib/vmapple/uuid.sh
328
create mode 100755 contrib/vmapple/uuid.sh
273
create mode 100644 docs/system/arm/vmapple.rst
329
create mode 100644 docs/system/arm/vmapple.rst
274
create mode 100644 hw/display/apple-gfx-mmio.m
330
create mode 100644 hw/display/apple-gfx-mmio.m
275
create mode 100644 hw/display/apple-gfx-pci.m
331
create mode 100644 hw/display/apple-gfx-pci.m
276
create mode 100644 hw/display/apple-gfx.h
332
create mode 100644 hw/display/apple-gfx.h
...
...
286
create mode 100644 hw/vmapple/virtio-blk.c
342
create mode 100644 hw/vmapple/virtio-blk.c
287
create mode 100644 hw/vmapple/vmapple.c
343
create mode 100644 hw/vmapple/vmapple.c
288
create mode 100644 include/hw/vmapple/vmapple.h
344
create mode 100644 include/hw/vmapple/vmapple.h
289
345
290
--
346
--
291
2.39.3 (Apple Git-145)
347
2.39.5 (Apple Git-154)
292
348
293
349
diff view generated by jsdifflib
Deleted patch
1
macOS's Cocoa event handling must be done on the initial (main) thread
2
of the process. Furthermore, if library or application code uses
3
libdispatch, the main dispatch queue must be handling events on the main
4
thread as well.
5
1
6
So far, this has affected Qemu in both the Cocoa and SDL UIs, although
7
in different ways: the Cocoa UI replaces the default qemu_main function
8
with one that spins Qemu's internal main event loop off onto a
9
background thread. SDL (which uses Cocoa internally) on the other hand
10
uses a polling approach within Qemu's main event loop. Events are
11
polled during the SDL UI's dpy_refresh callback, which happens to run
12
on the main thread by default.
13
14
As UIs are mutually exclusive, this works OK as long as nothing else
15
needs platform-native event handling. In the next patch, a new device is
16
introduced based on the ParavirtualizedGraphics.framework in macOS.
17
This uses libdispatch internally, and only works when events are being
18
handled on the main runloop. With the current system, it works when
19
using either the Cocoa or the SDL UI. However, it does not when running
20
headless. Moreover, any attempt to install a similar scheme to the
21
Cocoa UI's main thread replacement fails when combined with the SDL
22
UI.
23
24
This change tidies up main thread management to be more flexible.
25
26
* The qemu_main global function pointer is a custom function for the
27
main thread, and it may now be NULL. When it is, the main thread
28
runs the main Qemu loop. This represents the traditional setup.
29
* When non-null, spawning the main Qemu event loop on a separate
30
thread is now done centrally rather than inside the Cocoa UI code.
31
* For most platforms, qemu_main is indeed NULL by default, but on
32
Darwin, it defaults to a function that runs the CFRunLoop.
33
* The Cocoa UI sets qemu_main to a function which runs the
34
NSApplication event handling runloop, as is usual for a Cocoa app.
35
* The SDL UI overrides the qemu_main function to NULL, thus
36
specifying that Qemu's main loop must run on the main
37
thread.
38
* The GTK UI also overrides the qemu_main function to NULL.
39
* For other UIs, or in the absence of UIs, the platform's default
40
behaviour is followed.
41
42
This means that on macOS, the platform's runloop events are always
43
handled, regardless of chosen UI. The new PV graphics device will
44
thus work in all configurations. There is no functional change on other
45
operating systems.
46
47
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
48
Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
49
---
50
v5:
51
52
* Simplified the way of setting/clearing the main loop by going back
53
to setting qemu_main directly, but narrowing the scope of what it
54
needs to do, and it can now be NULL.
55
56
v6:
57
58
* Folded function qemu_run_default_main_on_new_thread's code into
59
main()
60
* Removed whitespace changes left over on lines near code removed
61
between v4 and v5
62
63
v9:
64
65
* Set qemu_main to NULL for GTK UI as well.
66
67
include/qemu-main.h | 3 +--
68
include/qemu/typedefs.h | 1 +
69
system/main.c | 50 ++++++++++++++++++++++++++++++++++----
70
ui/cocoa.m | 54 ++++++++++-------------------------------
71
ui/gtk.c | 3 +++
72
ui/sdl2.c | 4 +++
73
6 files changed, 67 insertions(+), 48 deletions(-)
74
75
diff --git a/include/qemu-main.h b/include/qemu-main.h
76
index XXXXXXX..XXXXXXX 100644
77
--- a/include/qemu-main.h
78
+++ b/include/qemu-main.h
79
@@ -XXX,XX +XXX,XX @@
80
#ifndef QEMU_MAIN_H
81
#define QEMU_MAIN_H
82
83
-int qemu_default_main(void);
84
-extern int (*qemu_main)(void);
85
+extern qemu_main_fn qemu_main;
86
87
#endif /* QEMU_MAIN_H */
88
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
89
index XXXXXXX..XXXXXXX 100644
90
--- a/include/qemu/typedefs.h
91
+++ b/include/qemu/typedefs.h
92
@@ -XXX,XX +XXX,XX @@ typedef struct IRQState *qemu_irq;
93
* Function types
94
*/
95
typedef void (*qemu_irq_handler)(void *opaque, int n, int level);
96
+typedef int (*qemu_main_fn)(void);
97
98
#endif /* QEMU_TYPEDEFS_H */
99
diff --git a/system/main.c b/system/main.c
100
index XXXXXXX..XXXXXXX 100644
101
--- a/system/main.c
102
+++ b/system/main.c
103
@@ -XXX,XX +XXX,XX @@
104
105
#include "qemu/osdep.h"
106
#include "qemu-main.h"
107
+#include "qemu/main-loop.h"
108
#include "sysemu/sysemu.h"
109
110
-#ifdef CONFIG_SDL
111
-#include <SDL.h>
112
+#ifdef CONFIG_DARWIN
113
+#include <CoreFoundation/CoreFoundation.h>
114
#endif
115
116
-int qemu_default_main(void)
117
+static int qemu_default_main(void)
118
{
119
int status;
120
121
@@ -XXX,XX +XXX,XX @@ int qemu_default_main(void)
122
return status;
123
}
124
125
-int (*qemu_main)(void) = qemu_default_main;
126
+/*
127
+ * Various macOS system libraries, including the Cocoa UI and anything using
128
+ * libdispatch, such as ParavirtualizedGraphics.framework, requires that the
129
+ * main runloop, on the main (initial) thread be running or at least regularly
130
+ * polled for events. A special mode is therefore supported, where the QEMU
131
+ * main loop runs on a separate thread and the main thread handles the
132
+ * CF/Cocoa runloop.
133
+ */
134
+
135
+static void *call_qemu_default_main(void *opaque)
136
+{
137
+ int status;
138
+
139
+ bql_lock();
140
+ status = qemu_default_main();
141
+ bql_unlock();
142
+
143
+ exit(status);
144
+}
145
+
146
+#ifdef CONFIG_DARWIN
147
+static int os_darwin_cfrunloop_main(void)
148
+{
149
+ CFRunLoopRun();
150
+ abort();
151
+}
152
+
153
+qemu_main_fn qemu_main = os_darwin_cfrunloop_main;
154
+#else
155
+qemu_main_fn qemu_main;
156
+#endif
157
158
int main(int argc, char **argv)
159
{
160
+ QemuThread main_loop_thread;
161
+
162
qemu_init(argc, argv);
163
- return qemu_main();
164
+ if (qemu_main) {
165
+ qemu_thread_create(&main_loop_thread, "qemu_main",
166
+ call_qemu_default_main, NULL, QEMU_THREAD_DETACHED);
167
+ bql_unlock();
168
+ return qemu_main();
169
+ } else {
170
+ qemu_default_main();
171
+ }
172
}
173
diff --git a/ui/cocoa.m b/ui/cocoa.m
174
index XXXXXXX..XXXXXXX 100644
175
--- a/ui/cocoa.m
176
+++ b/ui/cocoa.m
177
@@ -XXX,XX +XXX,XX @@
178
int height;
179
} QEMUScreen;
180
181
+@class QemuCocoaPasteboardTypeOwner;
182
+
183
static void cocoa_update(DisplayChangeListener *dcl,
184
int x, int y, int w, int h);
185
186
@@ -XXX,XX +XXX,XX @@ static void cocoa_switch(DisplayChangeListener *dcl,
187
static NSInteger cbchangecount = -1;
188
static QemuClipboardInfo *cbinfo;
189
static QemuEvent cbevent;
190
+static QemuCocoaPasteboardTypeOwner *cbowner;
191
192
// Utility functions to run specified code block with the BQL held
193
typedef void (^CodeBlock)(void);
194
@@ -XXX,XX +XXX,XX @@ - (void) dealloc
195
{
196
COCOA_DEBUG("QemuCocoaAppController: dealloc\n");
197
198
- if (cocoaView)
199
- [cocoaView release];
200
+ [cocoaView release];
201
+ [cbowner release];
202
+ cbowner = nil;
203
+
204
[super dealloc];
205
}
206
207
@@ -XXX,XX +XXX,XX @@ - (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSPasteboardType)t
208
209
@end
210
211
-static QemuCocoaPasteboardTypeOwner *cbowner;
212
-
213
static void cocoa_clipboard_notify(Notifier *notifier, void *data);
214
static void cocoa_clipboard_request(QemuClipboardInfo *info,
215
QemuClipboardType type);
216
@@ -XXX,XX +XXX,XX @@ static void cocoa_clipboard_request(QemuClipboardInfo *info,
217
}
218
}
219
220
-/*
221
- * The startup process for the OSX/Cocoa UI is complicated, because
222
- * OSX insists that the UI runs on the initial main thread, and so we
223
- * need to start a second thread which runs the qemu_default_main():
224
- * in main():
225
- * in cocoa_display_init():
226
- * assign cocoa_main to qemu_main
227
- * create application, menus, etc
228
- * in cocoa_main():
229
- * create qemu-main thread
230
- * enter OSX run loop
231
- */
232
-
233
-static void *call_qemu_main(void *opaque)
234
-{
235
- int status;
236
-
237
- COCOA_DEBUG("Second thread: calling qemu_default_main()\n");
238
- bql_lock();
239
- status = qemu_default_main();
240
- bql_unlock();
241
- COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n");
242
- [cbowner release];
243
- exit(status);
244
-}
245
-
246
static int cocoa_main(void)
247
{
248
- QemuThread thread;
249
-
250
- COCOA_DEBUG("Entered %s()\n", __func__);
251
-
252
- bql_unlock();
253
- qemu_thread_create(&thread, "qemu_main", call_qemu_main,
254
- NULL, QEMU_THREAD_DETACHED);
255
-
256
- // Start the main event loop
257
COCOA_DEBUG("Main thread: entering OSX run loop\n");
258
[NSApp run];
259
COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n");
260
@@ -XXX,XX +XXX,XX @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
261
262
COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
263
264
- qemu_main = cocoa_main;
265
-
266
// Pull this console process up to being a fully-fledged graphical
267
// app with a menubar and Dock icon
268
ProcessSerialNumber psn = { 0, kCurrentProcess };
269
@@ -XXX,XX +XXX,XX @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
270
qemu_clipboard_peer_register(&cbpeer);
271
272
[pool release];
273
+
274
+ /*
275
+ * The Cocoa UI will run the NSApplication runloop on the main thread
276
+ * rather than the default Core Foundation one.
277
+ */
278
+ qemu_main = cocoa_main;
279
}
280
281
static QemuDisplay qemu_display_cocoa = {
282
diff --git a/ui/gtk.c b/ui/gtk.c
283
index XXXXXXX..XXXXXXX 100644
284
--- a/ui/gtk.c
285
+++ b/ui/gtk.c
286
@@ -XXX,XX +XXX,XX @@
287
#include "qemu/cutils.h"
288
#include "qemu/error-report.h"
289
#include "qemu/main-loop.h"
290
+#include "qemu-main.h"
291
292
#include "ui/console.h"
293
#include "ui/gtk.h"
294
@@ -XXX,XX +XXX,XX @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
295
#ifdef CONFIG_GTK_CLIPBOARD
296
gd_clipboard_init(s);
297
#endif /* CONFIG_GTK_CLIPBOARD */
298
+
299
+ qemu_main = NULL;
300
}
301
302
static void early_gtk_display_init(DisplayOptions *opts)
303
diff --git a/ui/sdl2.c b/ui/sdl2.c
304
index XXXXXXX..XXXXXXX 100644
305
--- a/ui/sdl2.c
306
+++ b/ui/sdl2.c
307
@@ -XXX,XX +XXX,XX @@
308
#include "sysemu/sysemu.h"
309
#include "ui/win32-kbd-hook.h"
310
#include "qemu/log.h"
311
+#include "qemu-main.h"
312
313
static int sdl2_num_outputs;
314
static struct sdl2_console *sdl2_console;
315
@@ -XXX,XX +XXX,XX @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
316
}
317
318
atexit(sdl_cleanup);
319
+
320
+ /* SDL's event polling (in dpy_refresh) must happen on the main thread. */
321
+ qemu_main = NULL;
322
}
323
324
static QemuDisplay qemu_display_sdl2 = {
325
--
326
2.39.3 (Apple Git-145)
diff view generated by jsdifflib
Deleted patch
1
MacOS provides a framework (library) that allows any vmm to implement a
2
paravirtualized 3d graphics passthrough to the host metal stack called
3
ParavirtualizedGraphics.Framework (PVG). The library abstracts away
4
almost every aspect of the paravirtualized device model and only provides
5
and receives callbacks on MMIO access as well as to share memory address
6
space between the VM and PVG.
7
1
8
This patch implements a QEMU device that drives PVG for the VMApple
9
variant of it.
10
11
Signed-off-by: Alexander Graf <graf@amazon.com>
12
Co-authored-by: Alexander Graf <graf@amazon.com>
13
14
Subsequent changes:
15
16
* Cherry-pick/rebase conflict fixes, API use updates.
17
* Moved from hw/vmapple/ (useful outside that machine type)
18
* Overhaul of threading model, many thread safety improvements.
19
* Asynchronous rendering.
20
* Memory and object lifetime fixes.
21
* Refactoring to split generic and (vmapple) MMIO variant specific
22
code.
23
24
Implementation wise, most of the complexity lies in the differing threading
25
models of ParavirtualizedGraphics.framework, which uses libdispatch and
26
internal locks, versus QEMU, which heavily uses the BQL, especially during
27
memory-mapped device I/O. Great care has therefore been taken to prevent
28
deadlocks by never calling into PVG methods while holding the BQL, and
29
similarly never acquiring the BQL in a callback from PVG. Different strategies
30
have been used (libdispatch, blocking and non-blocking BHs, RCU, etc.)
31
depending on the specific requirements at each framework entry and exit point.
32
33
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
34
---
35
36
v2:
37
38
* Cherry-pick/rebase conflict fixes
39
* BQL function renaming
40
* Moved from hw/vmapple/ (useful outside that machine type)
41
* Code review comments: Switched to DEFINE_TYPES macro & little endian
42
MMIO.
43
* Removed some dead/superfluous code
44
* Mad set_mode thread & memory safe
45
* Added migration blocker due to lack of (de-)serialisation.
46
* Fixes to ObjC refcounting and autorelease pool usage.
47
* Fixed ObjC new/init misuse
48
* Switched to ObjC category extension for private property.
49
* Simplified task memory mapping and made it thread safe.
50
* Refactoring to split generic and vmapple MMIO variant specific
51
code.
52
* Switched to asynchronous MMIO writes on x86-64
53
* Rendering and graphics update are now done asynchronously
54
* Fixed cursor handling
55
* Coding convention fixes
56
* Removed software cursor compositing
57
58
v3:
59
60
* Rebased on latest upstream, fixed breakages including switching to Resettable methods.
61
* Squashed patches dealing with dGPUs, MMIO area size, and GPU picking.
62
* Allow re-entrant MMIO; this simplifies the code and solves the divergence
63
between x86-64 and arm64 variants.
64
65
v4:
66
67
* Renamed '-vmapple' device variant to '-mmio'
68
* MMIO device type now requires aarch64 host and guest
69
* Complete overhaul of the glue code for making Qemu's and
70
ParavirtualizedGraphics.framework's threading and synchronisation models
71
work together. Calls into PVG are from dispatch queues while the
72
BQL-holding initiating thread processes AIO context events; callbacks from
73
PVG are scheduled as BHs on the BQL/main AIO context, awaiting completion
74
where necessary.
75
* Guest frame rendering state is covered by the BQL, with only the PVG calls
76
outside the lock, and serialised on the named render_queue.
77
* Simplified logic for dropping frames in-flight during mode changes, fixed
78
bug in pending frames logic.
79
* Addressed smaller code review notes such as: function naming, object type
80
declarations, type names/declarations/casts, code formatting, #include
81
order, over-cautious ObjC retain/release, what goes in init vs realize,
82
etc.
83
84
v5:
85
86
* Smaller non-functional fixes in response to review comments, such as using
87
NULL for the AIO_WAIT_WHILE context argument, type name formatting,
88
deleting leftover debug code, logging improvements, state struct field
89
order and documentation improvements, etc.
90
* Instead of a single condition variable for all synchronous BH job types,
91
there is now one for each callback block. This reduces the number
92
of threads being awoken unnecessarily to near zero.
93
* MMIO device variant: Unified the BH job for raising interrupts.
94
* Use DMA APIs for PVG framework's guest memory read requests.
95
* Thread safety improvements: ensure mutable AppleGFXState fields are not
96
accessed outside the appropriate lock. Added dedicated mutex for the task
97
list.
98
* Retain references to MemoryRegions for which there exist mappings in each
99
PGTask, and for IOSurface mappings.
100
101
v6:
102
103
* Switched PGTask_s's' mapped_regions from GPtrArray to GArray
104
* Allow DisplaySurface to manage its own vram now that texture -> vram copy
105
occurs under BQL.
106
* Memory mapping operations now use RCU_READ_LOCK_GUARD() where possible
107
instead of a heavy-weight BH job to acquire the BQL.
108
* Changed PVG cursor and mode setting callbacks to kick off BHs instead of
109
libdispatch tasks which then locked the BQL explicitly.
110
* The single remaining callback which must wait for a BH to complete now
111
creates an ephemeral QemuSemaphore to await completion.
112
* Re-removed tracking of mapped surface manager memory regions. Just look up
113
and ref/unref the memory regions in the map/unmap callbacks.
114
* Re-ordered functions in apple-gfx.m to group them by area of functionality.
115
* Improved comments and tweaked some names.
116
117
v7:
118
119
* Use g_ptr_array_find() helper function
120
* Error handling coding style tweak
121
122
v8:
123
124
* Renamed apple_gfx_host_address_for_gpa_range() to
125
apple_gfx_host_ptr_for_gpa_range(), and made it return a void* instead of
126
uintptr_t. Fixed up callers and related code.
127
* Some adjustments to types used.
128
* Variable naming tweaks for better clarity.
129
* Fixed leak in unlikely realize error case.
130
* Fixed typo in unmap call.
131
* Don't bother with dummy argument for g_ptr_array_find(), NULL works too.
132
133
v9:
134
135
* Pass device pointer to graphic_console_init().
136
* Slightly re-ordered initialisation code.
137
* Simplified error handling during realize().
138
* Simplified code without functional changes, adjusted code & comment
139
formatting.
140
141
hw/display/Kconfig | 9 +
142
hw/display/apple-gfx-mmio.m | 281 +++++++++++++
143
hw/display/apple-gfx.h | 65 +++
144
hw/display/apple-gfx.m | 764 ++++++++++++++++++++++++++++++++++++
145
hw/display/meson.build | 4 +
146
hw/display/trace-events | 28 ++
147
meson.build | 4 +
148
7 files changed, 1155 insertions(+)
149
create mode 100644 hw/display/apple-gfx-mmio.m
150
create mode 100644 hw/display/apple-gfx.h
151
create mode 100644 hw/display/apple-gfx.m
152
153
diff --git a/hw/display/Kconfig b/hw/display/Kconfig
154
index XXXXXXX..XXXXXXX 100644
155
--- a/hw/display/Kconfig
156
+++ b/hw/display/Kconfig
157
@@ -XXX,XX +XXX,XX @@ config XLNX_DISPLAYPORT
158
159
config DM163
160
bool
161
+
162
+config MAC_PVG
163
+ bool
164
+ default y
165
+
166
+config MAC_PVG_MMIO
167
+ bool
168
+ depends on MAC_PVG && AARCH64
169
+
170
diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m
171
new file mode 100644
172
index XXXXXXX..XXXXXXX
173
--- /dev/null
174
+++ b/hw/display/apple-gfx-mmio.m
175
@@ -XXX,XX +XXX,XX @@
176
+/*
177
+ * QEMU Apple ParavirtualizedGraphics.framework device, MMIO (arm64) variant
178
+ *
179
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
180
+ *
181
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
182
+ * See the COPYING file in the top-level directory.
183
+ *
184
+ * SPDX-License-Identifier: GPL-2.0-or-later
185
+ *
186
+ * ParavirtualizedGraphics.framework is a set of libraries that macOS provides
187
+ * which implements 3d graphics passthrough to the host as well as a
188
+ * proprietary guest communication channel to drive it. This device model
189
+ * implements support to drive that library from within QEMU as an MMIO-based
190
+ * system device for macOS on arm64 VMs.
191
+ */
192
+
193
+#include "qemu/osdep.h"
194
+#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
195
+#include "apple-gfx.h"
196
+#include "monitor/monitor.h"
197
+#include "hw/sysbus.h"
198
+#include "hw/irq.h"
199
+#include "trace.h"
200
+#include "qemu/log.h"
201
+
202
+OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXMMIOState, APPLE_GFX_MMIO)
203
+
204
+/*
205
+ * ParavirtualizedGraphics.Framework only ships header files for the PCI
206
+ * variant which does not include IOSFC descriptors and host devices. We add
207
+ * their definitions here so that we can also work with the ARM version.
208
+ */
209
+typedef bool(^IOSFCRaiseInterrupt)(uint32_t vector);
210
+typedef bool(^IOSFCUnmapMemory)(
211
+ void *, void *, void *, void *, void *, void *);
212
+typedef bool(^IOSFCMapMemory)(
213
+ uint64_t phys, uint64_t len, bool ro, void **va, void *, void *);
214
+
215
+@interface PGDeviceDescriptor (IOSurfaceMapper)
216
+@property (readwrite, nonatomic) bool usingIOSurfaceMapper;
217
+@end
218
+
219
+@interface PGIOSurfaceHostDeviceDescriptor : NSObject
220
+-(PGIOSurfaceHostDeviceDescriptor *)init;
221
+@property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory;
222
+@property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory;
223
+@property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt;
224
+@end
225
+
226
+@interface PGIOSurfaceHostDevice : NSObject
227
+-(instancetype)initWithDescriptor:(PGIOSurfaceHostDeviceDescriptor *)desc;
228
+-(uint32_t)mmioReadAtOffset:(size_t)offset;
229
+-(void)mmioWriteAtOffset:(size_t)offset value:(uint32_t)value;
230
+@end
231
+
232
+struct AppleGFXMapSurfaceMemoryJob;
233
+struct AppleGFXMMIOState {
234
+ SysBusDevice parent_obj;
235
+
236
+ AppleGFXState common;
237
+
238
+ qemu_irq irq_gfx;
239
+ qemu_irq irq_iosfc;
240
+ MemoryRegion iomem_iosfc;
241
+ PGIOSurfaceHostDevice *pgiosfc;
242
+};
243
+
244
+typedef struct AppleGFXMMIOJob {
245
+ AppleGFXMMIOState *state;
246
+ uint64_t offset;
247
+ uint64_t value;
248
+ bool completed;
249
+} AppleGFXMMIOJob;
250
+
251
+static void iosfc_do_read(void *opaque)
252
+{
253
+ AppleGFXMMIOJob *job = opaque;
254
+ job->value = [job->state->pgiosfc mmioReadAtOffset:job->offset];
255
+ qatomic_set(&job->completed, true);
256
+ aio_wait_kick();
257
+}
258
+
259
+static uint64_t iosfc_read(void *opaque, hwaddr offset, unsigned size)
260
+{
261
+ AppleGFXMMIOJob job = {
262
+ .state = opaque,
263
+ .offset = offset,
264
+ .completed = false,
265
+ };
266
+ dispatch_queue_t queue =
267
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
268
+
269
+ dispatch_async_f(queue, &job, iosfc_do_read);
270
+ AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
271
+
272
+ trace_apple_gfx_mmio_iosfc_read(offset, job.value);
273
+ return job.value;
274
+}
275
+
276
+static void iosfc_do_write(void *opaque)
277
+{
278
+ AppleGFXMMIOJob *job = opaque;
279
+ [job->state->pgiosfc mmioWriteAtOffset:job->offset value:job->value];
280
+ qatomic_set(&job->completed, true);
281
+ aio_wait_kick();
282
+}
283
+
284
+static void iosfc_write(void *opaque, hwaddr offset, uint64_t val,
285
+ unsigned size)
286
+{
287
+ AppleGFXMMIOJob job = {
288
+ .state = opaque,
289
+ .offset = offset,
290
+ .value = val,
291
+ .completed = false,
292
+ };
293
+ dispatch_queue_t queue =
294
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
295
+
296
+ dispatch_async_f(queue, &job, iosfc_do_write);
297
+ AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
298
+
299
+ trace_apple_gfx_mmio_iosfc_write(offset, val);
300
+}
301
+
302
+static const MemoryRegionOps apple_iosfc_ops = {
303
+ .read = iosfc_read,
304
+ .write = iosfc_write,
305
+ .endianness = DEVICE_LITTLE_ENDIAN,
306
+ .valid = {
307
+ .min_access_size = 4,
308
+ .max_access_size = 8,
309
+ },
310
+ .impl = {
311
+ .min_access_size = 4,
312
+ .max_access_size = 8,
313
+ },
314
+};
315
+
316
+static void raise_irq_bh(void *opaque)
317
+{
318
+ qemu_irq *irq = opaque;
319
+
320
+ qemu_irq_pulse(*irq);
321
+}
322
+
323
+static void *apple_gfx_mmio_map_surface_memory(uint64_t guest_physical_address,
324
+ uint64_t length, bool read_only)
325
+{
326
+ void *mem;
327
+ MemoryRegion *region = NULL;
328
+
329
+ RCU_READ_LOCK_GUARD();
330
+ mem = apple_gfx_host_ptr_for_gpa_range(guest_physical_address,
331
+ length, read_only, &region);
332
+ if (mem) {
333
+ memory_region_ref(region);
334
+ }
335
+ return mem;
336
+}
337
+
338
+static bool apple_gfx_mmio_unmap_surface_memory(void *ptr)
339
+{
340
+ MemoryRegion *region;
341
+ ram_addr_t offset = 0;
342
+
343
+ RCU_READ_LOCK_GUARD();
344
+ region = memory_region_from_host(ptr, &offset);
345
+ if (!region) {
346
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: memory at %p to be unmapped not "
347
+ "found.\n",
348
+ __func__, ptr);
349
+ return false;
350
+ }
351
+
352
+ trace_apple_gfx_iosfc_unmap_memory_region(ptr, region);
353
+ memory_region_unref(region);
354
+ return true;
355
+}
356
+
357
+static PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device(
358
+ AppleGFXMMIOState *s)
359
+{
360
+ PGIOSurfaceHostDeviceDescriptor *iosfc_desc =
361
+ [PGIOSurfaceHostDeviceDescriptor new];
362
+ PGIOSurfaceHostDevice *iosfc_host_dev = nil;
363
+
364
+ iosfc_desc.mapMemory =
365
+ ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
366
+ *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
367
+
368
+ trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
369
+
370
+ return *va != NULL;
371
+ };
372
+
373
+ iosfc_desc.unmapMemory =
374
+ ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
375
+ return apple_gfx_mmio_unmap_surface_memory(va);
376
+ };
377
+
378
+ iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) {
379
+ trace_apple_gfx_iosfc_raise_irq(vector);
380
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
381
+ raise_irq_bh, &s->irq_iosfc);
382
+ return true;
383
+ };
384
+
385
+ iosfc_host_dev =
386
+ [[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc];
387
+ [iosfc_desc release];
388
+ return iosfc_host_dev;
389
+}
390
+
391
+static void apple_gfx_mmio_realize(DeviceState *dev, Error **errp)
392
+{
393
+ @autoreleasepool {
394
+ AppleGFXMMIOState *s = APPLE_GFX_MMIO(dev);
395
+ PGDeviceDescriptor *desc = [PGDeviceDescriptor new];
396
+
397
+ desc.raiseInterrupt = ^(uint32_t vector) {
398
+ trace_apple_gfx_raise_irq(vector);
399
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
400
+ raise_irq_bh, &s->irq_gfx);
401
+ };
402
+
403
+ desc.usingIOSurfaceMapper = true;
404
+ s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
405
+
406
+ if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) {
407
+ [s->pgiosfc release];
408
+ s->pgiosfc = nil;
409
+ }
410
+
411
+ [desc release];
412
+ desc = nil;
413
+ }
414
+}
415
+
416
+static void apple_gfx_mmio_init(Object *obj)
417
+{
418
+ AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj);
419
+
420
+ apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_MMIO);
421
+
422
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->common.iomem_gfx);
423
+ memory_region_init_io(&s->iomem_iosfc, obj, &apple_iosfc_ops, s,
424
+ TYPE_APPLE_GFX_MMIO, 0x10000);
425
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem_iosfc);
426
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_gfx);
427
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_iosfc);
428
+}
429
+
430
+static void apple_gfx_mmio_reset(Object *obj, ResetType type)
431
+{
432
+ AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj);
433
+ [s->common.pgdev reset];
434
+}
435
+
436
+
437
+static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data)
438
+{
439
+ DeviceClass *dc = DEVICE_CLASS(klass);
440
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
441
+
442
+ rc->phases.hold = apple_gfx_mmio_reset;
443
+ dc->hotpluggable = false;
444
+ dc->realize = apple_gfx_mmio_realize;
445
+}
446
+
447
+static TypeInfo apple_gfx_mmio_types[] = {
448
+ {
449
+ .name = TYPE_APPLE_GFX_MMIO,
450
+ .parent = TYPE_SYS_BUS_DEVICE,
451
+ .instance_size = sizeof(AppleGFXMMIOState),
452
+ .class_init = apple_gfx_mmio_class_init,
453
+ .instance_init = apple_gfx_mmio_init,
454
+ }
455
+};
456
+DEFINE_TYPES(apple_gfx_mmio_types)
457
diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h
458
new file mode 100644
459
index XXXXXXX..XXXXXXX
460
--- /dev/null
461
+++ b/hw/display/apple-gfx.h
462
@@ -XXX,XX +XXX,XX @@
463
+/*
464
+ * Data structures and functions shared between variants of the macOS
465
+ * ParavirtualizedGraphics.framework based apple-gfx display adapter.
466
+ *
467
+ * SPDX-License-Identifier: GPL-2.0-or-later
468
+ */
469
+
470
+#ifndef QEMU_APPLE_GFX_H
471
+#define QEMU_APPLE_GFX_H
472
+
473
+#define TYPE_APPLE_GFX_MMIO "apple-gfx-mmio"
474
+#define TYPE_APPLE_GFX_PCI "apple-gfx-pci"
475
+
476
+#include "qemu/osdep.h"
477
+#include <dispatch/dispatch.h>
478
+#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
479
+#include "qemu/typedefs.h"
480
+#include "exec/memory.h"
481
+#include "ui/surface.h"
482
+
483
+@class PGDeviceDescriptor;
484
+@protocol PGDevice;
485
+@protocol PGDisplay;
486
+@protocol MTLDevice;
487
+@protocol MTLTexture;
488
+@protocol MTLCommandQueue;
489
+
490
+typedef QTAILQ_HEAD(, PGTask_s) PGTaskList;
491
+
492
+typedef struct AppleGFXState {
493
+ /* Initialised on init/realize() */
494
+ MemoryRegion iomem_gfx;
495
+ id<PGDevice> pgdev;
496
+ id<PGDisplay> pgdisp;
497
+ QemuConsole *con;
498
+ id<MTLDevice> mtl;
499
+ id<MTLCommandQueue> mtl_queue;
500
+ dispatch_queue_t render_queue;
501
+
502
+ /* List `tasks` is protected by task_mutex */
503
+ QemuMutex task_mutex;
504
+ PGTaskList tasks;
505
+
506
+ /* Mutable state (BQL protected) */
507
+ QEMUCursor *cursor;
508
+ DisplaySurface *surface;
509
+ id<MTLTexture> texture;
510
+ int8_t pending_frames; /* # guest frames in the rendering pipeline */
511
+ bool gfx_update_requested; /* QEMU display system wants a new frame */
512
+ bool new_frame_ready; /* Guest has rendered a frame, ready to be used */
513
+ bool using_managed_texture_storage;
514
+
515
+ /* Mutable state (atomic) */
516
+ bool cursor_show;
517
+} AppleGFXState;
518
+
519
+void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name);
520
+bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
521
+ PGDeviceDescriptor *desc, Error **errp);
522
+void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
523
+ uint64_t length, bool read_only,
524
+ MemoryRegion **mapping_in_region);
525
+
526
+#endif
527
+
528
diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m
529
new file mode 100644
530
index XXXXXXX..XXXXXXX
531
--- /dev/null
532
+++ b/hw/display/apple-gfx.m
533
@@ -XXX,XX +XXX,XX @@
534
+/*
535
+ * QEMU Apple ParavirtualizedGraphics.framework device
536
+ *
537
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
538
+ *
539
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
540
+ * See the COPYING file in the top-level directory.
541
+ *
542
+ * SPDX-License-Identifier: GPL-2.0-or-later
543
+ *
544
+ * ParavirtualizedGraphics.framework is a set of libraries that macOS provides
545
+ * which implements 3d graphics passthrough to the host as well as a
546
+ * proprietary guest communication channel to drive it. This device model
547
+ * implements support to drive that library from within QEMU.
548
+ */
549
+
550
+#include "qemu/osdep.h"
551
+#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
552
+#include <mach/mach_vm.h>
553
+#include "apple-gfx.h"
554
+#include "trace.h"
555
+#include "qemu-main.h"
556
+#include "exec/address-spaces.h"
557
+#include "migration/blocker.h"
558
+#include "monitor/monitor.h"
559
+#include "qemu/main-loop.h"
560
+#include "qemu/cutils.h"
561
+#include "qemu/log.h"
562
+#include "qapi/visitor.h"
563
+#include "qapi/error.h"
564
+#include "sysemu/dma.h"
565
+#include "ui/console.h"
566
+
567
+static const PGDisplayCoord_t apple_gfx_modes[] = {
568
+ { .x = 1440, .y = 1080 },
569
+ { .x = 1280, .y = 1024 },
570
+};
571
+
572
+/* ------ PGTask and task operations: new/destroy/map/unmap ------ */
573
+
574
+/*
575
+ * This implements the type declared in <ParavirtualizedGraphics/PGDevice.h>
576
+ * which is opaque from the framework's point of view. It is used in callbacks
577
+ * in the form of its typedef PGTask_t, which also already exists in the
578
+ * framework headers.
579
+ *
580
+ * A "task" in PVG terminology represents a host-virtual contiguous address
581
+ * range which is reserved in a large chunk on task creation. The mapMemory
582
+ * callback then requests ranges of guest system memory (identified by their
583
+ * GPA) to be mapped into subranges of this reserved address space.
584
+ * This type of operation isn't well-supported by QEMU's memory subsystem,
585
+ * but it is fortunately trivial to achieve with Darwin's mach_vm_remap() call,
586
+ * which allows us to refer to the same backing memory via multiple virtual
587
+ * address ranges. The Mach VM APIs are therefore used throughout for managing
588
+ * task memory.
589
+ */
590
+struct PGTask_s {
591
+ QTAILQ_ENTRY(PGTask_s) node;
592
+ AppleGFXState *s;
593
+ mach_vm_address_t address;
594
+ uint64_t len;
595
+ /*
596
+ * All unique MemoryRegions for which a mapping has been created in in this
597
+ * task, and on which we have thus called memory_region_ref(). There are
598
+ * usually very few regions of system RAM in total, so we expect this array
599
+ * to be very short. Therefore, no need for sorting or fancy search
600
+ * algorithms, linear search will do.
601
+ * Protected by AppleGFXState's task_mutex.
602
+ */
603
+ GPtrArray *mapped_regions;
604
+};
605
+
606
+static Error *apple_gfx_mig_blocker;
607
+
608
+static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len)
609
+{
610
+ mach_vm_address_t task_mem;
611
+ PGTask_t *task;
612
+ kern_return_t r;
613
+
614
+ r = mach_vm_allocate(mach_task_self(), &task_mem, len, VM_FLAGS_ANYWHERE);
615
+ if (r != KERN_SUCCESS) {
616
+ return NULL;
617
+ }
618
+
619
+ task = g_new0(PGTask_t, 1);
620
+ task->s = s;
621
+ task->address = task_mem;
622
+ task->len = len;
623
+ task->mapped_regions = g_ptr_array_sized_new(2 /* Usually enough */);
624
+
625
+ QEMU_LOCK_GUARD(&s->task_mutex);
626
+ QTAILQ_INSERT_TAIL(&s->tasks, task, node);
627
+
628
+ return task;
629
+}
630
+
631
+static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
632
+{
633
+ GPtrArray *regions = task->mapped_regions;
634
+ MemoryRegion *region;
635
+ size_t i;
636
+
637
+ for (i = 0; i < regions->len; ++i) {
638
+ region = g_ptr_array_index(regions, i);
639
+ memory_region_unref(region);
640
+ }
641
+ g_ptr_array_unref(regions);
642
+
643
+ mach_vm_deallocate(mach_task_self(), task->address, task->len);
644
+
645
+ QEMU_LOCK_GUARD(&s->task_mutex);
646
+ QTAILQ_REMOVE(&s->tasks, task, node);
647
+ g_free(task);
648
+}
649
+
650
+void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
651
+ uint64_t length, bool read_only,
652
+ MemoryRegion **mapping_in_region)
653
+{
654
+ MemoryRegion *ram_region;
655
+ char *host_ptr;
656
+ hwaddr ram_region_offset = 0;
657
+ hwaddr ram_region_length = length;
658
+
659
+ ram_region = address_space_translate(&address_space_memory,
660
+ guest_physical,
661
+ &ram_region_offset,
662
+ &ram_region_length, !read_only,
663
+ MEMTXATTRS_UNSPECIFIED);
664
+
665
+ if (!ram_region || ram_region_length < length ||
666
+ !memory_access_is_direct(ram_region, !read_only)) {
667
+ return NULL;
668
+ }
669
+
670
+ host_ptr = memory_region_get_ram_ptr(ram_region);
671
+ if (!host_ptr) {
672
+ return NULL;
673
+ }
674
+ host_ptr += ram_region_offset;
675
+ *mapping_in_region = ram_region;
676
+ return host_ptr;
677
+}
678
+
679
+static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
680
+ uint64_t virtual_offset,
681
+ PGPhysicalMemoryRange_t *ranges,
682
+ uint32_t range_count, bool read_only)
683
+{
684
+ kern_return_t r;
685
+ void *source_ptr;
686
+ mach_vm_address_t target;
687
+ vm_prot_t cur_protection, max_protection;
688
+ bool success = true;
689
+ MemoryRegion *region;
690
+
691
+ RCU_READ_LOCK_GUARD();
692
+ QEMU_LOCK_GUARD(&s->task_mutex);
693
+
694
+ trace_apple_gfx_map_memory(task, range_count, virtual_offset, read_only);
695
+ for (int i = 0; i < range_count; i++) {
696
+ PGPhysicalMemoryRange_t *range = &ranges[i];
697
+
698
+ target = task->address + virtual_offset;
699
+ virtual_offset += range->physicalLength;
700
+
701
+ trace_apple_gfx_map_memory_range(i, range->physicalAddress,
702
+ range->physicalLength);
703
+
704
+ region = NULL;
705
+ source_ptr = apple_gfx_host_ptr_for_gpa_range(range->physicalAddress,
706
+ range->physicalLength,
707
+ read_only, &region);
708
+ if (!source_ptr) {
709
+ success = false;
710
+ continue;
711
+ }
712
+
713
+ if (!g_ptr_array_find(task->mapped_regions, region, NULL)) {
714
+ g_ptr_array_add(task->mapped_regions, region);
715
+ memory_region_ref(region);
716
+ }
717
+
718
+ cur_protection = 0;
719
+ max_protection = 0;
720
+ /* Map guest RAM at range->physicalAddress into PG task memory range */
721
+ r = mach_vm_remap(mach_task_self(),
722
+ &target, range->physicalLength, vm_page_size - 1,
723
+ VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
724
+ mach_task_self(), (mach_vm_address_t)source_ptr,
725
+ false /* shared mapping, no copy */,
726
+ &cur_protection, &max_protection,
727
+ VM_INHERIT_COPY);
728
+ trace_apple_gfx_remap(r, source_ptr, target);
729
+ g_assert(r == KERN_SUCCESS);
730
+ }
731
+
732
+ return success;
733
+}
734
+
735
+static void apple_gfx_task_unmap_memory(AppleGFXState *s, PGTask_t *task,
736
+ uint64_t virtual_offset, uint64_t length)
737
+{
738
+ kern_return_t r;
739
+ mach_vm_address_t range_address;
740
+
741
+ trace_apple_gfx_unmap_memory(task, virtual_offset, length);
742
+
743
+ /*
744
+ * Replace task memory range with fresh 0 pages, undoing the mapping
745
+ * from guest RAM.
746
+ */
747
+ range_address = task->address + virtual_offset;
748
+ r = mach_vm_allocate(mach_task_self(), &range_address, length,
749
+ VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE);
750
+ g_assert(r == KERN_SUCCESS);
751
+}
752
+
753
+/* ------ Rendering and frame management ------ */
754
+
755
+static void apple_gfx_render_frame_completed(AppleGFXState *s,
756
+ uint32_t width, uint32_t height);
757
+
758
+static void apple_gfx_render_new_frame_bql_unlock(AppleGFXState *s)
759
+{
760
+ BOOL r;
761
+ bool managed_texture = s->using_managed_texture_storage;
762
+ uint32_t width = surface_width(s->surface);
763
+ uint32_t height = surface_height(s->surface);
764
+ MTLRegion region = MTLRegionMake2D(0, 0, width, height);
765
+ id<MTLCommandBuffer> command_buffer = [s->mtl_queue commandBuffer];
766
+ id<MTLTexture> texture = s->texture;
767
+
768
+ assert(bql_locked());
769
+ [texture retain];
770
+
771
+ bql_unlock();
772
+
773
+ /*
774
+ * This is not safe to call from the BQL due to PVG-internal locks causing
775
+ * deadlocks.
776
+ */
777
+ r = [s->pgdisp encodeCurrentFrameToCommandBuffer:command_buffer
778
+ texture:texture
779
+ region:region];
780
+ if (!r) {
781
+ [texture release];
782
+ bql_lock();
783
+ --s->pending_frames;
784
+ bql_unlock();
785
+ qemu_log_mask(LOG_GUEST_ERROR,
786
+ "%s: encodeCurrentFrameToCommandBuffer:texture:region: "
787
+ "failed\n", __func__);
788
+ return;
789
+ }
790
+
791
+ if (managed_texture) {
792
+ /* "Managed" textures exist in both VRAM and RAM and must be synced. */
793
+ id<MTLBlitCommandEncoder> blit = [command_buffer blitCommandEncoder];
794
+ [blit synchronizeResource:texture];
795
+ [blit endEncoding];
796
+ }
797
+ [texture release];
798
+ [command_buffer addCompletedHandler:
799
+ ^(id<MTLCommandBuffer> cb)
800
+ {
801
+ dispatch_async(s->render_queue, ^{
802
+ apple_gfx_render_frame_completed(s, width, height);
803
+ });
804
+ }];
805
+ [command_buffer commit];
806
+}
807
+
808
+static void copy_mtl_texture_to_surface_mem(id<MTLTexture> texture, void *vram)
809
+{
810
+ /* TODO: Skip this entirely on a pure Metal or headless/guest-only
811
+ * rendering path, else use a blit command encoder? Needs careful
812
+ * (double?) buffering design. */
813
+ size_t width = texture.width, height = texture.height;
814
+ MTLRegion region = MTLRegionMake2D(0, 0, width, height);
815
+ [texture getBytes:vram
816
+ bytesPerRow:(width * 4)
817
+ bytesPerImage:(width * height * 4)
818
+ fromRegion:region
819
+ mipmapLevel:0
820
+ slice:0];
821
+}
822
+
823
+static void apple_gfx_render_frame_completed(AppleGFXState *s,
824
+ uint32_t width, uint32_t height)
825
+{
826
+ bql_lock();
827
+ --s->pending_frames;
828
+ assert(s->pending_frames >= 0);
829
+
830
+ /* Only update display if mode hasn't changed since we started rendering. */
831
+ if (width == surface_width(s->surface) &&
832
+ height == surface_height(s->surface)) {
833
+ copy_mtl_texture_to_surface_mem(s->texture, surface_data(s->surface));
834
+ if (s->gfx_update_requested) {
835
+ s->gfx_update_requested = false;
836
+ dpy_gfx_update_full(s->con);
837
+ graphic_hw_update_done(s->con);
838
+ s->new_frame_ready = false;
839
+ } else {
840
+ s->new_frame_ready = true;
841
+ }
842
+ }
843
+ if (s->pending_frames > 0) {
844
+ apple_gfx_render_new_frame_bql_unlock(s);
845
+ } else {
846
+ bql_unlock();
847
+ }
848
+}
849
+
850
+static void apple_gfx_fb_update_display(void *opaque)
851
+{
852
+ AppleGFXState *s = opaque;
853
+
854
+ assert(bql_locked());
855
+ if (s->new_frame_ready) {
856
+ dpy_gfx_update_full(s->con);
857
+ s->new_frame_ready = false;
858
+ graphic_hw_update_done(s->con);
859
+ } else if (s->pending_frames > 0) {
860
+ s->gfx_update_requested = true;
861
+ } else {
862
+ graphic_hw_update_done(s->con);
863
+ }
864
+}
865
+
866
+static const GraphicHwOps apple_gfx_fb_ops = {
867
+ .gfx_update = apple_gfx_fb_update_display,
868
+ .gfx_update_async = true,
869
+};
870
+
871
+/* ------ Mouse cursor and display mode setting ------ */
872
+
873
+static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height)
874
+{
875
+ MTLTextureDescriptor *textureDescriptor;
876
+
877
+ if (s->surface &&
878
+ width == surface_width(s->surface) &&
879
+ height == surface_height(s->surface)) {
880
+ return;
881
+ }
882
+
883
+ [s->texture release];
884
+
885
+ s->surface = qemu_create_displaysurface(width, height);
886
+
887
+ @autoreleasepool {
888
+ textureDescriptor =
889
+ [MTLTextureDescriptor
890
+ texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
891
+ width:width
892
+ height:height
893
+ mipmapped:NO];
894
+ textureDescriptor.usage = s->pgdisp.minimumTextureUsage;
895
+ s->texture = [s->mtl newTextureWithDescriptor:textureDescriptor];
896
+ }
897
+
898
+ s->using_managed_texture_storage =
899
+ (s->texture.storageMode == MTLStorageModeManaged);
900
+ dpy_gfx_replace_surface(s->con, s->surface);
901
+}
902
+
903
+static void update_cursor(AppleGFXState *s)
904
+{
905
+ assert(bql_locked());
906
+ dpy_mouse_set(s->con, s->pgdisp.cursorPosition.x,
907
+ s->pgdisp.cursorPosition.y, qatomic_read(&s->cursor_show));
908
+}
909
+
910
+static void update_cursor_bh(void *opaque)
911
+{
912
+ AppleGFXState *s = opaque;
913
+ update_cursor(s);
914
+}
915
+
916
+typedef struct AppleGFXSetCursorGlyphJob {
917
+ AppleGFXState *s;
918
+ NSBitmapImageRep *glyph;
919
+ PGDisplayCoord_t hotspot;
920
+} AppleGFXSetCursorGlyphJob;
921
+
922
+static void set_cursor_glyph(void *opaque)
923
+{
924
+ AppleGFXSetCursorGlyphJob *job = opaque;
925
+ AppleGFXState *s = job->s;
926
+ NSBitmapImageRep *glyph = job->glyph;
927
+ uint32_t bpp = glyph.bitsPerPixel;
928
+ size_t width = glyph.pixelsWide;
929
+ size_t height = glyph.pixelsHigh;
930
+ size_t padding_bytes_per_row = glyph.bytesPerRow - width * 4;
931
+ const uint8_t* px_data = glyph.bitmapData;
932
+
933
+ trace_apple_gfx_cursor_set(bpp, width, height);
934
+
935
+ if (s->cursor) {
936
+ cursor_unref(s->cursor);
937
+ s->cursor = NULL;
938
+ }
939
+
940
+ if (bpp == 32) { /* Shouldn't be anything else, but just to be safe...*/
941
+ s->cursor = cursor_alloc(width, height);
942
+ s->cursor->hot_x = job->hotspot.x;
943
+ s->cursor->hot_y = job->hotspot.y;
944
+
945
+ uint32_t *dest_px = s->cursor->data;
946
+
947
+ for (size_t y = 0; y < height; ++y) {
948
+ for (size_t x = 0; x < width; ++x) {
949
+ /*
950
+ * NSBitmapImageRep's red & blue channels are swapped
951
+ * compared to QEMUCursor's.
952
+ */
953
+ *dest_px =
954
+ (px_data[0] << 16u) |
955
+ (px_data[1] << 8u) |
956
+ (px_data[2] << 0u) |
957
+ (px_data[3] << 24u);
958
+ ++dest_px;
959
+ px_data += 4;
960
+ }
961
+ px_data += padding_bytes_per_row;
962
+ }
963
+ dpy_cursor_define(s->con, s->cursor);
964
+ update_cursor(s);
965
+ }
966
+ [glyph release];
967
+
968
+ g_free(job);
969
+}
970
+
971
+/* ------ DMA (device reading system memory) ------ */
972
+
973
+typedef struct AppleGFXReadMemoryJob {
974
+ QemuSemaphore sem;
975
+ hwaddr physical_address;
976
+ uint64_t length;
977
+ void *dst;
978
+ bool success;
979
+} AppleGFXReadMemoryJob;
980
+
981
+static void apple_gfx_do_read_memory(void *opaque)
982
+{
983
+ AppleGFXReadMemoryJob *job = opaque;
984
+ MemTxResult r;
985
+
986
+ r = dma_memory_read(&address_space_memory, job->physical_address,
987
+ job->dst, job->length, MEMTXATTRS_UNSPECIFIED);
988
+ job->success = r == MEMTX_OK;
989
+
990
+ qemu_sem_post(&job->sem);
991
+}
992
+
993
+static bool apple_gfx_read_memory(AppleGFXState *s, hwaddr physical_address,
994
+ uint64_t length, void *dst)
995
+{
996
+ AppleGFXReadMemoryJob job = {
997
+ .physical_address = physical_address, .length = length, .dst = dst
998
+ };
999
+
1000
+ trace_apple_gfx_read_memory(physical_address, length, dst);
1001
+
1002
+ /* Performing DMA requires BQL, so do it in a BH. */
1003
+ qemu_sem_init(&job.sem, 0);
1004
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
1005
+ apple_gfx_do_read_memory, &job);
1006
+ qemu_sem_wait(&job.sem);
1007
+ qemu_sem_destroy(&job.sem);
1008
+ return job.success;
1009
+}
1010
+
1011
+/* ------ Memory-mapped device I/O operations ------ */
1012
+
1013
+static dispatch_queue_t get_background_queue(void)
1014
+{
1015
+ return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1016
+}
1017
+
1018
+typedef struct AppleGFXIOJob {
1019
+ AppleGFXState *state;
1020
+ uint64_t offset;
1021
+ uint64_t value;
1022
+ bool completed;
1023
+} AppleGFXIOJob;
1024
+
1025
+static void apple_gfx_do_read(void *opaque)
1026
+{
1027
+ AppleGFXIOJob *job = opaque;
1028
+ job->value = [job->state->pgdev mmioReadAtOffset:job->offset];
1029
+ qatomic_set(&job->completed, true);
1030
+ aio_wait_kick();
1031
+}
1032
+
1033
+static uint64_t apple_gfx_read(void *opaque, hwaddr offset, unsigned size)
1034
+{
1035
+ AppleGFXIOJob job = {
1036
+ .state = opaque,
1037
+ .offset = offset,
1038
+ .completed = false,
1039
+ };
1040
+ dispatch_queue_t queue = get_background_queue();
1041
+
1042
+ dispatch_async_f(queue, &job, apple_gfx_do_read);
1043
+ AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
1044
+
1045
+ trace_apple_gfx_read(offset, job.value);
1046
+ return job.value;
1047
+}
1048
+
1049
+static void apple_gfx_do_write(void *opaque)
1050
+{
1051
+ AppleGFXIOJob *job = opaque;
1052
+ [job->state->pgdev mmioWriteAtOffset:job->offset value:job->value];
1053
+ qatomic_set(&job->completed, true);
1054
+ aio_wait_kick();
1055
+}
1056
+
1057
+static void apple_gfx_write(void *opaque, hwaddr offset, uint64_t val,
1058
+ unsigned size)
1059
+{
1060
+ /*
1061
+ * The methods mmioReadAtOffset: and especially mmioWriteAtOffset: can
1062
+ * trigger synchronous operations on other dispatch queues, which in turn
1063
+ * may call back out on one or more of the callback blocks. For this reason,
1064
+ * and as we are holding the BQL, we invoke the I/O methods on a pool
1065
+ * thread and handle AIO tasks while we wait. Any work in the callbacks
1066
+ * requiring the BQL will in turn schedule BHs which this thread will
1067
+ * process while waiting.
1068
+ */
1069
+ AppleGFXIOJob job = {
1070
+ .state = opaque,
1071
+ .offset = offset,
1072
+ .value = val,
1073
+ .completed = false,
1074
+ };
1075
+ dispatch_queue_t queue = get_background_queue();
1076
+
1077
+ dispatch_async_f(queue, &job, apple_gfx_do_write);
1078
+ AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
1079
+
1080
+ trace_apple_gfx_write(offset, val);
1081
+}
1082
+
1083
+static const MemoryRegionOps apple_gfx_ops = {
1084
+ .read = apple_gfx_read,
1085
+ .write = apple_gfx_write,
1086
+ .endianness = DEVICE_LITTLE_ENDIAN,
1087
+ .valid = {
1088
+ .min_access_size = 4,
1089
+ .max_access_size = 8,
1090
+ },
1091
+ .impl = {
1092
+ .min_access_size = 4,
1093
+ .max_access_size = 4,
1094
+ },
1095
+};
1096
+
1097
+static size_t apple_gfx_get_default_mmio_range_size(void)
1098
+{
1099
+ size_t mmio_range_size;
1100
+ @autoreleasepool {
1101
+ PGDeviceDescriptor *desc = [PGDeviceDescriptor new];
1102
+ mmio_range_size = desc.mmioLength;
1103
+ [desc release];
1104
+ }
1105
+ return mmio_range_size;
1106
+}
1107
+
1108
+/* ------ Initialisation and startup ------ */
1109
+
1110
+void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name)
1111
+{
1112
+ size_t mmio_range_size = apple_gfx_get_default_mmio_range_size();
1113
+
1114
+ trace_apple_gfx_common_init(obj_name, mmio_range_size);
1115
+ memory_region_init_io(&s->iomem_gfx, obj, &apple_gfx_ops, s, obj_name,
1116
+ mmio_range_size);
1117
+
1118
+ /* TODO: PVG framework supports serialising device state: integrate it! */
1119
+}
1120
+
1121
+static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s,
1122
+ PGDeviceDescriptor *desc)
1123
+{
1124
+ desc.createTask = ^(uint64_t vmSize, void * _Nullable * _Nonnull baseAddress) {
1125
+ PGTask_t *task = apple_gfx_new_task(s, vmSize);
1126
+ *baseAddress = (void *)task->address;
1127
+ trace_apple_gfx_create_task(vmSize, *baseAddress);
1128
+ return task;
1129
+ };
1130
+
1131
+ desc.destroyTask = ^(PGTask_t * _Nonnull task) {
1132
+ trace_apple_gfx_destroy_task(task, task->mapped_regions->len);
1133
+
1134
+ apple_gfx_destroy_task(s, task);
1135
+ };
1136
+
1137
+ desc.mapMemory = ^bool(PGTask_t * _Nonnull task, uint32_t range_count,
1138
+ uint64_t virtual_offset, bool read_only,
1139
+ PGPhysicalMemoryRange_t * _Nonnull ranges) {
1140
+ return apple_gfx_task_map_memory(s, task, virtual_offset,
1141
+ ranges, range_count, read_only);
1142
+ };
1143
+
1144
+ desc.unmapMemory = ^bool(PGTask_t * _Nonnull task, uint64_t virtual_offset,
1145
+ uint64_t length) {
1146
+ apple_gfx_task_unmap_memory(s, task, virtual_offset, length);
1147
+ return true;
1148
+ };
1149
+
1150
+ desc.readMemory = ^bool(uint64_t physical_address, uint64_t length,
1151
+ void * _Nonnull dst) {
1152
+ return apple_gfx_read_memory(s, physical_address, length, dst);
1153
+ };
1154
+}
1155
+
1156
+static PGDisplayDescriptor *apple_gfx_prepare_display_descriptor(AppleGFXState *s)
1157
+{
1158
+ PGDisplayDescriptor *disp_desc = [PGDisplayDescriptor new];
1159
+
1160
+ disp_desc.name = @"QEMU display";
1161
+ disp_desc.sizeInMillimeters = NSMakeSize(400., 300.); /* A 20" display */
1162
+ disp_desc.queue = dispatch_get_main_queue();
1163
+ disp_desc.newFrameEventHandler = ^(void) {
1164
+ trace_apple_gfx_new_frame();
1165
+ dispatch_async(s->render_queue, ^{
1166
+ /* Drop frames if we get too far ahead. */
1167
+ bql_lock();
1168
+ if (s->pending_frames >= 2) {
1169
+ bql_unlock();
1170
+ return;
1171
+ }
1172
+ ++s->pending_frames;
1173
+ if (s->pending_frames > 1) {
1174
+ bql_unlock();
1175
+ return;
1176
+ }
1177
+ @autoreleasepool {
1178
+ apple_gfx_render_new_frame_bql_unlock(s);
1179
+ }
1180
+ });
1181
+ };
1182
+ disp_desc.modeChangeHandler = ^(PGDisplayCoord_t sizeInPixels,
1183
+ OSType pixelFormat) {
1184
+ trace_apple_gfx_mode_change(sizeInPixels.x, sizeInPixels.y);
1185
+
1186
+ BQL_LOCK_GUARD();
1187
+ set_mode(s, sizeInPixels.x, sizeInPixels.y);
1188
+ };
1189
+ disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
1190
+ PGDisplayCoord_t hotspot) {
1191
+ AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
1192
+ job->s = s;
1193
+ job->glyph = glyph;
1194
+ job->hotspot = hotspot;
1195
+ [glyph retain];
1196
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
1197
+ set_cursor_glyph, job);
1198
+ };
1199
+ disp_desc.cursorShowHandler = ^(BOOL show) {
1200
+ trace_apple_gfx_cursor_show(show);
1201
+ qatomic_set(&s->cursor_show, show);
1202
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
1203
+ update_cursor_bh, s);
1204
+ };
1205
+ disp_desc.cursorMoveHandler = ^(void) {
1206
+ trace_apple_gfx_cursor_move();
1207
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
1208
+ update_cursor_bh, s);
1209
+ };
1210
+
1211
+ return disp_desc;
1212
+}
1213
+
1214
+static NSArray<PGDisplayMode*>* apple_gfx_prepare_display_mode_array(void)
1215
+{
1216
+ PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)];
1217
+ NSArray<PGDisplayMode*>* mode_array = nil;
1218
+ int i;
1219
+
1220
+ for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) {
1221
+ modes[i] =
1222
+ [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.];
1223
+ }
1224
+
1225
+ mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)];
1226
+
1227
+ for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) {
1228
+ [modes[i] release];
1229
+ modes[i] = nil;
1230
+ }
1231
+
1232
+ return mode_array;
1233
+}
1234
+
1235
+static id<MTLDevice> copy_suitable_metal_device(void)
1236
+{
1237
+ id<MTLDevice> dev = nil;
1238
+ NSArray<id<MTLDevice>> *devs = MTLCopyAllDevices();
1239
+
1240
+ /* Prefer a unified memory GPU. Failing that, pick a non-removable GPU. */
1241
+ for (size_t i = 0; i < devs.count; ++i) {
1242
+ if (devs[i].hasUnifiedMemory) {
1243
+ dev = devs[i];
1244
+ break;
1245
+ }
1246
+ if (!devs[i].removable) {
1247
+ dev = devs[i];
1248
+ }
1249
+ }
1250
+
1251
+ if (dev != nil) {
1252
+ [dev retain];
1253
+ } else {
1254
+ dev = MTLCreateSystemDefaultDevice();
1255
+ }
1256
+ [devs release];
1257
+
1258
+ return dev;
1259
+}
1260
+
1261
+bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
1262
+ PGDeviceDescriptor *desc, Error **errp)
1263
+{
1264
+ PGDisplayDescriptor *disp_desc = nil;
1265
+
1266
+ if (apple_gfx_mig_blocker == NULL) {
1267
+ error_setg(&apple_gfx_mig_blocker,
1268
+ "Migration state blocked by apple-gfx display device");
1269
+ if (migrate_add_blocker(&apple_gfx_mig_blocker, errp) < 0) {
1270
+ return false;
1271
+ }
1272
+ }
1273
+
1274
+ qemu_mutex_init(&s->task_mutex);
1275
+ QTAILQ_INIT(&s->tasks);
1276
+ s->render_queue = dispatch_queue_create("apple-gfx.render",
1277
+ DISPATCH_QUEUE_SERIAL);
1278
+ s->mtl = copy_suitable_metal_device();
1279
+ s->mtl_queue = [s->mtl newCommandQueue];
1280
+
1281
+ desc.device = s->mtl;
1282
+
1283
+ apple_gfx_register_task_mapping_handlers(s, desc);
1284
+
1285
+ s->cursor_show = true;
1286
+
1287
+ s->pgdev = PGNewDeviceWithDescriptor(desc);
1288
+
1289
+ disp_desc = apple_gfx_prepare_display_descriptor(s);
1290
+ s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc
1291
+ port:0 serialNum:1234];
1292
+ [disp_desc release];
1293
+ s->pgdisp.modeList = apple_gfx_prepare_display_mode_array();
1294
+
1295
+ s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s);
1296
+ return true;
1297
+}
1298
diff --git a/hw/display/meson.build b/hw/display/meson.build
1299
index XXXXXXX..XXXXXXX 100644
1300
--- a/hw/display/meson.build
1301
+++ b/hw/display/meson.build
1302
@@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c'))
1303
1304
system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman])
1305
1306
+system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal])
1307
+if cpu == 'aarch64'
1308
+ system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.m'), pvg, metal])
1309
+endif
1310
1311
if config_all_devices.has_key('CONFIG_VIRTIO_GPU')
1312
virtio_gpu_ss = ss.source_set()
1313
diff --git a/hw/display/trace-events b/hw/display/trace-events
1314
index XXXXXXX..XXXXXXX 100644
1315
--- a/hw/display/trace-events
1316
+++ b/hw/display/trace-events
1317
@@ -XXX,XX +XXX,XX @@ dm163_bits_ppi(unsigned dest_width) "dest_width : %u"
1318
dm163_leds(int led, uint32_t value) "led %d: 0x%x"
1319
dm163_channels(int channel, uint8_t value) "channel %d: 0x%x"
1320
dm163_refresh_rate(uint32_t rr) "refresh rate %d"
1321
+
1322
+# apple-gfx.m
1323
+apple_gfx_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64
1324
+apple_gfx_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64
1325
+apple_gfx_create_task(uint32_t vm_size, void *va) "vm_size=0x%x base_addr=%p"
1326
+apple_gfx_destroy_task(void *task, unsigned int num_mapped_regions) "task=%p, task->mapped_regions->len=%u"
1327
+apple_gfx_map_memory(void *task, uint32_t range_count, uint64_t virtual_offset, uint32_t read_only) "task=%p range_count=0x%x virtual_offset=0x%"PRIx64" read_only=%d"
1328
+apple_gfx_map_memory_range(uint32_t i, uint64_t phys_addr, uint64_t phys_len) "[%d] phys_addr=0x%"PRIx64" phys_len=0x%"PRIx64
1329
+apple_gfx_remap(uint64_t retval, void *source_ptr, uint64_t target) "retval=%"PRId64" source=%p target=0x%"PRIx64
1330
+apple_gfx_unmap_memory(void *task, uint64_t virtual_offset, uint64_t length) "task=%p virtual_offset=0x%"PRIx64" length=0x%"PRIx64
1331
+apple_gfx_read_memory(uint64_t phys_address, uint64_t length, void *dst) "phys_addr=0x%"PRIx64" length=0x%"PRIx64" dest=%p"
1332
+apple_gfx_raise_irq(uint32_t vector) "vector=0x%x"
1333
+apple_gfx_new_frame(void) ""
1334
+apple_gfx_mode_change(uint64_t x, uint64_t y) "x=%"PRId64" y=%"PRId64
1335
+apple_gfx_cursor_set(uint32_t bpp, uint64_t width, uint64_t height) "bpp=%d width=%"PRId64" height=0x%"PRId64
1336
+apple_gfx_cursor_show(uint32_t show) "show=%d"
1337
+apple_gfx_cursor_move(void) ""
1338
+apple_gfx_common_init(const char *device_name, size_t mmio_size) "device: %s; MMIO size: %zu bytes"
1339
+
1340
+# apple-gfx-mmio.m
1341
+apple_gfx_mmio_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64
1342
+apple_gfx_mmio_iosfc_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64
1343
+apple_gfx_iosfc_map_memory(uint64_t phys, uint64_t len, uint32_t ro, void *va, void *e, void *f, void* va_result) "phys=0x%"PRIx64" len=0x%"PRIx64" ro=%d va=%p e=%p f=%p -> *va=%p"
1344
+apple_gfx_iosfc_map_memory_new_region(size_t i, void *region, uint64_t start, uint64_t end) "index=%zu, region=%p, 0x%"PRIx64"-0x%"PRIx64
1345
+apple_gfx_iosfc_unmap_memory(void *a, void *b, void *c, void *d, void *e, void *f) "a=%p b=%p c=%p d=%p e=%p f=%p"
1346
+apple_gfx_iosfc_unmap_memory_region(void* mem, void *region) "unmapping @ %p from memory region %p"
1347
+apple_gfx_iosfc_raise_irq(uint32_t vector) "vector=0x%x"
1348
+
1349
diff --git a/meson.build b/meson.build
1350
index XXXXXXX..XXXXXXX 100644
1351
--- a/meson.build
1352
+++ b/meson.build
1353
@@ -XXX,XX +XXX,XX @@ socket = []
1354
version_res = []
1355
coref = []
1356
iokit = []
1357
+pvg = []
1358
+metal = []
1359
emulator_link_args = []
1360
midl = not_found
1361
widl = not_found
1362
@@ -XXX,XX +XXX,XX @@ elif host_os == 'darwin'
1363
coref = dependency('appleframeworks', modules: 'CoreFoundation')
1364
iokit = dependency('appleframeworks', modules: 'IOKit', required: false)
1365
host_dsosuf = '.dylib'
1366
+ pvg = dependency('appleframeworks', modules: 'ParavirtualizedGraphics')
1367
+ metal = dependency('appleframeworks', modules: 'Metal')
1368
elif host_os == 'sunos'
1369
socket = [cc.find_library('socket'),
1370
cc.find_library('nsl'),
1371
--
1372
2.39.3 (Apple Git-145)
1373
1374
diff view generated by jsdifflib
Deleted patch
1
This change wires up the PCI variant of the paravirtualised
2
graphics device, mainly useful for x86-64 macOS guests, implemented
3
by macOS's ParavirtualizedGraphics.framework. It builds on code
4
shared with the vmapple/mmio variant of the PVG device.
5
1
6
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
7
---
8
9
v4:
10
11
* Threading improvements analogous to those in common apple-gfx code
12
and mmio device variant.
13
* Smaller code review issues addressed.
14
15
v5:
16
17
* Minor error handling improvement.
18
19
v6:
20
21
* Removed an unused function parameter.
22
23
v9:
24
25
* Fixup of changed common call.
26
* Whitespace and comment formatting tweaks.
27
28
hw/display/Kconfig | 4 +
29
hw/display/apple-gfx-pci.m | 150 +++++++++++++++++++++++++++++++++++++
30
hw/display/meson.build | 1 +
31
3 files changed, 155 insertions(+)
32
create mode 100644 hw/display/apple-gfx-pci.m
33
34
diff --git a/hw/display/Kconfig b/hw/display/Kconfig
35
index XXXXXXX..XXXXXXX 100644
36
--- a/hw/display/Kconfig
37
+++ b/hw/display/Kconfig
38
@@ -XXX,XX +XXX,XX @@ config MAC_PVG_MMIO
39
bool
40
depends on MAC_PVG && AARCH64
41
42
+config MAC_PVG_PCI
43
+ bool
44
+ depends on MAC_PVG && PCI
45
+ default y if PCI_DEVICES
46
diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m
47
new file mode 100644
48
index XXXXXXX..XXXXXXX
49
--- /dev/null
50
+++ b/hw/display/apple-gfx-pci.m
51
@@ -XXX,XX +XXX,XX @@
52
+/*
53
+ * QEMU Apple ParavirtualizedGraphics.framework device, PCI variant
54
+ *
55
+ * Copyright © 2023-2024 Phil Dennis-Jordan
56
+ *
57
+ * SPDX-License-Identifier: GPL-2.0-or-later
58
+ *
59
+ * ParavirtualizedGraphics.framework is a set of libraries that macOS provides
60
+ * which implements 3d graphics passthrough to the host as well as a
61
+ * proprietary guest communication channel to drive it. This device model
62
+ * implements support to drive that library from within QEMU as a PCI device
63
+ * aimed primarily at x86-64 macOS VMs.
64
+ */
65
+
66
+#include "apple-gfx.h"
67
+#include "hw/pci/pci_device.h"
68
+#include "hw/pci/msi.h"
69
+#include "qapi/error.h"
70
+#include "trace.h"
71
+#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
72
+
73
+OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXPCIState, APPLE_GFX_PCI)
74
+
75
+struct AppleGFXPCIState {
76
+ PCIDevice parent_obj;
77
+
78
+ AppleGFXState common;
79
+};
80
+
81
+static const char* apple_gfx_pci_option_rom_path = NULL;
82
+
83
+static void apple_gfx_init_option_rom_path(void)
84
+{
85
+ NSURL *option_rom_url = PGCopyOptionROMURL();
86
+ const char *option_rom_path = option_rom_url.fileSystemRepresentation;
87
+ apple_gfx_pci_option_rom_path = g_strdup(option_rom_path);
88
+ [option_rom_url release];
89
+}
90
+
91
+static void apple_gfx_pci_init(Object *obj)
92
+{
93
+ AppleGFXPCIState *s = APPLE_GFX_PCI(obj);
94
+
95
+ if (!apple_gfx_pci_option_rom_path) {
96
+ /*
97
+ * The following is done on device not class init to avoid running
98
+ * ObjC code before fork() in -daemonize mode.
99
+ */
100
+ PCIDeviceClass *pci = PCI_DEVICE_CLASS(object_get_class(obj));
101
+ apple_gfx_init_option_rom_path();
102
+ pci->romfile = apple_gfx_pci_option_rom_path;
103
+ }
104
+
105
+ apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_PCI);
106
+}
107
+
108
+typedef struct AppleGFXPCIInterruptJob {
109
+ PCIDevice *device;
110
+ uint32_t vector;
111
+} AppleGFXPCIInterruptJob;
112
+
113
+static void apple_gfx_pci_raise_interrupt(void *opaque)
114
+{
115
+ AppleGFXPCIInterruptJob *job = opaque;
116
+
117
+ if (msi_enabled(job->device)) {
118
+ msi_notify(job->device, job->vector);
119
+ }
120
+ g_free(job);
121
+}
122
+
123
+static void apple_gfx_pci_interrupt(PCIDevice *dev, uint32_t vector)
124
+{
125
+ AppleGFXPCIInterruptJob *job;
126
+
127
+ trace_apple_gfx_raise_irq(vector);
128
+ job = g_malloc0(sizeof(*job));
129
+ job->device = dev;
130
+ job->vector = vector;
131
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
132
+ apple_gfx_pci_raise_interrupt, job);
133
+}
134
+
135
+static void apple_gfx_pci_realize(PCIDevice *dev, Error **errp)
136
+{
137
+ AppleGFXPCIState *s = APPLE_GFX_PCI(dev);
138
+ int ret;
139
+
140
+ pci_register_bar(dev, PG_PCI_BAR_MMIO,
141
+ PCI_BASE_ADDRESS_SPACE_MEMORY, &s->common.iomem_gfx);
142
+
143
+ ret = msi_init(dev, 0x0 /* config offset; 0 = find space */,
144
+ PG_PCI_MAX_MSI_VECTORS, true /* msi64bit */,
145
+ false /* msi_per_vector_mask */, errp);
146
+ if (ret != 0) {
147
+ return;
148
+ }
149
+
150
+ @autoreleasepool {
151
+ PGDeviceDescriptor *desc = [PGDeviceDescriptor new];
152
+ desc.raiseInterrupt = ^(uint32_t vector) {
153
+ apple_gfx_pci_interrupt(dev, vector);
154
+ };
155
+
156
+ apple_gfx_common_realize(&s->common, DEVICE(dev), desc, errp);
157
+ [desc release];
158
+ desc = nil;
159
+ }
160
+}
161
+
162
+static void apple_gfx_pci_reset(Object *obj, ResetType type)
163
+{
164
+ AppleGFXPCIState *s = APPLE_GFX_PCI(obj);
165
+ [s->common.pgdev reset];
166
+}
167
+
168
+static void apple_gfx_pci_class_init(ObjectClass *klass, void *data)
169
+{
170
+ DeviceClass *dc = DEVICE_CLASS(klass);
171
+ PCIDeviceClass *pci = PCI_DEVICE_CLASS(klass);
172
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
173
+
174
+ rc->phases.hold = apple_gfx_pci_reset;
175
+ dc->desc = "macOS Paravirtualized Graphics PCI Display Controller";
176
+ dc->hotpluggable = false;
177
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
178
+
179
+ pci->vendor_id = PG_PCI_VENDOR_ID;
180
+ pci->device_id = PG_PCI_DEVICE_ID;
181
+ pci->class_id = PCI_CLASS_DISPLAY_OTHER;
182
+ pci->realize = apple_gfx_pci_realize;
183
+
184
+ // TODO: Property for setting mode list
185
+}
186
+
187
+static TypeInfo apple_gfx_pci_types[] = {
188
+ {
189
+ .name = TYPE_APPLE_GFX_PCI,
190
+ .parent = TYPE_PCI_DEVICE,
191
+ .instance_size = sizeof(AppleGFXPCIState),
192
+ .class_init = apple_gfx_pci_class_init,
193
+ .instance_init = apple_gfx_pci_init,
194
+ .interfaces = (InterfaceInfo[]) {
195
+ { INTERFACE_PCIE_DEVICE },
196
+ { },
197
+ },
198
+ }
199
+};
200
+DEFINE_TYPES(apple_gfx_pci_types)
201
+
202
diff --git a/hw/display/meson.build b/hw/display/meson.build
203
index XXXXXXX..XXXXXXX 100644
204
--- a/hw/display/meson.build
205
+++ b/hw/display/meson.build
206
@@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pv
207
if cpu == 'aarch64'
208
system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.m'), pvg, metal])
209
endif
210
+system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx-pci.m'), pvg, metal])
211
212
if config_all_devices.has_key('CONFIG_VIRTIO_GPU')
213
virtio_gpu_ss = ss.source_set()
214
--
215
2.39.3 (Apple Git-145)
216
217
diff view generated by jsdifflib
Deleted patch
1
This change adds a property 'display_modes' on the graphics device
2
which permits specifying a list of display modes. (screen resolution
3
and refresh rate)
4
1
5
The property is an array of a custom type to make the syntax slightly
6
less awkward to use, for example:
7
8
-device '{"driver":"apple-gfx-pci", "display-modes":["1920x1080@60", "3840x2160@60"]}'
9
10
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
11
---
12
13
v4:
14
15
* Switched to the native array property type, which recently gained
16
     command line support.
17
* The property has also been added to the -mmio variant.
18
* Tidied up the code a little.
19
20
v5:
21
22
* Better error handling and buffer management in property parsing and
23
output.
24
25
v6:
26
27
* Switched to using NSMutableArray for the mode list to avoid need for
28
allocating a temporary array - previously done with alloca.
29
30
v7:
31
32
* Simplified error handling in property parsing
33
34
v8:
35
36
* More consistent integer variable types.
37
38
v9:
39
40
* Re-ordered type definitions so we can drop a 'struct' keyword.
41
42
hw/display/apple-gfx-mmio.m | 8 +++
43
hw/display/apple-gfx-pci.m | 9 ++-
44
hw/display/apple-gfx.h | 11 +++
45
hw/display/apple-gfx.m | 135 +++++++++++++++++++++++++++++++-----
46
hw/display/trace-events | 2 +
47
5 files changed, 145 insertions(+), 20 deletions(-)
48
49
diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m
50
index XXXXXXX..XXXXXXX 100644
51
--- a/hw/display/apple-gfx-mmio.m
52
+++ b/hw/display/apple-gfx-mmio.m
53
@@ -XXX,XX +XXX,XX @@ static void apple_gfx_mmio_reset(Object *obj, ResetType type)
54
[s->common.pgdev reset];
55
}
56
57
+static Property apple_gfx_mmio_properties[] = {
58
+ DEFINE_PROP_ARRAY("display-modes", AppleGFXMMIOState,
59
+ common.num_display_modes, common.display_modes,
60
+ qdev_prop_display_mode, AppleGFXDisplayMode),
61
+ DEFINE_PROP_END_OF_LIST(),
62
+};
63
64
static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data)
65
{
66
@@ -XXX,XX +XXX,XX @@ static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data)
67
rc->phases.hold = apple_gfx_mmio_reset;
68
dc->hotpluggable = false;
69
dc->realize = apple_gfx_mmio_realize;
70
+
71
+ device_class_set_props(dc, apple_gfx_mmio_properties);
72
}
73
74
static TypeInfo apple_gfx_mmio_types[] = {
75
diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m
76
index XXXXXXX..XXXXXXX 100644
77
--- a/hw/display/apple-gfx-pci.m
78
+++ b/hw/display/apple-gfx-pci.m
79
@@ -XXX,XX +XXX,XX @@ static void apple_gfx_pci_reset(Object *obj, ResetType type)
80
[s->common.pgdev reset];
81
}
82
83
+static Property apple_gfx_pci_properties[] = {
84
+ DEFINE_PROP_ARRAY("display-modes", AppleGFXPCIState,
85
+ common.num_display_modes, common.display_modes,
86
+ qdev_prop_display_mode, AppleGFXDisplayMode),
87
+ DEFINE_PROP_END_OF_LIST(),
88
+};
89
+
90
static void apple_gfx_pci_class_init(ObjectClass *klass, void *data)
91
{
92
DeviceClass *dc = DEVICE_CLASS(klass);
93
@@ -XXX,XX +XXX,XX @@ static void apple_gfx_pci_class_init(ObjectClass *klass, void *data)
94
pci->class_id = PCI_CLASS_DISPLAY_OTHER;
95
pci->realize = apple_gfx_pci_realize;
96
97
- // TODO: Property for setting mode list
98
+ device_class_set_props(dc, apple_gfx_pci_properties);
99
}
100
101
static TypeInfo apple_gfx_pci_types[] = {
102
diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h
103
index XXXXXXX..XXXXXXX 100644
104
--- a/hw/display/apple-gfx.h
105
+++ b/hw/display/apple-gfx.h
106
@@ -XXX,XX +XXX,XX @@
107
#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
108
#include "qemu/typedefs.h"
109
#include "exec/memory.h"
110
+#include "hw/qdev-properties.h"
111
#include "ui/surface.h"
112
113
@class PGDeviceDescriptor;
114
@@ -XXX,XX +XXX,XX @@
115
116
typedef QTAILQ_HEAD(, PGTask_s) PGTaskList;
117
118
+typedef struct AppleGFXDisplayMode {
119
+ uint16_t width_px;
120
+ uint16_t height_px;
121
+ uint16_t refresh_rate_hz;
122
+} AppleGFXDisplayMode;
123
+
124
typedef struct AppleGFXState {
125
/* Initialised on init/realize() */
126
MemoryRegion iomem_gfx;
127
@@ -XXX,XX +XXX,XX @@ typedef struct AppleGFXState {
128
id<MTLDevice> mtl;
129
id<MTLCommandQueue> mtl_queue;
130
dispatch_queue_t render_queue;
131
+ AppleGFXDisplayMode *display_modes;
132
+ uint32_t num_display_modes;
133
134
/* List `tasks` is protected by task_mutex */
135
QemuMutex task_mutex;
136
@@ -XXX,XX +XXX,XX @@ void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
137
uint64_t length, bool read_only,
138
MemoryRegion **mapping_in_region);
139
140
+extern const PropertyInfo qdev_prop_display_mode;
141
+
142
#endif
143
144
diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m
145
index XXXXXXX..XXXXXXX 100644
146
--- a/hw/display/apple-gfx.m
147
+++ b/hw/display/apple-gfx.m
148
@@ -XXX,XX +XXX,XX @@
149
#include "sysemu/dma.h"
150
#include "ui/console.h"
151
152
-static const PGDisplayCoord_t apple_gfx_modes[] = {
153
- { .x = 1440, .y = 1080 },
154
- { .x = 1280, .y = 1024 },
155
+static const AppleGFXDisplayMode apple_gfx_default_modes[] = {
156
+ { 1920, 1080, 60 },
157
+ { 1440, 1080, 60 },
158
+ { 1280, 1024, 60 },
159
};
160
161
/* ------ PGTask and task operations: new/destroy/map/unmap ------ */
162
@@ -XXX,XX +XXX,XX @@ static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s,
163
return disp_desc;
164
}
165
166
-static NSArray<PGDisplayMode*>* apple_gfx_prepare_display_mode_array(void)
167
+static NSArray<PGDisplayMode *> *apple_gfx_create_display_mode_array(
168
+ const AppleGFXDisplayMode display_modes[], uint32_t display_mode_count)
169
{
170
- PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)];
171
- NSArray<PGDisplayMode*>* mode_array = nil;
172
- int i;
173
-
174
- for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) {
175
- modes[i] =
176
- [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.];
177
- }
178
-
179
- mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)];
180
-
181
- for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) {
182
- [modes[i] release];
183
- modes[i] = nil;
184
+ uint32_t i;
185
+ PGDisplayMode *mode_obj;
186
+ NSMutableArray<PGDisplayMode *> *mode_array =
187
+ [[NSMutableArray alloc] initWithCapacity:display_mode_count];
188
+
189
+ for (i = 0; i < display_mode_count; i++) {
190
+ const AppleGFXDisplayMode *mode = &display_modes[i];
191
+ trace_apple_gfx_display_mode(i, mode->width_px, mode->height_px);
192
+ PGDisplayCoord_t mode_size = { mode->width_px, mode->height_px };
193
+
194
+ mode_obj =
195
+ [[PGDisplayMode alloc] initWithSizeInPixels:mode_size
196
+ refreshRateInHz:mode->refresh_rate_hz];
197
+ [mode_array addObject:mode_obj];
198
+ [mode_obj release];
199
}
200
201
return mode_array;
202
@@ -XXX,XX +XXX,XX @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
203
PGDeviceDescriptor *desc, Error **errp)
204
{
205
PGDisplayDescriptor *disp_desc = nil;
206
+ const AppleGFXDisplayMode *display_modes = apple_gfx_default_modes;
207
+ uint32_t num_display_modes = ARRAY_SIZE(apple_gfx_default_modes);
208
+ NSArray<PGDisplayMode *> *mode_array;
209
210
if (apple_gfx_mig_blocker == NULL) {
211
error_setg(&apple_gfx_mig_blocker,
212
@@ -XXX,XX +XXX,XX @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
213
s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc
214
port:0 serialNum:1234];
215
[disp_desc release];
216
- s->pgdisp.modeList = apple_gfx_prepare_display_mode_array();
217
+
218
+ if (s->display_modes != NULL && s->num_display_modes > 0) {
219
+ trace_apple_gfx_common_realize_modes_property(s->num_display_modes);
220
+ display_modes = s->display_modes;
221
+ num_display_modes = s->num_display_modes;
222
+ }
223
+ s->pgdisp.modeList = mode_array =
224
+ apple_gfx_create_display_mode_array(display_modes, num_display_modes);
225
+ [mode_array release];
226
227
s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s);
228
return true;
229
}
230
+
231
+/* ------ Display mode list device property ------ */
232
+
233
+static void apple_gfx_get_display_mode(Object *obj, Visitor *v,
234
+ const char *name, void *opaque,
235
+ Error **errp)
236
+{
237
+ Property *prop = opaque;
238
+ AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop);
239
+ /* 3 uint16s (max 5 digits) + 2 separator characters + nul. */
240
+ char buffer[5 * 3 + 2 + 1];
241
+ char *pos = buffer;
242
+
243
+ int rc = snprintf(buffer, sizeof(buffer),
244
+ "%"PRIu16"x%"PRIu16"@%"PRIu16,
245
+ mode->width_px, mode->height_px,
246
+ mode->refresh_rate_hz);
247
+ assert(rc < sizeof(buffer));
248
+
249
+ visit_type_str(v, name, &pos, errp);
250
+}
251
+
252
+static void apple_gfx_set_display_mode(Object *obj, Visitor *v,
253
+ const char *name, void *opaque,
254
+ Error **errp)
255
+{
256
+ Property *prop = opaque;
257
+ AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop);
258
+ const char *endptr;
259
+ g_autofree char *str = NULL;
260
+ int ret;
261
+ int val;
262
+
263
+ if (!visit_type_str(v, name, &str, errp)) {
264
+ return;
265
+ }
266
+
267
+ endptr = str;
268
+
269
+ ret = qemu_strtoi(endptr, &endptr, 10, &val);
270
+ if (ret || val > UINT16_MAX || val <= 0) {
271
+ error_setg(errp, "width in '%s' must be a decimal integer number "
272
+ "of pixels in the range 1..65535", name);
273
+ return;
274
+ }
275
+ mode->width_px = val;
276
+ if (*endptr != 'x') {
277
+ goto separator_error;
278
+ }
279
+
280
+ ret = qemu_strtoi(endptr + 1, &endptr, 10, &val);
281
+ if (ret || val > UINT16_MAX || val <= 0) {
282
+ error_setg(errp, "height in '%s' must be a decimal integer number "
283
+ "of pixels in the range 1..65535", name);
284
+ return;
285
+ }
286
+ mode->height_px = val;
287
+ if (*endptr != '@') {
288
+ goto separator_error;
289
+ }
290
+
291
+ ret = qemu_strtoi(endptr + 1, &endptr, 10, &val);
292
+ if (ret || val > UINT16_MAX || val <= 0) {
293
+ error_setg(errp, "refresh rate in '%s'"
294
+ " must be a positive decimal integer (Hertz)", name);
295
+ return;
296
+ }
297
+ mode->refresh_rate_hz = val;
298
+ return;
299
+
300
+separator_error:
301
+ error_setg(errp, "Each display mode takes the format "
302
+ "'<width>x<height>@<rate>'");
303
+}
304
+
305
+const PropertyInfo qdev_prop_display_mode = {
306
+ .name = "display_mode",
307
+ .description =
308
+ "Display mode in pixels and Hertz, as <width>x<height>@<refresh-rate> "
309
+ "Example: 3840x2160@60",
310
+ .get = apple_gfx_get_display_mode,
311
+ .set = apple_gfx_set_display_mode,
312
+};
313
diff --git a/hw/display/trace-events b/hw/display/trace-events
314
index XXXXXXX..XXXXXXX 100644
315
--- a/hw/display/trace-events
316
+++ b/hw/display/trace-events
317
@@ -XXX,XX +XXX,XX @@ apple_gfx_cursor_set(uint32_t bpp, uint64_t width, uint64_t height) "bpp=%d widt
318
apple_gfx_cursor_show(uint32_t show) "show=%d"
319
apple_gfx_cursor_move(void) ""
320
apple_gfx_common_init(const char *device_name, size_t mmio_size) "device: %s; MMIO size: %zu bytes"
321
+apple_gfx_common_realize_modes_property(uint32_t num_modes) "using %u modes supplied by 'display-modes' device property"
322
+apple_gfx_display_mode(uint32_t mode_idx, uint16_t width_px, uint16_t height_px) "mode %2"PRIu32": %4"PRIu16"x%4"PRIu16
323
324
# apple-gfx-mmio.m
325
apple_gfx_mmio_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64
326
--
327
2.39.3 (Apple Git-145)
diff view generated by jsdifflib
Deleted patch
1
I'm happy to take responsibility for the macOS PV graphics code. As
2
HVF patches don't seem to get much attention at the moment, I'm also
3
adding myself as designated reviewer for HVF and x86 HVF to try and
4
improve that.
5
1
6
I anticipate that the resulting workload should be covered by the
7
funding I'm receiving for improving Qemu in combination with macOS. As
8
of right now this runs out at the end of 2024; I expect the workload on
9
apple-gfx should be relatively minor and manageable in my spare time
10
beyond that. I may have to remove myself from more general HVF duties
11
once the contract runs out if it's more than I can manage.
12
13
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
14
Reviewed-by: Roman Bolshakov <rbolshakov@ddn.com>
15
---
16
MAINTAINERS | 7 +++++++
17
1 file changed, 7 insertions(+)
18
19
diff --git a/MAINTAINERS b/MAINTAINERS
20
index XXXXXXX..XXXXXXX 100644
21
--- a/MAINTAINERS
22
+++ b/MAINTAINERS
23
@@ -XXX,XX +XXX,XX @@ F: target/arm/hvf/
24
X86 HVF CPUs
25
M: Cameron Esfahani <dirty@apple.com>
26
M: Roman Bolshakov <rbolshakov@ddn.com>
27
+R: Phil Dennis-Jordan <phil@philjordan.eu>
28
W: https://wiki.qemu.org/Features/HVF
29
S: Maintained
30
F: target/i386/hvf/
31
@@ -XXX,XX +XXX,XX @@ F: target/i386/hvf/
32
HVF
33
M: Cameron Esfahani <dirty@apple.com>
34
M: Roman Bolshakov <rbolshakov@ddn.com>
35
+R: Phil Dennis-Jordan <phil@philjordan.eu>
36
W: https://wiki.qemu.org/Features/HVF
37
S: Maintained
38
F: accel/hvf/
39
@@ -XXX,XX +XXX,XX @@ F: hw/display/edid*
40
F: include/hw/display/edid.h
41
F: qemu-edid.c
42
43
+macOS PV Graphics (apple-gfx)
44
+M: Phil Dennis-Jordan <phil@philjordan.eu>
45
+S: Maintained
46
+F: hw/display/apple-gfx*
47
+
48
PIIX4 South Bridge (i82371AB)
49
M: Hervé Poussineau <hpoussin@reactos.org>
50
M: Philippe Mathieu-Daudé <philmd@linaro.org>
51
--
52
2.39.3 (Apple Git-145)
53
54
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Graf <graf@amazon.com>
2
1
3
We will introduce a number of devices that are specific to the vmapple
4
target machine. To keep them all tidily together, let's put them into
5
a single target directory.
6
7
Signed-off-by: Alexander Graf <graf@amazon.com>
8
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
9
Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
10
---
11
MAINTAINERS | 7 +++++++
12
hw/Kconfig | 1 +
13
hw/meson.build | 1 +
14
hw/vmapple/Kconfig | 1 +
15
hw/vmapple/meson.build | 0
16
hw/vmapple/trace-events | 2 ++
17
hw/vmapple/trace.h | 1 +
18
meson.build | 1 +
19
8 files changed, 14 insertions(+)
20
create mode 100644 hw/vmapple/Kconfig
21
create mode 100644 hw/vmapple/meson.build
22
create mode 100644 hw/vmapple/trace-events
23
create mode 100644 hw/vmapple/trace.h
24
25
diff --git a/MAINTAINERS b/MAINTAINERS
26
index XXXXXXX..XXXXXXX 100644
27
--- a/MAINTAINERS
28
+++ b/MAINTAINERS
29
@@ -XXX,XX +XXX,XX @@ F: hw/hyperv/hv-balloon*.h
30
F: include/hw/hyperv/dynmem-proto.h
31
F: include/hw/hyperv/hv-balloon.h
32
33
+VMapple
34
+M: Alexander Graf <agraf@csgraf.de>
35
+R: Phil Dennis-Jordan <phil@philjordan.eu>
36
+S: Maintained
37
+F: hw/vmapple/*
38
+F: include/hw/vmapple/*
39
+
40
Subsystems
41
----------
42
Overall Audio backends
43
diff --git a/hw/Kconfig b/hw/Kconfig
44
index XXXXXXX..XXXXXXX 100644
45
--- a/hw/Kconfig
46
+++ b/hw/Kconfig
47
@@ -XXX,XX +XXX,XX @@ source ufs/Kconfig
48
source usb/Kconfig
49
source virtio/Kconfig
50
source vfio/Kconfig
51
+source vmapple/Kconfig
52
source xen/Kconfig
53
source watchdog/Kconfig
54
55
diff --git a/hw/meson.build b/hw/meson.build
56
index XXXXXXX..XXXXXXX 100644
57
--- a/hw/meson.build
58
+++ b/hw/meson.build
59
@@ -XXX,XX +XXX,XX @@ subdir('ufs')
60
subdir('usb')
61
subdir('vfio')
62
subdir('virtio')
63
+subdir('vmapple')
64
subdir('watchdog')
65
subdir('xen')
66
subdir('xenpv')
67
diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig
68
new file mode 100644
69
index XXXXXXX..XXXXXXX
70
--- /dev/null
71
+++ b/hw/vmapple/Kconfig
72
@@ -0,0 +1 @@
73
+
74
diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build
75
new file mode 100644
76
index XXXXXXX..XXXXXXX
77
diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events
78
new file mode 100644
79
index XXXXXXX..XXXXXXX
80
--- /dev/null
81
+++ b/hw/vmapple/trace-events
82
@@ -XXX,XX +XXX,XX @@
83
+# See docs/devel/tracing.rst for syntax documentation.
84
+
85
diff --git a/hw/vmapple/trace.h b/hw/vmapple/trace.h
86
new file mode 100644
87
index XXXXXXX..XXXXXXX
88
--- /dev/null
89
+++ b/hw/vmapple/trace.h
90
@@ -0,0 +1 @@
91
+#include "trace/trace-hw_vmapple.h"
92
diff --git a/meson.build b/meson.build
93
index XXXXXXX..XXXXXXX 100644
94
--- a/meson.build
95
+++ b/meson.build
96
@@ -XXX,XX +XXX,XX @@ if have_system
97
'hw/usb',
98
'hw/vfio',
99
'hw/virtio',
100
+ 'hw/vmapple',
101
'hw/watchdog',
102
'hw/xen',
103
'hw/gpio',
104
--
105
2.39.3 (Apple Git-145)
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Graf <graf@amazon.com>
2
1
3
In addition to the ISA and PCI variants of pvpanic, let's add an MMIO
4
platform device that we can use in embedded arm environments.
5
6
Signed-off-by: Alexander Graf <graf@amazon.com>
7
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
8
Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
9
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
10
Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
11
---
12
13
v3:
14
* Rebased on upstream, updated a header path
15
16
hw/misc/Kconfig | 4 +++
17
hw/misc/meson.build | 1 +
18
hw/misc/pvpanic-mmio.c | 61 +++++++++++++++++++++++++++++++++++++++
19
include/hw/misc/pvpanic.h | 1 +
20
4 files changed, 67 insertions(+)
21
create mode 100644 hw/misc/pvpanic-mmio.c
22
23
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
24
index XXXXXXX..XXXXXXX 100644
25
--- a/hw/misc/Kconfig
26
+++ b/hw/misc/Kconfig
27
@@ -XXX,XX +XXX,XX @@ config PVPANIC_ISA
28
depends on ISA_BUS
29
select PVPANIC_COMMON
30
31
+config PVPANIC_MMIO
32
+ bool
33
+ select PVPANIC_COMMON
34
+
35
config AUX
36
bool
37
select I2C
38
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
39
index XXXXXXX..XXXXXXX 100644
40
--- a/hw/misc/meson.build
41
+++ b/hw/misc/meson.build
42
@@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c'))
43
44
system_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c'))
45
system_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c'))
46
+system_ss.add(when: 'CONFIG_PVPANIC_MMIO', if_true: files('pvpanic-mmio.c'))
47
system_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c'))
48
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
49
'aspeed_hace.c',
50
diff --git a/hw/misc/pvpanic-mmio.c b/hw/misc/pvpanic-mmio.c
51
new file mode 100644
52
index XXXXXXX..XXXXXXX
53
--- /dev/null
54
+++ b/hw/misc/pvpanic-mmio.c
55
@@ -XXX,XX +XXX,XX @@
56
+/*
57
+ * QEMU simulated pvpanic device (MMIO frontend)
58
+ *
59
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
60
+ *
61
+ * SPDX-License-Identifier: GPL-2.0-or-later
62
+ */
63
+
64
+#include "qemu/osdep.h"
65
+
66
+#include "hw/qdev-properties.h"
67
+#include "hw/misc/pvpanic.h"
68
+#include "hw/sysbus.h"
69
+#include "standard-headers/misc/pvpanic.h"
70
+
71
+OBJECT_DECLARE_SIMPLE_TYPE(PVPanicMMIOState, PVPANIC_MMIO_DEVICE)
72
+
73
+#define PVPANIC_MMIO_SIZE 0x2
74
+
75
+struct PVPanicMMIOState {
76
+ SysBusDevice parent_obj;
77
+
78
+ PVPanicState pvpanic;
79
+};
80
+
81
+static void pvpanic_mmio_initfn(Object *obj)
82
+{
83
+ PVPanicMMIOState *s = PVPANIC_MMIO_DEVICE(obj);
84
+
85
+ pvpanic_setup_io(&s->pvpanic, DEVICE(s), PVPANIC_MMIO_SIZE);
86
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->pvpanic.mr);
87
+}
88
+
89
+static Property pvpanic_mmio_properties[] = {
90
+ DEFINE_PROP_UINT8("events", PVPanicMMIOState, pvpanic.events,
91
+ PVPANIC_PANICKED | PVPANIC_CRASH_LOADED),
92
+ DEFINE_PROP_END_OF_LIST(),
93
+};
94
+
95
+static void pvpanic_mmio_class_init(ObjectClass *klass, void *data)
96
+{
97
+ DeviceClass *dc = DEVICE_CLASS(klass);
98
+
99
+ device_class_set_props(dc, pvpanic_mmio_properties);
100
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
101
+}
102
+
103
+static const TypeInfo pvpanic_mmio_info = {
104
+ .name = TYPE_PVPANIC_MMIO_DEVICE,
105
+ .parent = TYPE_SYS_BUS_DEVICE,
106
+ .instance_size = sizeof(PVPanicMMIOState),
107
+ .instance_init = pvpanic_mmio_initfn,
108
+ .class_init = pvpanic_mmio_class_init,
109
+};
110
+
111
+static void pvpanic_register_types(void)
112
+{
113
+ type_register_static(&pvpanic_mmio_info);
114
+}
115
+
116
+type_init(pvpanic_register_types)
117
diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h
118
index XXXXXXX..XXXXXXX 100644
119
--- a/include/hw/misc/pvpanic.h
120
+++ b/include/hw/misc/pvpanic.h
121
@@ -XXX,XX +XXX,XX @@
122
123
#define TYPE_PVPANIC_ISA_DEVICE "pvpanic"
124
#define TYPE_PVPANIC_PCI_DEVICE "pvpanic-pci"
125
+#define TYPE_PVPANIC_MMIO_DEVICE "pvpanic-mmio"
126
127
#define PVPANIC_IOPORT_PROP "ioport"
128
129
--
130
2.39.3 (Apple Git-145)
131
132
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Graf <graf@amazon.com>
2
1
3
MacOS unconditionally disables interrupts of the physical timer on boot
4
and then continues to use the virtual one. We don't really want to support
5
a full physical timer emulation, so let's just ignore those writes.
6
7
Signed-off-by: Alexander Graf <graf@amazon.com>
8
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
9
Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
10
---
11
target/arm/hvf/hvf.c | 9 +++++++++
12
1 file changed, 9 insertions(+)
13
14
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/target/arm/hvf/hvf.c
17
+++ b/target/arm/hvf/hvf.c
18
@@ -XXX,XX +XXX,XX @@
19
20
#include "qemu/osdep.h"
21
#include "qemu/error-report.h"
22
+#include "qemu/log.h"
23
24
#include "sysemu/runstate.h"
25
#include "sysemu/hvf.h"
26
@@ -XXX,XX +XXX,XX @@ void hvf_arm_init_debug(void)
27
#define SYSREG_OSLSR_EL1 SYSREG(2, 0, 1, 1, 4)
28
#define SYSREG_OSDLR_EL1 SYSREG(2, 0, 1, 3, 4)
29
#define SYSREG_CNTPCT_EL0 SYSREG(3, 3, 14, 0, 1)
30
+#define SYSREG_CNTP_CTL_EL0 SYSREG(3, 3, 14, 2, 1)
31
#define SYSREG_PMCR_EL0 SYSREG(3, 3, 9, 12, 0)
32
#define SYSREG_PMUSERENR_EL0 SYSREG(3, 3, 9, 14, 0)
33
#define SYSREG_PMCNTENSET_EL0 SYSREG(3, 3, 9, 12, 1)
34
@@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val)
35
case SYSREG_OSLAR_EL1:
36
env->cp15.oslsr_el1 = val & 1;
37
return 0;
38
+ case SYSREG_CNTP_CTL_EL0:
39
+ /*
40
+ * Guests should not rely on the physical counter, but macOS emits
41
+ * disable writes to it. Let it do so, but ignore the requests.
42
+ */
43
+ qemu_log_mask(LOG_UNIMP, "Unsupported write to CNTP_CTL_EL0\n");
44
+ return 0;
45
case SYSREG_OSDLR_EL1:
46
/* Dummy register */
47
return 0;
48
--
49
2.39.3 (Apple Git-145)
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Graf <graf@amazon.com>
2
1
3
Some boards such as vmapple don't do real legacy PCI IRQ swizzling.
4
Instead, they just keep allocating more board IRQ lines for each new
5
legacy IRQ. Let's support that mode by giving instantiators a new
6
"nr_irqs" property they can use to support more than 4 legacy IRQ lines.
7
In this mode, GPEX will export more IRQ lines, one for each device.
8
9
Signed-off-by: Alexander Graf <graf@amazon.com>
10
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
11
Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
12
---
13
14
v4:
15
16
* Turned pair of IRQ arrays into array of structs.
17
* Simplified swizzling logic selection.
18
19
hw/arm/sbsa-ref.c | 2 +-
20
hw/arm/virt.c | 2 +-
21
hw/i386/microvm.c | 2 +-
22
hw/loongarch/virt.c | 2 +-
23
hw/mips/loongson3_virt.c | 2 +-
24
hw/openrisc/virt.c | 12 +++++------
25
hw/pci-host/gpex.c | 43 ++++++++++++++++++++++++++++++--------
26
hw/riscv/virt.c | 12 +++++------
27
hw/xtensa/virt.c | 2 +-
28
include/hw/pci-host/gpex.h | 7 +++----
29
10 files changed, 55 insertions(+), 31 deletions(-)
30
31
diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
32
index XXXXXXX..XXXXXXX 100644
33
--- a/hw/arm/sbsa-ref.c
34
+++ b/hw/arm/sbsa-ref.c
35
@@ -XXX,XX +XXX,XX @@ static void create_pcie(SBSAMachineState *sms)
36
/* Map IO port space */
37
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio);
38
39
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
40
+ for (i = 0; i < PCI_NUM_PINS; i++) {
41
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
42
qdev_get_gpio_in(sms->gic, irq + i));
43
gpex_set_irq_num(GPEX_HOST(dev), i, irq + i);
44
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/hw/arm/virt.c
47
+++ b/hw/arm/virt.c
48
@@ -XXX,XX +XXX,XX @@ static void create_pcie(VirtMachineState *vms)
49
/* Map IO port space */
50
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio);
51
52
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
53
+ for (i = 0; i < PCI_NUM_PINS; i++) {
54
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
55
qdev_get_gpio_in(vms->gic, irq + i));
56
gpex_set_irq_num(GPEX_HOST(dev), i, irq + i);
57
diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c
58
index XXXXXXX..XXXXXXX 100644
59
--- a/hw/i386/microvm.c
60
+++ b/hw/i386/microvm.c
61
@@ -XXX,XX +XXX,XX @@ static void create_gpex(MicrovmMachineState *mms)
62
mms->gpex.mmio64.base, mmio64_alias);
63
}
64
65
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
66
+ for (i = 0; i < PCI_NUM_PINS; i++) {
67
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
68
x86ms->gsi[mms->gpex.irq + i]);
69
}
70
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
71
index XXXXXXX..XXXXXXX 100644
72
--- a/hw/loongarch/virt.c
73
+++ b/hw/loongarch/virt.c
74
@@ -XXX,XX +XXX,XX @@ static void virt_devices_init(DeviceState *pch_pic,
75
memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE,
76
pio_alias);
77
78
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
79
+ for (i = 0; i < PCI_NUM_PINS; i++) {
80
sysbus_connect_irq(d, i,
81
qdev_get_gpio_in(pch_pic, 16 + i));
82
gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i);
83
diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c
84
index XXXXXXX..XXXXXXX 100644
85
--- a/hw/mips/loongson3_virt.c
86
+++ b/hw/mips/loongson3_virt.c
87
@@ -XXX,XX +XXX,XX @@ static inline void loongson3_virt_devices_init(MachineState *machine,
88
virt_memmap[VIRT_PCIE_PIO].base, s->pio_alias);
89
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, virt_memmap[VIRT_PCIE_PIO].base);
90
91
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
92
+ for (i = 0; i < PCI_NUM_PINS; i++) {
93
irq = qdev_get_gpio_in(pic, PCIE_IRQ_BASE + i);
94
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
95
gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ_BASE + i);
96
diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c
97
index XXXXXXX..XXXXXXX 100644
98
--- a/hw/openrisc/virt.c
99
+++ b/hw/openrisc/virt.c
100
@@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base,
101
{
102
int pin, dev;
103
uint32_t irq_map_stride = 0;
104
- uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * 6] = {};
105
+ uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 6] = {};
106
uint32_t *irq_map = full_irq_map;
107
108
/*
109
@@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base,
110
* possible slot) seeing the interrupt-map-mask will allow the table
111
* to wrap to any number of devices.
112
*/
113
- for (dev = 0; dev < GPEX_NUM_IRQS; dev++) {
114
+ for (dev = 0; dev < PCI_NUM_PINS; dev++) {
115
int devfn = dev << 3;
116
117
- for (pin = 0; pin < GPEX_NUM_IRQS; pin++) {
118
- int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS);
119
+ for (pin = 0; pin < PCI_NUM_PINS; pin++) {
120
+ int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
121
int i = 0;
122
123
/* Fill PCI address cells */
124
@@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base,
125
}
126
127
qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map,
128
- GPEX_NUM_IRQS * GPEX_NUM_IRQS *
129
+ PCI_NUM_PINS * PCI_NUM_PINS *
130
irq_map_stride * sizeof(uint32_t));
131
132
qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask",
133
@@ -XXX,XX +XXX,XX @@ static void openrisc_virt_pcie_init(OR1KVirtState *state,
134
memory_region_add_subregion(get_system_memory(), pio_base, alias);
135
136
/* Connect IRQ lines. */
137
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
138
+ for (i = 0; i < PCI_NUM_PINS; i++) {
139
pcie_irq = get_per_cpu_irq(cpus, num_cpus, irq_base + i);
140
141
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pcie_irq);
142
diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
143
index XXXXXXX..XXXXXXX 100644
144
--- a/hw/pci-host/gpex.c
145
+++ b/hw/pci-host/gpex.c
146
@@ -XXX,XX +XXX,XX @@
147
#include "qemu/osdep.h"
148
#include "qapi/error.h"
149
#include "hw/irq.h"
150
+#include "hw/pci/pci_bus.h"
151
#include "hw/pci-host/gpex.h"
152
#include "hw/qdev-properties.h"
153
#include "migration/vmstate.h"
154
@@ -XXX,XX +XXX,XX @@
155
* GPEX host
156
*/
157
158
+struct GPEXIrq {
159
+ qemu_irq irq;
160
+ int irq_num;
161
+};
162
+
163
static void gpex_set_irq(void *opaque, int irq_num, int level)
164
{
165
GPEXHost *s = opaque;
166
167
- qemu_set_irq(s->irq[irq_num], level);
168
+ qemu_set_irq(s->irq[irq_num].irq, level);
169
}
170
171
int gpex_set_irq_num(GPEXHost *s, int index, int gsi)
172
{
173
- if (index >= GPEX_NUM_IRQS) {
174
+ if (index >= s->num_irqs) {
175
return -EINVAL;
176
}
177
178
- s->irq_num[index] = gsi;
179
+ s->irq[index].irq_num = gsi;
180
return 0;
181
}
182
183
@@ -XXX,XX +XXX,XX @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin)
184
{
185
PCIINTxRoute route;
186
GPEXHost *s = opaque;
187
- int gsi = s->irq_num[pin];
188
+ int gsi = s->irq[pin].irq_num;
189
190
route.irq = gsi;
191
if (gsi < 0) {
192
@@ -XXX,XX +XXX,XX @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin)
193
return route;
194
}
195
196
+static int gpex_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin)
197
+{
198
+ PCIBus *bus = pci_device_root_bus(pci_dev);
199
+
200
+ return (PCI_SLOT(pci_dev->devfn) + pin) % bus->nirq;
201
+}
202
+
203
static void gpex_host_realize(DeviceState *dev, Error **errp)
204
{
205
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
206
@@ -XXX,XX +XXX,XX @@ static void gpex_host_realize(DeviceState *dev, Error **errp)
207
PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
208
int i;
209
210
+ s->irq = g_malloc0_n(s->num_irqs, sizeof(*s->irq));
211
+
212
pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX);
213
sysbus_init_mmio(sbd, &pex->mmio);
214
215
@@ -XXX,XX +XXX,XX @@ static void gpex_host_realize(DeviceState *dev, Error **errp)
216
sysbus_init_mmio(sbd, &s->io_ioport);
217
}
218
219
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
220
- sysbus_init_irq(sbd, &s->irq[i]);
221
- s->irq_num[i] = -1;
222
+ for (i = 0; i < s->num_irqs; i++) {
223
+ sysbus_init_irq(sbd, &s->irq[i].irq);
224
+ s->irq[i].irq_num = -1;
225
}
226
227
pci->bus = pci_register_root_bus(dev, "pcie.0", gpex_set_irq,
228
- pci_swizzle_map_irq_fn, s, &s->io_mmio,
229
- &s->io_ioport, 0, 4, TYPE_PCIE_BUS);
230
+ gpex_swizzle_map_irq_fn,
231
+ s, &s->io_mmio, &s->io_ioport, 0,
232
+ s->num_irqs, TYPE_PCIE_BUS);
233
234
pci_bus_set_route_irq_fn(pci->bus, gpex_route_intx_pin_to_irq);
235
qdev_realize(DEVICE(&s->gpex_root), BUS(pci->bus), &error_fatal);
236
}
237
238
+static void gpex_host_unrealize(DeviceState *dev)
239
+{
240
+ GPEXHost *s = GPEX_HOST(dev);
241
+
242
+ g_free(s->irq);
243
+}
244
+
245
static const char *gpex_host_root_bus_path(PCIHostState *host_bridge,
246
PCIBus *rootbus)
247
{
248
@@ -XXX,XX +XXX,XX @@ static Property gpex_host_properties[] = {
249
gpex_cfg.mmio64.base, 0),
250
DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MMIO_SIZE, GPEXHost,
251
gpex_cfg.mmio64.size, 0),
252
+ DEFINE_PROP_UINT8("num-irqs", GPEXHost, num_irqs, PCI_NUM_PINS),
253
DEFINE_PROP_END_OF_LIST(),
254
};
255
256
@@ -XXX,XX +XXX,XX @@ static void gpex_host_class_init(ObjectClass *klass, void *data)
257
258
hc->root_bus_path = gpex_host_root_bus_path;
259
dc->realize = gpex_host_realize;
260
+ dc->unrealize = gpex_host_unrealize;
261
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
262
dc->fw_name = "pci";
263
device_class_set_props(dc, gpex_host_properties);
264
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
265
index XXXXXXX..XXXXXXX 100644
266
--- a/hw/riscv/virt.c
267
+++ b/hw/riscv/virt.c
268
@@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename,
269
{
270
int pin, dev;
271
uint32_t irq_map_stride = 0;
272
- uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS *
273
+ uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS *
274
FDT_MAX_INT_MAP_WIDTH] = {};
275
uint32_t *irq_map = full_irq_map;
276
277
@@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename,
278
* possible slot) seeing the interrupt-map-mask will allow the table
279
* to wrap to any number of devices.
280
*/
281
- for (dev = 0; dev < GPEX_NUM_IRQS; dev++) {
282
+ for (dev = 0; dev < PCI_NUM_PINS; dev++) {
283
int devfn = dev * 0x8;
284
285
- for (pin = 0; pin < GPEX_NUM_IRQS; pin++) {
286
- int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS);
287
+ for (pin = 0; pin < PCI_NUM_PINS; pin++) {
288
+ int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
289
int i = 0;
290
291
/* Fill PCI address cells */
292
@@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename,
293
}
294
295
qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map,
296
- GPEX_NUM_IRQS * GPEX_NUM_IRQS *
297
+ PCI_NUM_PINS * PCI_NUM_PINS *
298
irq_map_stride * sizeof(uint32_t));
299
300
qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask",
301
@@ -XXX,XX +XXX,XX @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
302
303
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base);
304
305
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
306
+ for (i = 0; i < PCI_NUM_PINS; i++) {
307
irq = qdev_get_gpio_in(irqchip, PCIE_IRQ + i);
308
309
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
310
diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c
311
index XXXXXXX..XXXXXXX 100644
312
--- a/hw/xtensa/virt.c
313
+++ b/hw/xtensa/virt.c
314
@@ -XXX,XX +XXX,XX @@ static void create_pcie(MachineState *ms, CPUXtensaState *env, int irq_base,
315
/* Connect IRQ lines. */
316
extints = xtensa_get_extints(env);
317
318
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
319
+ for (i = 0; i < PCI_NUM_PINS; i++) {
320
void *q = extints[irq_base + i];
321
322
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, q);
323
diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
324
index XXXXXXX..XXXXXXX 100644
325
--- a/include/hw/pci-host/gpex.h
326
+++ b/include/hw/pci-host/gpex.h
327
@@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(GPEXHost, GPEX_HOST)
328
#define TYPE_GPEX_ROOT_DEVICE "gpex-root"
329
OBJECT_DECLARE_SIMPLE_TYPE(GPEXRootState, GPEX_ROOT_DEVICE)
330
331
-#define GPEX_NUM_IRQS 4
332
-
333
struct GPEXRootState {
334
/*< private >*/
335
PCIDevice parent_obj;
336
@@ -XXX,XX +XXX,XX @@ struct GPEXConfig {
337
PCIBus *bus;
338
};
339
340
+typedef struct GPEXIrq GPEXIrq;
341
struct GPEXHost {
342
/*< private >*/
343
PCIExpressHost parent_obj;
344
@@ -XXX,XX +XXX,XX @@ struct GPEXHost {
345
MemoryRegion io_mmio;
346
MemoryRegion io_ioport_window;
347
MemoryRegion io_mmio_window;
348
- qemu_irq irq[GPEX_NUM_IRQS];
349
- int irq_num[GPEX_NUM_IRQS];
350
+ GPEXIrq *irq;
351
+ uint8_t num_irqs;
352
353
bool allow_unmapped_accesses;
354
355
--
356
2.39.3 (Apple Git-145)
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Graf <graf@amazon.com>
2
1
3
VMApple contains an "aes" engine device that it uses to encrypt and
4
decrypt its nvram. It has trivial hard coded keys it uses for that
5
purpose.
6
7
Add device emulation for this device model.
8
9
Signed-off-by: Alexander Graf <graf@amazon.com>
10
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
11
---
12
13
v3:
14
15
* Rebased on latest upstream and fixed minor breakages.
16
* Replaced legacy device reset method with Resettable method
17
18
v4:
19
20
* Improved logging of unimplemented functions and guest errors.
21
* Better adherence to naming and coding conventions.
22
* Cleaner error handling and recovery, including using g_autoptr
23
24
v5:
25
26
* More logging improvements
27
* Use xxx64_overflow() functions for hexdump buffer size calculations.
28
29
v7:
30
31
* Coding style tweaks.
32
33
v8:
34
35
* Further improved logging of guest errors.
36
37
v9:
38
39
* Replaced a use of cpu_physical_memory_write with dma_memory_write.
40
* Dropped unnecessary use of ternary operator for bool -> 0/1.
41
42
hw/vmapple/Kconfig | 2 +
43
hw/vmapple/aes.c | 582 +++++++++++++++++++++++++++++++++++
44
hw/vmapple/meson.build | 1 +
45
hw/vmapple/trace-events | 14 +
46
include/hw/vmapple/vmapple.h | 17 +
47
include/qemu/cutils.h | 15 +
48
util/hexdump.c | 18 ++
49
7 files changed, 649 insertions(+)
50
create mode 100644 hw/vmapple/aes.c
51
create mode 100644 include/hw/vmapple/vmapple.h
52
53
diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig
54
index XXXXXXX..XXXXXXX 100644
55
--- a/hw/vmapple/Kconfig
56
+++ b/hw/vmapple/Kconfig
57
@@ -1 +1,3 @@
58
+config VMAPPLE_AES
59
+ bool
60
61
diff --git a/hw/vmapple/aes.c b/hw/vmapple/aes.c
62
new file mode 100644
63
index XXXXXXX..XXXXXXX
64
--- /dev/null
65
+++ b/hw/vmapple/aes.c
66
@@ -XXX,XX +XXX,XX @@
67
+/*
68
+ * QEMU Apple AES device emulation
69
+ *
70
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
71
+ *
72
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
73
+ * See the COPYING file in the top-level directory.
74
+ *
75
+ * SPDX-License-Identifier: GPL-2.0-or-later
76
+ */
77
+
78
+#include "qemu/osdep.h"
79
+#include "trace.h"
80
+#include "crypto/hash.h"
81
+#include "crypto/aes.h"
82
+#include "crypto/cipher.h"
83
+#include "hw/irq.h"
84
+#include "hw/sysbus.h"
85
+#include "hw/vmapple/vmapple.h"
86
+#include "migration/vmstate.h"
87
+#include "qemu/cutils.h"
88
+#include "qemu/log.h"
89
+#include "qemu/module.h"
90
+#include "sysemu/dma.h"
91
+
92
+OBJECT_DECLARE_SIMPLE_TYPE(AESState, APPLE_AES)
93
+
94
+#define MAX_FIFO_SIZE 9
95
+
96
+#define CMD_KEY 0x1
97
+#define CMD_KEY_CONTEXT_SHIFT 27
98
+#define CMD_KEY_CONTEXT_MASK (0x1 << CMD_KEY_CONTEXT_SHIFT)
99
+#define CMD_KEY_SELECT_MAX_IDX 0x7
100
+#define CMD_KEY_SELECT_SHIFT 24
101
+#define CMD_KEY_SELECT_MASK (CMD_KEY_SELECT_MAX_IDX << CMD_KEY_SELECT_SHIFT)
102
+#define CMD_KEY_KEY_LEN_NUM 4u
103
+#define CMD_KEY_KEY_LEN_SHIFT 22
104
+#define CMD_KEY_KEY_LEN_MASK ((CMD_KEY_KEY_LEN_NUM - 1u) << CMD_KEY_KEY_LEN_SHIFT)
105
+#define CMD_KEY_ENCRYPT_SHIFT 20
106
+#define CMD_KEY_ENCRYPT_MASK (0x1 << CMD_KEY_ENCRYPT_SHIFT)
107
+#define CMD_KEY_BLOCK_MODE_SHIFT 16
108
+#define CMD_KEY_BLOCK_MODE_MASK (0x3 << CMD_KEY_BLOCK_MODE_SHIFT)
109
+#define CMD_IV 0x2
110
+#define CMD_IV_CONTEXT_SHIFT 26
111
+#define CMD_IV_CONTEXT_MASK (0x3 << CMD_KEY_CONTEXT_SHIFT)
112
+#define CMD_DSB 0x3
113
+#define CMD_SKG 0x4
114
+#define CMD_DATA 0x5
115
+#define CMD_DATA_KEY_CTX_SHIFT 27
116
+#define CMD_DATA_KEY_CTX_MASK (0x1 << CMD_DATA_KEY_CTX_SHIFT)
117
+#define CMD_DATA_IV_CTX_SHIFT 25
118
+#define CMD_DATA_IV_CTX_MASK (0x3 << CMD_DATA_IV_CTX_SHIFT)
119
+#define CMD_DATA_LEN_MASK 0xffffff
120
+#define CMD_STORE_IV 0x6
121
+#define CMD_STORE_IV_ADDR_MASK 0xffffff
122
+#define CMD_WRITE_REG 0x7
123
+#define CMD_FLAG 0x8
124
+#define CMD_FLAG_STOP_MASK BIT(26)
125
+#define CMD_FLAG_RAISE_IRQ_MASK BIT(27)
126
+#define CMD_FLAG_INFO_MASK 0xff
127
+#define CMD_MAX 0x10
128
+
129
+#define CMD_SHIFT 28
130
+
131
+#define REG_STATUS 0xc
132
+#define REG_STATUS_DMA_READ_RUNNING BIT(0)
133
+#define REG_STATUS_DMA_READ_PENDING BIT(1)
134
+#define REG_STATUS_DMA_WRITE_RUNNING BIT(2)
135
+#define REG_STATUS_DMA_WRITE_PENDING BIT(3)
136
+#define REG_STATUS_BUSY BIT(4)
137
+#define REG_STATUS_EXECUTING BIT(5)
138
+#define REG_STATUS_READY BIT(6)
139
+#define REG_STATUS_TEXT_DPA_SEEDED BIT(7)
140
+#define REG_STATUS_UNWRAP_DPA_SEEDED BIT(8)
141
+
142
+#define REG_IRQ_STATUS 0x18
143
+#define REG_IRQ_STATUS_INVALID_CMD BIT(2)
144
+#define REG_IRQ_STATUS_FLAG BIT(5)
145
+#define REG_IRQ_ENABLE 0x1c
146
+#define REG_WATERMARK 0x20
147
+#define REG_Q_STATUS 0x24
148
+#define REG_FLAG_INFO 0x30
149
+#define REG_FIFO 0x200
150
+
151
+static const uint32_t key_lens[CMD_KEY_KEY_LEN_NUM] = {
152
+ [0] = 16,
153
+ [1] = 24,
154
+ [2] = 32,
155
+ [3] = 64,
156
+};
157
+
158
+typedef struct Key {
159
+ uint32_t key_len;
160
+ uint8_t key[32];
161
+} Key;
162
+
163
+typedef struct IV {
164
+ uint32_t iv[4];
165
+} IV;
166
+
167
+static Key builtin_keys[CMD_KEY_SELECT_MAX_IDX + 1] = {
168
+ [1] = {
169
+ .key_len = 32,
170
+ .key = { 0x1 },
171
+ },
172
+ [2] = {
173
+ .key_len = 32,
174
+ .key = { 0x2 },
175
+ },
176
+ [3] = {
177
+ .key_len = 32,
178
+ .key = { 0x3 },
179
+ }
180
+};
181
+
182
+struct AESState {
183
+ SysBusDevice parent_obj;
184
+
185
+ qemu_irq irq;
186
+ MemoryRegion iomem1;
187
+ MemoryRegion iomem2;
188
+ AddressSpace *as;
189
+
190
+ uint32_t status;
191
+ uint32_t q_status;
192
+ uint32_t irq_status;
193
+ uint32_t irq_enable;
194
+ uint32_t watermark;
195
+ uint32_t flag_info;
196
+ uint32_t fifo[MAX_FIFO_SIZE];
197
+ uint32_t fifo_idx;
198
+ Key key[2];
199
+ IV iv[4];
200
+ bool is_encrypt;
201
+ QCryptoCipherMode block_mode;
202
+};
203
+
204
+static void aes_update_irq(AESState *s)
205
+{
206
+ qemu_set_irq(s->irq, !!(s->irq_status & s->irq_enable));
207
+}
208
+
209
+static uint64_t aes1_read(void *opaque, hwaddr offset, unsigned size)
210
+{
211
+ AESState *s = opaque;
212
+ uint64_t res = 0;
213
+
214
+ switch (offset) {
215
+ case REG_STATUS:
216
+ res = s->status;
217
+ break;
218
+ case REG_IRQ_STATUS:
219
+ res = s->irq_status;
220
+ break;
221
+ case REG_IRQ_ENABLE:
222
+ res = s->irq_enable;
223
+ break;
224
+ case REG_WATERMARK:
225
+ res = s->watermark;
226
+ break;
227
+ case REG_Q_STATUS:
228
+ res = s->q_status;
229
+ break;
230
+ case REG_FLAG_INFO:
231
+ res = s->flag_info;
232
+ break;
233
+
234
+ default:
235
+ qemu_log_mask(LOG_UNIMP, "%s: Unknown AES MMIO offset %" PRIx64 "\n",
236
+ __func__, offset);
237
+ break;
238
+ }
239
+
240
+ trace_aes_read(offset, res);
241
+
242
+ return res;
243
+}
244
+
245
+static void fifo_append(AESState *s, uint64_t val)
246
+{
247
+ if (s->fifo_idx == MAX_FIFO_SIZE) {
248
+ /* Exceeded the FIFO. Bail out */
249
+ return;
250
+ }
251
+
252
+ s->fifo[s->fifo_idx++] = val;
253
+}
254
+
255
+static bool has_payload(AESState *s, uint32_t elems)
256
+{
257
+ return s->fifo_idx >= (elems + 1);
258
+}
259
+
260
+static bool cmd_key(AESState *s)
261
+{
262
+ uint32_t cmd = s->fifo[0];
263
+ uint32_t key_select = (cmd & CMD_KEY_SELECT_MASK) >> CMD_KEY_SELECT_SHIFT;
264
+ uint32_t ctxt = (cmd & CMD_KEY_CONTEXT_MASK) >> CMD_KEY_CONTEXT_SHIFT;
265
+ uint32_t key_len;
266
+
267
+ switch ((cmd & CMD_KEY_BLOCK_MODE_MASK) >> CMD_KEY_BLOCK_MODE_SHIFT) {
268
+ case 0:
269
+ s->block_mode = QCRYPTO_CIPHER_MODE_ECB;
270
+ break;
271
+ case 1:
272
+ s->block_mode = QCRYPTO_CIPHER_MODE_CBC;
273
+ break;
274
+ default:
275
+ return false;
276
+ }
277
+
278
+ s->is_encrypt = cmd & CMD_KEY_ENCRYPT_MASK;
279
+ key_len = key_lens[(cmd & CMD_KEY_KEY_LEN_MASK) >> CMD_KEY_KEY_LEN_SHIFT];
280
+
281
+ if (key_select) {
282
+ trace_aes_cmd_key_select_builtin(ctxt, key_select,
283
+ s->is_encrypt ? "en" : "de",
284
+ QCryptoCipherMode_str(s->block_mode));
285
+ s->key[ctxt] = builtin_keys[key_select];
286
+ } else {
287
+ trace_aes_cmd_key_select_new(ctxt, key_len,
288
+ s->is_encrypt ? "en" : "de",
289
+ QCryptoCipherMode_str(s->block_mode));
290
+ if (key_len > sizeof(s->key[ctxt].key)) {
291
+ return false;
292
+ }
293
+ if (!has_payload(s, key_len / sizeof(uint32_t))) {
294
+ /* wait for payload */
295
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__);
296
+ return false;
297
+ }
298
+ memcpy(&s->key[ctxt].key, &s->fifo[1], key_len);
299
+ s->key[ctxt].key_len = key_len;
300
+ }
301
+
302
+ return true;
303
+}
304
+
305
+static bool cmd_iv(AESState *s)
306
+{
307
+ uint32_t cmd = s->fifo[0];
308
+ uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT;
309
+
310
+ if (!has_payload(s, 4)) {
311
+ /* wait for payload */
312
+ return false;
313
+ }
314
+ memcpy(&s->iv[ctxt].iv, &s->fifo[1], sizeof(s->iv[ctxt].iv));
315
+ trace_aes_cmd_iv(ctxt, s->fifo[1], s->fifo[2], s->fifo[3], s->fifo[4]);
316
+
317
+ return true;
318
+}
319
+
320
+static void dump_data(const char *desc, const void *p, size_t len)
321
+{
322
+ static const size_t MAX_LEN = 0x1000;
323
+ char hex[MAX_LEN * 2 + 1] = "";
324
+
325
+ if (len > MAX_LEN) {
326
+ return;
327
+ }
328
+
329
+ qemu_hexdump_to_buffer(hex, sizeof(hex), p, len);
330
+ trace_aes_dump_data(desc, hex);
331
+}
332
+
333
+static bool cmd_data(AESState *s)
334
+{
335
+ uint32_t cmd = s->fifo[0];
336
+ uint32_t ctxt_iv = 0;
337
+ uint32_t ctxt_key = (cmd & CMD_DATA_KEY_CTX_MASK) >> CMD_DATA_KEY_CTX_SHIFT;
338
+ uint32_t len = cmd & CMD_DATA_LEN_MASK;
339
+ uint64_t src_addr = s->fifo[2];
340
+ uint64_t dst_addr = s->fifo[3];
341
+ QCryptoCipherAlgo alg;
342
+ g_autoptr(QCryptoCipher) cipher = NULL;
343
+ g_autoptr(GByteArray) src = NULL;
344
+ g_autoptr(GByteArray) dst = NULL;
345
+ MemTxResult r;
346
+
347
+ src_addr |= ((uint64_t)s->fifo[1] << 16) & 0xffff00000000ULL;
348
+ dst_addr |= ((uint64_t)s->fifo[1] << 32) & 0xffff00000000ULL;
349
+
350
+ trace_aes_cmd_data(ctxt_key, ctxt_iv, src_addr, dst_addr, len);
351
+
352
+ if (!has_payload(s, 3)) {
353
+ /* wait for payload */
354
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__);
355
+ return false;
356
+ }
357
+
358
+ if (ctxt_key >= ARRAY_SIZE(s->key) ||
359
+ ctxt_iv >= ARRAY_SIZE(s->iv)) {
360
+ /* Invalid input */
361
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key or iv\n", __func__);
362
+ return false;
363
+ }
364
+
365
+ src = g_byte_array_sized_new(len);
366
+ g_byte_array_set_size(src, len);
367
+ dst = g_byte_array_sized_new(len);
368
+ g_byte_array_set_size(dst, len);
369
+
370
+ r = dma_memory_read(s->as, src_addr, src->data, len, MEMTXATTRS_UNSPECIFIED);
371
+ if (r != MEMTX_OK) {
372
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA read of %"PRIu32" bytes "
373
+ "from 0x%"PRIx64" failed. (r=%d)\n",
374
+ __func__, len, src_addr, r);
375
+ return false;
376
+ }
377
+
378
+ dump_data("cmd_data(): src_data=", src->data, len);
379
+
380
+ switch (s->key[ctxt_key].key_len) {
381
+ case 128 / 8:
382
+ alg = QCRYPTO_CIPHER_ALGO_AES_128;
383
+ break;
384
+ case 192 / 8:
385
+ alg = QCRYPTO_CIPHER_ALGO_AES_192;
386
+ break;
387
+ case 256 / 8:
388
+ alg = QCRYPTO_CIPHER_ALGO_AES_256;
389
+ break;
390
+ default:
391
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key length\n", __func__);
392
+ return false;
393
+ }
394
+ cipher = qcrypto_cipher_new(alg, s->block_mode,
395
+ s->key[ctxt_key].key,
396
+ s->key[ctxt_key].key_len, NULL);
397
+ if (!cipher) {
398
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to create cipher object\n",
399
+ __func__);
400
+ return false;
401
+ }
402
+ if (s->block_mode != QCRYPTO_CIPHER_MODE_ECB) {
403
+ if (qcrypto_cipher_setiv(cipher, (void *)s->iv[ctxt_iv].iv,
404
+ sizeof(s->iv[ctxt_iv].iv), NULL) != 0) {
405
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to set IV\n", __func__);
406
+ return false;
407
+ }
408
+ }
409
+ if (s->is_encrypt) {
410
+ if (qcrypto_cipher_encrypt(cipher, src->data, dst->data, len, NULL) != 0) {
411
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Encryption failed\n", __func__);
412
+ return false;
413
+ }
414
+ } else {
415
+ if (qcrypto_cipher_decrypt(cipher, src->data, dst->data, len, NULL) != 0) {
416
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Decryption failed\n", __func__);
417
+ return false;
418
+ }
419
+ }
420
+
421
+ dump_data("cmd_data(): dst_data=", dst->data, len);
422
+ r = dma_memory_write(s->as, dst_addr, dst->data, len, MEMTXATTRS_UNSPECIFIED);
423
+ if (r != MEMTX_OK) {
424
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA write of %"PRIu32" bytes "
425
+ "to 0x%"PRIx64" failed. (r=%d)\n",
426
+ __func__, len, src_addr, r);
427
+ return false;
428
+ }
429
+
430
+ return true;
431
+}
432
+
433
+static bool cmd_store_iv(AESState *s)
434
+{
435
+ uint32_t cmd = s->fifo[0];
436
+ uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT;
437
+ uint64_t addr = s->fifo[1];
438
+ MemTxResult dma_result;
439
+
440
+ if (!has_payload(s, 1)) {
441
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__);
442
+ return false;
443
+ }
444
+
445
+ if (ctxt >= ARRAY_SIZE(s->iv)) {
446
+ qemu_log_mask(LOG_GUEST_ERROR,
447
+ "%s: Invalid context. ctxt = %u, allowed: 0..%zu\n",
448
+ __func__, ctxt, ARRAY_SIZE(s->iv) - 1);
449
+ return false;
450
+ }
451
+
452
+ addr |= ((uint64_t)cmd << 32) & 0xff00000000ULL;
453
+ dma_result = dma_memory_write(&address_space_memory, addr,
454
+ &s->iv[ctxt].iv, sizeof(s->iv[ctxt].iv),
455
+ MEMTXATTRS_UNSPECIFIED);
456
+
457
+ trace_aes_cmd_store_iv(ctxt, addr, s->iv[ctxt].iv[0], s->iv[ctxt].iv[1],
458
+ s->iv[ctxt].iv[2], s->iv[ctxt].iv[3]);
459
+
460
+ return dma_result == MEMTX_OK;
461
+}
462
+
463
+static bool cmd_flag(AESState *s)
464
+{
465
+ uint32_t cmd = s->fifo[0];
466
+ uint32_t raise_irq = cmd & CMD_FLAG_RAISE_IRQ_MASK;
467
+
468
+ /* We always process data when it's coming in, so fire an IRQ immediately */
469
+ if (raise_irq) {
470
+ s->irq_status |= REG_IRQ_STATUS_FLAG;
471
+ }
472
+
473
+ s->flag_info = cmd & CMD_FLAG_INFO_MASK;
474
+
475
+ trace_aes_cmd_flag(!!raise_irq, s->flag_info);
476
+
477
+ return true;
478
+}
479
+
480
+static void fifo_process(AESState *s)
481
+{
482
+ uint32_t cmd = s->fifo[0] >> CMD_SHIFT;
483
+ bool success = false;
484
+
485
+ if (!s->fifo_idx) {
486
+ return;
487
+ }
488
+
489
+ switch (cmd) {
490
+ case CMD_KEY:
491
+ success = cmd_key(s);
492
+ break;
493
+ case CMD_IV:
494
+ success = cmd_iv(s);
495
+ break;
496
+ case CMD_DATA:
497
+ success = cmd_data(s);
498
+ break;
499
+ case CMD_STORE_IV:
500
+ success = cmd_store_iv(s);
501
+ break;
502
+ case CMD_FLAG:
503
+ success = cmd_flag(s);
504
+ break;
505
+ default:
506
+ s->irq_status |= REG_IRQ_STATUS_INVALID_CMD;
507
+ break;
508
+ }
509
+
510
+ if (success) {
511
+ s->fifo_idx = 0;
512
+ }
513
+
514
+ trace_aes_fifo_process(cmd, success);
515
+}
516
+
517
+static void aes1_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
518
+{
519
+ AESState *s = opaque;
520
+
521
+ trace_aes_write(offset, val);
522
+
523
+ switch (offset) {
524
+ case REG_IRQ_STATUS:
525
+ s->irq_status &= ~val;
526
+ break;
527
+ case REG_IRQ_ENABLE:
528
+ s->irq_enable = val;
529
+ break;
530
+ case REG_FIFO:
531
+ fifo_append(s, val);
532
+ fifo_process(s);
533
+ break;
534
+ default:
535
+ qemu_log_mask(LOG_UNIMP,
536
+ "%s: Unknown AES MMIO offset %"PRIx64", data %"PRIx64"\n",
537
+ __func__, offset, val);
538
+ return;
539
+ }
540
+
541
+ aes_update_irq(s);
542
+}
543
+
544
+static const MemoryRegionOps aes1_ops = {
545
+ .read = aes1_read,
546
+ .write = aes1_write,
547
+ .endianness = DEVICE_NATIVE_ENDIAN,
548
+ .valid = {
549
+ .min_access_size = 4,
550
+ .max_access_size = 8,
551
+ },
552
+ .impl = {
553
+ .min_access_size = 4,
554
+ .max_access_size = 4,
555
+ },
556
+};
557
+
558
+static uint64_t aes2_read(void *opaque, hwaddr offset, unsigned size)
559
+{
560
+ uint64_t res = 0;
561
+
562
+ switch (offset) {
563
+ case 0:
564
+ res = 0;
565
+ break;
566
+ default:
567
+ qemu_log_mask(LOG_UNIMP,
568
+ "%s: Unknown AES MMIO 2 offset %"PRIx64"\n",
569
+ __func__, offset);
570
+ break;
571
+ }
572
+
573
+ trace_aes_2_read(offset, res);
574
+
575
+ return res;
576
+}
577
+
578
+static void aes2_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
579
+{
580
+ trace_aes_2_write(offset, val);
581
+
582
+ switch (offset) {
583
+ default:
584
+ qemu_log_mask(LOG_UNIMP,
585
+ "%s: Unknown AES MMIO 2 offset %"PRIx64", data %"PRIx64"\n",
586
+ __func__, offset, val);
587
+ return;
588
+ }
589
+}
590
+
591
+static const MemoryRegionOps aes2_ops = {
592
+ .read = aes2_read,
593
+ .write = aes2_write,
594
+ .endianness = DEVICE_NATIVE_ENDIAN,
595
+ .valid = {
596
+ .min_access_size = 4,
597
+ .max_access_size = 8,
598
+ },
599
+ .impl = {
600
+ .min_access_size = 4,
601
+ .max_access_size = 4,
602
+ },
603
+};
604
+
605
+static void aes_reset(Object *obj, ResetType type)
606
+{
607
+ AESState *s = APPLE_AES(obj);
608
+
609
+ s->status = 0x3f80;
610
+ s->q_status = 2;
611
+ s->irq_status = 0;
612
+ s->irq_enable = 0;
613
+ s->watermark = 0;
614
+}
615
+
616
+static void aes_init(Object *obj)
617
+{
618
+ AESState *s = APPLE_AES(obj);
619
+
620
+ memory_region_init_io(&s->iomem1, obj, &aes1_ops, s, TYPE_APPLE_AES, 0x4000);
621
+ memory_region_init_io(&s->iomem2, obj, &aes2_ops, s, TYPE_APPLE_AES, 0x4000);
622
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem1);
623
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem2);
624
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
625
+ s->as = &address_space_memory;
626
+}
627
+
628
+static void aes_class_init(ObjectClass *klass, void *data)
629
+{
630
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
631
+
632
+ rc->phases.hold = aes_reset;
633
+}
634
+
635
+static const TypeInfo aes_info = {
636
+ .name = TYPE_APPLE_AES,
637
+ .parent = TYPE_SYS_BUS_DEVICE,
638
+ .instance_size = sizeof(AESState),
639
+ .class_init = aes_class_init,
640
+ .instance_init = aes_init,
641
+};
642
+
643
+static void aes_register_types(void)
644
+{
645
+ type_register_static(&aes_info);
646
+}
647
+
648
+type_init(aes_register_types)
649
diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build
650
index XXXXXXX..XXXXXXX 100644
651
--- a/hw/vmapple/meson.build
652
+++ b/hw/vmapple/meson.build
653
@@ -0,0 +1 @@
654
+system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c'))
655
diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events
656
index XXXXXXX..XXXXXXX 100644
657
--- a/hw/vmapple/trace-events
658
+++ b/hw/vmapple/trace-events
659
@@ -XXX,XX +XXX,XX @@
660
# See docs/devel/tracing.rst for syntax documentation.
661
662
+# aes.c
663
+aes_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64
664
+aes_cmd_key_select_builtin(uint32_t ctx, uint32_t key_id, const char *direction, const char *cipher) "[%d] Selecting builtin key %d to %scrypt with %s"
665
+aes_cmd_key_select_new(uint32_t ctx, uint32_t key_len, const char *direction, const char *cipher) "[%d] Selecting new key size=%d to %scrypt with %s"
666
+aes_cmd_iv(uint32_t ctx, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] 0x%08x 0x%08x 0x%08x 0x%08x"
667
+aes_cmd_data(uint32_t key, uint32_t iv, uint64_t src, uint64_t dst, uint32_t len) "[key=%d iv=%d] src=0x%"PRIx64" dst=0x%"PRIx64" len=0x%x"
668
+aes_cmd_store_iv(uint32_t ctx, uint64_t addr, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] addr=0x%"PRIx64"x -> 0x%08x 0x%08x 0x%08x 0x%08x"
669
+aes_cmd_flag(uint32_t raise, uint32_t flag_info) "raise=%d flag_info=0x%x"
670
+aes_fifo_process(uint32_t cmd, uint32_t success) "cmd=%d success=%d"
671
+aes_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64
672
+aes_2_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64
673
+aes_2_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64
674
+aes_dump_data(const char *desc, const char *hex) "%s%s"
675
+
676
diff --git a/include/hw/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h
677
new file mode 100644
678
index XXXXXXX..XXXXXXX
679
--- /dev/null
680
+++ b/include/hw/vmapple/vmapple.h
681
@@ -XXX,XX +XXX,XX @@
682
+/*
683
+ * Devices specific to the VMApple machine type
684
+ *
685
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
686
+ *
687
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
688
+ * See the COPYING file in the top-level directory.
689
+ *
690
+ * SPDX-License-Identifier: GPL-2.0-or-later
691
+ */
692
+
693
+#ifndef HW_VMAPPLE_VMAPPLE_H
694
+#define HW_VMAPPLE_VMAPPLE_H
695
+
696
+#define TYPE_APPLE_AES "apple-aes"
697
+
698
+#endif /* HW_VMAPPLE_VMAPPLE_H */
699
diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h
700
index XXXXXXX..XXXXXXX 100644
701
--- a/include/qemu/cutils.h
702
+++ b/include/qemu/cutils.h
703
@@ -XXX,XX +XXX,XX @@ GString *qemu_hexdump_line(GString *str, const void *buf, size_t len,
704
void qemu_hexdump(FILE *fp, const char *prefix,
705
const void *bufptr, size_t size);
706
707
+/**
708
+ * qemu_hexdump_to_buffer:
709
+ * @buffer: output string buffer
710
+ * @buffer_size: amount of available space in buffer. Must be at least
711
+ * data_size*2+1.
712
+ * @data: input bytes
713
+ * @data_size: number of bytes in data
714
+ *
715
+ * Converts the @data_size bytes in @data into hex digit pairs, writing them to
716
+ * @buffer. Finally, a nul terminating character is written; @buffer therefore
717
+ * needs space for (data_size*2+1) chars.
718
+ */
719
+void qemu_hexdump_to_buffer(char *restrict buffer, size_t buffer_size,
720
+ const uint8_t *restrict data, size_t data_size);
721
+
722
#endif
723
diff --git a/util/hexdump.c b/util/hexdump.c
724
index XXXXXXX..XXXXXXX 100644
725
--- a/util/hexdump.c
726
+++ b/util/hexdump.c
727
@@ -XXX,XX +XXX,XX @@
728
729
#include "qemu/osdep.h"
730
#include "qemu/cutils.h"
731
+#include "qemu/host-utils.h"
732
733
static inline char hexdump_nibble(unsigned x)
734
{
735
@@ -XXX,XX +XXX,XX @@ void qemu_hexdump(FILE *fp, const char *prefix,
736
}
737
738
}
739
+
740
+void qemu_hexdump_to_buffer(char *restrict buffer, size_t buffer_size,
741
+ const uint8_t *restrict data, size_t data_size)
742
+{
743
+ size_t i;
744
+ uint64_t required_buffer_size;
745
+ bool overflow = umul64_overflow(data_size, 2, &required_buffer_size);
746
+ overflow |= uadd64_overflow(required_buffer_size, 1, &required_buffer_size);
747
+ assert(!overflow && buffer_size >= required_buffer_size);
748
+
749
+ for (i = 0; i < data_size; i++) {
750
+ uint8_t val = data[i];
751
+ *(buffer++) = hexdump_nibble(val >> 4);
752
+ *(buffer++) = hexdump_nibble(val & 0xf);
753
+ }
754
+ *buffer = '\0';
755
+}
756
--
757
2.39.3 (Apple Git-145)
758
759
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Graf <graf@amazon.com>
2
1
3
The VMApple machine exposes AUX and ROOT block devices (as well as USB OTG
4
emulation) via virtio-pci as well as a special, simple backdoor platform
5
device.
6
7
This patch implements this backdoor platform device to the best of my
8
understanding. I left out any USB OTG parts; they're only needed for
9
guest recovery and I don't understand the protocol yet.
10
11
Signed-off-by: Alexander Graf <graf@amazon.com>
12
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
13
---
14
15
v4:
16
17
* Moved most header code to .c, rest to vmapple.h
18
* Better compliance with coding, naming, and formatting conventions.
19
20
v8:
21
22
* Replaced uses of cpu_physical_memory_read with dma_memory_read.
23
* Replaced an instance of g_free with g_autofree.
24
25
v9:
26
27
* Replaced uses of cpu_physical_memory_write with dma_memory_write.
28
29
hw/vmapple/Kconfig | 3 +
30
hw/vmapple/bdif.c | 275 +++++++++++++++++++++++++++++++++++
31
hw/vmapple/meson.build | 1 +
32
hw/vmapple/trace-events | 5 +
33
include/hw/vmapple/vmapple.h | 2 +
34
5 files changed, 286 insertions(+)
35
create mode 100644 hw/vmapple/bdif.c
36
37
diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig
38
index XXXXXXX..XXXXXXX 100644
39
--- a/hw/vmapple/Kconfig
40
+++ b/hw/vmapple/Kconfig
41
@@ -XXX,XX +XXX,XX @@
42
config VMAPPLE_AES
43
bool
44
45
+config VMAPPLE_BDIF
46
+ bool
47
+
48
diff --git a/hw/vmapple/bdif.c b/hw/vmapple/bdif.c
49
new file mode 100644
50
index XXXXXXX..XXXXXXX
51
--- /dev/null
52
+++ b/hw/vmapple/bdif.c
53
@@ -XXX,XX +XXX,XX @@
54
+/*
55
+ * VMApple Backdoor Interface
56
+ *
57
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
58
+ *
59
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
60
+ * See the COPYING file in the top-level directory.
61
+ *
62
+ * SPDX-License-Identifier: GPL-2.0-or-later
63
+ */
64
+
65
+#include "qemu/osdep.h"
66
+#include "qemu/units.h"
67
+#include "qemu/log.h"
68
+#include "qemu/module.h"
69
+#include "trace.h"
70
+#include "hw/vmapple/vmapple.h"
71
+#include "hw/sysbus.h"
72
+#include "hw/block/block.h"
73
+#include "qapi/error.h"
74
+#include "sysemu/block-backend.h"
75
+#include "sysemu/dma.h"
76
+
77
+OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF)
78
+
79
+struct VMAppleBdifState {
80
+ SysBusDevice parent_obj;
81
+
82
+ BlockBackend *aux;
83
+ BlockBackend *root;
84
+ MemoryRegion mmio;
85
+};
86
+
87
+#define VMAPPLE_BDIF_SIZE 0x00200000
88
+
89
+#define REG_DEVID_MASK 0xffff0000
90
+#define DEVID_ROOT 0x00000000
91
+#define DEVID_AUX 0x00010000
92
+#define DEVID_USB 0x00100000
93
+
94
+#define REG_STATUS 0x0
95
+#define REG_STATUS_ACTIVE BIT(0)
96
+#define REG_CFG 0x4
97
+#define REG_CFG_ACTIVE BIT(1)
98
+#define REG_UNK1 0x8
99
+#define REG_BUSY 0x10
100
+#define REG_BUSY_READY BIT(0)
101
+#define REG_UNK2 0x400
102
+#define REG_CMD 0x408
103
+#define REG_NEXT_DEVICE 0x420
104
+#define REG_UNK3 0x434
105
+
106
+typedef struct VblkSector {
107
+ uint32_t pad;
108
+ uint32_t pad2;
109
+ uint32_t sector;
110
+ uint32_t pad3;
111
+} VblkSector;
112
+
113
+typedef struct VblkReqCmd {
114
+ uint64_t addr;
115
+ uint32_t len;
116
+ uint32_t flags;
117
+} VblkReqCmd;
118
+
119
+typedef struct VblkReq {
120
+ VblkReqCmd sector;
121
+ VblkReqCmd data;
122
+ VblkReqCmd retval;
123
+} VblkReq;
124
+
125
+#define VBLK_DATA_FLAGS_READ 0x00030001
126
+#define VBLK_DATA_FLAGS_WRITE 0x00010001
127
+
128
+#define VBLK_RET_SUCCESS 0
129
+#define VBLK_RET_FAILED 1
130
+
131
+static uint64_t bdif_read(void *opaque, hwaddr offset, unsigned size)
132
+{
133
+ uint64_t ret = -1;
134
+ uint64_t devid = offset & REG_DEVID_MASK;
135
+
136
+ switch (offset & ~REG_DEVID_MASK) {
137
+ case REG_STATUS:
138
+ ret = REG_STATUS_ACTIVE;
139
+ break;
140
+ case REG_CFG:
141
+ ret = REG_CFG_ACTIVE;
142
+ break;
143
+ case REG_UNK1:
144
+ ret = 0x420;
145
+ break;
146
+ case REG_BUSY:
147
+ ret = REG_BUSY_READY;
148
+ break;
149
+ case REG_UNK2:
150
+ ret = 0x1;
151
+ break;
152
+ case REG_UNK3:
153
+ ret = 0x0;
154
+ break;
155
+ case REG_NEXT_DEVICE:
156
+ switch (devid) {
157
+ case DEVID_ROOT:
158
+ ret = 0x8000000;
159
+ break;
160
+ case DEVID_AUX:
161
+ ret = 0x10000;
162
+ break;
163
+ }
164
+ break;
165
+ }
166
+
167
+ trace_bdif_read(offset, size, ret);
168
+ return ret;
169
+}
170
+
171
+static void le2cpu_sector(VblkSector *sector)
172
+{
173
+ sector->sector = le32_to_cpu(sector->sector);
174
+}
175
+
176
+static void le2cpu_reqcmd(VblkReqCmd *cmd)
177
+{
178
+ cmd->addr = le64_to_cpu(cmd->addr);
179
+ cmd->len = le32_to_cpu(cmd->len);
180
+ cmd->flags = le32_to_cpu(cmd->flags);
181
+}
182
+
183
+static void le2cpu_req(VblkReq *req)
184
+{
185
+ le2cpu_reqcmd(&req->sector);
186
+ le2cpu_reqcmd(&req->data);
187
+ le2cpu_reqcmd(&req->retval);
188
+}
189
+
190
+static void vblk_cmd(uint64_t devid, BlockBackend *blk, uint64_t gp_addr,
191
+ uint64_t static_off)
192
+{
193
+ VblkReq req;
194
+ VblkSector sector;
195
+ uint64_t off = 0;
196
+ g_autofree char *buf = NULL;
197
+ uint8_t ret = VBLK_RET_FAILED;
198
+ int r;
199
+ MemTxResult dma_result;
200
+
201
+ dma_result = dma_memory_read(&address_space_memory, gp_addr,
202
+ &req, sizeof(req), MEMTXATTRS_UNSPECIFIED);
203
+ if (dma_result != MEMTX_OK) {
204
+ goto out;
205
+ }
206
+
207
+ le2cpu_req(&req);
208
+
209
+ if (req.sector.len != sizeof(sector)) {
210
+ goto out;
211
+ }
212
+
213
+ /* Read the vblk command */
214
+ dma_result = dma_memory_read(&address_space_memory, req.sector.addr,
215
+ &sector, sizeof(sector),
216
+ MEMTXATTRS_UNSPECIFIED);
217
+ if (dma_result != MEMTX_OK) {
218
+ goto out;
219
+ }
220
+ le2cpu_sector(&sector);
221
+
222
+ off = sector.sector * 512ULL + static_off;
223
+
224
+ /* Sanity check that we're not allocating bogus sizes */
225
+ if (req.data.len > 128 * MiB) {
226
+ goto out;
227
+ }
228
+
229
+ buf = g_malloc0(req.data.len);
230
+ switch (req.data.flags) {
231
+ case VBLK_DATA_FLAGS_READ:
232
+ r = blk_pread(blk, off, req.data.len, buf, 0);
233
+ trace_bdif_vblk_read(devid == DEVID_AUX ? "aux" : "root",
234
+ req.data.addr, off, req.data.len, r);
235
+ if (r < 0) {
236
+ goto out;
237
+ }
238
+ dma_result = dma_memory_write(&address_space_memory, req.data.addr, buf,
239
+ req.data.len, MEMTXATTRS_UNSPECIFIED);
240
+ if (dma_result == MEMTX_OK) {
241
+ ret = VBLK_RET_SUCCESS;
242
+ }
243
+ break;
244
+ case VBLK_DATA_FLAGS_WRITE:
245
+ /* Not needed, iBoot only reads */
246
+ break;
247
+ default:
248
+ break;
249
+ }
250
+
251
+out:
252
+ dma_memory_write(&address_space_memory, req.retval.addr, &ret, 1,
253
+ MEMTXATTRS_UNSPECIFIED);
254
+}
255
+
256
+static void bdif_write(void *opaque, hwaddr offset,
257
+ uint64_t value, unsigned size)
258
+{
259
+ VMAppleBdifState *s = opaque;
260
+ uint64_t devid = (offset & REG_DEVID_MASK);
261
+
262
+ trace_bdif_write(offset, size, value);
263
+
264
+ switch (offset & ~REG_DEVID_MASK) {
265
+ case REG_CMD:
266
+ switch (devid) {
267
+ case DEVID_ROOT:
268
+ vblk_cmd(devid, s->root, value, 0x0);
269
+ break;
270
+ case DEVID_AUX:
271
+ vblk_cmd(devid, s->aux, value, 0x0);
272
+ break;
273
+ }
274
+ break;
275
+ }
276
+}
277
+
278
+static const MemoryRegionOps bdif_ops = {
279
+ .read = bdif_read,
280
+ .write = bdif_write,
281
+ .endianness = DEVICE_NATIVE_ENDIAN,
282
+ .valid = {
283
+ .min_access_size = 1,
284
+ .max_access_size = 8,
285
+ },
286
+ .impl = {
287
+ .min_access_size = 1,
288
+ .max_access_size = 8,
289
+ },
290
+};
291
+
292
+static void bdif_init(Object *obj)
293
+{
294
+ VMAppleBdifState *s = VMAPPLE_BDIF(obj);
295
+
296
+ memory_region_init_io(&s->mmio, obj, &bdif_ops, obj,
297
+ "VMApple Backdoor Interface", VMAPPLE_BDIF_SIZE);
298
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
299
+}
300
+
301
+static Property bdif_properties[] = {
302
+ DEFINE_PROP_DRIVE("aux", VMAppleBdifState, aux),
303
+ DEFINE_PROP_DRIVE("root", VMAppleBdifState, root),
304
+ DEFINE_PROP_END_OF_LIST(),
305
+};
306
+
307
+static void bdif_class_init(ObjectClass *klass, void *data)
308
+{
309
+ DeviceClass *dc = DEVICE_CLASS(klass);
310
+
311
+ dc->desc = "VMApple Backdoor Interface";
312
+ device_class_set_props(dc, bdif_properties);
313
+}
314
+
315
+static const TypeInfo bdif_info = {
316
+ .name = TYPE_VMAPPLE_BDIF,
317
+ .parent = TYPE_SYS_BUS_DEVICE,
318
+ .instance_size = sizeof(VMAppleBdifState),
319
+ .instance_init = bdif_init,
320
+ .class_init = bdif_class_init,
321
+};
322
+
323
+static void bdif_register_types(void)
324
+{
325
+ type_register_static(&bdif_info);
326
+}
327
+
328
+type_init(bdif_register_types)
329
diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build
330
index XXXXXXX..XXXXXXX 100644
331
--- a/hw/vmapple/meson.build
332
+++ b/hw/vmapple/meson.build
333
@@ -1 +1,2 @@
334
system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c'))
335
+system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c'))
336
diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events
337
index XXXXXXX..XXXXXXX 100644
338
--- a/hw/vmapple/trace-events
339
+++ b/hw/vmapple/trace-events
340
@@ -XXX,XX +XXX,XX @@ aes_2_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64
341
aes_2_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64
342
aes_dump_data(const char *desc, const char *hex) "%s%s"
343
344
+# bdif.c
345
+bdif_read(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64
346
+bdif_write(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64
347
+bdif_vblk_read(const char *dev, uint64_t addr, uint64_t offset, uint32_t len, int r) "dev=%s addr=0x%"PRIx64" off=0x%"PRIx64" size=0x%x r=%d"
348
+
349
diff --git a/include/hw/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h
350
index XXXXXXX..XXXXXXX 100644
351
--- a/include/hw/vmapple/vmapple.h
352
+++ b/include/hw/vmapple/vmapple.h
353
@@ -XXX,XX +XXX,XX @@
354
355
#define TYPE_APPLE_AES "apple-aes"
356
357
+#define TYPE_VMAPPLE_BDIF "vmapple-bdif"
358
+
359
#endif /* HW_VMAPPLE_VMAPPLE_H */
360
--
361
2.39.3 (Apple Git-145)
362
363
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Graf <graf@amazon.com>
2
1
3
Instead of device tree or other more standardized means, VMApple passes
4
platform configuration to the first stage boot loader in a binary encoded
5
format that resides at a dedicated RAM region in physical address space.
6
7
This patch models this configuration space as a qdev device which we can
8
then map at the fixed location in the address space. That way, we can
9
influence and annotate all configuration fields easily.
10
11
Signed-off-by: Alexander Graf <graf@amazon.com>
12
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
13
---
14
15
v3:
16
17
* Replaced legacy device reset method with Resettable method
18
19
v4:
20
21
* Fixed initialisation of default values for properties
22
* Dropped superfluous endianness conversions
23
* Moved most header code to .c, device name #define goes in vmapple.h
24
25
v5:
26
27
* Improved error reporting in case of string property buffer overflow.
28
29
v7:
30
31
* Changed error messages for overrun of properties with
32
fixed-length strings to be more useful to users than developers.
33
34
v8:
35
36
* Consistent parenthesising of macro arguments for better safety.
37
38
hw/vmapple/Kconfig | 3 +
39
hw/vmapple/cfg.c | 196 +++++++++++++++++++++++++++++++++++
40
hw/vmapple/meson.build | 1 +
41
include/hw/vmapple/vmapple.h | 2 +
42
4 files changed, 202 insertions(+)
43
create mode 100644 hw/vmapple/cfg.c
44
45
diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig
46
index XXXXXXX..XXXXXXX 100644
47
--- a/hw/vmapple/Kconfig
48
+++ b/hw/vmapple/Kconfig
49
@@ -XXX,XX +XXX,XX @@ config VMAPPLE_AES
50
config VMAPPLE_BDIF
51
bool
52
53
+config VMAPPLE_CFG
54
+ bool
55
+
56
diff --git a/hw/vmapple/cfg.c b/hw/vmapple/cfg.c
57
new file mode 100644
58
index XXXXXXX..XXXXXXX
59
--- /dev/null
60
+++ b/hw/vmapple/cfg.c
61
@@ -XXX,XX +XXX,XX @@
62
+/*
63
+ * VMApple Configuration Region
64
+ *
65
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
66
+ *
67
+ * SPDX-License-Identifier: GPL-2.0-or-later
68
+ *
69
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
70
+ * See the COPYING file in the top-level directory.
71
+ */
72
+
73
+#include "qemu/osdep.h"
74
+#include "hw/vmapple/vmapple.h"
75
+#include "hw/sysbus.h"
76
+#include "qemu/log.h"
77
+#include "qemu/module.h"
78
+#include "qapi/error.h"
79
+#include "net/net.h"
80
+
81
+OBJECT_DECLARE_SIMPLE_TYPE(VMAppleCfgState, VMAPPLE_CFG)
82
+
83
+#define VMAPPLE_CFG_SIZE 0x00010000
84
+
85
+typedef struct VMAppleCfg {
86
+ uint32_t version; /* 0x000 */
87
+ uint32_t nr_cpus; /* 0x004 */
88
+ uint32_t unk1; /* 0x008 */
89
+ uint32_t unk2; /* 0x00c */
90
+ uint32_t unk3; /* 0x010 */
91
+ uint32_t unk4; /* 0x014 */
92
+ uint64_t ecid; /* 0x018 */
93
+ uint64_t ram_size; /* 0x020 */
94
+ uint32_t run_installer1; /* 0x028 */
95
+ uint32_t unk5; /* 0x02c */
96
+ uint32_t unk6; /* 0x030 */
97
+ uint32_t run_installer2; /* 0x034 */
98
+ uint32_t rnd; /* 0x038 */
99
+ uint32_t unk7; /* 0x03c */
100
+ MACAddr mac_en0; /* 0x040 */
101
+ uint8_t pad1[2];
102
+ MACAddr mac_en1; /* 0x048 */
103
+ uint8_t pad2[2];
104
+ MACAddr mac_wifi0; /* 0x050 */
105
+ uint8_t pad3[2];
106
+ MACAddr mac_bt0; /* 0x058 */
107
+ uint8_t pad4[2];
108
+ uint8_t reserved[0xa0]; /* 0x060 */
109
+ uint32_t cpu_ids[0x80]; /* 0x100 */
110
+ uint8_t scratch[0x200]; /* 0x180 */
111
+ char serial[32]; /* 0x380 */
112
+ char unk8[32]; /* 0x3a0 */
113
+ char model[32]; /* 0x3c0 */
114
+ uint8_t unk9[32]; /* 0x3e0 */
115
+ uint32_t unk10; /* 0x400 */
116
+ char soc_name[32]; /* 0x404 */
117
+} VMAppleCfg;
118
+
119
+struct VMAppleCfgState {
120
+ SysBusDevice parent_obj;
121
+ VMAppleCfg cfg;
122
+
123
+ MemoryRegion mem;
124
+ char *serial;
125
+ char *model;
126
+ char *soc_name;
127
+};
128
+
129
+static void vmapple_cfg_reset(Object *obj, ResetType type)
130
+{
131
+ VMAppleCfgState *s = VMAPPLE_CFG(obj);
132
+ VMAppleCfg *cfg;
133
+
134
+ cfg = memory_region_get_ram_ptr(&s->mem);
135
+ memset(cfg, 0, VMAPPLE_CFG_SIZE);
136
+ *cfg = s->cfg;
137
+}
138
+
139
+static bool set_fixlen_property_or_error(char *restrict dst,
140
+ const char *restrict src,
141
+ size_t dst_size, Error **errp,
142
+ const char *property_name)
143
+{
144
+ size_t len;
145
+
146
+ len = g_strlcpy(dst, src, dst_size);
147
+ if (len < dst_size) { /* len does not count nul terminator */
148
+ return true;
149
+ }
150
+
151
+ error_setg(errp,
152
+ "Failed to set property '%s' on VMApple 'cfg' device: length "
153
+ "(%zu) exceeds maximum of %zu",
154
+ property_name, len, dst_size - 1);
155
+ return false;
156
+}
157
+
158
+#define set_fixlen_property_or_return(dst_array, src, errp, property_name) \
159
+ do { \
160
+ if (!set_fixlen_property_or_error((dst_array), (src), \
161
+ ARRAY_SIZE(dst_array), \
162
+ (errp), (property_name))) { \
163
+ return; \
164
+ } \
165
+ } while (0)
166
+
167
+static void vmapple_cfg_realize(DeviceState *dev, Error **errp)
168
+{
169
+ VMAppleCfgState *s = VMAPPLE_CFG(dev);
170
+ uint32_t i;
171
+
172
+ if (!s->serial) {
173
+ s->serial = g_strdup("1234");
174
+ }
175
+ if (!s->model) {
176
+ s->model = g_strdup("VM0001");
177
+ }
178
+ if (!s->soc_name) {
179
+ s->soc_name = g_strdup("Apple M1 (Virtual)");
180
+ }
181
+
182
+ set_fixlen_property_or_return(s->cfg.serial, s->serial, errp, "serial");
183
+ set_fixlen_property_or_return(s->cfg.model, s->model, errp, "model");
184
+ set_fixlen_property_or_return(s->cfg.soc_name, s->soc_name, errp, "soc_name");
185
+ set_fixlen_property_or_return(s->cfg.unk8, "D/A", errp, "unk8");
186
+ s->cfg.version = 2;
187
+ s->cfg.unk1 = 1;
188
+ s->cfg.unk2 = 1;
189
+ s->cfg.unk3 = 0x20;
190
+ s->cfg.unk4 = 0;
191
+ s->cfg.unk5 = 1;
192
+ s->cfg.unk6 = 1;
193
+ s->cfg.unk7 = 0;
194
+ s->cfg.unk10 = 1;
195
+
196
+ if (s->cfg.nr_cpus > ARRAY_SIZE(s->cfg.cpu_ids)) {
197
+ error_setg(errp,
198
+ "Failed to create %u CPUs, vmapple machine supports %zu max",
199
+ s->cfg.nr_cpus, ARRAY_SIZE(s->cfg.cpu_ids));
200
+ return;
201
+ }
202
+ for (i = 0; i < s->cfg.nr_cpus; i++) {
203
+ s->cfg.cpu_ids[i] = i;
204
+ }
205
+}
206
+
207
+static void vmapple_cfg_init(Object *obj)
208
+{
209
+ VMAppleCfgState *s = VMAPPLE_CFG(obj);
210
+
211
+ memory_region_init_ram(&s->mem, obj, "VMApple Config", VMAPPLE_CFG_SIZE,
212
+ &error_fatal);
213
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mem);
214
+}
215
+
216
+static Property vmapple_cfg_properties[] = {
217
+ DEFINE_PROP_UINT32("nr-cpus", VMAppleCfgState, cfg.nr_cpus, 1),
218
+ DEFINE_PROP_UINT64("ecid", VMAppleCfgState, cfg.ecid, 0),
219
+ DEFINE_PROP_UINT64("ram-size", VMAppleCfgState, cfg.ram_size, 0),
220
+ DEFINE_PROP_UINT32("run_installer1", VMAppleCfgState, cfg.run_installer1, 0),
221
+ DEFINE_PROP_UINT32("run_installer2", VMAppleCfgState, cfg.run_installer2, 0),
222
+ DEFINE_PROP_UINT32("rnd", VMAppleCfgState, cfg.rnd, 0),
223
+ DEFINE_PROP_MACADDR("mac-en0", VMAppleCfgState, cfg.mac_en0),
224
+ DEFINE_PROP_MACADDR("mac-en1", VMAppleCfgState, cfg.mac_en1),
225
+ DEFINE_PROP_MACADDR("mac-wifi0", VMAppleCfgState, cfg.mac_wifi0),
226
+ DEFINE_PROP_MACADDR("mac-bt0", VMAppleCfgState, cfg.mac_bt0),
227
+ DEFINE_PROP_STRING("serial", VMAppleCfgState, serial),
228
+ DEFINE_PROP_STRING("model", VMAppleCfgState, model),
229
+ DEFINE_PROP_STRING("soc_name", VMAppleCfgState, soc_name),
230
+ DEFINE_PROP_END_OF_LIST(),
231
+};
232
+
233
+static void vmapple_cfg_class_init(ObjectClass *klass, void *data)
234
+{
235
+ DeviceClass *dc = DEVICE_CLASS(klass);
236
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
237
+
238
+ dc->realize = vmapple_cfg_realize;
239
+ dc->desc = "VMApple Configuration Region";
240
+ device_class_set_props(dc, vmapple_cfg_properties);
241
+ rc->phases.hold = vmapple_cfg_reset;
242
+}
243
+
244
+static const TypeInfo vmapple_cfg_info = {
245
+ .name = TYPE_VMAPPLE_CFG,
246
+ .parent = TYPE_SYS_BUS_DEVICE,
247
+ .instance_size = sizeof(VMAppleCfgState),
248
+ .instance_init = vmapple_cfg_init,
249
+ .class_init = vmapple_cfg_class_init,
250
+};
251
+
252
+static void vmapple_cfg_register_types(void)
253
+{
254
+ type_register_static(&vmapple_cfg_info);
255
+}
256
+
257
+type_init(vmapple_cfg_register_types)
258
diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build
259
index XXXXXXX..XXXXXXX 100644
260
--- a/hw/vmapple/meson.build
261
+++ b/hw/vmapple/meson.build
262
@@ -XXX,XX +XXX,XX @@
263
system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c'))
264
system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c'))
265
+system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c'))
266
diff --git a/include/hw/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h
267
index XXXXXXX..XXXXXXX 100644
268
--- a/include/hw/vmapple/vmapple.h
269
+++ b/include/hw/vmapple/vmapple.h
270
@@ -XXX,XX +XXX,XX @@
271
272
#define TYPE_VMAPPLE_BDIF "vmapple-bdif"
273
274
+#define TYPE_VMAPPLE_CFG "vmapple-cfg"
275
+
276
#endif /* HW_VMAPPLE_VMAPPLE_H */
277
--
278
2.39.3 (Apple Git-145)
279
280
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Graf <graf@amazon.com>
2
1
3
Apple has its own virtio-blk PCI device ID where it deviates from the
4
official virtio-pci spec slightly: It puts a new "apple type"
5
field at a static offset in config space and introduces a new barrier
6
command.
7
8
This patch first creates a mechanism for virtio-blk downstream classes to
9
handle unknown commands. It then creates such a downstream class and a new
10
vmapple-virtio-blk-pci class which support the additional apple type config
11
identifier as well as the barrier command.
12
13
It then exposes 2 subclasses from that that we can use to expose root and
14
aux virtio-blk devices: "vmapple-virtio-root" and "vmapple-virtio-aux".
15
16
Signed-off-by: Alexander Graf <graf@amazon.com>
17
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
18
Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
19
---
20
21
v4:
22
23
* Use recommended object type declaration pattern.
24
* Correctly log unimplemented code paths.
25
* Most header code moved to .c, type name #defines moved to vmapple.h
26
27
v5:
28
29
* Corrected handling of potentially unaligned writes to virtio config area.
30
* Simplified passing through device variant type to subobject.
31
32
v9:
33
34
* Correctly specify class_size for VMAppleVirtIOBlkClass
35
36
hw/block/virtio-blk.c | 19 ++-
37
hw/vmapple/Kconfig | 3 +
38
hw/vmapple/meson.build | 1 +
39
hw/vmapple/virtio-blk.c | 227 +++++++++++++++++++++++++++++++++
40
include/hw/pci/pci_ids.h | 1 +
41
include/hw/virtio/virtio-blk.h | 12 +-
42
include/hw/vmapple/vmapple.h | 4 +
43
7 files changed, 262 insertions(+), 5 deletions(-)
44
create mode 100644 hw/vmapple/virtio-blk.c
45
46
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
47
index XXXXXXX..XXXXXXX 100644
48
--- a/hw/block/virtio-blk.c
49
+++ b/hw/block/virtio-blk.c
50
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq,
51
req->mr_next = NULL;
52
}
53
54
-static void virtio_blk_free_request(VirtIOBlockReq *req)
55
+void virtio_blk_free_request(VirtIOBlockReq *req)
56
{
57
g_free(req);
58
}
59
60
-static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
61
+void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
62
{
63
VirtIOBlock *s = req->dev;
64
VirtIODevice *vdev = VIRTIO_DEVICE(s);
65
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
66
break;
67
}
68
default:
69
- virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
70
- virtio_blk_free_request(req);
71
+ {
72
+ /*
73
+ * Give subclasses a chance to handle unknown requests. This way the
74
+ * class lookup is not in the hot path.
75
+ */
76
+ VirtIOBlkClass *vbk = VIRTIO_BLK_GET_CLASS(s);
77
+ if (!vbk->handle_unknown_request ||
78
+ !vbk->handle_unknown_request(req, mrb, type)) {
79
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
80
+ virtio_blk_free_request(req);
81
+ }
82
+ }
83
}
84
return 0;
85
}
86
@@ -XXX,XX +XXX,XX @@ static const TypeInfo virtio_blk_info = {
87
.instance_size = sizeof(VirtIOBlock),
88
.instance_init = virtio_blk_instance_init,
89
.class_init = virtio_blk_class_init,
90
+ .class_size = sizeof(VirtIOBlkClass),
91
};
92
93
static void virtio_register_types(void)
94
diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig
95
index XXXXXXX..XXXXXXX 100644
96
--- a/hw/vmapple/Kconfig
97
+++ b/hw/vmapple/Kconfig
98
@@ -XXX,XX +XXX,XX @@ config VMAPPLE_BDIF
99
config VMAPPLE_CFG
100
bool
101
102
+config VMAPPLE_VIRTIO_BLK
103
+ bool
104
+
105
diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build
106
index XXXXXXX..XXXXXXX 100644
107
--- a/hw/vmapple/meson.build
108
+++ b/hw/vmapple/meson.build
109
@@ -XXX,XX +XXX,XX @@
110
system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c'))
111
system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c'))
112
system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c'))
113
+system_ss.add(when: 'CONFIG_VMAPPLE_VIRTIO_BLK', if_true: files('virtio-blk.c'))
114
diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c
115
new file mode 100644
116
index XXXXXXX..XXXXXXX
117
--- /dev/null
118
+++ b/hw/vmapple/virtio-blk.c
119
@@ -XXX,XX +XXX,XX @@
120
+/*
121
+ * VMApple specific VirtIO Block implementation
122
+ *
123
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
124
+ *
125
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
126
+ * See the COPYING file in the top-level directory.
127
+ *
128
+ * SPDX-License-Identifier: GPL-2.0-or-later
129
+ *
130
+ * VMApple uses almost standard VirtIO Block, but with a few key differences:
131
+ *
132
+ * - Different PCI device/vendor ID
133
+ * - An additional "type" identifier to differentiate AUX and Root volumes
134
+ * - An additional BARRIER command
135
+ */
136
+
137
+#include "qemu/osdep.h"
138
+#include "hw/vmapple/vmapple.h"
139
+#include "hw/virtio/virtio-blk.h"
140
+#include "hw/virtio/virtio-pci.h"
141
+#include "qemu/bswap.h"
142
+#include "qemu/log.h"
143
+#include "qemu/module.h"
144
+#include "qapi/error.h"
145
+
146
+OBJECT_DECLARE_TYPE(VMAppleVirtIOBlk, VMAppleVirtIOBlkClass, VMAPPLE_VIRTIO_BLK)
147
+
148
+typedef struct VMAppleVirtIOBlkClass {
149
+ VirtIOBlkClass parent;
150
+
151
+ void (*get_config)(VirtIODevice *vdev, uint8_t *config);
152
+} VMAppleVirtIOBlkClass;
153
+
154
+typedef struct VMAppleVirtIOBlk {
155
+ VirtIOBlock parent_obj;
156
+
157
+ uint32_t apple_type;
158
+} VMAppleVirtIOBlk;
159
+
160
+/*
161
+ * vmapple-virtio-blk-pci: This extends VirtioPCIProxy.
162
+ */
163
+#define TYPE_VMAPPLE_VIRTIO_BLK_PCI "vmapple-virtio-blk-pci-base"
164
+OBJECT_DECLARE_SIMPLE_TYPE(VMAppleVirtIOBlkPCI, VMAPPLE_VIRTIO_BLK_PCI)
165
+
166
+#define VIRTIO_BLK_T_APPLE_BARRIER 0x10000
167
+
168
+#define VIRTIO_APPLE_TYPE_ROOT 1
169
+#define VIRTIO_APPLE_TYPE_AUX 2
170
+
171
+static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req,
172
+ MultiReqBuffer *mrb,
173
+ uint32_t type)
174
+{
175
+ switch (type) {
176
+ case VIRTIO_BLK_T_APPLE_BARRIER:
177
+ qemu_log_mask(LOG_UNIMP, "%s: Barrier requests are currently no-ops\n",
178
+ __func__);
179
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
180
+ virtio_blk_free_request(req);
181
+ return true;
182
+ default:
183
+ return false;
184
+ }
185
+}
186
+
187
+/*
188
+ * VMApple virtio-blk uses the same config format as normal virtio, with one
189
+ * exception: It adds an "apple type" specififer at the same location that
190
+ * the spec reserves for max_secure_erase_sectors. Let's hook into the
191
+ * get_config code path here, run it as usual and then patch in the apple type.
192
+ */
193
+static void vmapple_virtio_blk_get_config(VirtIODevice *vdev, uint8_t *config)
194
+{
195
+ VMAppleVirtIOBlk *dev = VMAPPLE_VIRTIO_BLK(vdev);
196
+ VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_GET_CLASS(dev);
197
+ struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config;
198
+
199
+ vvbk->get_config(vdev, config);
200
+
201
+ g_assert(dev->parent_obj.config_size >= endof(struct virtio_blk_config, zoned));
202
+
203
+ /* Apple abuses the field for max_secure_erase_sectors as type id */
204
+ stl_he_p(&blkcfg->max_secure_erase_sectors, dev->apple_type);
205
+}
206
+
207
+static void vmapple_virtio_blk_class_init(ObjectClass *klass, void *data)
208
+{
209
+ VirtIOBlkClass *vbk = VIRTIO_BLK_CLASS(klass);
210
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
211
+ VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_CLASS(klass);
212
+
213
+ vbk->handle_unknown_request = vmapple_virtio_blk_handle_unknown_request;
214
+ vvbk->get_config = vdc->get_config;
215
+ vdc->get_config = vmapple_virtio_blk_get_config;
216
+}
217
+
218
+static const TypeInfo vmapple_virtio_blk_info = {
219
+ .name = TYPE_VMAPPLE_VIRTIO_BLK,
220
+ .parent = TYPE_VIRTIO_BLK,
221
+ .instance_size = sizeof(VMAppleVirtIOBlk),
222
+ .class_size = sizeof(VMAppleVirtIOBlkClass),
223
+ .class_init = vmapple_virtio_blk_class_init,
224
+};
225
+
226
+/* PCI Devices */
227
+
228
+struct VMAppleVirtIOBlkPCI {
229
+ VirtIOPCIProxy parent_obj;
230
+ VMAppleVirtIOBlk vdev;
231
+ uint32_t apple_type;
232
+};
233
+
234
+
235
+static Property vmapple_virtio_blk_pci_properties[] = {
236
+ DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
237
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
238
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
239
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
240
+ DEV_NVECTORS_UNSPECIFIED),
241
+ DEFINE_PROP_END_OF_LIST(),
242
+};
243
+
244
+static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
245
+{
246
+ VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev);
247
+ DeviceState *vdev = DEVICE(&dev->vdev);
248
+ VirtIOBlkConf *conf = &dev->vdev.parent_obj.conf;
249
+
250
+ if (conf->num_queues == VIRTIO_BLK_AUTO_NUM_QUEUES) {
251
+ conf->num_queues = virtio_pci_optimal_num_queues(0);
252
+ }
253
+
254
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
255
+ vpci_dev->nvectors = conf->num_queues + 1;
256
+ }
257
+
258
+ /*
259
+ * We don't support zones, but we need the additional config space size.
260
+ * Let's just expose the feature so the rest of the virtio-blk logic
261
+ * allocates enough space for us. The guest will ignore zones anyway.
262
+ */
263
+ virtio_add_feature(&dev->vdev.parent_obj.host_features, VIRTIO_BLK_F_ZONED);
264
+ /* Propagate the apple type down to the virtio-blk device */
265
+ dev->vdev.apple_type = dev->apple_type;
266
+ /* and spawn the virtio-blk device */
267
+ qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
268
+
269
+ /*
270
+ * The virtio-pci machinery adjusts its vendor/device ID based on whether
271
+ * we support modern or legacy virtio. Let's patch it back to the Apple
272
+ * identifiers here.
273
+ */
274
+ pci_config_set_vendor_id(vpci_dev->pci_dev.config, PCI_VENDOR_ID_APPLE);
275
+ pci_config_set_device_id(vpci_dev->pci_dev.config,
276
+ PCI_DEVICE_ID_APPLE_VIRTIO_BLK);
277
+}
278
+
279
+static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass, void *data)
280
+{
281
+ DeviceClass *dc = DEVICE_CLASS(klass);
282
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
283
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
284
+
285
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
286
+ device_class_set_props(dc, vmapple_virtio_blk_pci_properties);
287
+ k->realize = vmapple_virtio_blk_pci_realize;
288
+ pcidev_k->vendor_id = PCI_VENDOR_ID_APPLE;
289
+ pcidev_k->device_id = PCI_DEVICE_ID_APPLE_VIRTIO_BLK;
290
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
291
+ pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
292
+}
293
+
294
+static void vmapple_virtio_blk_pci_instance_init(Object *obj)
295
+{
296
+ VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj);
297
+
298
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
299
+ TYPE_VMAPPLE_VIRTIO_BLK);
300
+}
301
+
302
+static const VirtioPCIDeviceTypeInfo vmapple_virtio_blk_pci_info = {
303
+ .base_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI,
304
+ .generic_name = "vmapple-virtio-blk-pci",
305
+ .instance_size = sizeof(VMAppleVirtIOBlkPCI),
306
+ .instance_init = vmapple_virtio_blk_pci_instance_init,
307
+ .class_init = vmapple_virtio_blk_pci_class_init,
308
+};
309
+
310
+static void vmapple_virtio_root_instance_init(Object *obj)
311
+{
312
+ VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj);
313
+
314
+ dev->apple_type = VIRTIO_APPLE_TYPE_ROOT;
315
+}
316
+
317
+static const TypeInfo vmapple_virtio_root_info = {
318
+ .name = TYPE_VMAPPLE_VIRTIO_ROOT,
319
+ .parent = "vmapple-virtio-blk-pci",
320
+ .instance_size = sizeof(VMAppleVirtIOBlkPCI),
321
+ .instance_init = vmapple_virtio_root_instance_init,
322
+};
323
+
324
+static void vmapple_virtio_aux_instance_init(Object *obj)
325
+{
326
+ VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj);
327
+
328
+ dev->apple_type = VIRTIO_APPLE_TYPE_AUX;
329
+}
330
+
331
+static const TypeInfo vmapple_virtio_aux_info = {
332
+ .name = TYPE_VMAPPLE_VIRTIO_AUX,
333
+ .parent = "vmapple-virtio-blk-pci",
334
+ .instance_size = sizeof(VMAppleVirtIOBlkPCI),
335
+ .instance_init = vmapple_virtio_aux_instance_init,
336
+};
337
+
338
+static void vmapple_virtio_blk_register_types(void)
339
+{
340
+ type_register_static(&vmapple_virtio_blk_info);
341
+ virtio_pci_types_register(&vmapple_virtio_blk_pci_info);
342
+ type_register_static(&vmapple_virtio_root_info);
343
+ type_register_static(&vmapple_virtio_aux_info);
344
+}
345
+
346
+type_init(vmapple_virtio_blk_register_types)
347
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
348
index XXXXXXX..XXXXXXX 100644
349
--- a/include/hw/pci/pci_ids.h
350
+++ b/include/hw/pci/pci_ids.h
351
@@ -XXX,XX +XXX,XX @@
352
#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020
353
#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b
354
#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021
355
+#define PCI_DEVICE_ID_APPLE_VIRTIO_BLK 0x1a00
356
357
#define PCI_VENDOR_ID_SUN 0x108e
358
#define PCI_DEVICE_ID_SUN_EBUS 0x1000
359
diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h
360
index XXXXXXX..XXXXXXX 100644
361
--- a/include/hw/virtio/virtio-blk.h
362
+++ b/include/hw/virtio/virtio-blk.h
363
@@ -XXX,XX +XXX,XX @@
364
#include "qapi/qapi-types-virtio.h"
365
366
#define TYPE_VIRTIO_BLK "virtio-blk-device"
367
-OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBlock, VIRTIO_BLK)
368
+OBJECT_DECLARE_TYPE(VirtIOBlock, VirtIOBlkClass, VIRTIO_BLK)
369
370
/* This is the last element of the write scatter-gather list */
371
struct virtio_blk_inhdr
372
@@ -XXX,XX +XXX,XX @@ typedef struct MultiReqBuffer {
373
bool is_write;
374
} MultiReqBuffer;
375
376
+typedef struct VirtIOBlkClass {
377
+ /*< private >*/
378
+ VirtioDeviceClass parent;
379
+ /*< public >*/
380
+ bool (*handle_unknown_request)(VirtIOBlockReq *req, MultiReqBuffer *mrb,
381
+ uint32_t type);
382
+} VirtIOBlkClass;
383
+
384
void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq);
385
+void virtio_blk_free_request(VirtIOBlockReq *req);
386
+void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status);
387
388
#endif
389
diff --git a/include/hw/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h
390
index XXXXXXX..XXXXXXX 100644
391
--- a/include/hw/vmapple/vmapple.h
392
+++ b/include/hw/vmapple/vmapple.h
393
@@ -XXX,XX +XXX,XX @@
394
395
#define TYPE_VMAPPLE_CFG "vmapple-cfg"
396
397
+#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk"
398
+#define TYPE_VMAPPLE_VIRTIO_ROOT "vmapple-virtio-root"
399
+#define TYPE_VMAPPLE_VIRTIO_AUX "vmapple-virtio-aux"
400
+
401
#endif /* HW_VMAPPLE_VMAPPLE_H */
402
--
403
2.39.3 (Apple Git-145)
404
405
diff view generated by jsdifflib
Deleted patch
1
The virtio_blk_free_request() function has been a 1-liner forwarding
2
to g_free() for a while now. We may as well call g_free on the request
3
pointer directly.
4
1
5
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
6
Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
7
---
8
hw/block/virtio-blk.c | 43 +++++++++++++++-------------------
9
hw/vmapple/virtio-blk.c | 2 +-
10
include/hw/virtio/virtio-blk.h | 1 -
11
3 files changed, 20 insertions(+), 26 deletions(-)
12
13
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/hw/block/virtio-blk.c
16
+++ b/hw/block/virtio-blk.c
17
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq,
18
req->mr_next = NULL;
19
}
20
21
-void virtio_blk_free_request(VirtIOBlockReq *req)
22
-{
23
- g_free(req);
24
-}
25
-
26
void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
27
{
28
VirtIOBlock *s = req->dev;
29
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
30
if (acct_failed) {
31
block_acct_failed(blk_get_stats(s->blk), &req->acct);
32
}
33
- virtio_blk_free_request(req);
34
+ g_free(req);
35
}
36
37
blk_error_action(s->blk, action, is_read, error);
38
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_rw_complete(void *opaque, int ret)
39
40
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
41
block_acct_done(blk_get_stats(s->blk), &req->acct);
42
- virtio_blk_free_request(req);
43
+ g_free(req);
44
}
45
}
46
47
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_flush_complete(void *opaque, int ret)
48
49
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
50
block_acct_done(blk_get_stats(s->blk), &req->acct);
51
- virtio_blk_free_request(req);
52
+ g_free(req);
53
}
54
55
static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret)
56
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret)
57
if (is_write_zeroes) {
58
block_acct_done(blk_get_stats(s->blk), &req->acct);
59
}
60
- virtio_blk_free_request(req);
61
+ g_free(req);
62
}
63
64
static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq)
65
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
66
67
fail:
68
virtio_blk_req_complete(req, status);
69
- virtio_blk_free_request(req);
70
+ g_free(req);
71
}
72
73
static inline void submit_requests(VirtIOBlock *s, MultiReqBuffer *mrb,
74
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_zone_report_complete(void *opaque, int ret)
75
76
out:
77
virtio_blk_req_complete(req, err_status);
78
- virtio_blk_free_request(req);
79
+ g_free(req);
80
g_free(data->zone_report_data.zones);
81
g_free(data);
82
}
83
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_handle_zone_report(VirtIOBlockReq *req,
84
return;
85
out:
86
virtio_blk_req_complete(req, err_status);
87
- virtio_blk_free_request(req);
88
+ g_free(req);
89
}
90
91
static void virtio_blk_zone_mgmt_complete(void *opaque, int ret)
92
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_zone_mgmt_complete(void *opaque, int ret)
93
}
94
95
virtio_blk_req_complete(req, err_status);
96
- virtio_blk_free_request(req);
97
+ g_free(req);
98
}
99
100
static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op)
101
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op)
102
return 0;
103
out:
104
virtio_blk_req_complete(req, err_status);
105
- virtio_blk_free_request(req);
106
+ g_free(req);
107
return err_status;
108
}
109
110
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_zone_append_complete(void *opaque, int ret)
111
112
out:
113
virtio_blk_req_complete(req, err_status);
114
- virtio_blk_free_request(req);
115
+ g_free(req);
116
g_free(data);
117
}
118
119
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_zone_append(VirtIOBlockReq *req,
120
121
out:
122
virtio_blk_req_complete(req, err_status);
123
- virtio_blk_free_request(req);
124
+ g_free(req);
125
return err_status;
126
}
127
128
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
129
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
130
block_acct_invalid(blk_get_stats(s->blk),
131
is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
132
- virtio_blk_free_request(req);
133
+ g_free(req);
134
return 0;
135
}
136
137
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
138
VIRTIO_BLK_ID_BYTES));
139
iov_from_buf(in_iov, in_num, 0, serial, size);
140
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
141
- virtio_blk_free_request(req);
142
+ g_free(req);
143
break;
144
}
145
case VIRTIO_BLK_T_ZONE_APPEND & ~VIRTIO_BLK_T_OUT:
146
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
147
if (unlikely(!(type & VIRTIO_BLK_T_OUT) ||
148
out_len > sizeof(dwz_hdr))) {
149
virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
150
- virtio_blk_free_request(req);
151
+ g_free(req);
152
return 0;
153
}
154
155
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
156
is_write_zeroes);
157
if (err_status != VIRTIO_BLK_S_OK) {
158
virtio_blk_req_complete(req, err_status);
159
- virtio_blk_free_request(req);
160
+ g_free(req);
161
}
162
163
break;
164
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
165
if (!vbk->handle_unknown_request ||
166
!vbk->handle_unknown_request(req, mrb, type)) {
167
virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
168
- virtio_blk_free_request(req);
169
+ g_free(req);
170
}
171
}
172
}
173
@@ -XXX,XX +XXX,XX @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
174
while ((req = virtio_blk_get_request(s, vq))) {
175
if (virtio_blk_handle_request(req, &mrb)) {
176
virtqueue_detach_element(req->vq, &req->elem, 0);
177
- virtio_blk_free_request(req);
178
+ g_free(req);
179
break;
180
}
181
}
182
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_dma_restart_bh(void *opaque)
183
while (req) {
184
next = req->next;
185
virtqueue_detach_element(req->vq, &req->elem, 0);
186
- virtio_blk_free_request(req);
187
+ g_free(req);
188
req = next;
189
}
190
break;
191
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_reset(VirtIODevice *vdev)
192
/* No other threads can access req->vq here */
193
virtqueue_detach_element(req->vq, &req->elem, 0);
194
195
- virtio_blk_free_request(req);
196
+ g_free(req);
197
}
198
}
199
200
diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c
201
index XXXXXXX..XXXXXXX 100644
202
--- a/hw/vmapple/virtio-blk.c
203
+++ b/hw/vmapple/virtio-blk.c
204
@@ -XXX,XX +XXX,XX @@ static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req,
205
qemu_log_mask(LOG_UNIMP, "%s: Barrier requests are currently no-ops\n",
206
__func__);
207
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
208
- virtio_blk_free_request(req);
209
+ g_free(req);
210
return true;
211
default:
212
return false;
213
diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h
214
index XXXXXXX..XXXXXXX 100644
215
--- a/include/hw/virtio/virtio-blk.h
216
+++ b/include/hw/virtio/virtio-blk.h
217
@@ -XXX,XX +XXX,XX @@ typedef struct VirtIOBlkClass {
218
} VirtIOBlkClass;
219
220
void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq);
221
-void virtio_blk_free_request(VirtIOBlockReq *req);
222
void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status);
223
224
#endif
225
--
226
2.39.3 (Apple Git-145)
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Graf <graf@amazon.com>
2
1
3
Apple defines a new "vmapple" machine type as part of its proprietary
4
macOS Virtualization.Framework vmm. This machine type is similar to the
5
virt one, but with subtle differences in base devices, a few special
6
vmapple device additions and a vastly different boot chain.
7
8
This patch reimplements this machine type in QEMU. To use it, you
9
have to have a readily installed version of macOS for VMApple,
10
run on macOS with -accel hvf, pass the Virtualization.Framework
11
boot rom (AVPBooter) in via -bios, pass the aux and root volume as pflash
12
and pass aux and root volume as virtio drives. In addition, you also
13
need to find the machine UUID and pass that as -M vmapple,uuid= parameter:
14
15
$ qemu-system-aarch64 -accel hvf -M vmapple,uuid=0x1234 -m 4G \
16
-bios /System/Library/Frameworks/Virtualization.framework/Versions/A/Resources/AVPBooter.vmapple2.bin
17
-drive file=aux,if=pflash,format=raw \
18
-drive file=root,if=pflash,format=raw \
19
-drive file=aux,if=none,id=aux,format=raw \
20
-device vmapple-virtio-aux,drive=aux \
21
-drive file=root,if=none,id=root,format=raw \
22
-device vmapple-virtio-root,drive=root
23
24
With all these in place, you should be able to see macOS booting
25
successfully.
26
27
Known issues:
28
- Keyboard and mouse/tablet input is laggy. The reason for this is
29
either that macOS's XHCI driver is broken when the device/platform
30
does not support MSI/MSI-X, or there's some unfortunate interplay
31
with Qemu's XHCI implementation in this scenario.
32
- Currently only macOS 12 guests are supported. The boot process for
33
13+ will need further investigation and adjustment.
34
35
Signed-off-by: Alexander Graf <graf@amazon.com>
36
Co-authored-by: Phil Dennis-Jordan <phil@philjordan.eu>
37
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
38
---
39
40
v3:
41
* Rebased on latest upstream, updated affinity and NIC creation
42
API usage
43
* Included Apple-variant virtio-blk in build dependency
44
* Updated API usage for setting 'redist-region-count' array-typed property on GIC.
45
* Switched from virtio HID devices (for which macOS 12 does not contain
46
drivers) to an XHCI USB controller and USB HID devices.
47
48
v4:
49
* Fixups for v4 changes to the other patches in the set.
50
* Corrected the assert macro to use
51
* Removed superfluous endian conversions corresponding to cfg's.
52
* Init error handling improvement.
53
* No need to select CPU type on TCG, as only HVF is supported.
54
* Machine type version bumped to 9.2
55
* #include order improved
56
57
v5:
58
* Fixed memory reservation for ecam alias region.
59
* Better error handling setting properties on devices.
60
* Simplified the machine ECID/UUID extraction script and actually created a
61
file for it rather than quoting its code in documentation.
62
63
v7:
64
* Tiny error handling fix, un-inlined function.
65
66
v8:
67
* Use object_property_add_uint64_ptr rather than defining custom UUID
68
property get/set functions.
69
70
v9:
71
* Documentation improvements
72
* Fixed variable name and struct field used during pvpanic device creation.
73
74
75
MAINTAINERS | 1 +
76
contrib/vmapple/uuid.sh | 9 +
77
docs/system/arm/vmapple.rst | 63 ++++
78
docs/system/target-arm.rst | 1 +
79
hw/vmapple/Kconfig | 20 ++
80
hw/vmapple/meson.build | 1 +
81
hw/vmapple/vmapple.c | 639 ++++++++++++++++++++++++++++++++++++
82
7 files changed, 734 insertions(+)
83
create mode 100755 contrib/vmapple/uuid.sh
84
create mode 100644 docs/system/arm/vmapple.rst
85
create mode 100644 hw/vmapple/vmapple.c
86
87
diff --git a/MAINTAINERS b/MAINTAINERS
88
index XXXXXXX..XXXXXXX 100644
89
--- a/MAINTAINERS
90
+++ b/MAINTAINERS
91
@@ -XXX,XX +XXX,XX @@ R: Phil Dennis-Jordan <phil@philjordan.eu>
92
S: Maintained
93
F: hw/vmapple/*
94
F: include/hw/vmapple/*
95
+F: docs/system/arm/vmapple.rst
96
97
Subsystems
98
----------
99
diff --git a/contrib/vmapple/uuid.sh b/contrib/vmapple/uuid.sh
100
new file mode 100755
101
index XXXXXXX..XXXXXXX
102
--- /dev/null
103
+++ b/contrib/vmapple/uuid.sh
104
@@ -XXX,XX +XXX,XX @@
105
+#!/bin/sh
106
+# Used for converting a guest provisioned using Virtualization.framework
107
+# for use with the QEMU 'vmapple' aarch64 machine type.
108
+#
109
+# Extracts the Machine UUID from Virtualization.framework VM JSON file.
110
+# (as produced by 'macosvm', passed as command line argument)
111
+
112
+plutil -extract machineId raw "$1" | base64 -d | plutil -extract ECID raw -
113
+
114
diff --git a/docs/system/arm/vmapple.rst b/docs/system/arm/vmapple.rst
115
new file mode 100644
116
index XXXXXXX..XXXXXXX
117
--- /dev/null
118
+++ b/docs/system/arm/vmapple.rst
119
@@ -XXX,XX +XXX,XX @@
120
+VMApple machine emulation
121
+========================================================================================
122
+
123
+VMApple is the device model that the macOS built-in hypervisor called "Virtualization.framework"
124
+exposes to Apple Silicon macOS guests. The "vmapple" machine model in QEMU implements the same
125
+device model, but does not use any code from Virtualization.Framework.
126
+
127
+Prerequisites
128
+-------------
129
+
130
+To run the vmapple machine model, you need to
131
+
132
+ * Run on Apple Silicon
133
+ * Run on macOS 12.0 or above
134
+ * Have an already installed copy of a Virtualization.Framework macOS 12 virtual
135
+ machine. Note that newer versions than 12.x are currently NOT supported on
136
+ the guest side. I will assume that you installed it using the
137
+ `macosvm <https://github.com/s-u/macosvm>` CLI.
138
+
139
+First, we need to extract the UUID from the virtual machine that you installed. You can do this
140
+by running the shell script in contrib/vmapple/uuid.sh on the macosvm.json file.
141
+
142
+.. code-block:: bash
143
+ :caption: uuid.sh script to extract the UUID from a macosvm.json file
144
+
145
+ $ contrib/vmapple/uuid.sh "path/to/macosvm.json"
146
+
147
+Now we also need to trim the aux partition. It contains metadata that we can just discard:
148
+
149
+.. code-block:: bash
150
+ :caption: Command to trim the aux file
151
+
152
+ $ dd if="aux.img" of="aux.img.trimmed" bs=$(( 0x4000 )) skip=1
153
+
154
+How to run
155
+----------
156
+
157
+Then, we can launch QEMU with the Virtualization.Framework pre-boot environment and the readily
158
+installed target disk images. I recommend to port forward the VM's ssh and vnc ports to the host
159
+to get better interactive access into the target system:
160
+
161
+.. code-block:: bash
162
+ :caption: Example execution command line
163
+
164
+ $ UUID=$(uuid.sh macosvm.json)
165
+ $ AVPBOOTER=/System/Library/Frameworks/Virtualization.framework/Resources/AVPBooter.vmapple2.bin
166
+ $ AUX=aux.img.trimmed
167
+ $ DISK=disk.img
168
+ $ qemu-system-aarch64 \
169
+ -serial mon:stdio \
170
+ -m 4G \
171
+ -accel hvf \
172
+ -M vmapple,uuid="$UUID" \
173
+ -bios "$AVPBOOTER" \
174
+ -drive file="$AUX",if=pflash,format=raw \
175
+ -drive file="$DISK",if=pflash,format=raw \
176
+ -drive file="$AUX",if=none,id=aux,format=raw \
177
+ -drive file="$DISK",if=none,id=root,format=raw \
178
+ -device vmapple-virtio-aux,drive=aux \
179
+ -device vmapple-virtio-root,drive=root \
180
+ -netdev user,id=net0,ipv6=off,hostfwd=tcp::2222-:22,hostfwd=tcp::5901-:5900 \
181
+ -device virtio-net-pci,netdev=net0
182
+
183
diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst
184
index XXXXXXX..XXXXXXX 100644
185
--- a/docs/system/target-arm.rst
186
+++ b/docs/system/target-arm.rst
187
@@ -XXX,XX +XXX,XX @@ Board-specific documentation
188
arm/stellaris
189
arm/stm32
190
arm/virt
191
+ arm/vmapple
192
arm/xenpvh
193
arm/xlnx-versal-virt
194
arm/xlnx-zynq
195
diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig
196
index XXXXXXX..XXXXXXX 100644
197
--- a/hw/vmapple/Kconfig
198
+++ b/hw/vmapple/Kconfig
199
@@ -XXX,XX +XXX,XX @@ config VMAPPLE_CFG
200
config VMAPPLE_VIRTIO_BLK
201
bool
202
203
+config VMAPPLE
204
+ bool
205
+ depends on ARM
206
+ depends on HVF
207
+ default y if ARM
208
+ imply PCI_DEVICES
209
+ select ARM_GIC
210
+ select PLATFORM_BUS
211
+ select PCI_EXPRESS
212
+ select PCI_EXPRESS_GENERIC_BRIDGE
213
+ select PL011 # UART
214
+ select PL031 # RTC
215
+ select PL061 # GPIO
216
+ select GPIO_PWR
217
+ select PVPANIC_MMIO
218
+ select VMAPPLE_AES
219
+ select VMAPPLE_BDIF
220
+ select VMAPPLE_CFG
221
+ select MAC_PVG_MMIO
222
+ select VMAPPLE_VIRTIO_BLK
223
diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build
224
index XXXXXXX..XXXXXXX 100644
225
--- a/hw/vmapple/meson.build
226
+++ b/hw/vmapple/meson.build
227
@@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c'))
228
system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c'))
229
system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c'))
230
system_ss.add(when: 'CONFIG_VMAPPLE_VIRTIO_BLK', if_true: files('virtio-blk.c'))
231
+specific_ss.add(when: 'CONFIG_VMAPPLE', if_true: files('vmapple.c'))
232
diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c
233
new file mode 100644
234
index XXXXXXX..XXXXXXX
235
--- /dev/null
236
+++ b/hw/vmapple/vmapple.c
237
@@ -XXX,XX +XXX,XX @@
238
+/*
239
+ * VMApple machine emulation
240
+ *
241
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
242
+ *
243
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
244
+ * See the COPYING file in the top-level directory.
245
+ *
246
+ * SPDX-License-Identifier: GPL-2.0-or-later
247
+ *
248
+ * VMApple is the device model that the macOS built-in hypervisor called
249
+ * "Virtualization.framework" exposes to Apple Silicon macOS guests. The
250
+ * machine model in this file implements the same device model in QEMU, but
251
+ * does not use any code from Virtualization.Framework.
252
+ */
253
+
254
+#include "qemu/osdep.h"
255
+#include "qemu/bitops.h"
256
+#include "qemu/datadir.h"
257
+#include "qemu/error-report.h"
258
+#include "qemu/guest-random.h"
259
+#include "qemu/help-texts.h"
260
+#include "qemu/log.h"
261
+#include "qemu/module.h"
262
+#include "qemu/option.h"
263
+#include "qemu/units.h"
264
+#include "monitor/qdev.h"
265
+#include "hw/boards.h"
266
+#include "hw/irq.h"
267
+#include "hw/loader.h"
268
+#include "hw/qdev-properties.h"
269
+#include "hw/sysbus.h"
270
+#include "hw/usb.h"
271
+#include "hw/arm/boot.h"
272
+#include "hw/arm/primecell.h"
273
+#include "hw/char/pl011.h"
274
+#include "hw/intc/arm_gic.h"
275
+#include "hw/intc/arm_gicv3_common.h"
276
+#include "hw/misc/pvpanic.h"
277
+#include "hw/pci-host/gpex.h"
278
+#include "hw/usb/xhci.h"
279
+#include "hw/virtio/virtio-pci.h"
280
+#include "hw/vmapple/vmapple.h"
281
+#include "net/net.h"
282
+#include "qapi/error.h"
283
+#include "qapi/qmp/qlist.h"
284
+#include "qapi/visitor.h"
285
+#include "qapi/qapi-visit-common.h"
286
+#include "standard-headers/linux/input.h"
287
+#include "sysemu/hvf.h"
288
+#include "sysemu/kvm.h"
289
+#include "sysemu/reset.h"
290
+#include "sysemu/runstate.h"
291
+#include "sysemu/sysemu.h"
292
+#include "target/arm/internals.h"
293
+#include "target/arm/kvm_arm.h"
294
+
295
+struct VMAppleMachineClass {
296
+ MachineClass parent;
297
+};
298
+
299
+struct VMAppleMachineState {
300
+ MachineState parent;
301
+
302
+ Notifier machine_done;
303
+ struct arm_boot_info bootinfo;
304
+ MemMapEntry *memmap;
305
+ const int *irqmap;
306
+ DeviceState *gic;
307
+ DeviceState *cfg;
308
+ DeviceState *pvpanic;
309
+ Notifier powerdown_notifier;
310
+ PCIBus *bus;
311
+ MemoryRegion fw_mr;
312
+ MemoryRegion ecam_alias;
313
+ uint64_t uuid;
314
+};
315
+
316
+#define DEFINE_VMAPPLE_MACHINE_LATEST(major, minor, latest) \
317
+ static void vmapple##major##_##minor##_class_init(ObjectClass *oc, \
318
+ void *data) \
319
+ { \
320
+ MachineClass *mc = MACHINE_CLASS(oc); \
321
+ vmapple_machine_##major##_##minor##_options(mc); \
322
+ mc->desc = "QEMU " # major "." # minor " Apple Virtual Machine"; \
323
+ if (latest) { \
324
+ mc->alias = "vmapple"; \
325
+ } \
326
+ } \
327
+ static const TypeInfo machvmapple##major##_##minor##_info = { \
328
+ .name = MACHINE_TYPE_NAME("vmapple-" # major "." # minor), \
329
+ .parent = TYPE_VMAPPLE_MACHINE, \
330
+ .class_init = vmapple##major##_##minor##_class_init, \
331
+ }; \
332
+ static void machvmapple_machine_##major##_##minor##_init(void) \
333
+ { \
334
+ type_register_static(&machvmapple##major##_##minor##_info); \
335
+ } \
336
+ type_init(machvmapple_machine_##major##_##minor##_init);
337
+
338
+#define DEFINE_VMAPPLE_MACHINE_AS_LATEST(major, minor) \
339
+ DEFINE_VMAPPLE_MACHINE_LATEST(major, minor, true)
340
+#define DEFINE_VMAPPLE_MACHINE(major, minor) \
341
+ DEFINE_VMAPPLE_MACHINE_LATEST(major, minor, false)
342
+
343
+#define TYPE_VMAPPLE_MACHINE MACHINE_TYPE_NAME("vmapple")
344
+OBJECT_DECLARE_TYPE(VMAppleMachineState, VMAppleMachineClass, VMAPPLE_MACHINE)
345
+
346
+/* Number of external interrupt lines to configure the GIC with */
347
+#define NUM_IRQS 256
348
+
349
+enum {
350
+ VMAPPLE_FIRMWARE,
351
+ VMAPPLE_CONFIG,
352
+ VMAPPLE_MEM,
353
+ VMAPPLE_GIC_DIST,
354
+ VMAPPLE_GIC_REDIST,
355
+ VMAPPLE_UART,
356
+ VMAPPLE_RTC,
357
+ VMAPPLE_PCIE,
358
+ VMAPPLE_PCIE_MMIO,
359
+ VMAPPLE_PCIE_ECAM,
360
+ VMAPPLE_GPIO,
361
+ VMAPPLE_PVPANIC,
362
+ VMAPPLE_APV_GFX,
363
+ VMAPPLE_APV_IOSFC,
364
+ VMAPPLE_AES_1,
365
+ VMAPPLE_AES_2,
366
+ VMAPPLE_BDOOR,
367
+ VMAPPLE_MEMMAP_LAST,
368
+};
369
+
370
+static MemMapEntry memmap[] = {
371
+ [VMAPPLE_FIRMWARE] = { 0x00100000, 0x00100000 },
372
+ [VMAPPLE_CONFIG] = { 0x00400000, 0x00010000 },
373
+
374
+ [VMAPPLE_GIC_DIST] = { 0x10000000, 0x00010000 },
375
+ [VMAPPLE_GIC_REDIST] = { 0x10010000, 0x00400000 },
376
+
377
+ [VMAPPLE_UART] = { 0x20010000, 0x00010000 },
378
+ [VMAPPLE_RTC] = { 0x20050000, 0x00001000 },
379
+ [VMAPPLE_GPIO] = { 0x20060000, 0x00001000 },
380
+ [VMAPPLE_PVPANIC] = { 0x20070000, 0x00000002 },
381
+ [VMAPPLE_BDOOR] = { 0x30000000, 0x00200000 },
382
+ [VMAPPLE_APV_GFX] = { 0x30200000, 0x00010000 },
383
+ [VMAPPLE_APV_IOSFC] = { 0x30210000, 0x00010000 },
384
+ [VMAPPLE_AES_1] = { 0x30220000, 0x00004000 },
385
+ [VMAPPLE_AES_2] = { 0x30230000, 0x00004000 },
386
+ [VMAPPLE_PCIE_ECAM] = { 0x40000000, 0x10000000 },
387
+ [VMAPPLE_PCIE_MMIO] = { 0x50000000, 0x1fff0000 },
388
+
389
+ /* Actual RAM size depends on configuration */
390
+ [VMAPPLE_MEM] = { 0x70000000ULL, GiB},
391
+};
392
+
393
+static const int irqmap[] = {
394
+ [VMAPPLE_UART] = 1,
395
+ [VMAPPLE_RTC] = 2,
396
+ [VMAPPLE_GPIO] = 0x5,
397
+ [VMAPPLE_APV_IOSFC] = 0x10,
398
+ [VMAPPLE_APV_GFX] = 0x11,
399
+ [VMAPPLE_AES_1] = 0x12,
400
+ [VMAPPLE_PCIE] = 0x20,
401
+};
402
+
403
+#define GPEX_NUM_IRQS 16
404
+
405
+static void create_bdif(VMAppleMachineState *vms, MemoryRegion *mem)
406
+{
407
+ DeviceState *bdif;
408
+ SysBusDevice *bdif_sb;
409
+ DriveInfo *di_aux = drive_get(IF_PFLASH, 0, 0);
410
+ DriveInfo *di_root = drive_get(IF_PFLASH, 0, 1);
411
+
412
+ if (!di_aux) {
413
+ error_report("No AUX device. Please specify one as pflash drive.");
414
+ exit(1);
415
+ }
416
+
417
+ if (!di_root) {
418
+ /* Fall back to the first IF_VIRTIO device as root device */
419
+ di_root = drive_get(IF_VIRTIO, 0, 0);
420
+ }
421
+
422
+ if (!di_root) {
423
+ error_report("No root device. Please specify one as virtio drive.");
424
+ exit(1);
425
+ }
426
+
427
+ /* PV backdoor device */
428
+ bdif = qdev_new(TYPE_VMAPPLE_BDIF);
429
+ bdif_sb = SYS_BUS_DEVICE(bdif);
430
+ sysbus_mmio_map(bdif_sb, 0, vms->memmap[VMAPPLE_BDOOR].base);
431
+
432
+ qdev_prop_set_drive(DEVICE(bdif), "aux", blk_by_legacy_dinfo(di_aux));
433
+ qdev_prop_set_drive(DEVICE(bdif), "root", blk_by_legacy_dinfo(di_root));
434
+
435
+ sysbus_realize_and_unref(bdif_sb, &error_fatal);
436
+}
437
+
438
+static void create_pvpanic(VMAppleMachineState *vms, MemoryRegion *mem)
439
+{
440
+ SysBusDevice *pvpanic;
441
+
442
+ vms->pvpanic = qdev_new(TYPE_PVPANIC_MMIO_DEVICE);
443
+ pvpanic = SYS_BUS_DEVICE(vms->pvpanic);
444
+ sysbus_mmio_map(pvpanic, 0, vms->memmap[VMAPPLE_PVPANIC].base);
445
+
446
+ sysbus_realize_and_unref(pvpanic, &error_fatal);
447
+}
448
+
449
+static void create_cfg(VMAppleMachineState *vms, MemoryRegion *mem)
450
+{
451
+ SysBusDevice *cfg;
452
+ MachineState *machine = MACHINE(vms);
453
+ uint32_t rnd = 1;
454
+
455
+ vms->cfg = qdev_new(TYPE_VMAPPLE_CFG);
456
+ cfg = SYS_BUS_DEVICE(vms->cfg);
457
+ sysbus_mmio_map(cfg, 0, vms->memmap[VMAPPLE_CONFIG].base);
458
+
459
+ qemu_guest_getrandom_nofail(&rnd, sizeof(rnd));
460
+
461
+ qdev_prop_set_uint32(vms->cfg, "nr-cpus", machine->smp.cpus);
462
+ qdev_prop_set_uint64(vms->cfg, "ecid", vms->uuid);
463
+ qdev_prop_set_uint64(vms->cfg, "ram-size", machine->ram_size);
464
+ qdev_prop_set_uint32(vms->cfg, "rnd", rnd);
465
+
466
+ sysbus_realize_and_unref(cfg, &error_fatal);
467
+}
468
+
469
+static void create_gfx(VMAppleMachineState *vms, MemoryRegion *mem)
470
+{
471
+ int irq_gfx = vms->irqmap[VMAPPLE_APV_GFX];
472
+ int irq_iosfc = vms->irqmap[VMAPPLE_APV_IOSFC];
473
+ SysBusDevice *gfx;
474
+
475
+ gfx = SYS_BUS_DEVICE(qdev_new("apple-gfx-mmio"));
476
+ sysbus_mmio_map(gfx, 0, vms->memmap[VMAPPLE_APV_GFX].base);
477
+ sysbus_mmio_map(gfx, 1, vms->memmap[VMAPPLE_APV_IOSFC].base);
478
+ sysbus_connect_irq(gfx, 0, qdev_get_gpio_in(vms->gic, irq_gfx));
479
+ sysbus_connect_irq(gfx, 1, qdev_get_gpio_in(vms->gic, irq_iosfc));
480
+ sysbus_realize_and_unref(gfx, &error_fatal);
481
+}
482
+
483
+static void create_aes(VMAppleMachineState *vms, MemoryRegion *mem)
484
+{
485
+ int irq = vms->irqmap[VMAPPLE_AES_1];
486
+ SysBusDevice *aes;
487
+
488
+ aes = SYS_BUS_DEVICE(qdev_new(TYPE_APPLE_AES));
489
+ sysbus_mmio_map(aes, 0, vms->memmap[VMAPPLE_AES_1].base);
490
+ sysbus_mmio_map(aes, 1, vms->memmap[VMAPPLE_AES_2].base);
491
+ sysbus_connect_irq(aes, 0, qdev_get_gpio_in(vms->gic, irq));
492
+ sysbus_realize_and_unref(aes, &error_fatal);
493
+}
494
+
495
+static int arm_gic_ppi_index(int cpu_nr, int ppi_index)
496
+{
497
+ return NUM_IRQS + cpu_nr * GIC_INTERNAL + ppi_index;
498
+}
499
+
500
+static void create_gic(VMAppleMachineState *vms, MemoryRegion *mem)
501
+{
502
+ MachineState *ms = MACHINE(vms);
503
+ /* We create a standalone GIC */
504
+ SysBusDevice *gicbusdev;
505
+ QList *redist_region_count;
506
+ int i;
507
+ unsigned int smp_cpus = ms->smp.cpus;
508
+
509
+ vms->gic = qdev_new(gicv3_class_name());
510
+ qdev_prop_set_uint32(vms->gic, "revision", 3);
511
+ qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus);
512
+ /*
513
+ * Note that the num-irq property counts both internal and external
514
+ * interrupts; there are always 32 of the former (mandated by GIC spec).
515
+ */
516
+ qdev_prop_set_uint32(vms->gic, "num-irq", NUM_IRQS + 32);
517
+
518
+ uint32_t redist0_capacity =
519
+ vms->memmap[VMAPPLE_GIC_REDIST].size / GICV3_REDIST_SIZE;
520
+ uint32_t redist0_count = MIN(smp_cpus, redist0_capacity);
521
+
522
+ redist_region_count = qlist_new();
523
+ qlist_append_int(redist_region_count, redist0_count);
524
+ qdev_prop_set_array(vms->gic, "redist-region-count", redist_region_count);
525
+
526
+ gicbusdev = SYS_BUS_DEVICE(vms->gic);
527
+ sysbus_realize_and_unref(gicbusdev, &error_fatal);
528
+ sysbus_mmio_map(gicbusdev, 0, vms->memmap[VMAPPLE_GIC_DIST].base);
529
+ sysbus_mmio_map(gicbusdev, 1, vms->memmap[VMAPPLE_GIC_REDIST].base);
530
+
531
+ /*
532
+ * Wire the outputs from each CPU's generic timer and the GICv3
533
+ * maintenance interrupt signal to the appropriate GIC PPI inputs,
534
+ * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs.
535
+ */
536
+ for (i = 0; i < smp_cpus; i++) {
537
+ DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
538
+
539
+ /* Map the virt timer to PPI 27 */
540
+ qdev_connect_gpio_out(cpudev, GTIMER_VIRT,
541
+ qdev_get_gpio_in(vms->gic,
542
+ arm_gic_ppi_index(i, 27)));
543
+
544
+ /* Map the GIC IRQ and FIQ lines to CPU */
545
+ sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
546
+ sysbus_connect_irq(gicbusdev, i + smp_cpus,
547
+ qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
548
+ }
549
+}
550
+
551
+static void create_uart(const VMAppleMachineState *vms, int uart,
552
+ MemoryRegion *mem, Chardev *chr)
553
+{
554
+ hwaddr base = vms->memmap[uart].base;
555
+ int irq = vms->irqmap[uart];
556
+ DeviceState *dev = qdev_new(TYPE_PL011);
557
+ SysBusDevice *s = SYS_BUS_DEVICE(dev);
558
+
559
+ qdev_prop_set_chr(dev, "chardev", chr);
560
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
561
+ memory_region_add_subregion(mem, base,
562
+ sysbus_mmio_get_region(s, 0));
563
+ sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq));
564
+}
565
+
566
+static void create_rtc(const VMAppleMachineState *vms)
567
+{
568
+ hwaddr base = vms->memmap[VMAPPLE_RTC].base;
569
+ int irq = vms->irqmap[VMAPPLE_RTC];
570
+
571
+ sysbus_create_simple("pl031", base, qdev_get_gpio_in(vms->gic, irq));
572
+}
573
+
574
+static DeviceState *gpio_key_dev;
575
+static void vmapple_powerdown_req(Notifier *n, void *opaque)
576
+{
577
+ /* use gpio Pin 3 for power button event */
578
+ qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
579
+}
580
+
581
+static void create_gpio_devices(const VMAppleMachineState *vms, int gpio,
582
+ MemoryRegion *mem)
583
+{
584
+ DeviceState *pl061_dev;
585
+ hwaddr base = vms->memmap[gpio].base;
586
+ int irq = vms->irqmap[gpio];
587
+ SysBusDevice *s;
588
+
589
+ pl061_dev = qdev_new("pl061");
590
+ /* Pull lines down to 0 if not driven by the PL061 */
591
+ qdev_prop_set_uint32(pl061_dev, "pullups", 0);
592
+ qdev_prop_set_uint32(pl061_dev, "pulldowns", 0xff);
593
+ s = SYS_BUS_DEVICE(pl061_dev);
594
+ sysbus_realize_and_unref(s, &error_fatal);
595
+ memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0));
596
+ sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq));
597
+ gpio_key_dev = sysbus_create_simple("gpio-key", -1,
598
+ qdev_get_gpio_in(pl061_dev, 3));
599
+}
600
+
601
+static void vmapple_firmware_init(VMAppleMachineState *vms,
602
+ MemoryRegion *sysmem)
603
+{
604
+ hwaddr size = vms->memmap[VMAPPLE_FIRMWARE].size;
605
+ hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base;
606
+ const char *bios_name;
607
+ int image_size;
608
+ char *fname;
609
+
610
+ bios_name = MACHINE(vms)->firmware;
611
+ if (!bios_name) {
612
+ error_report("No firmware specified");
613
+ exit(1);
614
+ }
615
+
616
+ fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
617
+ if (!fname) {
618
+ error_report("Could not find ROM image '%s'", bios_name);
619
+ exit(1);
620
+ }
621
+
622
+ memory_region_init_ram(&vms->fw_mr, NULL, "firmware", size, &error_fatal);
623
+ image_size = load_image_mr(fname, &vms->fw_mr);
624
+
625
+ g_free(fname);
626
+ if (image_size < 0) {
627
+ error_report("Could not load ROM image '%s'", bios_name);
628
+ exit(1);
629
+ }
630
+
631
+ memory_region_add_subregion(get_system_memory(), base, &vms->fw_mr);
632
+}
633
+
634
+static void create_pcie(VMAppleMachineState *vms)
635
+{
636
+ hwaddr base_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].base;
637
+ hwaddr size_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].size;
638
+ hwaddr base_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].base;
639
+ hwaddr size_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].size;
640
+ int irq = vms->irqmap[VMAPPLE_PCIE];
641
+ MemoryRegion *mmio_alias;
642
+ MemoryRegion *mmio_reg;
643
+ MemoryRegion *ecam_reg;
644
+ DeviceState *dev;
645
+ int i;
646
+ PCIHostState *pci;
647
+ DeviceState *usb_controller;
648
+ USBBus *usb_bus;
649
+
650
+ dev = qdev_new(TYPE_GPEX_HOST);
651
+ qdev_prop_set_uint32(dev, "num-irqs", GPEX_NUM_IRQS);
652
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
653
+
654
+ /* Map only the first size_ecam bytes of ECAM space */
655
+ ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
656
+ memory_region_init_alias(&vms->ecam_alias, OBJECT(dev), "pcie-ecam",
657
+ ecam_reg, 0, size_ecam);
658
+ memory_region_add_subregion(get_system_memory(), base_ecam,
659
+ &vms->ecam_alias);
660
+
661
+ /*
662
+ * Map the MMIO window from [0x50000000-0x7fff0000] in PCI space into
663
+ * system address space at [0x50000000-0x7fff0000].
664
+ */
665
+ mmio_alias = g_new0(MemoryRegion, 1);
666
+ mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
667
+ memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio",
668
+ mmio_reg, base_mmio, size_mmio);
669
+ memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
670
+
671
+ for (i = 0; i < GPEX_NUM_IRQS; i++) {
672
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
673
+ qdev_get_gpio_in(vms->gic, irq + i));
674
+ gpex_set_irq_num(GPEX_HOST(dev), i, irq + i);
675
+ }
676
+
677
+ pci = PCI_HOST_BRIDGE(dev);
678
+ vms->bus = pci->bus;
679
+ g_assert(vms->bus);
680
+
681
+ while ((dev = qemu_create_nic_device("virtio-net-pci", true, NULL))) {
682
+ qdev_realize_and_unref(dev, BUS(vms->bus), &error_fatal);
683
+ }
684
+
685
+ usb_controller = qdev_new(TYPE_QEMU_XHCI);
686
+ qdev_realize_and_unref(usb_controller, BUS(pci->bus), &error_fatal);
687
+
688
+ usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS,
689
+ &error_fatal));
690
+ usb_create_simple(usb_bus, "usb-kbd");
691
+ usb_create_simple(usb_bus, "usb-tablet");
692
+}
693
+
694
+static void vmapple_reset(void *opaque)
695
+{
696
+ VMAppleMachineState *vms = opaque;
697
+ hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base;
698
+
699
+ cpu_set_pc(first_cpu, base);
700
+}
701
+
702
+static void mach_vmapple_init(MachineState *machine)
703
+{
704
+ VMAppleMachineState *vms = VMAPPLE_MACHINE(machine);
705
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
706
+ const CPUArchIdList *possible_cpus;
707
+ MemoryRegion *sysmem = get_system_memory();
708
+ int n;
709
+ unsigned int smp_cpus = machine->smp.cpus;
710
+ unsigned int max_cpus = machine->smp.max_cpus;
711
+
712
+ vms->memmap = memmap;
713
+ machine->usb = true;
714
+
715
+ possible_cpus = mc->possible_cpu_arch_ids(machine);
716
+ assert(possible_cpus->len == max_cpus);
717
+ for (n = 0; n < possible_cpus->len; n++) {
718
+ Object *cpu;
719
+ CPUState *cs;
720
+
721
+ if (n >= smp_cpus) {
722
+ break;
723
+ }
724
+
725
+ cpu = object_new(possible_cpus->cpus[n].type);
726
+ object_property_set_int(cpu, "mp-affinity",
727
+ possible_cpus->cpus[n].arch_id, &error_fatal);
728
+
729
+ cs = CPU(cpu);
730
+ cs->cpu_index = n;
731
+
732
+ numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpu),
733
+ &error_fatal);
734
+
735
+ if (object_property_find(cpu, "has_el3")) {
736
+ object_property_set_bool(cpu, "has_el3", false, &error_fatal);
737
+ }
738
+ if (object_property_find(cpu, "has_el2")) {
739
+ object_property_set_bool(cpu, "has_el2", false, &error_fatal);
740
+ }
741
+ object_property_set_int(cpu, "psci-conduit", QEMU_PSCI_CONDUIT_HVC,
742
+ &error_fatal);
743
+
744
+ /* Secondary CPUs start in PSCI powered-down state */
745
+ if (n > 0) {
746
+ object_property_set_bool(cpu, "start-powered-off", true,
747
+ &error_fatal);
748
+ }
749
+
750
+ object_property_set_link(cpu, "memory", OBJECT(sysmem), &error_abort);
751
+ qdev_realize(DEVICE(cpu), NULL, &error_fatal);
752
+ object_unref(cpu);
753
+ }
754
+
755
+ memory_region_add_subregion(sysmem, vms->memmap[VMAPPLE_MEM].base,
756
+ machine->ram);
757
+
758
+ create_gic(vms, sysmem);
759
+ create_bdif(vms, sysmem);
760
+ create_pvpanic(vms, sysmem);
761
+ create_aes(vms, sysmem);
762
+ create_gfx(vms, sysmem);
763
+ create_uart(vms, VMAPPLE_UART, sysmem, serial_hd(0));
764
+ create_rtc(vms);
765
+ create_pcie(vms);
766
+
767
+ create_gpio_devices(vms, VMAPPLE_GPIO, sysmem);
768
+
769
+ vmapple_firmware_init(vms, sysmem);
770
+ create_cfg(vms, sysmem);
771
+
772
+ /* connect powerdown request */
773
+ vms->powerdown_notifier.notify = vmapple_powerdown_req;
774
+ qemu_register_powerdown_notifier(&vms->powerdown_notifier);
775
+
776
+ vms->bootinfo.ram_size = machine->ram_size;
777
+ vms->bootinfo.board_id = -1;
778
+ vms->bootinfo.loader_start = vms->memmap[VMAPPLE_MEM].base;
779
+ vms->bootinfo.skip_dtb_autoload = true;
780
+ vms->bootinfo.firmware_loaded = true;
781
+ arm_load_kernel(ARM_CPU(first_cpu), machine, &vms->bootinfo);
782
+
783
+ qemu_register_reset(vmapple_reset, vms);
784
+}
785
+
786
+static CpuInstanceProperties
787
+vmapple_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
788
+{
789
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
790
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
791
+
792
+ assert(cpu_index < possible_cpus->len);
793
+ return possible_cpus->cpus[cpu_index].props;
794
+}
795
+
796
+
797
+static int64_t vmapple_get_default_cpu_node_id(const MachineState *ms, int idx)
798
+{
799
+ return idx % ms->numa_state->num_nodes;
800
+}
801
+
802
+static const CPUArchIdList *vmapple_possible_cpu_arch_ids(MachineState *ms)
803
+{
804
+ int n;
805
+ unsigned int max_cpus = ms->smp.max_cpus;
806
+
807
+ if (ms->possible_cpus) {
808
+ assert(ms->possible_cpus->len == max_cpus);
809
+ return ms->possible_cpus;
810
+ }
811
+
812
+ ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
813
+ sizeof(CPUArchId) * max_cpus);
814
+ ms->possible_cpus->len = max_cpus;
815
+ for (n = 0; n < ms->possible_cpus->len; n++) {
816
+ ms->possible_cpus->cpus[n].type = ms->cpu_type;
817
+ ms->possible_cpus->cpus[n].arch_id =
818
+ arm_build_mp_affinity(n, GICV3_TARGETLIST_BITS);
819
+ ms->possible_cpus->cpus[n].props.has_thread_id = true;
820
+ ms->possible_cpus->cpus[n].props.thread_id = n;
821
+ }
822
+ return ms->possible_cpus;
823
+}
824
+
825
+static void vmapple_machine_class_init(ObjectClass *oc, void *data)
826
+{
827
+ MachineClass *mc = MACHINE_CLASS(oc);
828
+
829
+ mc->init = mach_vmapple_init;
830
+ mc->max_cpus = 32;
831
+ mc->block_default_type = IF_VIRTIO;
832
+ mc->no_cdrom = 1;
833
+ mc->pci_allow_0_address = true;
834
+ mc->minimum_page_bits = 12;
835
+ mc->possible_cpu_arch_ids = vmapple_possible_cpu_arch_ids;
836
+ mc->cpu_index_to_instance_props = vmapple_cpu_index_to_props;
837
+ mc->default_cpu_type = ARM_CPU_TYPE_NAME("host");
838
+ mc->get_default_cpu_node_id = vmapple_get_default_cpu_node_id;
839
+ mc->default_ram_id = "mach-vmapple.ram";
840
+
841
+ object_register_sugar_prop(TYPE_VIRTIO_PCI, "disable-legacy",
842
+ "on", true);
843
+}
844
+
845
+static void vmapple_instance_init(Object *obj)
846
+{
847
+ VMAppleMachineState *vms = VMAPPLE_MACHINE(obj);
848
+
849
+ vms->irqmap = irqmap;
850
+
851
+ object_property_add_uint64_ptr(obj, "uuid", &vms->uuid,
852
+ OBJ_PROP_FLAG_READWRITE);
853
+ object_property_set_description(obj, "uuid", "Machine UUID (SDOM)");
854
+}
855
+
856
+static const TypeInfo vmapple_machine_info = {
857
+ .name = TYPE_VMAPPLE_MACHINE,
858
+ .parent = TYPE_MACHINE,
859
+ .abstract = true,
860
+ .instance_size = sizeof(VMAppleMachineState),
861
+ .class_size = sizeof(VMAppleMachineClass),
862
+ .class_init = vmapple_machine_class_init,
863
+ .instance_init = vmapple_instance_init,
864
+};
865
+
866
+static void machvmapple_machine_init(void)
867
+{
868
+ type_register_static(&vmapple_machine_info);
869
+}
870
+type_init(machvmapple_machine_init);
871
+
872
+static void vmapple_machine_9_2_options(MachineClass *mc)
873
+{
874
+}
875
+DEFINE_VMAPPLE_MACHINE_AS_LATEST(9, 2)
876
+
877
--
878
2.39.3 (Apple Git-145)
879
880
diff view generated by jsdifflib
Deleted patch
1
This reduces the type hierarchy of the vmapple-virtio-blk-pci-base
2
type and vmapple-virtio-root/vmapple-virtio-aux leaf types with a single
3
vmapple-virtio-blk-pci type which exposes a 'variant' enum property
4
which can be set to 'aux' or 'root'.
5
1
6
This change removes a bunch of device type boilerplate at the cost of
7
defining a new qapi enum and qdev property type.
8
9
Documentation for the vmapple machine type is also updated.
10
11
Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
12
---
13
docs/system/arm/vmapple.rst | 4 +--
14
hw/core/qdev-properties-system.c | 8 +++++
15
hw/vmapple/virtio-blk.c | 50 +++++++----------------------
16
include/hw/qdev-properties-system.h | 6 ++++
17
include/hw/vmapple/vmapple.h | 4 +--
18
qapi/virtio.json | 14 ++++++++
19
6 files changed, 43 insertions(+), 43 deletions(-)
20
21
diff --git a/docs/system/arm/vmapple.rst b/docs/system/arm/vmapple.rst
22
index XXXXXXX..XXXXXXX 100644
23
--- a/docs/system/arm/vmapple.rst
24
+++ b/docs/system/arm/vmapple.rst
25
@@ -XXX,XX +XXX,XX @@ to get better interactive access into the target system:
26
-drive file="$DISK",if=pflash,format=raw \
27
-drive file="$AUX",if=none,id=aux,format=raw \
28
-drive file="$DISK",if=none,id=root,format=raw \
29
- -device vmapple-virtio-aux,drive=aux \
30
- -device vmapple-virtio-root,drive=root \
31
+ -device vmapple-virtio-blk-pci,variant=aux,drive=aux \
32
+ -device vmapple-virtio-blk-pci,variant=root,drive=root \
33
-netdev user,id=net0,ipv6=off,hostfwd=tcp::2222-:22,hostfwd=tcp::5901-:5900 \
34
-device virtio-net-pci,netdev=net0
35
36
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
37
index XXXXXXX..XXXXXXX 100644
38
--- a/hw/core/qdev-properties-system.c
39
+++ b/hw/core/qdev-properties-system.c
40
@@ -XXX,XX +XXX,XX @@ const PropertyInfo qdev_prop_iothread_vq_mapping_list = {
41
.set = set_iothread_vq_mapping_list,
42
.release = release_iothread_vq_mapping_list,
43
};
44
+
45
+const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = {
46
+ .name = "VMAppleVirtioBlkVariant",
47
+ .enum_table = &VMAppleVirtioBlkVariant_lookup,
48
+ .get = qdev_propinfo_get_enum,
49
+ .set = qdev_propinfo_set_enum,
50
+ .set_default_value = qdev_propinfo_set_default_value_enum,
51
+};
52
diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c
53
index XXXXXXX..XXXXXXX 100644
54
--- a/hw/vmapple/virtio-blk.c
55
+++ b/hw/vmapple/virtio-blk.c
56
@@ -XXX,XX +XXX,XX @@
57
#include "qemu/module.h"
58
#include "qapi/error.h"
59
60
+#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk"
61
OBJECT_DECLARE_TYPE(VMAppleVirtIOBlk, VMAppleVirtIOBlkClass, VMAPPLE_VIRTIO_BLK)
62
63
typedef struct VMAppleVirtIOBlkClass {
64
@@ -XXX,XX +XXX,XX @@ typedef struct VMAppleVirtIOBlk {
65
/*
66
* vmapple-virtio-blk-pci: This extends VirtioPCIProxy.
67
*/
68
-#define TYPE_VMAPPLE_VIRTIO_BLK_PCI "vmapple-virtio-blk-pci-base"
69
OBJECT_DECLARE_SIMPLE_TYPE(VMAppleVirtIOBlkPCI, VMAPPLE_VIRTIO_BLK_PCI)
70
71
#define VIRTIO_BLK_T_APPLE_BARRIER 0x10000
72
73
-#define VIRTIO_APPLE_TYPE_ROOT 1
74
-#define VIRTIO_APPLE_TYPE_AUX 2
75
-
76
static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req,
77
MultiReqBuffer *mrb,
78
uint32_t type)
79
@@ -XXX,XX +XXX,XX @@ static const TypeInfo vmapple_virtio_blk_info = {
80
struct VMAppleVirtIOBlkPCI {
81
VirtIOPCIProxy parent_obj;
82
VMAppleVirtIOBlk vdev;
83
- uint32_t apple_type;
84
+ VMAppleVirtioBlkVariant variant;
85
};
86
87
88
@@ -XXX,XX +XXX,XX @@ static Property vmapple_virtio_blk_pci_properties[] = {
89
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
90
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
91
DEV_NVECTORS_UNSPECIFIED),
92
+ DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT("variant", VMAppleVirtIOBlkPCI, variant,
93
+ VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED),
94
DEFINE_PROP_END_OF_LIST(),
95
};
96
97
@@ -XXX,XX +XXX,XX @@ static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **err
98
DeviceState *vdev = DEVICE(&dev->vdev);
99
VirtIOBlkConf *conf = &dev->vdev.parent_obj.conf;
100
101
+ if (dev->variant == VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED) {
102
+ error_setg(errp, "Device " TYPE_VMAPPLE_VIRTIO_BLK_PCI ": must specify "
103
+ "a variant, 'aux' or 'root'");
104
+ return;
105
+ }
106
+
107
if (conf->num_queues == VIRTIO_BLK_AUTO_NUM_QUEUES) {
108
conf->num_queues = virtio_pci_optimal_num_queues(0);
109
}
110
@@ -XXX,XX +XXX,XX @@ static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **err
111
*/
112
virtio_add_feature(&dev->vdev.parent_obj.host_features, VIRTIO_BLK_F_ZONED);
113
/* Propagate the apple type down to the virtio-blk device */
114
- dev->vdev.apple_type = dev->apple_type;
115
+ dev->vdev.apple_type = dev->variant;
116
/* and spawn the virtio-blk device */
117
qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
118
119
@@ -XXX,XX +XXX,XX @@ static void vmapple_virtio_blk_pci_instance_init(Object *obj)
120
}
121
122
static const VirtioPCIDeviceTypeInfo vmapple_virtio_blk_pci_info = {
123
- .base_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI,
124
- .generic_name = "vmapple-virtio-blk-pci",
125
+ .generic_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI,
126
.instance_size = sizeof(VMAppleVirtIOBlkPCI),
127
.instance_init = vmapple_virtio_blk_pci_instance_init,
128
.class_init = vmapple_virtio_blk_pci_class_init,
129
};
130
131
-static void vmapple_virtio_root_instance_init(Object *obj)
132
-{
133
- VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj);
134
-
135
- dev->apple_type = VIRTIO_APPLE_TYPE_ROOT;
136
-}
137
-
138
-static const TypeInfo vmapple_virtio_root_info = {
139
- .name = TYPE_VMAPPLE_VIRTIO_ROOT,
140
- .parent = "vmapple-virtio-blk-pci",
141
- .instance_size = sizeof(VMAppleVirtIOBlkPCI),
142
- .instance_init = vmapple_virtio_root_instance_init,
143
-};
144
-
145
-static void vmapple_virtio_aux_instance_init(Object *obj)
146
-{
147
- VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj);
148
-
149
- dev->apple_type = VIRTIO_APPLE_TYPE_AUX;
150
-}
151
-
152
-static const TypeInfo vmapple_virtio_aux_info = {
153
- .name = TYPE_VMAPPLE_VIRTIO_AUX,
154
- .parent = "vmapple-virtio-blk-pci",
155
- .instance_size = sizeof(VMAppleVirtIOBlkPCI),
156
- .instance_init = vmapple_virtio_aux_instance_init,
157
-};
158
-
159
static void vmapple_virtio_blk_register_types(void)
160
{
161
type_register_static(&vmapple_virtio_blk_info);
162
virtio_pci_types_register(&vmapple_virtio_blk_pci_info);
163
- type_register_static(&vmapple_virtio_root_info);
164
- type_register_static(&vmapple_virtio_aux_info);
165
}
166
167
type_init(vmapple_virtio_blk_register_types)
168
diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h
169
index XXXXXXX..XXXXXXX 100644
170
--- a/include/hw/qdev-properties-system.h
171
+++ b/include/hw/qdev-properties-system.h
172
@@ -XXX,XX +XXX,XX @@ extern const PropertyInfo qdev_prop_pcie_link_speed;
173
extern const PropertyInfo qdev_prop_pcie_link_width;
174
extern const PropertyInfo qdev_prop_cpus390entitlement;
175
extern const PropertyInfo qdev_prop_iothread_vq_mapping_list;
176
+extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant;
177
+
178
179
#define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \
180
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t)
181
@@ -XXX,XX +XXX,XX @@ extern const PropertyInfo qdev_prop_iothread_vq_mapping_list;
182
DEFINE_PROP(_name, _state, _field, qdev_prop_iothread_vq_mapping_list, \
183
IOThreadVirtQueueMappingList *)
184
185
+#define DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT(_n, _s, _f, _d) \
186
+ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_vmapple_virtio_blk_variant, \
187
+ VMAppleVirtioBlkVariant)
188
+
189
#endif
190
diff --git a/include/hw/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h
191
index XXXXXXX..XXXXXXX 100644
192
--- a/include/hw/vmapple/vmapple.h
193
+++ b/include/hw/vmapple/vmapple.h
194
@@ -XXX,XX +XXX,XX @@
195
196
#define TYPE_VMAPPLE_CFG "vmapple-cfg"
197
198
-#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk"
199
-#define TYPE_VMAPPLE_VIRTIO_ROOT "vmapple-virtio-root"
200
-#define TYPE_VMAPPLE_VIRTIO_AUX "vmapple-virtio-aux"
201
+#define TYPE_VMAPPLE_VIRTIO_BLK_PCI "vmapple-virtio-blk-pci"
202
203
#endif /* HW_VMAPPLE_VMAPPLE_H */
204
diff --git a/qapi/virtio.json b/qapi/virtio.json
205
index XXXXXXX..XXXXXXX 100644
206
--- a/qapi/virtio.json
207
+++ b/qapi/virtio.json
208
@@ -XXX,XX +XXX,XX @@
209
##
210
{ 'enum': 'GranuleMode',
211
'data': [ '4k', '8k', '16k', '64k', 'host' ] }
212
+
213
+##
214
+# @VMAppleVirtioBlkVariant:
215
+#
216
+# @unspecified: The default, not a valid setting.
217
+#
218
+# @root: Block device holding the root volume
219
+#
220
+# @aux: Block device holding auxiliary data required for boot
221
+#
222
+# Since: 9.2
223
+##
224
+{ 'enum': 'VMAppleVirtioBlkVariant',
225
+ 'data': [ 'unspecified', 'root', 'aux' ] }
226
--
227
2.39.3 (Apple Git-145)
diff view generated by jsdifflib