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