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