... | ... | ||
---|---|---|---|
31 | hosts only because ParavirtualizedGraphics.framework is a black box | 31 | hosts only because ParavirtualizedGraphics.framework is a black box |
32 | implementing most of the logic behind the apple-gfx device.) | 32 | implementing most of the logic behind the apple-gfx device.) |
33 | * PCI devices use legacy IRQs, not MSI/MSI-X. As far as I can tell, | 33 | * PCI devices use legacy IRQs, not MSI/MSI-X. As far as I can tell, |
34 | we'd need to include the GICv3 ITS, but it's unclear to me what | 34 | we'd need to include the GICv3 ITS, but it's unclear to me what |
35 | exactly needs wiring up. | 35 | exactly needs wiring up. |
36 | * 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 |
37 | macOS isn't working correctly. My current conclusion is that the | 37 | available, correct functioning of the USB controller (and thus |
38 | OS's XHCI driver simply was never designed to work with legacy IRQs. | 38 | keyboard/tablet) requires a small workaround in the XHCI controller |
39 | The upshot is that keyboard and mouse/tablet input is very laggy. | 39 | device. This is part of another patch series: |
40 | The solution would be to implement MSI(-X) support or figure out how | 40 | https://patchew.org/QEMU/20241208191646.64857-1-phil@philjordan.eu/ |
41 | to make hcd-xhci-sysbus work with the macOS guest, if at all possible. | ||
42 | (EHCI and UHCI/OHCI controllers are not an option as the VMAPPLE | ||
43 | guest kernel does not include drivers for these.) | ||
44 | * The guest OS must first be provisioned using Virtualization.framework; | 41 | * The guest OS must first be provisioned using Virtualization.framework; |
45 | the disk images can subsequently be used in Qemu. (See docs.) | 42 | the disk images can subsequently be used in Qemu. (See docs.) |
46 | 43 | ||
47 | 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 |
48 | 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 |
... | ... | ||
69 | CPU-based drawing. For maximum efficiency, the Metal texture | 66 | CPU-based drawing. For maximum efficiency, the Metal texture |
70 | containing the guest framebuffer could be drawn directly to | 67 | containing the guest framebuffer could be drawn directly to |
71 | 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 |
72 | to the OpenGL/virgl render path on other platforms.) | 69 | to the OpenGL/virgl render path on other platforms.) |
73 | 70 | ||
74 | My part of this work has been sponsored by Sauce Labs Inc. | 71 | Some of my part of this work has been sponsored by Sauce Labs Inc. |
75 | 72 | ||
76 | --- | 73 | --- |
77 | 74 | ||
78 | v2 -> v3: | 75 | v2 -> v3: |
79 | 76 | ||
... | ... | ||
135 | docs. | 132 | docs. |
136 | * Various smaller fixes in apple-gfx/-mmio, apple-gfx-pci, vmapple/aes, | 133 | * Various smaller fixes in apple-gfx/-mmio, apple-gfx-pci, vmapple/aes, |
137 | vmapple/cfg, vmapple/virtio-blk, and vmapple machine type. | 134 | vmapple/cfg, vmapple/virtio-blk, and vmapple machine type. |
138 | * Added SPDX license identifiers where they were missing. | 135 | * Added SPDX license identifiers where they were missing. |
139 | 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. | ||
231 | |||
140 | Alexander Graf (9): | 232 | Alexander Graf (9): |
141 | hw: Add vmapple subdir | 233 | hw: Add vmapple subdir |
142 | hw/misc/pvpanic: Add MMIO interface | 234 | hw/misc/pvpanic: Add MMIO interface |
143 | hvf: arm: Ignore writes to CNTP_CTL_EL0 | 235 | hvf: arm: Ignore writes to CNTP_CTL_EL0 |
144 | gpex: Allow more than 4 legacy IRQs | 236 | gpex: Allow more than 4 legacy IRQs |
... | ... | ||
155 | hw/display/apple-gfx: Adds PCI implementation | 247 | hw/display/apple-gfx: Adds PCI implementation |
156 | hw/display/apple-gfx: Adds configurable mode list | 248 | hw/display/apple-gfx: Adds configurable mode list |
157 | MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF | 249 | MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF |
158 | hw/block/virtio-blk: Replaces request free function with g_free | 250 | hw/block/virtio-blk: Replaces request free function with g_free |
159 | 251 | ||
160 | MAINTAINERS | 15 + | 252 | MAINTAINERS | 15 + |
161 | contrib/vmapple/uuid.sh | 9 + | 253 | contrib/vmapple/uuid.sh | 9 + |
162 | docs/system/arm/vmapple.rst | 60 +++ | 254 | docs/system/arm/vmapple.rst | 63 ++ |
163 | docs/system/target-arm.rst | 1 + | 255 | docs/system/target-arm.rst | 1 + |
164 | hw/Kconfig | 1 + | 256 | hw/Kconfig | 1 + |
165 | hw/arm/sbsa-ref.c | 2 +- | 257 | hw/arm/sbsa-ref.c | 2 +- |
166 | hw/arm/virt.c | 2 +- | 258 | hw/arm/virt.c | 2 +- |
167 | hw/block/virtio-blk.c | 58 ++- | 259 | hw/block/virtio-blk.c | 58 +- |
168 | hw/display/Kconfig | 13 + | 260 | hw/core/qdev-properties-system.c | 8 + |
169 | hw/display/apple-gfx-mmio.m | 395 +++++++++++++++ | 261 | hw/display/Kconfig | 13 + |
170 | hw/display/apple-gfx-pci.m | 156 ++++++ | 262 | hw/display/apple-gfx-mmio.m | 289 +++++++++ |
171 | hw/display/apple-gfx.h | 91 ++++ | 263 | hw/display/apple-gfx-pci.m | 157 +++++ |
172 | hw/display/apple-gfx.m | 870 +++++++++++++++++++++++++++++++++ | 264 | hw/display/apple-gfx.h | 77 +++ |
173 | hw/display/meson.build | 5 + | 265 | hw/display/apple-gfx.m | 880 ++++++++++++++++++++++++++++ |
174 | hw/display/trace-events | 30 ++ | 266 | hw/display/meson.build | 7 + |
175 | hw/i386/microvm.c | 2 +- | 267 | hw/display/trace-events | 30 + |
176 | hw/loongarch/virt.c | 2 +- | 268 | hw/i386/microvm.c | 2 +- |
177 | hw/meson.build | 1 + | 269 | hw/loongarch/virt.c | 12 +- |
178 | hw/mips/loongson3_virt.c | 2 +- | 270 | hw/meson.build | 1 + |
179 | hw/misc/Kconfig | 4 + | 271 | hw/mips/loongson3_virt.c | 2 +- |
180 | hw/misc/meson.build | 1 + | 272 | hw/misc/Kconfig | 4 + |
181 | hw/misc/pvpanic-mmio.c | 61 +++ | 273 | hw/misc/meson.build | 1 + |
182 | hw/openrisc/virt.c | 12 +- | 274 | hw/misc/pvpanic-mmio.c | 61 ++ |
183 | hw/pci-host/gpex.c | 43 +- | 275 | hw/openrisc/virt.c | 12 +- |
184 | hw/riscv/virt.c | 12 +- | 276 | hw/pci-host/gpex.c | 43 +- |
185 | hw/vmapple/Kconfig | 32 ++ | 277 | hw/riscv/virt.c | 12 +- |
186 | hw/vmapple/aes.c | 578 ++++++++++++++++++++++ | 278 | hw/vmapple/Kconfig | 32 + |
187 | hw/vmapple/bdif.c | 261 ++++++++++ | 279 | hw/vmapple/aes.c | 581 ++++++++++++++++++ |
188 | hw/vmapple/cfg.c | 203 ++++++++ | 280 | hw/vmapple/bdif.c | 275 +++++++++ |
189 | hw/vmapple/meson.build | 5 + | 281 | hw/vmapple/cfg.c | 196 +++++++ |
190 | hw/vmapple/trace-events | 21 + | 282 | hw/vmapple/meson.build | 5 + |
191 | hw/vmapple/trace.h | 1 + | 283 | hw/vmapple/trace-events | 21 + |
192 | hw/vmapple/virtio-blk.c | 226 +++++++++ | 284 | hw/vmapple/trace.h | 1 + |
193 | hw/vmapple/vmapple.c | 659 +++++++++++++++++++++++++ | 285 | hw/vmapple/virtio-blk.c | 205 +++++++ |
194 | hw/xtensa/virt.c | 2 +- | 286 | hw/vmapple/vmapple.c | 646 ++++++++++++++++++++ |
195 | include/hw/misc/pvpanic.h | 1 + | 287 | hw/xen/xen-pvh-common.c | 2 +- |
196 | include/hw/pci-host/gpex.h | 7 +- | 288 | hw/xtensa/virt.c | 2 +- |
197 | include/hw/pci/pci_ids.h | 1 + | 289 | include/hw/misc/pvpanic.h | 1 + |
198 | include/hw/virtio/virtio-blk.h | 11 +- | 290 | include/hw/pci-host/gpex.h | 7 +- |
199 | include/hw/vmapple/vmapple.h | 25 + | 291 | include/hw/pci/pci_ids.h | 1 + |
200 | include/qemu-main.h | 3 +- | 292 | include/hw/qdev-properties-system.h | 5 + |
201 | include/qemu/cutils.h | 15 + | 293 | include/hw/virtio/virtio-blk.h | 11 +- |
202 | include/qemu/typedefs.h | 1 + | 294 | include/hw/vmapple/vmapple.h | 23 + |
203 | meson.build | 5 + | 295 | include/qemu-main.h | 14 +- |
204 | system/main.c | 56 ++- | 296 | include/qemu/cutils.h | 15 + |
205 | target/arm/hvf/hvf.c | 9 + | 297 | meson.build | 5 + |
206 | ui/cocoa.m | 58 +-- | 298 | qapi/virtio.json | 14 + |
207 | ui/sdl2.c | 4 + | 299 | system/main.c | 37 +- |
208 | util/hexdump.c | 18 + | 300 | target/arm/hvf/hvf.c | 9 + |
209 | 49 files changed, 3942 insertions(+), 108 deletions(-) | 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(-) | ||
210 | create mode 100755 contrib/vmapple/uuid.sh | 306 | create mode 100755 contrib/vmapple/uuid.sh |
211 | create mode 100644 docs/system/arm/vmapple.rst | 307 | create mode 100644 docs/system/arm/vmapple.rst |
212 | create mode 100644 hw/display/apple-gfx-mmio.m | 308 | create mode 100644 hw/display/apple-gfx-mmio.m |
213 | create mode 100644 hw/display/apple-gfx-pci.m | 309 | create mode 100644 hw/display/apple-gfx-pci.m |
214 | create mode 100644 hw/display/apple-gfx.h | 310 | create mode 100644 hw/display/apple-gfx.h |
... | ... | ||
224 | create mode 100644 hw/vmapple/virtio-blk.c | 320 | create mode 100644 hw/vmapple/virtio-blk.c |
225 | create mode 100644 hw/vmapple/vmapple.c | 321 | create mode 100644 hw/vmapple/vmapple.c |
226 | create mode 100644 include/hw/vmapple/vmapple.h | 322 | create mode 100644 include/hw/vmapple/vmapple.h |
227 | 323 | ||
228 | -- | 324 | -- |
229 | 2.39.3 (Apple Git-145) | 325 | 2.39.5 (Apple Git-154) |
326 | |||
327 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
33 | * The Cocoa UI sets qemu_main to a function which runs the | 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. | 34 | NSApplication event handling runloop, as is usual for a Cocoa app. |
35 | * The SDL UI overrides the qemu_main function to NULL, thus | 35 | * The SDL UI overrides the qemu_main function to NULL, thus |
36 | specifying that Qemu's main loop must run on the main | 36 | specifying that Qemu's main loop must run on the main |
37 | thread. | 37 | thread. |
38 | * The GTK UI also overrides the qemu_main function to NULL. | ||
38 | * For other UIs, or in the absence of UIs, the platform's default | 39 | * For other UIs, or in the absence of UIs, the platform's default |
39 | behaviour is followed. | 40 | behaviour is followed. |
40 | 41 | ||
41 | This means that on macOS, the platform's runloop events are always | 42 | This means that on macOS, the platform's runloop events are always |
42 | handled, regardless of chosen UI. The new PV graphics device will | 43 | handled, regardless of chosen UI. The new PV graphics device will |
43 | thus work in all configurations. There is no functional change on other | 44 | thus work in all configurations. There is no functional change on other |
44 | operating systems. | 45 | operating systems. |
45 | 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.) | ||
55 | |||
46 | 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> | ||
47 | --- | 59 | --- |
48 | 60 | ||
49 | v5: | 61 | v5: |
50 | 62 | ||
51 | * Simplified the way of setting/clearing the main loop by going back | 63 | * Simplified the way of setting/clearing the main loop by going back |
52 | to setting qemu_main directly, but narrowing the scope of what it | 64 | to setting qemu_main directly, but narrowing the scope of what it |
53 | needs to do, and it can now be NULL. | 65 | needs to do, and it can now be NULL. |
54 | 66 | ||
55 | include/qemu-main.h | 3 +-- | 67 | v6: |
56 | include/qemu/typedefs.h | 1 + | 68 | |
57 | system/main.c | 56 +++++++++++++++++++++++++++++++++++---- | 69 | * Folded function qemu_run_default_main_on_new_thread's code into |
58 | ui/cocoa.m | 58 +++++++++++------------------------------ | 70 | main() |
59 | ui/sdl2.c | 4 +++ | 71 | * Removed whitespace changes left over on lines near code removed |
60 | 5 files changed, 72 insertions(+), 50 deletions(-) | 72 | between v4 and v5 |
73 | |||
74 | v9: | ||
75 | |||
76 | * Set qemu_main to NULL for GTK UI as well. | ||
77 | |||
78 | v10: | ||
79 | |||
80 | * Added comments clarifying the functionality and purpose of qemu_main. | ||
81 | |||
82 | v11: | ||
83 | |||
84 | * Removed the qemu_main_fn typedef again. | ||
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(-) | ||
61 | 102 | ||
62 | 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 |
63 | index XXXXXXX..XXXXXXX 100644 | 104 | index XXXXXXX..XXXXXXX 100644 |
64 | --- a/include/qemu-main.h | 105 | --- a/include/qemu-main.h |
65 | +++ b/include/qemu-main.h | 106 | +++ b/include/qemu-main.h |
66 | @@ -XXX,XX +XXX,XX @@ | 107 | @@ -XXX,XX +XXX,XX @@ |
67 | #ifndef QEMU_MAIN_H | 108 | #ifndef QEMU_MAIN_H |
68 | #define QEMU_MAIN_H | 109 | #define QEMU_MAIN_H |
69 | 110 | ||
70 | -int qemu_default_main(void); | 111 | -int qemu_default_main(void); |
71 | -extern int (*qemu_main)(void); | 112 | +/* |
72 | +extern qemu_main_fn qemu_main; | 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 | + */ | ||
125 | extern int (*qemu_main)(void); | ||
73 | 126 | ||
74 | #endif /* QEMU_MAIN_H */ | 127 | #endif /* QEMU_MAIN_H */ |
75 | diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h | ||
76 | index XXXXXXX..XXXXXXX 100644 | ||
77 | --- a/include/qemu/typedefs.h | ||
78 | +++ b/include/qemu/typedefs.h | ||
79 | @@ -XXX,XX +XXX,XX @@ typedef struct IRQState *qemu_irq; | ||
80 | * Function types | ||
81 | */ | ||
82 | typedef void (*qemu_irq_handler)(void *opaque, int n, int level); | ||
83 | +typedef int (*qemu_main_fn)(void); | ||
84 | |||
85 | #endif /* QEMU_TYPEDEFS_H */ | ||
86 | diff --git a/system/main.c b/system/main.c | 128 | diff --git a/system/main.c b/system/main.c |
87 | index XXXXXXX..XXXXXXX 100644 | 129 | index XXXXXXX..XXXXXXX 100644 |
88 | --- a/system/main.c | 130 | --- a/system/main.c |
89 | +++ b/system/main.c | 131 | +++ b/system/main.c |
90 | @@ -XXX,XX +XXX,XX @@ | 132 | @@ -XXX,XX +XXX,XX @@ |
91 | 133 | ||
92 | #include "qemu/osdep.h" | 134 | #include "qemu/osdep.h" |
93 | #include "qemu-main.h" | 135 | #include "qemu-main.h" |
94 | +#include "qemu/main-loop.h" | 136 | +#include "qemu/main-loop.h" |
95 | #include "sysemu/sysemu.h" | 137 | #include "sysemu/sysemu.h" |
96 | 138 | ||
97 | -#ifdef CONFIG_SDL | 139 | #ifdef CONFIG_SDL |
98 | -#include <SDL.h> | 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) | ||
99 | +#ifdef CONFIG_DARWIN | 149 | +#ifdef CONFIG_DARWIN |
100 | +#include <CoreFoundation/CoreFoundation.h> | 150 | +#include <CoreFoundation/CoreFoundation.h> |
101 | #endif | 151 | +#endif |
102 | 152 | + | |
103 | -int qemu_default_main(void) | 153 | +static void *qemu_default_main(void *opaque) |
104 | +static int qemu_default_main(void) | ||
105 | { | 154 | { |
106 | int status; | 155 | int status; |
107 | 156 | ||
108 | @@ -XXX,XX +XXX,XX @@ int qemu_default_main(void) | 157 | + bql_lock(); |
109 | return status; | 158 | status = qemu_main_loop(); |
110 | } | 159 | qemu_cleanup(status); |
160 | + bql_unlock(); | ||
161 | |||
162 | - return status; | ||
163 | + exit(status); | ||
164 | } | ||
111 | 165 | ||
112 | -int (*qemu_main)(void) = qemu_default_main; | 166 | -int (*qemu_main)(void) = qemu_default_main; |
113 | +/* | 167 | +int (*qemu_main)(void); |
114 | + * Various macOS system libraries, including the Cocoa UI and anything using | ||
115 | + * libdispatch, such as ParavirtualizedGraphics.framework, requires that the | ||
116 | + * main runloop, on the main (initial) thread be running or at least regularly | ||
117 | + * polled for events. A special mode is therefore supported, where the QEMU | ||
118 | + * main loop runs on a separate thread and the main thread handles the | ||
119 | + * CF/Cocoa runloop. | ||
120 | + */ | ||
121 | + | ||
122 | +static void *call_qemu_default_main(void *opaque) | ||
123 | +{ | ||
124 | + int status; | ||
125 | + | ||
126 | + bql_lock(); | ||
127 | + status = qemu_default_main(); | ||
128 | + bql_unlock(); | ||
129 | + | ||
130 | + exit(status); | ||
131 | +} | ||
132 | + | ||
133 | +static void qemu_run_default_main_on_new_thread(void) | ||
134 | +{ | ||
135 | + QemuThread thread; | ||
136 | + | ||
137 | + qemu_thread_create(&thread, "qemu_main", call_qemu_default_main, | ||
138 | + NULL, QEMU_THREAD_DETACHED); | ||
139 | +} | ||
140 | + | ||
141 | + | 168 | + |
142 | +#ifdef CONFIG_DARWIN | 169 | +#ifdef CONFIG_DARWIN |
143 | +static int os_darwin_cfrunloop_main(void) | 170 | +static int os_darwin_cfrunloop_main(void) |
144 | +{ | 171 | +{ |
145 | + CFRunLoopRun(); | 172 | + CFRunLoopRun(); |
146 | + abort(); | 173 | + g_assert_not_reached(); |
147 | +} | 174 | +} |
148 | + | 175 | +int (*qemu_main)(void) = os_darwin_cfrunloop_main; |
149 | +qemu_main_fn qemu_main = os_darwin_cfrunloop_main; | ||
150 | +#else | ||
151 | +qemu_main_fn qemu_main; | ||
152 | +#endif | 176 | +#endif |
153 | 177 | ||
154 | int main(int argc, char **argv) | 178 | int main(int argc, char **argv) |
155 | { | 179 | { |
156 | qemu_init(argc, argv); | 180 | qemu_init(argc, argv); |
157 | - return qemu_main(); | 181 | - return qemu_main(); |
182 | + bql_unlock(); | ||
158 | + if (qemu_main) { | 183 | + if (qemu_main) { |
159 | + qemu_run_default_main_on_new_thread(); | 184 | + QemuThread main_loop_thread; |
160 | + bql_unlock(); | 185 | + qemu_thread_create(&main_loop_thread, "qemu_main", |
186 | + qemu_default_main, NULL, QEMU_THREAD_DETACHED); | ||
161 | + return qemu_main(); | 187 | + return qemu_main(); |
162 | + } else { | 188 | + } else { |
163 | + qemu_default_main(); | 189 | + qemu_default_main(NULL); |
164 | + } | 190 | + } |
165 | } | 191 | } |
166 | diff --git a/ui/cocoa.m b/ui/cocoa.m | 192 | diff --git a/ui/cocoa.m b/ui/cocoa.m |
167 | index XXXXXXX..XXXXXXX 100644 | 193 | index XXXXXXX..XXXXXXX 100644 |
168 | --- a/ui/cocoa.m | 194 | --- a/ui/cocoa.m |
... | ... | ||
270 | + */ | 296 | + */ |
271 | + qemu_main = cocoa_main; | 297 | + qemu_main = cocoa_main; |
272 | } | 298 | } |
273 | 299 | ||
274 | static QemuDisplay qemu_display_cocoa = { | 300 | static QemuDisplay qemu_display_cocoa = { |
275 | - .type = DISPLAY_TYPE_COCOA, | 301 | diff --git a/ui/gtk.c b/ui/gtk.c |
276 | - .init = cocoa_display_init, | 302 | index XXXXXXX..XXXXXXX 100644 |
277 | + .type = DISPLAY_TYPE_COCOA, | 303 | --- a/ui/gtk.c |
278 | + .init = cocoa_display_init, | 304 | +++ b/ui/gtk.c |
279 | }; | 305 | @@ -XXX,XX +XXX,XX @@ |
280 | 306 | #include "qemu/cutils.h" | |
281 | static void register_cocoa(void) | 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) | ||
282 | diff --git a/ui/sdl2.c b/ui/sdl2.c | 323 | diff --git a/ui/sdl2.c b/ui/sdl2.c |
283 | index XXXXXXX..XXXXXXX 100644 | 324 | index XXXXXXX..XXXXXXX 100644 |
284 | --- a/ui/sdl2.c | 325 | --- a/ui/sdl2.c |
285 | +++ b/ui/sdl2.c | 326 | +++ b/ui/sdl2.c |
286 | @@ -XXX,XX +XXX,XX @@ | 327 | @@ -XXX,XX +XXX,XX @@ |
... | ... | ||
300 | + qemu_main = NULL; | 341 | + qemu_main = NULL; |
301 | } | 342 | } |
302 | 343 | ||
303 | static QemuDisplay qemu_display_sdl2 = { | 344 | static QemuDisplay qemu_display_sdl2 = { |
304 | -- | 345 | -- |
305 | 2.39.3 (Apple Git-145) | 346 | 2.39.5 (Apple Git-154) | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
19 | * Asynchronous rendering. | 19 | * Asynchronous rendering. |
20 | * Memory and object lifetime fixes. | 20 | * Memory and object lifetime fixes. |
21 | * Refactoring to split generic and (vmapple) MMIO variant specific | 21 | * Refactoring to split generic and (vmapple) MMIO variant specific |
22 | code. | 22 | code. |
23 | 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 | |||
24 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | 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> | ||
25 | --- | 36 | --- |
37 | |||
26 | v2: | 38 | v2: |
27 | 39 | ||
28 | * Cherry-pick/rebase conflict fixes | 40 | * Cherry-pick/rebase conflict fixes |
29 | * BQL function renaming | 41 | * BQL function renaming |
30 | * Moved from hw/vmapple/ (useful outside that machine type) | 42 | * Moved from hw/vmapple/ (useful outside that machine type) |
... | ... | ||
86 | accessed outside the appropriate lock. Added dedicated mutex for the task | 98 | accessed outside the appropriate lock. Added dedicated mutex for the task |
87 | list. | 99 | list. |
88 | * Retain references to MemoryRegions for which there exist mappings in each | 100 | * Retain references to MemoryRegions for which there exist mappings in each |
89 | PGTask, and for IOSurface mappings. | 101 | PGTask, and for IOSurface mappings. |
90 | 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 | |||
91 | hw/display/Kconfig | 9 + | 168 | hw/display/Kconfig | 9 + |
92 | hw/display/apple-gfx-mmio.m | 387 ++++++++++++++++++ | 169 | hw/display/apple-gfx-mmio.m | 281 +++++++++++++ |
93 | hw/display/apple-gfx.h | 79 ++++ | 170 | hw/display/apple-gfx.h | 66 +++ |
94 | hw/display/apple-gfx.m | 773 ++++++++++++++++++++++++++++++++++++ | 171 | hw/display/apple-gfx.m | 783 ++++++++++++++++++++++++++++++++++++ |
95 | hw/display/meson.build | 4 + | 172 | hw/display/meson.build | 6 + |
96 | hw/display/trace-events | 28 ++ | 173 | hw/display/trace-events | 28 ++ |
97 | meson.build | 4 + | 174 | meson.build | 4 + |
98 | 7 files changed, 1284 insertions(+) | 175 | 7 files changed, 1177 insertions(+) |
99 | create mode 100644 hw/display/apple-gfx-mmio.m | 176 | create mode 100644 hw/display/apple-gfx-mmio.m |
100 | create mode 100644 hw/display/apple-gfx.h | 177 | create mode 100644 hw/display/apple-gfx.h |
101 | create mode 100644 hw/display/apple-gfx.m | 178 | create mode 100644 hw/display/apple-gfx.m |
102 | 179 | ||
103 | diff --git a/hw/display/Kconfig b/hw/display/Kconfig | 180 | diff --git a/hw/display/Kconfig b/hw/display/Kconfig |
... | ... | ||
183 | +struct AppleGFXMMIOState { | 260 | +struct AppleGFXMMIOState { |
184 | + SysBusDevice parent_obj; | 261 | + SysBusDevice parent_obj; |
185 | + | 262 | + |
186 | + AppleGFXState common; | 263 | + AppleGFXState common; |
187 | + | 264 | + |
188 | + QemuCond iosfc_map_job_cond; | ||
189 | + QemuCond iosfc_unmap_job_cond; | ||
190 | + qemu_irq irq_gfx; | 265 | + qemu_irq irq_gfx; |
191 | + qemu_irq irq_iosfc; | 266 | + qemu_irq irq_iosfc; |
192 | + MemoryRegion iomem_iosfc; | 267 | + MemoryRegion iomem_iosfc; |
193 | + PGIOSurfaceHostDevice *pgiosfc; | 268 | + PGIOSurfaceHostDevice *pgiosfc; |
194 | + | ||
195 | + GArray *iosfc_mapped_regions; /* array of AppleGFXMMIOMappedRegion */ | ||
196 | +}; | 269 | +}; |
197 | + | 270 | + |
198 | +typedef struct AppleGFXMMIOJob { | 271 | +typedef struct AppleGFXMMIOJob { |
199 | + AppleGFXMMIOState *state; | 272 | + AppleGFXMMIOState *state; |
200 | + uint64_t offset; | 273 | + uint64_t offset; |
... | ... | ||
215 | + AppleGFXMMIOJob job = { | 288 | + AppleGFXMMIOJob job = { |
216 | + .state = opaque, | 289 | + .state = opaque, |
217 | + .offset = offset, | 290 | + .offset = offset, |
218 | + .completed = false, | 291 | + .completed = false, |
219 | + }; | 292 | + }; |
220 | + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); | 293 | + dispatch_queue_t queue = |
294 | + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); | ||
221 | + | 295 | + |
222 | + dispatch_async_f(queue, &job, iosfc_do_read); | 296 | + dispatch_async_f(queue, &job, iosfc_do_read); |
223 | + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); | 297 | + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); |
224 | + | 298 | + |
225 | + trace_apple_gfx_mmio_iosfc_read(offset, job.value); | 299 | + trace_apple_gfx_mmio_iosfc_read(offset, job.value); |
... | ... | ||
241 | + .state = opaque, | 315 | + .state = opaque, |
242 | + .offset = offset, | 316 | + .offset = offset, |
243 | + .value = val, | 317 | + .value = val, |
244 | + .completed = false, | 318 | + .completed = false, |
245 | + }; | 319 | + }; |
246 | + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); | 320 | + dispatch_queue_t queue = |
321 | + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); | ||
247 | + | 322 | + |
248 | + dispatch_async_f(queue, &job, iosfc_do_write); | 323 | + dispatch_async_f(queue, &job, iosfc_do_write); |
249 | + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); | 324 | + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); |
250 | + | 325 | + |
251 | + trace_apple_gfx_mmio_iosfc_write(offset, val); | 326 | + trace_apple_gfx_mmio_iosfc_write(offset, val); |
... | ... | ||
263 | + .min_access_size = 4, | 338 | + .min_access_size = 4, |
264 | + .max_access_size = 8, | 339 | + .max_access_size = 8, |
265 | + }, | 340 | + }, |
266 | +}; | 341 | +}; |
267 | + | 342 | + |
268 | +static void raise_irq(void *opaque) | 343 | +static void raise_irq_bh(void *opaque) |
269 | +{ | 344 | +{ |
270 | + qemu_irq *irq = opaque; | 345 | + qemu_irq *irq = opaque; |
271 | + | 346 | + |
272 | + qemu_irq_pulse(*irq); | 347 | + qemu_irq_pulse(*irq); |
273 | +} | 348 | +} |
274 | + | 349 | + |
275 | +typedef struct AppleGFXMapSurfaceMemoryJob { | 350 | +static void *apple_gfx_mmio_map_surface_memory(uint64_t guest_physical_address, |
276 | + uint64_t guest_physical_address; | 351 | + uint64_t length, bool read_only) |
277 | + uint64_t guest_physical_length; | 352 | +{ |
278 | + void *result_mem; | 353 | + void *mem; |
279 | + AppleGFXMMIOState *state; | 354 | + MemoryRegion *region = NULL; |
280 | + bool read_only; | 355 | + |
281 | + bool success; | 356 | + RCU_READ_LOCK_GUARD(); |
282 | + bool done; | 357 | + mem = apple_gfx_host_ptr_for_gpa_range(guest_physical_address, |
283 | +} AppleGFXMapSurfaceMemoryJob; | 358 | + length, read_only, ®ion); |
284 | + | 359 | + if (mem) { |
285 | +typedef struct AppleGFXMMIOMappedRegion { | 360 | + memory_region_ref(region); |
361 | + } | ||
362 | + return mem; | ||
363 | +} | ||
364 | + | ||
365 | +static bool apple_gfx_mmio_unmap_surface_memory(void *ptr) | ||
366 | +{ | ||
286 | + MemoryRegion *region; | 367 | + MemoryRegion *region; |
287 | + uint64_t map_count; | 368 | + ram_addr_t offset = 0; |
288 | + uintptr_t host_virtual_start; | 369 | + |
289 | + uintptr_t host_virtual_end; | 370 | + RCU_READ_LOCK_GUARD(); |
290 | +} AppleGFXMMIOMappedRegion; | 371 | + region = memory_region_from_host(ptr, &offset); |
291 | + | 372 | + if (!region) { |
292 | +static void apple_gfx_mmio_map_surface_memory(void *opaque) | ||
293 | +{ | ||
294 | + AppleGFXMapSurfaceMemoryJob *job = opaque; | ||
295 | + AppleGFXMMIOState *s = job->state; | ||
296 | + mach_vm_address_t mem; | ||
297 | + MemoryRegion *region = NULL; | ||
298 | + GArray *regions = s->iosfc_mapped_regions; | ||
299 | + AppleGFXMMIOMappedRegion *mapped_region; | ||
300 | + size_t i; | ||
301 | + | ||
302 | + mem = apple_gfx_host_address_for_gpa_range(job->guest_physical_address, | ||
303 | + job->guest_physical_length, | ||
304 | + job->read_only, ®ion); | ||
305 | + | ||
306 | + if (mem != 0) { | ||
307 | + for (i = 0; i < regions->len; ++i) { | ||
308 | + mapped_region = &g_array_index(regions, AppleGFXMMIOMappedRegion, i); | ||
309 | + if (region == mapped_region->region) { | ||
310 | + ++mapped_region->map_count; | ||
311 | + break; | ||
312 | + } | ||
313 | + } | ||
314 | + | ||
315 | + if (i >= regions->len) { | ||
316 | + /* No existing mapping to this region found, keep a reference and save | ||
317 | + */ | ||
318 | + uintptr_t start = (uintptr_t)memory_region_get_ram_ptr(region); | ||
319 | + AppleGFXMMIOMappedRegion new_region = { | ||
320 | + region, 1, | ||
321 | + start, | ||
322 | + start + memory_region_size(region) | ||
323 | + }; | ||
324 | + memory_region_ref(region); | ||
325 | + g_array_append_val(regions, new_region); | ||
326 | + trace_apple_gfx_iosfc_map_memory_new_region( | ||
327 | + i, region, start, new_region.host_virtual_end); | ||
328 | + } | ||
329 | + } | ||
330 | + | ||
331 | + qemu_mutex_lock(&s->common.job_mutex); | ||
332 | + job->result_mem = (void *)mem; | ||
333 | + job->success = mem != 0; | ||
334 | + job->done = true; | ||
335 | + qemu_cond_broadcast(&s->iosfc_map_job_cond); | ||
336 | + qemu_mutex_unlock(&s->common.job_mutex); | ||
337 | +} | ||
338 | + | ||
339 | +typedef struct AppleGFXUnmapSurfaceMemoryJob { | ||
340 | + void *virtual_address; | ||
341 | + AppleGFXMMIOState *state; | ||
342 | + bool done; | ||
343 | +} AppleGFXUnmapSurfaceMemoryJob; | ||
344 | + | ||
345 | +static AppleGFXMMIOMappedRegion *find_mapped_region_containing(GArray *regions, | ||
346 | + uintptr_t va, | ||
347 | + size_t *index) | ||
348 | +{ | ||
349 | + size_t i; | ||
350 | + AppleGFXMMIOMappedRegion *mapped_region; | ||
351 | + | ||
352 | + for (i = 0; i < regions->len; ++i) { | ||
353 | + mapped_region = &g_array_index(regions, AppleGFXMMIOMappedRegion, i); | ||
354 | + if (va >= mapped_region->host_virtual_start && | ||
355 | + va < mapped_region->host_virtual_end) { | ||
356 | + *index = i; | ||
357 | + return mapped_region; | ||
358 | + } | ||
359 | + } | ||
360 | + return NULL; | ||
361 | +} | ||
362 | + | ||
363 | +static void apple_gfx_mmio_unmap_surface_memory(void *opaque) | ||
364 | +{ | ||
365 | + AppleGFXUnmapSurfaceMemoryJob *job = opaque; | ||
366 | + AppleGFXMMIOState *s = job->state; | ||
367 | + uintptr_t mem = (uintptr_t)job->virtual_address; | ||
368 | + GArray *regions = s->iosfc_mapped_regions; | ||
369 | + size_t region_index; | ||
370 | + AppleGFXMMIOMappedRegion *mapped_region = | ||
371 | + find_mapped_region_containing(regions, mem, ®ion_index); | ||
372 | + | ||
373 | + if (mapped_region) { | ||
374 | + trace_apple_gfx_iosfc_unmap_memory_region(mem, region_index, mapped_region->map_count, mapped_region->region); | ||
375 | + if (--mapped_region->map_count == 0) { | ||
376 | + memory_region_unref(mapped_region->region); | ||
377 | + g_array_remove_index_fast(regions, region_index); | ||
378 | + } | ||
379 | + } else { | ||
380 | + qemu_log_mask(LOG_GUEST_ERROR, "%s: memory at %p to be unmapped not " | 373 | + qemu_log_mask(LOG_GUEST_ERROR, "%s: memory at %p to be unmapped not " |
381 | + "found in any of %u mapped regions.\n", | 374 | + "found.\n", |
382 | + __func__, | 375 | + __func__, ptr); |
383 | + job->virtual_address, regions->len); | 376 | + return false; |
384 | + } | 377 | + } |
385 | + | 378 | + |
386 | + qemu_mutex_lock(&s->common.job_mutex); | 379 | + trace_apple_gfx_iosfc_unmap_memory_region(ptr, region); |
387 | + job->done = true; | 380 | + memory_region_unref(region); |
388 | + qemu_cond_broadcast(&s->iosfc_unmap_job_cond); | 381 | + return true; |
389 | + qemu_mutex_unlock(&s->common.job_mutex); | ||
390 | +} | 382 | +} |
391 | + | 383 | + |
392 | +static PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device( | 384 | +static PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device( |
393 | + AppleGFXMMIOState *s) | 385 | + AppleGFXMMIOState *s) |
394 | +{ | 386 | +{ |
395 | + PGIOSurfaceHostDeviceDescriptor *iosfc_desc = | 387 | + PGIOSurfaceHostDeviceDescriptor *iosfc_desc = |
396 | + [PGIOSurfaceHostDeviceDescriptor new]; | 388 | + [PGIOSurfaceHostDeviceDescriptor new]; |
397 | + PGIOSurfaceHostDevice *iosfc_host_dev = nil; | 389 | + PGIOSurfaceHostDevice *iosfc_host_dev; |
398 | + | 390 | + |
399 | + iosfc_desc.mapMemory = | 391 | + iosfc_desc.mapMemory = |
400 | + ^bool(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) { |
401 | + AppleGFXMapSurfaceMemoryJob job = { | 393 | + *va = apple_gfx_mmio_map_surface_memory(phys, len, ro); |
402 | + .guest_physical_address = phys, .guest_physical_length = len, | 394 | + |
403 | + .read_only = ro, .state = s, | 395 | + trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va); |
404 | + }; | 396 | + |
405 | + | 397 | + return *va != NULL; |
406 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), | ||
407 | + apple_gfx_mmio_map_surface_memory, &job); | ||
408 | + apple_gfx_await_bh_job(&s->common, &s->iosfc_map_job_cond, &job.done); | ||
409 | + | ||
410 | + *va = job.result_mem; | ||
411 | + | ||
412 | + trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va, | ||
413 | + job.success); | ||
414 | + | ||
415 | + return job.success; | ||
416 | + }; | 398 | + }; |
417 | + | 399 | + |
418 | + iosfc_desc.unmapMemory = | 400 | + iosfc_desc.unmapMemory = |
419 | + ^bool(void *va, void *b, void *c, void *d, void *e, void *f) { | 401 | + ^bool(void *va, void *b, void *c, void *d, void *e, void *f) { |
420 | + AppleGFXUnmapSurfaceMemoryJob job = { va, s }; | 402 | + return apple_gfx_mmio_unmap_surface_memory(va); |
421 | + trace_apple_gfx_iosfc_unmap_memory(va, b, c, d, e, f); | ||
422 | + | ||
423 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), | ||
424 | + apple_gfx_mmio_unmap_surface_memory, &job); | ||
425 | + apple_gfx_await_bh_job(&s->common, &s->iosfc_unmap_job_cond, &job.done); | ||
426 | + | ||
427 | + return true; | ||
428 | + }; | 403 | + }; |
429 | + | 404 | + |
430 | + iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) { | 405 | + iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) { |
431 | + trace_apple_gfx_iosfc_raise_irq(vector); | 406 | + trace_apple_gfx_iosfc_raise_irq(vector); |
432 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), | 407 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), |
433 | + raise_irq, &s->irq_iosfc); | 408 | + raise_irq_bh, &s->irq_iosfc); |
434 | + return true; | 409 | + return true; |
435 | + }; | 410 | + }; |
436 | + | 411 | + |
437 | + iosfc_host_dev = | 412 | + iosfc_host_dev = |
438 | + [[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc]; | 413 | + [[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc]; |
... | ... | ||
447 | + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; | 422 | + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; |
448 | + | 423 | + |
449 | + desc.raiseInterrupt = ^(uint32_t vector) { | 424 | + desc.raiseInterrupt = ^(uint32_t vector) { |
450 | + trace_apple_gfx_raise_irq(vector); | 425 | + trace_apple_gfx_raise_irq(vector); |
451 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), | 426 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), |
452 | + raise_irq, &s->irq_gfx); | 427 | + raise_irq_bh, &s->irq_gfx); |
453 | + }; | 428 | + }; |
454 | + | 429 | + |
455 | + desc.usingIOSurfaceMapper = true; | 430 | + desc.usingIOSurfaceMapper = true; |
456 | + s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s); | 431 | + s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s); |
457 | + | 432 | + |
458 | + s->iosfc_mapped_regions = | 433 | + if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) { |
459 | + g_array_sized_new(false /* no termination */, true /* clear */, | 434 | + [s->pgiosfc release]; |
460 | + sizeof(AppleGFXMMIOMappedRegion), | 435 | + s->pgiosfc = nil; |
461 | + 2 /* Usually no more RAM regions*/); | 436 | + } |
462 | + | ||
463 | + apple_gfx_common_realize(&s->common, desc, errp); | ||
464 | + qemu_cond_init(&s->iosfc_map_job_cond); | ||
465 | + qemu_cond_init(&s->iosfc_unmap_job_cond); | ||
466 | + | 437 | + |
467 | + [desc release]; | 438 | + [desc release]; |
468 | + desc = nil; | 439 | + desc = nil; |
469 | + } | 440 | + } |
470 | +} | 441 | +} |
... | ... | ||
551 | + id<PGDevice> pgdev; | 522 | + id<PGDevice> pgdev; |
552 | + id<PGDisplay> pgdisp; | 523 | + id<PGDisplay> pgdisp; |
553 | + QemuConsole *con; | 524 | + QemuConsole *con; |
554 | + id<MTLDevice> mtl; | 525 | + id<MTLDevice> mtl; |
555 | + id<MTLCommandQueue> mtl_queue; | 526 | + id<MTLCommandQueue> mtl_queue; |
556 | + dispatch_queue_t render_queue; | 527 | + |
557 | + /* | 528 | + /* List `tasks` is protected by task_mutex */ |
558 | + * QemuMutex & QemuConds for awaiting completion of PVG memory-mapping and | ||
559 | + * reading requests after submitting them to run in the AIO context. | ||
560 | + * QemuCond (rather than QemuEvent) are used so multiple concurrent jobs | ||
561 | + * can be handled safely. | ||
562 | + * The state associated with each job is tracked in a AppleGFX*Job struct | ||
563 | + * for each kind of job; instances are allocated on the caller's stack. | ||
564 | + * This struct also contains the completion flag which is used in | ||
565 | + * conjunction with the condition variable. | ||
566 | + */ | ||
567 | + QemuMutex job_mutex; | ||
568 | + QemuCond task_map_job_cond; | ||
569 | + QemuCond mem_read_job_cond; | ||
570 | + | ||
571 | + /* tasks is protected by task_mutex */ | ||
572 | + QemuMutex task_mutex; | 529 | + QemuMutex task_mutex; |
573 | + PGTaskList tasks; | 530 | + PGTaskList tasks; |
574 | + | 531 | + |
575 | + /* Mutable state (BQL) */ | 532 | + /* Mutable state (BQL protected) */ |
576 | + QEMUCursor *cursor; | 533 | + QEMUCursor *cursor; |
577 | + bool cursor_show; | ||
578 | + bool gfx_update_requested; | ||
579 | + bool new_frame_ready; | ||
580 | + bool using_managed_texture_storage; | ||
581 | + int32_t pending_frames; | ||
582 | + void *vram; | ||
583 | + DisplaySurface *surface; | 534 | + DisplaySurface *surface; |
584 | + id<MTLTexture> texture; | 535 | + id<MTLTexture> texture; |
536 | + int8_t pending_frames; /* # guest frames in the rendering pipeline */ | ||
537 | + bool gfx_update_requested; /* QEMU display system wants a new frame */ | ||
538 | + bool new_frame_ready; /* Guest has rendered a frame, ready to be used */ | ||
539 | + bool using_managed_texture_storage; | ||
540 | + uint32_t rendering_frame_width; | ||
541 | + uint32_t rendering_frame_height; | ||
542 | + | ||
543 | + /* Mutable state (atomic) */ | ||
544 | + bool cursor_show; | ||
585 | +} AppleGFXState; | 545 | +} AppleGFXState; |
586 | + | 546 | + |
587 | +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name); | 547 | +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name); |
588 | +void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc, | 548 | +bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, |
589 | + Error **errp); | 549 | + PGDeviceDescriptor *desc, Error **errp); |
590 | +uintptr_t apple_gfx_host_address_for_gpa_range(uint64_t guest_physical, | 550 | +void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical, |
591 | + uint64_t length, bool read_only, | 551 | + uint64_t length, bool read_only, |
592 | + MemoryRegion **mapping_in_region); | 552 | + MemoryRegion **mapping_in_region); |
593 | +void apple_gfx_await_bh_job(AppleGFXState *s, QemuCond *job_cond, | ||
594 | + bool *job_done_flag); | ||
595 | + | 553 | + |
596 | +#endif | 554 | +#endif |
597 | + | 555 | + |
598 | 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 |
599 | new file mode 100644 | 557 | new file mode 100644 |
... | ... | ||
637 | +static const PGDisplayCoord_t apple_gfx_modes[] = { | 595 | +static const PGDisplayCoord_t apple_gfx_modes[] = { |
638 | + { .x = 1440, .y = 1080 }, | 596 | + { .x = 1440, .y = 1080 }, |
639 | + { .x = 1280, .y = 1024 }, | 597 | + { .x = 1280, .y = 1024 }, |
640 | +}; | 598 | +}; |
641 | + | 599 | + |
642 | +/* This implements a type defined in <ParavirtualizedGraphics/PGDevice.h> | 600 | +static Error *apple_gfx_mig_blocker; |
643 | + * which is opaque from the framework's point of view. Typedef PGTask_t already | 601 | +static uint32_t next_pgdisplay_serial_num = 1; |
644 | + * exists in the framework headers. */ | 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 | + */ | ||
645 | +struct PGTask_s { | 626 | +struct PGTask_s { |
646 | + QTAILQ_ENTRY(PGTask_s) node; | 627 | + QTAILQ_ENTRY(PGTask_s) node; |
647 | + AppleGFXState *s; | 628 | + AppleGFXState *s; |
648 | + mach_vm_address_t address; | 629 | + mach_vm_address_t address; |
649 | + uint64_t len; | 630 | + uint64_t len; |
650 | + /* | 631 | + /* |
651 | + * All unique MemoryRegions for which a mapping has been created in in this | 632 | + * All unique MemoryRegions for which a mapping has been created in in this |
652 | + * task, and on which we have thus called memory_region_ref(). There are | 633 | + * task, and on which we have thus called memory_region_ref(). There are |
653 | + * usually very few regions of system RAM in total, so we expect this array | 634 | + * usually very few regions of system RAM in total, so we expect this array |
654 | + * to be very short. Therefore, no need for sorting or fancy search | 635 | + * to be very short. Therefore, no need for sorting or fancy search |
655 | + * algorithms, linear search will do. */ | 636 | + * algorithms, linear search will do. |
656 | + GArray *mapped_regions; | 637 | + * Protected by AppleGFXState's task_mutex. |
638 | + */ | ||
639 | + GPtrArray *mapped_regions; | ||
657 | +}; | 640 | +}; |
658 | + | ||
659 | +static Error *apple_gfx_mig_blocker; | ||
660 | + | ||
661 | +static void apple_gfx_render_frame_completed(AppleGFXState *s, | ||
662 | + uint32_t width, uint32_t height); | ||
663 | + | ||
664 | +static dispatch_queue_t get_background_queue(void) | ||
665 | +{ | ||
666 | + return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); | ||
667 | +} | ||
668 | + | 641 | + |
669 | +static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len) | 642 | +static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len) |
670 | +{ | 643 | +{ |
671 | + mach_vm_address_t task_mem; | 644 | + mach_vm_address_t task_mem; |
672 | + PGTask_t *task; | 645 | + PGTask_t *task; |
... | ... | ||
679 | + | 652 | + |
680 | + task = g_new0(PGTask_t, 1); | 653 | + task = g_new0(PGTask_t, 1); |
681 | + task->s = s; | 654 | + task->s = s; |
682 | + task->address = task_mem; | 655 | + task->address = task_mem; |
683 | + task->len = len; | 656 | + task->len = len; |
684 | + task->mapped_regions = g_array_sized_new(false /* no termination */, | 657 | + task->mapped_regions = g_ptr_array_sized_new(2 /* Usually enough */); |
685 | + true /* clear */, | ||
686 | + sizeof(MemoryRegion *), | ||
687 | + 2 /* Usually no more RAM regions*/); | ||
688 | + | 658 | + |
689 | + QEMU_LOCK_GUARD(&s->task_mutex); | 659 | + QEMU_LOCK_GUARD(&s->task_mutex); |
690 | + QTAILQ_INSERT_TAIL(&s->tasks, task, node); | 660 | + QTAILQ_INSERT_TAIL(&s->tasks, task, node); |
691 | + | 661 | + |
692 | + return task; | 662 | + return task; |
693 | +} | 663 | +} |
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, ®ion); | ||
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 ------ */ | ||
694 | + | 1054 | + |
695 | +typedef struct AppleGFXIOJob { | 1055 | +typedef struct AppleGFXIOJob { |
696 | + AppleGFXState *state; | 1056 | + AppleGFXState *state; |
697 | + uint64_t offset; | 1057 | + uint64_t offset; |
698 | + uint64_t value; | 1058 | + uint64_t value; |
... | ... | ||
732 | +} | 1092 | +} |
733 | + | 1093 | + |
734 | +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, |
735 | + unsigned size) | 1095 | + unsigned size) |
736 | +{ | 1096 | +{ |
737 | + /* The methods mmioReadAtOffset: and especially mmioWriteAtOffset: can | 1097 | + /* |
738 | + * trigger and block on operations on other dispatch queues, which in turn | 1098 | + * The methods mmioReadAtOffset: and especially mmioWriteAtOffset: can |
1099 | + * trigger synchronous operations on other dispatch queues, which in turn | ||
739 | + * may call back out on one or more of the callback blocks. For this reason, | 1100 | + * may call back out on one or more of the callback blocks. For this reason, |
740 | + * and as we are holding the BQL, we invoke the I/O methods on a pool | 1101 | + * and as we are holding the BQL, we invoke the I/O methods on a pool |
741 | + * thread and handle AIO tasks while we wait. Any work in the callbacks | 1102 | + * thread and handle AIO tasks while we wait. Any work in the callbacks |
742 | + * requiring the BQL will in turn schedule BHs which this thread will | 1103 | + * requiring the BQL will in turn schedule BHs which this thread will |
743 | + * process while waiting. */ | 1104 | + * process while waiting. |
1105 | + */ | ||
744 | + AppleGFXIOJob job = { | 1106 | + AppleGFXIOJob job = { |
745 | + .state = opaque, | 1107 | + .state = opaque, |
746 | + .offset = offset, | 1108 | + .offset = offset, |
747 | + .value = val, | 1109 | + .value = val, |
748 | + .completed = false, | 1110 | + .completed = false, |
... | ... | ||
767 | + .min_access_size = 4, | 1129 | + .min_access_size = 4, |
768 | + .max_access_size = 4, | 1130 | + .max_access_size = 4, |
769 | + }, | 1131 | + }, |
770 | +}; | 1132 | +}; |
771 | + | 1133 | + |
772 | +static void apple_gfx_render_new_frame_bql_unlock(AppleGFXState *s) | ||
773 | +{ | ||
774 | + BOOL r; | ||
775 | + bool managed_texture = s->using_managed_texture_storage; | ||
776 | + uint32_t width = surface_width(s->surface); | ||
777 | + uint32_t height = surface_height(s->surface); | ||
778 | + MTLRegion region = MTLRegionMake2D(0, 0, width, height); | ||
779 | + id<MTLCommandBuffer> command_buffer = [s->mtl_queue commandBuffer]; | ||
780 | + id<MTLTexture> texture = s->texture; | ||
781 | + | ||
782 | + assert(bql_locked()); | ||
783 | + [texture retain]; | ||
784 | + | ||
785 | + bql_unlock(); | ||
786 | + | ||
787 | + /* This is not safe to call from the BQL due to PVG-internal locks causing | ||
788 | + * deadlocks. */ | ||
789 | + r = [s->pgdisp encodeCurrentFrameToCommandBuffer:command_buffer | ||
790 | + texture:texture | ||
791 | + region:region]; | ||
792 | + if (!r) { | ||
793 | + [texture release]; | ||
794 | + bql_lock(); | ||
795 | + --s->pending_frames; | ||
796 | + bql_unlock(); | ||
797 | + qemu_log_mask(LOG_GUEST_ERROR, | ||
798 | + "%s: encodeCurrentFrameToCommandBuffer:texture:region: " | ||
799 | + "failed\n", __func__); | ||
800 | + return; | ||
801 | + } | ||
802 | + | ||
803 | + if (managed_texture) { | ||
804 | + /* "Managed" textures exist in both VRAM and RAM and must be synced. */ | ||
805 | + id<MTLBlitCommandEncoder> blit = [command_buffer blitCommandEncoder]; | ||
806 | + [blit synchronizeResource:texture]; | ||
807 | + [blit endEncoding]; | ||
808 | + } | ||
809 | + [texture release]; | ||
810 | + [command_buffer addCompletedHandler: | ||
811 | + ^(id<MTLCommandBuffer> cb) | ||
812 | + { | ||
813 | + dispatch_async(s->render_queue, ^{ | ||
814 | + apple_gfx_render_frame_completed(s, width, height); | ||
815 | + }); | ||
816 | + }]; | ||
817 | + [command_buffer commit]; | ||
818 | +} | ||
819 | + | ||
820 | +static void copy_mtl_texture_to_surface_mem(id<MTLTexture> texture, void *vram) | ||
821 | +{ | ||
822 | + /* TODO: Skip this entirely on a pure Metal or headless/guest-only | ||
823 | + * rendering path, else use a blit command encoder? Needs careful | ||
824 | + * (double?) buffering design. */ | ||
825 | + size_t width = texture.width, height = texture.height; | ||
826 | + MTLRegion region = MTLRegionMake2D(0, 0, width, height); | ||
827 | + [texture getBytes:vram | ||
828 | + bytesPerRow:(width * 4) | ||
829 | + bytesPerImage:(width * height * 4) | ||
830 | + fromRegion:region | ||
831 | + mipmapLevel:0 | ||
832 | + slice:0]; | ||
833 | +} | ||
834 | + | ||
835 | +static void apple_gfx_render_frame_completed(AppleGFXState *s, | ||
836 | + uint32_t width, uint32_t height) | ||
837 | +{ | ||
838 | + bql_lock(); | ||
839 | + --s->pending_frames; | ||
840 | + assert(s->pending_frames >= 0); | ||
841 | + | ||
842 | + /* Only update display if mode hasn't changed since we started rendering. */ | ||
843 | + if (width == surface_width(s->surface) && | ||
844 | + height == surface_height(s->surface)) { | ||
845 | + copy_mtl_texture_to_surface_mem(s->texture, s->vram); | ||
846 | + if (s->gfx_update_requested) { | ||
847 | + s->gfx_update_requested = false; | ||
848 | + dpy_gfx_update_full(s->con); | ||
849 | + graphic_hw_update_done(s->con); | ||
850 | + s->new_frame_ready = false; | ||
851 | + } else { | ||
852 | + s->new_frame_ready = true; | ||
853 | + } | ||
854 | + } | ||
855 | + if (s->pending_frames > 0) { | ||
856 | + apple_gfx_render_new_frame_bql_unlock(s); | ||
857 | + } else { | ||
858 | + bql_unlock(); | ||
859 | + } | ||
860 | +} | ||
861 | + | ||
862 | +static void apple_gfx_fb_update_display(void *opaque) | ||
863 | +{ | ||
864 | + AppleGFXState *s = opaque; | ||
865 | + | ||
866 | + assert(bql_locked()); | ||
867 | + if (s->new_frame_ready) { | ||
868 | + dpy_gfx_update_full(s->con); | ||
869 | + s->new_frame_ready = false; | ||
870 | + graphic_hw_update_done(s->con); | ||
871 | + } else if (s->pending_frames > 0) { | ||
872 | + s->gfx_update_requested = true; | ||
873 | + } else { | ||
874 | + graphic_hw_update_done(s->con); | ||
875 | + } | ||
876 | +} | ||
877 | + | ||
878 | +static const GraphicHwOps apple_gfx_fb_ops = { | ||
879 | + .gfx_update = apple_gfx_fb_update_display, | ||
880 | + .gfx_update_async = true, | ||
881 | +}; | ||
882 | + | ||
883 | +static void update_cursor(AppleGFXState *s) | ||
884 | +{ | ||
885 | + assert(bql_locked()); | ||
886 | + dpy_mouse_set(s->con, s->pgdisp.cursorPosition.x, | ||
887 | + s->pgdisp.cursorPosition.y, s->cursor_show); | ||
888 | +} | ||
889 | + | ||
890 | +static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height) | ||
891 | +{ | ||
892 | + MTLTextureDescriptor *textureDescriptor; | ||
893 | + | ||
894 | + if (s->surface && | ||
895 | + width == surface_width(s->surface) && | ||
896 | + height == surface_height(s->surface)) { | ||
897 | + return; | ||
898 | + } | ||
899 | + | ||
900 | + g_free(s->vram); | ||
901 | + [s->texture release]; | ||
902 | + | ||
903 | + s->vram = g_malloc0_n(width * height, 4); | ||
904 | + s->surface = qemu_create_displaysurface_from(width, height, PIXMAN_LE_a8r8g8b8, | ||
905 | + width * 4, s->vram); | ||
906 | + | ||
907 | + @autoreleasepool { | ||
908 | + textureDescriptor = | ||
909 | + [MTLTextureDescriptor | ||
910 | + texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm | ||
911 | + width:width | ||
912 | + height:height | ||
913 | + mipmapped:NO]; | ||
914 | + textureDescriptor.usage = s->pgdisp.minimumTextureUsage; | ||
915 | + s->texture = [s->mtl newTextureWithDescriptor:textureDescriptor]; | ||
916 | + } | ||
917 | + | ||
918 | + s->using_managed_texture_storage = | ||
919 | + (s->texture.storageMode == MTLStorageModeManaged); | ||
920 | + dpy_gfx_replace_surface(s->con, s->surface); | ||
921 | +} | ||
922 | + | ||
923 | +static void create_fb(AppleGFXState *s) | ||
924 | +{ | ||
925 | + s->con = graphic_console_init(NULL, 0, &apple_gfx_fb_ops, s); | ||
926 | + set_mode(s, 1440, 1080); | ||
927 | + | ||
928 | + s->cursor_show = true; | ||
929 | +} | ||
930 | + | ||
931 | +static size_t apple_gfx_get_default_mmio_range_size(void) | 1134 | +static size_t apple_gfx_get_default_mmio_range_size(void) |
932 | +{ | 1135 | +{ |
933 | + size_t mmio_range_size; | 1136 | + size_t mmio_range_size; |
934 | + @autoreleasepool { | 1137 | + @autoreleasepool { |
935 | + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; | 1138 | + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; |
936 | + mmio_range_size = desc.mmioLength; | 1139 | + mmio_range_size = desc.mmioLength; |
937 | + [desc release]; | 1140 | + [desc release]; |
938 | + } | 1141 | + } |
939 | + return mmio_range_size; | 1142 | + return mmio_range_size; |
940 | +} | 1143 | +} |
941 | + | 1144 | + |
1145 | +/* ------ Initialisation and startup ------ */ | ||
1146 | + | ||
942 | +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) |
943 | +{ | 1148 | +{ |
944 | + 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(); |
945 | + | 1150 | + |
946 | + trace_apple_gfx_common_init(obj_name, mmio_range_size); | 1151 | + trace_apple_gfx_common_init(obj_name, mmio_range_size); |
947 | + 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, |
948 | + mmio_range_size); | 1153 | + mmio_range_size); |
949 | + | 1154 | + |
950 | + /* TODO: PVG framework supports serialising device state: integrate it! */ | 1155 | + /* TODO: PVG framework supports serialising device state: integrate it! */ |
951 | +} | ||
952 | + | ||
953 | +typedef struct AppleGFXMapMemoryJob { | ||
954 | + AppleGFXState *state; | ||
955 | + PGTask_t *task; | ||
956 | + uint64_t virtual_offset; | ||
957 | + PGPhysicalMemoryRange_t *ranges; | ||
958 | + uint32_t range_count; | ||
959 | + bool read_only; | ||
960 | + bool success; | ||
961 | + bool done; | ||
962 | +} AppleGFXMapMemoryJob; | ||
963 | + | ||
964 | +uintptr_t apple_gfx_host_address_for_gpa_range(uint64_t guest_physical, | ||
965 | + uint64_t length, bool read_only, | ||
966 | + MemoryRegion **mapping_in_region) | ||
967 | +{ | ||
968 | + MemoryRegion *ram_region; | ||
969 | + uintptr_t host_address; | ||
970 | + hwaddr ram_region_offset = 0; | ||
971 | + hwaddr ram_region_length = length; | ||
972 | + | ||
973 | + ram_region = address_space_translate(&address_space_memory, | ||
974 | + guest_physical, | ||
975 | + &ram_region_offset, | ||
976 | + &ram_region_length, !read_only, | ||
977 | + MEMTXATTRS_UNSPECIFIED); | ||
978 | + | ||
979 | + if (!ram_region || ram_region_length < length || | ||
980 | + !memory_access_is_direct(ram_region, !read_only)) { | ||
981 | + return 0; | ||
982 | + } | ||
983 | + | ||
984 | + host_address = (uintptr_t)memory_region_get_ram_ptr(ram_region); | ||
985 | + if (host_address == 0) { | ||
986 | + return 0; | ||
987 | + } | ||
988 | + host_address += ram_region_offset; | ||
989 | + *mapping_in_region = ram_region; | ||
990 | + return host_address; | ||
991 | +} | ||
992 | + | ||
993 | +/* Returns false if the region is already in the array */ | ||
994 | +static bool add_new_region(GArray *regions, MemoryRegion *region) | ||
995 | +{ | ||
996 | + MemoryRegion *existing; | ||
997 | + size_t i; | ||
998 | + | ||
999 | + for (i = 0; i < regions->len; ++i) { | ||
1000 | + existing = g_array_index(regions, MemoryRegion *, i); | ||
1001 | + if (existing == region) { | ||
1002 | + return false; | ||
1003 | + } | ||
1004 | + } | ||
1005 | + g_array_append_val(regions, region); | ||
1006 | + return true; | ||
1007 | +} | ||
1008 | + | ||
1009 | +static void apple_gfx_map_memory(void *opaque) | ||
1010 | +{ | ||
1011 | + AppleGFXMapMemoryJob *job = opaque; | ||
1012 | + AppleGFXState *s = job->state; | ||
1013 | + PGTask_t *task = job->task; | ||
1014 | + uint32_t range_count = job->range_count; | ||
1015 | + uint64_t virtual_offset = job->virtual_offset; | ||
1016 | + PGPhysicalMemoryRange_t *ranges = job->ranges; | ||
1017 | + bool read_only = job->read_only; | ||
1018 | + kern_return_t r; | ||
1019 | + mach_vm_address_t target, source; | ||
1020 | + vm_prot_t cur_protection, max_protection; | ||
1021 | + bool success = true; | ||
1022 | + MemoryRegion *region; | ||
1023 | + | ||
1024 | + g_assert(bql_locked()); | ||
1025 | + | ||
1026 | + trace_apple_gfx_map_memory(task, range_count, virtual_offset, read_only); | ||
1027 | + for (int i = 0; i < range_count; i++) { | ||
1028 | + PGPhysicalMemoryRange_t *range = &ranges[i]; | ||
1029 | + | ||
1030 | + target = task->address + virtual_offset; | ||
1031 | + virtual_offset += range->physicalLength; | ||
1032 | + | ||
1033 | + trace_apple_gfx_map_memory_range(i, range->physicalAddress, | ||
1034 | + range->physicalLength); | ||
1035 | + | ||
1036 | + region = NULL; | ||
1037 | + source = apple_gfx_host_address_for_gpa_range(range->physicalAddress, | ||
1038 | + range->physicalLength, | ||
1039 | + read_only, ®ion); | ||
1040 | + if (source == 0) { | ||
1041 | + success = false; | ||
1042 | + continue; | ||
1043 | + } | ||
1044 | + | ||
1045 | + if (add_new_region(task->mapped_regions, region)) { | ||
1046 | + memory_region_ref(region); | ||
1047 | + } | ||
1048 | + | ||
1049 | + cur_protection = 0; | ||
1050 | + max_protection = 0; | ||
1051 | + // Map guest RAM at range->physicalAddress into PG task memory range | ||
1052 | + r = mach_vm_remap(mach_task_self(), | ||
1053 | + &target, range->physicalLength, vm_page_size - 1, | ||
1054 | + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, | ||
1055 | + mach_task_self(), | ||
1056 | + source, false /* shared mapping, no copy */, | ||
1057 | + &cur_protection, &max_protection, | ||
1058 | + VM_INHERIT_COPY); | ||
1059 | + trace_apple_gfx_remap(r, source, target); | ||
1060 | + g_assert(r == KERN_SUCCESS); | ||
1061 | + } | ||
1062 | + | ||
1063 | + qemu_mutex_lock(&s->job_mutex); | ||
1064 | + job->success = success; | ||
1065 | + job->done = true; | ||
1066 | + qemu_cond_broadcast(&s->task_map_job_cond); | ||
1067 | + qemu_mutex_unlock(&s->job_mutex); | ||
1068 | +} | ||
1069 | + | ||
1070 | +void apple_gfx_await_bh_job(AppleGFXState *s, QemuCond *job_cond, bool *job_done_flag) | ||
1071 | +{ | ||
1072 | + qemu_mutex_lock(&s->job_mutex); | ||
1073 | + while (!*job_done_flag) { | ||
1074 | + qemu_cond_wait(job_cond, &s->job_mutex); | ||
1075 | + } | ||
1076 | + qemu_mutex_unlock(&s->job_mutex); | ||
1077 | +} | ||
1078 | + | ||
1079 | +typedef struct AppleGFXReadMemoryJob { | ||
1080 | + AppleGFXState *s; | ||
1081 | + hwaddr physical_address; | ||
1082 | + uint64_t length; | ||
1083 | + void *dst; | ||
1084 | + bool done; | ||
1085 | + bool success; | ||
1086 | +} AppleGFXReadMemoryJob; | ||
1087 | + | ||
1088 | +static void apple_gfx_do_read_memory(void *opaque) | ||
1089 | +{ | ||
1090 | + AppleGFXReadMemoryJob *job = opaque; | ||
1091 | + AppleGFXState *s = job->s; | ||
1092 | + MemTxResult r; | ||
1093 | + | ||
1094 | + r = dma_memory_read(&address_space_memory, job->physical_address, | ||
1095 | + job->dst, job->length, MEMTXATTRS_UNSPECIFIED); | ||
1096 | + job->success = r == MEMTX_OK; | ||
1097 | + | ||
1098 | + qemu_mutex_lock(&s->job_mutex); | ||
1099 | + job->done = true; | ||
1100 | + qemu_cond_broadcast(&s->mem_read_job_cond); | ||
1101 | + qemu_mutex_unlock(&s->job_mutex); | ||
1102 | +} | ||
1103 | + | ||
1104 | +static bool apple_gfx_read_memory(AppleGFXState *s, hwaddr physical_address, | ||
1105 | + uint64_t length, void *dst) | ||
1106 | +{ | ||
1107 | + AppleGFXReadMemoryJob job = { | ||
1108 | + s, physical_address, length, dst | ||
1109 | + }; | ||
1110 | + | ||
1111 | + trace_apple_gfx_read_memory(physical_address, length, dst); | ||
1112 | + | ||
1113 | + /* Traversing the memory map requires RCU/BQL, so do it in a BH. */ | ||
1114 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), apple_gfx_do_read_memory, | ||
1115 | + &job); | ||
1116 | + apple_gfx_await_bh_job(s, &s->mem_read_job_cond, &job.done); | ||
1117 | + return job.success; | ||
1118 | +} | ||
1119 | + | ||
1120 | +static void apple_gfx_task_destroy(AppleGFXState *s, PGTask_t *task) | ||
1121 | +{ | ||
1122 | + GArray *regions = task->mapped_regions; | ||
1123 | + MemoryRegion *region; | ||
1124 | + size_t i; | ||
1125 | + | ||
1126 | + for (i = 0; i < regions->len; ++i) { | ||
1127 | + region = g_array_index(regions, MemoryRegion *, i); | ||
1128 | + memory_region_unref(region); | ||
1129 | + } | ||
1130 | + g_array_unref(regions); | ||
1131 | + | ||
1132 | + mach_vm_deallocate(mach_task_self(), task->address, task->len); | ||
1133 | + | ||
1134 | + QEMU_LOCK_GUARD(&s->task_mutex); | ||
1135 | + QTAILQ_REMOVE(&s->tasks, task, node); | ||
1136 | + g_free(task); | ||
1137 | +} | 1156 | +} |
1138 | + | 1157 | + |
1139 | +static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, | 1158 | +static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, |
1140 | + PGDeviceDescriptor *desc) | 1159 | + PGDeviceDescriptor *desc) |
1141 | +{ | 1160 | +{ |
... | ... | ||
1147 | + }; | 1166 | + }; |
1148 | + | 1167 | + |
1149 | + desc.destroyTask = ^(PGTask_t * _Nonnull task) { | 1168 | + desc.destroyTask = ^(PGTask_t * _Nonnull task) { |
1150 | + trace_apple_gfx_destroy_task(task, task->mapped_regions->len); | 1169 | + trace_apple_gfx_destroy_task(task, task->mapped_regions->len); |
1151 | + | 1170 | + |
1152 | + apple_gfx_task_destroy(s, task); | 1171 | + apple_gfx_destroy_task(s, task); |
1153 | + }; | 1172 | + }; |
1154 | + | 1173 | + |
1155 | + desc.mapMemory = ^bool(PGTask_t * _Nonnull task, uint32_t range_count, | 1174 | + desc.mapMemory = ^bool(PGTask_t * _Nonnull task, uint32_t range_count, |
1156 | + uint64_t virtual_offset, bool read_only, | 1175 | + uint64_t virtual_offset, bool read_only, |
1157 | + PGPhysicalMemoryRange_t * _Nonnull ranges) { | 1176 | + PGPhysicalMemoryRange_t * _Nonnull ranges) { |
1158 | + AppleGFXMapMemoryJob job = { | 1177 | + return apple_gfx_task_map_memory(s, task, virtual_offset, |
1159 | + .state = s, | 1178 | + ranges, range_count, read_only); |
1160 | + .task = task, .ranges = ranges, .range_count = range_count, | 1179 | + }; |
1161 | + .read_only = read_only, .virtual_offset = virtual_offset, | 1180 | + |
1162 | + .done = false, .success = true, | 1181 | + desc.unmapMemory = ^bool(PGTask_t * _Nonnull task, uint64_t virtual_offset, |
1163 | + }; | ||
1164 | + if (range_count > 0) { | ||
1165 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), | ||
1166 | + apple_gfx_map_memory, &job); | ||
1167 | + apple_gfx_await_bh_job(s, &s->task_map_job_cond, &job.done); | ||
1168 | + } | ||
1169 | + return job.success; | ||
1170 | + }; | ||
1171 | + | ||
1172 | + desc.unmapMemory = ^bool(PGTask_t * _Nonnull task, uint64_t virtualOffset, | ||
1173 | + uint64_t length) { | 1182 | + uint64_t length) { |
1174 | + kern_return_t r; | 1183 | + apple_gfx_task_unmap_memory(s, task, virtual_offset, length); |
1175 | + mach_vm_address_t range_address; | ||
1176 | + | ||
1177 | + trace_apple_gfx_unmap_memory(task, virtualOffset, length); | ||
1178 | + | ||
1179 | + /* Replace task memory range with fresh pages, undoing the mapping | ||
1180 | + * from guest RAM. */ | ||
1181 | + range_address = task->address + virtualOffset; | ||
1182 | + r = mach_vm_allocate(mach_task_self(), &range_address, length, | ||
1183 | + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); | ||
1184 | + g_assert(r == KERN_SUCCESS); | ||
1185 | + | ||
1186 | + return true; | 1184 | + return true; |
1187 | + }; | 1185 | + }; |
1188 | + | 1186 | + |
1189 | + desc.readMemory = ^bool(uint64_t physical_address, uint64_t length, | 1187 | + desc.readMemory = ^bool(uint64_t physical_address, uint64_t length, |
1190 | + void * _Nonnull dst) { | 1188 | + void * _Nonnull dst) { |
1191 | + return apple_gfx_read_memory(s, physical_address, length, dst); | 1189 | + return apple_gfx_read_memory(s, physical_address, length, dst); |
1192 | + }; | 1190 | + }; |
1191 | +} | ||
1192 | + | ||
1193 | +static void new_frame_handler_bh(void *opaque) | ||
1194 | +{ | ||
1195 | + AppleGFXState *s = opaque; | ||
1196 | + | ||
1197 | + /* Drop frames if guest gets too far ahead. */ | ||
1198 | + if (s->pending_frames >= 2) { | ||
1199 | + return; | ||
1200 | + } | ||
1201 | + ++s->pending_frames; | ||
1202 | + if (s->pending_frames > 1) { | ||
1203 | + return; | ||
1204 | + } | ||
1205 | + | ||
1206 | + @autoreleasepool { | ||
1207 | + apple_gfx_render_new_frame(s); | ||
1208 | + } | ||
1193 | +} | 1209 | +} |
1194 | + | 1210 | + |
1195 | +static PGDisplayDescriptor *apple_gfx_prepare_display_descriptor(AppleGFXState *s) | 1211 | +static PGDisplayDescriptor *apple_gfx_prepare_display_descriptor(AppleGFXState *s) |
1196 | +{ | 1212 | +{ |
1197 | + PGDisplayDescriptor *disp_desc = [PGDisplayDescriptor new]; | 1213 | + PGDisplayDescriptor *disp_desc = [PGDisplayDescriptor new]; |
1198 | + | 1214 | + |
1199 | + disp_desc.name = @"QEMU display"; | 1215 | + disp_desc.name = @"QEMU display"; |
1200 | + disp_desc.sizeInMillimeters = NSMakeSize(400., 300.); /* A 20" display */ | 1216 | + disp_desc.sizeInMillimeters = NSMakeSize(400., 300.); /* A 20" display */ |
1201 | + disp_desc.queue = dispatch_get_main_queue(); | 1217 | + disp_desc.queue = dispatch_get_main_queue(); |
1202 | + disp_desc.newFrameEventHandler = ^(void) { | 1218 | + disp_desc.newFrameEventHandler = ^(void) { |
1203 | + trace_apple_gfx_new_frame(); | 1219 | + trace_apple_gfx_new_frame(); |
1204 | + dispatch_async(s->render_queue, ^{ | 1220 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), new_frame_handler_bh, s); |
1205 | + /* Drop frames if we get too far ahead. */ | ||
1206 | + bql_lock(); | ||
1207 | + if (s->pending_frames >= 2) { | ||
1208 | + bql_unlock(); | ||
1209 | + return; | ||
1210 | + } | ||
1211 | + ++s->pending_frames; | ||
1212 | + if (s->pending_frames > 1) { | ||
1213 | + bql_unlock(); | ||
1214 | + return; | ||
1215 | + } | ||
1216 | + @autoreleasepool { | ||
1217 | + apple_gfx_render_new_frame_bql_unlock(s); | ||
1218 | + } | ||
1219 | + }); | ||
1220 | + }; | 1221 | + }; |
1221 | + disp_desc.modeChangeHandler = ^(PGDisplayCoord_t sizeInPixels, | 1222 | + disp_desc.modeChangeHandler = ^(PGDisplayCoord_t sizeInPixels, |
1222 | + OSType pixelFormat) { | 1223 | + OSType pixelFormat) { |
1223 | + trace_apple_gfx_mode_change(sizeInPixels.x, sizeInPixels.y); | 1224 | + trace_apple_gfx_mode_change(sizeInPixels.x, sizeInPixels.y); |
1224 | + | 1225 | + |
1225 | + BQL_LOCK_GUARD(); | 1226 | + BQL_LOCK_GUARD(); |
1226 | + set_mode(s, sizeInPixels.x, sizeInPixels.y); | 1227 | + set_mode(s, sizeInPixels.x, sizeInPixels.y); |
1227 | + }; | 1228 | + }; |
1228 | + disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph, | 1229 | + disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph, |
1229 | + PGDisplayCoord_t hotSpot) { | 1230 | + PGDisplayCoord_t hotspot) { |
1231 | + AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job)); | ||
1232 | + job->s = s; | ||
1233 | + job->glyph = glyph; | ||
1234 | + job->hotspot = hotspot; | ||
1230 | + [glyph retain]; | 1235 | + [glyph retain]; |
1231 | + dispatch_async(get_background_queue(), ^{ | 1236 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), |
1232 | + BQL_LOCK_GUARD(); | 1237 | + set_cursor_glyph, job); |
1233 | + uint32_t bpp = glyph.bitsPerPixel; | ||
1234 | + size_t width = glyph.pixelsWide; | ||
1235 | + size_t height = glyph.pixelsHigh; | ||
1236 | + size_t padding_bytes_per_row = glyph.bytesPerRow - width * 4; | ||
1237 | + const uint8_t* px_data = glyph.bitmapData; | ||
1238 | + | ||
1239 | + trace_apple_gfx_cursor_set(bpp, width, height); | ||
1240 | + | ||
1241 | + if (s->cursor) { | ||
1242 | + cursor_unref(s->cursor); | ||
1243 | + s->cursor = NULL; | ||
1244 | + } | ||
1245 | + | ||
1246 | + if (bpp == 32) { /* Shouldn't be anything else, but just to be safe...*/ | ||
1247 | + s->cursor = cursor_alloc(width, height); | ||
1248 | + s->cursor->hot_x = hotSpot.x; | ||
1249 | + s->cursor->hot_y = hotSpot.y; | ||
1250 | + | ||
1251 | + uint32_t *dest_px = s->cursor->data; | ||
1252 | + | ||
1253 | + for (size_t y = 0; y < height; ++y) { | ||
1254 | + for (size_t x = 0; x < width; ++x) { | ||
1255 | + /* NSBitmapImageRep's red & blue channels are swapped | ||
1256 | + * compared to QEMUCursor's. */ | ||
1257 | + *dest_px = | ||
1258 | + (px_data[0] << 16u) | | ||
1259 | + (px_data[1] << 8u) | | ||
1260 | + (px_data[2] << 0u) | | ||
1261 | + (px_data[3] << 24u); | ||
1262 | + ++dest_px; | ||
1263 | + px_data += 4; | ||
1264 | + } | ||
1265 | + px_data += padding_bytes_per_row; | ||
1266 | + } | ||
1267 | + dpy_cursor_define(s->con, s->cursor); | ||
1268 | + update_cursor(s); | ||
1269 | + } | ||
1270 | + [glyph release]; | ||
1271 | + }); | ||
1272 | + }; | 1238 | + }; |
1273 | + disp_desc.cursorShowHandler = ^(BOOL show) { | 1239 | + disp_desc.cursorShowHandler = ^(BOOL show) { |
1274 | + dispatch_async(get_background_queue(), ^{ | 1240 | + trace_apple_gfx_cursor_show(show); |
1275 | + BQL_LOCK_GUARD(); | 1241 | + qatomic_set(&s->cursor_show, show); |
1276 | + trace_apple_gfx_cursor_show(show); | 1242 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), |
1277 | + s->cursor_show = show; | 1243 | + update_cursor_bh, s); |
1278 | + update_cursor(s); | ||
1279 | + }); | ||
1280 | + }; | 1244 | + }; |
1281 | + disp_desc.cursorMoveHandler = ^(void) { | 1245 | + disp_desc.cursorMoveHandler = ^(void) { |
1282 | + dispatch_async(get_background_queue(), ^{ | 1246 | + trace_apple_gfx_cursor_move(); |
1283 | + BQL_LOCK_GUARD(); | 1247 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), |
1284 | + trace_apple_gfx_cursor_move(); | 1248 | + update_cursor_bh, s); |
1285 | + update_cursor(s); | ||
1286 | + }); | ||
1287 | + }; | 1249 | + }; |
1288 | + | 1250 | + |
1289 | + return disp_desc; | 1251 | + return disp_desc; |
1290 | +} | 1252 | +} |
1291 | + | 1253 | + |
1292 | +static NSArray<PGDisplayMode*>* apple_gfx_prepare_display_mode_array(void) | 1254 | +static NSArray<PGDisplayMode*>* apple_gfx_prepare_display_mode_array(void) |
1293 | +{ | 1255 | +{ |
1294 | + PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; | 1256 | + PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; |
1295 | + NSArray<PGDisplayMode*>* mode_array = nil; | 1257 | + NSArray<PGDisplayMode*>* mode_array; |
1296 | + int i; | 1258 | + int i; |
1297 | + | 1259 | + |
1298 | + for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { | 1260 | + for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { |
1299 | + modes[i] = | 1261 | + modes[i] = |
1300 | + [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.]; | 1262 | + [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.]; |
... | ... | ||
1334 | + [devs release]; | 1296 | + [devs release]; |
1335 | + | 1297 | + |
1336 | + return dev; | 1298 | + return dev; |
1337 | +} | 1299 | +} |
1338 | + | 1300 | + |
1339 | +void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc, | 1301 | +bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, |
1340 | + Error **errp) | 1302 | + PGDeviceDescriptor *desc, Error **errp) |
1341 | +{ | 1303 | +{ |
1342 | + PGDisplayDescriptor *disp_desc = nil; | 1304 | + PGDisplayDescriptor *disp_desc; |
1343 | + | 1305 | + |
1344 | + if (apple_gfx_mig_blocker == NULL) { | 1306 | + if (apple_gfx_mig_blocker == NULL) { |
1345 | + error_setg(&apple_gfx_mig_blocker, | 1307 | + error_setg(&apple_gfx_mig_blocker, |
1346 | + "Migration state blocked by apple-gfx display device"); | 1308 | + "Migration state blocked by apple-gfx display device"); |
1347 | + if (migrate_add_blocker(&apple_gfx_mig_blocker, errp) < 0) { | 1309 | + if (migrate_add_blocker(&apple_gfx_mig_blocker, errp) < 0) { |
1348 | + return; | 1310 | + return false; |
1349 | + } | 1311 | + } |
1350 | + } | 1312 | + } |
1351 | + | 1313 | + |
1352 | + qemu_mutex_init(&s->task_mutex); | 1314 | + qemu_mutex_init(&s->task_mutex); |
1353 | + QTAILQ_INIT(&s->tasks); | 1315 | + QTAILQ_INIT(&s->tasks); |
1354 | + s->render_queue = dispatch_queue_create("apple-gfx.render", | ||
1355 | + DISPATCH_QUEUE_SERIAL); | ||
1356 | + s->mtl = copy_suitable_metal_device(); | 1316 | + s->mtl = copy_suitable_metal_device(); |
1357 | + s->mtl_queue = [s->mtl newCommandQueue]; | 1317 | + s->mtl_queue = [s->mtl newCommandQueue]; |
1358 | + | 1318 | + |
1359 | + desc.device = s->mtl; | 1319 | + desc.device = s->mtl; |
1360 | + | 1320 | + |
1361 | + apple_gfx_register_task_mapping_handlers(s, desc); | 1321 | + apple_gfx_register_task_mapping_handlers(s, desc); |
1362 | + | 1322 | + |
1323 | + s->cursor_show = true; | ||
1324 | + | ||
1363 | + s->pgdev = PGNewDeviceWithDescriptor(desc); | 1325 | + s->pgdev = PGNewDeviceWithDescriptor(desc); |
1364 | + | 1326 | + |
1365 | + disp_desc = apple_gfx_prepare_display_descriptor(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 | + */ | ||
1366 | + s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc | 1336 | + s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc |
1367 | + port:0 serialNum:1234]; | 1337 | + port:0 |
1338 | + serialNum:next_pgdisplay_serial_num++]; | ||
1368 | + [disp_desc release]; | 1339 | + [disp_desc release]; |
1369 | + s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); | 1340 | + s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); |
1370 | + | 1341 | + |
1371 | + create_fb(s); | 1342 | + s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s); |
1372 | + | 1343 | + return true; |
1373 | + qemu_mutex_init(&s->job_mutex); | ||
1374 | + qemu_cond_init(&s->task_map_job_cond); | ||
1375 | + qemu_cond_init(&s->mem_read_job_cond); | ||
1376 | +} | 1344 | +} |
1377 | 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 |
1378 | index XXXXXXX..XXXXXXX 100644 | 1346 | index XXXXXXX..XXXXXXX 100644 |
1379 | --- a/hw/display/meson.build | 1347 | --- a/hw/display/meson.build |
1380 | +++ b/hw/display/meson.build | 1348 | +++ b/hw/display/meson.build |
1381 | @@ -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')) |
1382 | 1350 | ||
1383 | 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]) |
1384 | 1352 | ||
1385 | +system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) | 1353 | +if host_os == 'darwin' |
1386 | +if cpu == 'aarch64' | 1354 | + system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) |
1387 | + system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.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 | ||
1388 | +endif | 1358 | +endif |
1389 | 1359 | ||
1390 | if config_all_devices.has_key('CONFIG_VIRTIO_GPU') | 1360 | if config_all_devices.has_key('CONFIG_VIRTIO_GPU') |
1391 | virtio_gpu_ss = ss.source_set() | 1361 | virtio_gpu_ss = ss.source_set() |
1392 | 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 |
... | ... | ||
1403 | +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 |
1404 | +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" |
1405 | +apple_gfx_destroy_task(void *task, unsigned int num_mapped_regions) "task=%p, task->mapped_regions->len=%u" | 1375 | +apple_gfx_destroy_task(void *task, unsigned int num_mapped_regions) "task=%p, task->mapped_regions->len=%u" |
1406 | +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" |
1407 | +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 |
1408 | +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 |
1409 | +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 |
1410 | +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" |
1411 | +apple_gfx_raise_irq(uint32_t vector) "vector=0x%x" | 1381 | +apple_gfx_raise_irq(uint32_t vector) "vector=0x%x" |
1412 | +apple_gfx_new_frame(void) "" | 1382 | +apple_gfx_new_frame(void) "" |
1413 | +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 |
... | ... | ||
1417 | +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" |
1418 | + | 1388 | + |
1419 | +# apple-gfx-mmio.m | 1389 | +# apple-gfx-mmio.m |
1420 | +apple_gfx_mmio_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 |
1421 | +apple_gfx_mmio_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 |
1422 | +apple_gfx_iosfc_map_memory(uint64_t phys, uint64_t len, uint32_t ro, void *va, void *e, void *f, void* va_result, int success) "phys=0x%"PRIx64" len=0x%"PRIx64" ro=%d va=%p e=%p f=%p -> *va=%p, success = %d" | 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" |
1423 | +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 | 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 |
1424 | +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" | 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" |
1425 | +apple_gfx_iosfc_unmap_memory_region(uint64_t mem, size_t region_index, uint64_t map_count, void *region) "mapping @ 0x%"PRIx64" in region %zu, map count %"PRIu64", memory region %p" | 1395 | +apple_gfx_iosfc_unmap_memory_region(void* mem, void *region) "unmapping @ %p from memory region %p" |
1426 | +apple_gfx_iosfc_raise_irq(uint32_t vector) "vector=0x%x" | 1396 | +apple_gfx_iosfc_raise_irq(uint32_t vector) "vector=0x%x" |
1427 | + | 1397 | + |
1428 | diff --git a/meson.build b/meson.build | 1398 | diff --git a/meson.build b/meson.build |
1429 | index XXXXXXX..XXXXXXX 100644 | 1399 | index XXXXXXX..XXXXXXX 100644 |
1430 | --- a/meson.build | 1400 | --- a/meson.build |
1431 | +++ b/meson.build | 1401 | +++ b/meson.build |
1432 | @@ -XXX,XX +XXX,XX @@ socket = [] | 1402 | @@ -XXX,XX +XXX,XX @@ socket = [] |
1433 | version_res = [] | 1403 | version_res = [] |
1434 | coref = [] | 1404 | coref = [] |
1435 | iokit = [] | 1405 | iokit = [] |
1436 | +pvg = [] | 1406 | +pvg = not_found |
1437 | +metal = [] | 1407 | +metal = [] |
1438 | emulator_link_args = [] | 1408 | emulator_link_args = [] |
1439 | midl = not_found | 1409 | midl = not_found |
1440 | widl = not_found | 1410 | widl = not_found |
1441 | @@ -XXX,XX +XXX,XX @@ elif host_os == 'darwin' | 1411 | @@ -XXX,XX +XXX,XX @@ elif host_os == 'darwin' |
... | ... | ||
1446 | + metal = dependency('appleframeworks', modules: 'Metal') | 1416 | + metal = dependency('appleframeworks', modules: 'Metal') |
1447 | elif host_os == 'sunos' | 1417 | elif host_os == 'sunos' |
1448 | socket = [cc.find_library('socket'), | 1418 | socket = [cc.find_library('socket'), |
1449 | cc.find_library('nsl'), | 1419 | cc.find_library('nsl'), |
1450 | -- | 1420 | -- |
1451 | 2.39.3 (Apple Git-145) | 1421 | 2.39.5 (Apple Git-154) |
1452 | 1422 | ||
1453 | 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 | 9 | ||
9 | v4: | 10 | v4: |
10 | 11 | ||
11 | * Threading improvements analogous to those in common apple-gfx code | 12 | * Threading improvements analogous to those in common apple-gfx code |
12 | and mmio device variant. | 13 | and mmio device variant. |
13 | * Addressed some smaller issues raised in code review. | 14 | * Smaller code review issues addressed. |
14 | 15 | ||
15 | v5: | 16 | v5: |
16 | 17 | ||
17 | * Minor error handling improvement. | 18 | * Minor error handling improvement. |
18 | 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 | |||
19 | hw/display/Kconfig | 4 + | 33 | hw/display/Kconfig | 4 + |
20 | hw/display/apple-gfx-pci.m | 149 +++++++++++++++++++++++++++++++++++++ | 34 | hw/display/apple-gfx-pci.m | 150 +++++++++++++++++++++++++++++++++++++ |
21 | hw/display/meson.build | 1 + | 35 | hw/display/meson.build | 1 + |
22 | 3 files changed, 154 insertions(+) | 36 | 3 files changed, 155 insertions(+) |
23 | create mode 100644 hw/display/apple-gfx-pci.m | 37 | create mode 100644 hw/display/apple-gfx-pci.m |
24 | 38 | ||
25 | diff --git a/hw/display/Kconfig b/hw/display/Kconfig | 39 | diff --git a/hw/display/Kconfig b/hw/display/Kconfig |
26 | index XXXXXXX..XXXXXXX 100644 | 40 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/hw/display/Kconfig | 41 | --- a/hw/display/Kconfig |
... | ... | ||
82 | +static void apple_gfx_pci_init(Object *obj) | 96 | +static void apple_gfx_pci_init(Object *obj) |
83 | +{ | 97 | +{ |
84 | + AppleGFXPCIState *s = APPLE_GFX_PCI(obj); | 98 | + AppleGFXPCIState *s = APPLE_GFX_PCI(obj); |
85 | + | 99 | + |
86 | + if (!apple_gfx_pci_option_rom_path) { | 100 | + if (!apple_gfx_pci_option_rom_path) { |
87 | + /* The following is done on device not class init to avoid running | 101 | + /* |
88 | + * ObjC code before fork() in -daemonize mode. */ | 102 | + * The following is done on device not class init to avoid running |
103 | + * ObjC code before fork() in -daemonize mode. | ||
104 | + */ | ||
89 | + PCIDeviceClass *pci = PCI_DEVICE_CLASS(object_get_class(obj)); | 105 | + PCIDeviceClass *pci = PCI_DEVICE_CLASS(object_get_class(obj)); |
90 | + apple_gfx_init_option_rom_path(); | 106 | + apple_gfx_init_option_rom_path(); |
91 | + pci->romfile = apple_gfx_pci_option_rom_path; | 107 | + pci->romfile = apple_gfx_pci_option_rom_path; |
92 | + } | 108 | + } |
93 | + | 109 | + |
... | ... | ||
107 | + msi_notify(job->device, job->vector); | 123 | + msi_notify(job->device, job->vector); |
108 | + } | 124 | + } |
109 | + g_free(job); | 125 | + g_free(job); |
110 | +} | 126 | +} |
111 | + | 127 | + |
112 | +static void apple_gfx_pci_interrupt(PCIDevice *dev, AppleGFXPCIState *s, | 128 | +static void apple_gfx_pci_interrupt(PCIDevice *dev, uint32_t vector) |
113 | + uint32_t vector) | ||
114 | +{ | 129 | +{ |
115 | + AppleGFXPCIInterruptJob *job; | 130 | + AppleGFXPCIInterruptJob *job; |
116 | + | 131 | + |
117 | + trace_apple_gfx_raise_irq(vector); | 132 | + trace_apple_gfx_raise_irq(vector); |
118 | + job = g_malloc0(sizeof(*job)); | 133 | + job = g_malloc0(sizeof(*job)); |
... | ... | ||
130 | + pci_register_bar(dev, PG_PCI_BAR_MMIO, | 145 | + pci_register_bar(dev, PG_PCI_BAR_MMIO, |
131 | + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->common.iomem_gfx); | 146 | + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->common.iomem_gfx); |
132 | + | 147 | + |
133 | + ret = msi_init(dev, 0x0 /* config offset; 0 = find space */, | 148 | + ret = msi_init(dev, 0x0 /* config offset; 0 = find space */, |
134 | + PG_PCI_MAX_MSI_VECTORS, true /* msi64bit */, | 149 | + PG_PCI_MAX_MSI_VECTORS, true /* msi64bit */, |
135 | + false /*msi_per_vector_mask*/, errp); | 150 | + false /* msi_per_vector_mask */, errp); |
136 | + if (ret != 0) { | 151 | + if (ret != 0) { |
137 | + return; | 152 | + return; |
138 | + } | 153 | + } |
139 | + | 154 | + |
140 | + @autoreleasepool { | 155 | + @autoreleasepool { |
141 | + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; | 156 | + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; |
142 | + desc.raiseInterrupt = ^(uint32_t vector) { | 157 | + desc.raiseInterrupt = ^(uint32_t vector) { |
143 | + apple_gfx_pci_interrupt(dev, s, vector); | 158 | + apple_gfx_pci_interrupt(dev, vector); |
144 | + }; | 159 | + }; |
145 | + | 160 | + |
146 | + apple_gfx_common_realize(&s->common, desc, errp); | 161 | + apple_gfx_common_realize(&s->common, DEVICE(dev), desc, errp); |
147 | + [desc release]; | 162 | + [desc release]; |
148 | + desc = nil; | 163 | + desc = nil; |
149 | + } | 164 | + } |
150 | +} | 165 | +} |
151 | + | 166 | + |
... | ... | ||
169 | + pci->vendor_id = PG_PCI_VENDOR_ID; | 184 | + pci->vendor_id = PG_PCI_VENDOR_ID; |
170 | + pci->device_id = PG_PCI_DEVICE_ID; | 185 | + pci->device_id = PG_PCI_DEVICE_ID; |
171 | + pci->class_id = PCI_CLASS_DISPLAY_OTHER; | 186 | + pci->class_id = PCI_CLASS_DISPLAY_OTHER; |
172 | + pci->realize = apple_gfx_pci_realize; | 187 | + pci->realize = apple_gfx_pci_realize; |
173 | + | 188 | + |
174 | + // TODO: Property for setting mode list | 189 | + /* TODO: Property for setting mode list */ |
175 | +} | 190 | +} |
176 | + | 191 | + |
177 | +static TypeInfo apple_gfx_pci_types[] = { | 192 | +static TypeInfo apple_gfx_pci_types[] = { |
178 | + { | 193 | + { |
179 | + .name = TYPE_APPLE_GFX_PCI, | 194 | + .name = TYPE_APPLE_GFX_PCI, |
... | ... | ||
191 | + | 206 | + |
192 | diff --git a/hw/display/meson.build b/hw/display/meson.build | 207 | diff --git a/hw/display/meson.build b/hw/display/meson.build |
193 | index XXXXXXX..XXXXXXX 100644 | 208 | index XXXXXXX..XXXXXXX 100644 |
194 | --- a/hw/display/meson.build | 209 | --- a/hw/display/meson.build |
195 | +++ b/hw/display/meson.build | 210 | +++ b/hw/display/meson.build |
196 | @@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pv | 211 | @@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_ |
197 | if cpu == 'aarch64' | 212 | |
198 | system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.m'), pvg, metal]) | 213 | if host_os == 'darwin' |
199 | endif | 214 | system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) |
200 | +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]) |
201 | 216 | if cpu == 'aarch64' | |
202 | 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]) |
203 | virtio_gpu_ss = ss.source_set() | 218 | endif |
204 | -- | 219 | -- |
205 | 2.39.3 (Apple Git-145) | 220 | 2.39.5 (Apple Git-154) |
206 | 221 | ||
207 | 222 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
6 | less awkward to use, for example: | 6 | less awkward to use, for example: |
7 | 7 | ||
8 | -device '{"driver":"apple-gfx-pci", "display-modes":["1920x1080@60", "3840x2160@60"]}' | 8 | -device '{"driver":"apple-gfx-pci", "display-modes":["1920x1080@60", "3840x2160@60"]}' |
9 | 9 | ||
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 | --- |
12 | 14 | ||
13 | v4: | 15 | v4: |
14 | 16 | ||
15 | * Switched to the native array property type, which recently gained | 17 | * Switched to the native array property type, which recently gained |
... | ... | ||
20 | v5: | 22 | v5: |
21 | 23 | ||
22 | * Better error handling and buffer management in property parsing and | 24 | * Better error handling and buffer management in property parsing and |
23 | output. | 25 | output. |
24 | 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 | |||
25 | hw/display/apple-gfx-mmio.m | 8 +++ | 44 | hw/display/apple-gfx-mmio.m | 8 +++ |
26 | hw/display/apple-gfx-pci.m | 9 ++- | 45 | hw/display/apple-gfx-pci.m | 9 ++- |
27 | hw/display/apple-gfx.h | 12 ++++ | 46 | hw/display/apple-gfx.h | 11 +++ |
28 | hw/display/apple-gfx.m | 121 ++++++++++++++++++++++++++++++++---- | 47 | hw/display/apple-gfx.m | 135 +++++++++++++++++++++++++++++++----- |
29 | hw/display/trace-events | 2 + | 48 | hw/display/trace-events | 2 + |
30 | 5 files changed, 139 insertions(+), 13 deletions(-) | 49 | 5 files changed, 145 insertions(+), 20 deletions(-) |
31 | 50 | ||
32 | diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m | 51 | diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m |
33 | index XXXXXXX..XXXXXXX 100644 | 52 | index XXXXXXX..XXXXXXX 100644 |
34 | --- a/hw/display/apple-gfx-mmio.m | 53 | --- a/hw/display/apple-gfx-mmio.m |
35 | +++ b/hw/display/apple-gfx-mmio.m | 54 | +++ b/hw/display/apple-gfx-mmio.m |
... | ... | ||
75 | DeviceClass *dc = DEVICE_CLASS(klass); | 94 | DeviceClass *dc = DEVICE_CLASS(klass); |
76 | @@ -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) |
77 | pci->class_id = PCI_CLASS_DISPLAY_OTHER; | 96 | pci->class_id = PCI_CLASS_DISPLAY_OTHER; |
78 | pci->realize = apple_gfx_pci_realize; | 97 | pci->realize = apple_gfx_pci_realize; |
79 | 98 | ||
80 | - // TODO: Property for setting mode list | 99 | - /* TODO: Property for setting mode list */ |
81 | + device_class_set_props(dc, apple_gfx_pci_properties); | 100 | + device_class_set_props(dc, apple_gfx_pci_properties); |
82 | } | 101 | } |
83 | 102 | ||
84 | static TypeInfo apple_gfx_pci_types[] = { | 103 | static TypeInfo apple_gfx_pci_types[] = { |
85 | 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 |
... | ... | ||
95 | 114 | ||
96 | @class PGDeviceDescriptor; | 115 | @class PGDeviceDescriptor; |
97 | @@ -XXX,XX +XXX,XX @@ | 116 | @@ -XXX,XX +XXX,XX @@ |
98 | 117 | ||
99 | typedef QTAILQ_HEAD(, PGTask_s) PGTaskList; | 118 | typedef QTAILQ_HEAD(, PGTask_s) PGTaskList; |
100 | |||
101 | +struct AppleGFXDisplayMode; | ||
102 | typedef struct AppleGFXState { | ||
103 | /* Initialised on init/realize() */ | ||
104 | MemoryRegion iomem_gfx; | ||
105 | @@ -XXX,XX +XXX,XX @@ typedef struct AppleGFXState { | ||
106 | id<MTLDevice> mtl; | ||
107 | id<MTLCommandQueue> mtl_queue; | ||
108 | dispatch_queue_t render_queue; | ||
109 | + struct AppleGFXDisplayMode *display_modes; | ||
110 | + uint32_t num_display_modes; | ||
111 | /* | ||
112 | * QemuMutex & QemuConds for awaiting completion of PVG memory-mapping and | ||
113 | * reading requests after submitting them to run in the AIO context. | ||
114 | @@ -XXX,XX +XXX,XX @@ typedef struct AppleGFXState { | ||
115 | id<MTLTexture> texture; | ||
116 | } AppleGFXState; | ||
117 | 119 | ||
118 | +typedef struct AppleGFXDisplayMode { | 120 | +typedef struct AppleGFXDisplayMode { |
119 | + uint16_t width_px; | 121 | + uint16_t width_px; |
120 | + uint16_t height_px; | 122 | + uint16_t height_px; |
121 | + uint16_t refresh_rate_hz; | 123 | + uint16_t refresh_rate_hz; |
122 | +} AppleGFXDisplayMode; | 124 | +} AppleGFXDisplayMode; |
123 | + | 125 | + |
124 | void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name); | 126 | typedef struct AppleGFXState { |
125 | void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc, | 127 | /* Initialised on init/realize() */ |
126 | Error **errp); | 128 | MemoryRegion iomem_gfx; |
127 | @@ -XXX,XX +XXX,XX @@ uintptr_t apple_gfx_host_address_for_gpa_range(uint64_t guest_physical, | 129 | @@ -XXX,XX +XXX,XX @@ typedef struct AppleGFXState { |
128 | void apple_gfx_await_bh_job(AppleGFXState *s, QemuCond *job_cond, | 130 | QemuConsole *con; |
129 | bool *job_done_flag); | 131 | id<MTLDevice> mtl; |
132 | id<MTLCommandQueue> mtl_queue; | ||
133 | + AppleGFXDisplayMode *display_modes; | ||
134 | + uint32_t num_display_modes; | ||
135 | |||
136 | /* List `tasks` is protected by task_mutex */ | ||
137 | QemuMutex task_mutex; | ||
138 | @@ -XXX,XX +XXX,XX @@ void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical, | ||
139 | uint64_t length, bool read_only, | ||
140 | MemoryRegion **mapping_in_region); | ||
130 | 141 | ||
131 | +extern const PropertyInfo qdev_prop_display_mode; | 142 | +extern const PropertyInfo qdev_prop_display_mode; |
132 | + | 143 | + |
133 | #endif | 144 | #endif |
134 | 145 | ||
... | ... | ||
147 | + { 1920, 1080, 60 }, | 158 | + { 1920, 1080, 60 }, |
148 | + { 1440, 1080, 60 }, | 159 | + { 1440, 1080, 60 }, |
149 | + { 1280, 1024, 60 }, | 160 | + { 1280, 1024, 60 }, |
150 | }; | 161 | }; |
151 | 162 | ||
152 | /* This implements a type defined in <ParavirtualizedGraphics/PGDevice.h> | 163 | static Error *apple_gfx_mig_blocker; |
153 | @@ -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) |
154 | static void create_fb(AppleGFXState *s) | ||
155 | { | ||
156 | s->con = graphic_console_init(NULL, 0, &apple_gfx_fb_ops, s); | ||
157 | - set_mode(s, 1440, 1080); | ||
158 | |||
159 | s->cursor_show = true; | ||
160 | } | ||
161 | @@ -XXX,XX +XXX,XX @@ static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, | ||
162 | return disp_desc; | 165 | return disp_desc; |
163 | } | 166 | } |
164 | 167 | ||
165 | -static NSArray<PGDisplayMode*>* apple_gfx_prepare_display_mode_array(void) | 168 | -static NSArray<PGDisplayMode*>* apple_gfx_prepare_display_mode_array(void) |
166 | +static NSArray<PGDisplayMode*>* apple_gfx_create_display_mode_array( | 169 | +static NSArray<PGDisplayMode *> *apple_gfx_create_display_mode_array( |
167 | + const AppleGFXDisplayMode display_modes[], uint32_t display_mode_count) | 170 | + const AppleGFXDisplayMode display_modes[], uint32_t display_mode_count) |
168 | { | 171 | { |
169 | - PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; | 172 | - PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; |
170 | + PGDisplayMode **modes = alloca(sizeof(modes[0]) * display_mode_count); | 173 | - NSArray<PGDisplayMode*>* mode_array; |
171 | NSArray<PGDisplayMode*>* mode_array = nil; | ||
172 | - int i; | 174 | - int i; |
175 | - | ||
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; | ||
173 | + uint32_t i; | 186 | + uint32_t i; |
174 | 187 | + PGDisplayMode *mode_obj; | |
175 | - for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { | 188 | + NSMutableArray<PGDisplayMode *> *mode_array = |
189 | + [[NSMutableArray alloc] initWithCapacity:display_mode_count]; | ||
190 | + | ||
176 | + for (i = 0; i < display_mode_count; i++) { | 191 | + for (i = 0; i < display_mode_count; i++) { |
177 | + const AppleGFXDisplayMode *mode = &display_modes[i]; | 192 | + const AppleGFXDisplayMode *mode = &display_modes[i]; |
178 | + trace_apple_gfx_display_mode(i, mode->width_px, mode->height_px); | 193 | + trace_apple_gfx_display_mode(i, mode->width_px, mode->height_px); |
179 | + PGDisplayCoord_t mode_size = { mode->width_px, mode->height_px }; | 194 | + PGDisplayCoord_t mode_size = { mode->width_px, mode->height_px }; |
180 | modes[i] = | 195 | + |
181 | - [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.]; | 196 | + mode_obj = |
182 | + [[PGDisplayMode alloc] initWithSizeInPixels:mode_size | 197 | + [[PGDisplayMode alloc] initWithSizeInPixels:mode_size |
183 | + refreshRateInHz:mode->refresh_rate_hz]; | 198 | + refreshRateInHz:mode->refresh_rate_hz]; |
199 | + [mode_array addObject:mode_obj]; | ||
200 | + [mode_obj release]; | ||
184 | } | 201 | } |
185 | 202 | ||
186 | - mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)]; | 203 | return mode_array; |
187 | + mode_array = [NSArray arrayWithObjects:modes count:display_mode_count]; | 204 | @@ -XXX,XX +XXX,XX @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, |
188 | 205 | PGDeviceDescriptor *desc, Error **errp) | |
189 | - for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { | ||
190 | + for (i = 0; i < display_mode_count; i++) { | ||
191 | [modes[i] release]; | ||
192 | modes[i] = nil; | ||
193 | } | ||
194 | @@ -XXX,XX +XXX,XX @@ void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc, | ||
195 | Error **errp) | ||
196 | { | 206 | { |
197 | PGDisplayDescriptor *disp_desc = nil; | 207 | PGDisplayDescriptor *disp_desc; |
198 | + const AppleGFXDisplayMode *display_modes = apple_gfx_default_modes; | 208 | + const AppleGFXDisplayMode *display_modes = apple_gfx_default_modes; |
199 | + int num_display_modes = ARRAY_SIZE(apple_gfx_default_modes); | 209 | + uint32_t num_display_modes = ARRAY_SIZE(apple_gfx_default_modes); |
210 | + NSArray<PGDisplayMode *> *mode_array; | ||
200 | 211 | ||
201 | if (apple_gfx_mig_blocker == NULL) { | 212 | if (apple_gfx_mig_blocker == NULL) { |
202 | error_setg(&apple_gfx_mig_blocker, | 213 | error_setg(&apple_gfx_mig_blocker, |
203 | @@ -XXX,XX +XXX,XX @@ void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc, | 214 | @@ -XXX,XX +XXX,XX @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, |
204 | s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc | 215 | port:0 |
205 | port:0 serialNum:1234]; | 216 | serialNum:next_pgdisplay_serial_num++]; |
206 | [disp_desc release]; | 217 | [disp_desc release]; |
207 | - s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); | 218 | - s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); |
208 | + | 219 | + |
209 | + if (s->display_modes != NULL && s->num_display_modes > 0) { | 220 | + if (s->display_modes != NULL && s->num_display_modes > 0) { |
210 | + trace_apple_gfx_common_realize_modes_property(s->num_display_modes); | 221 | + trace_apple_gfx_common_realize_modes_property(s->num_display_modes); |
211 | + display_modes = s->display_modes; | 222 | + display_modes = s->display_modes; |
212 | + num_display_modes = s->num_display_modes; | 223 | + num_display_modes = s->num_display_modes; |
213 | + } | 224 | + } |
214 | + s->pgdisp.modeList = | 225 | + s->pgdisp.modeList = mode_array = |
215 | + apple_gfx_create_display_mode_array(display_modes, num_display_modes); | 226 | + apple_gfx_create_display_mode_array(display_modes, num_display_modes); |
216 | 227 | + [mode_array release]; | |
217 | create_fb(s); | 228 | |
218 | 229 | s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s); | |
219 | @@ -XXX,XX +XXX,XX @@ void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc, | 230 | return true; |
220 | qemu_cond_init(&s->task_map_job_cond); | 231 | } |
221 | qemu_cond_init(&s->mem_read_job_cond); | 232 | + |
222 | } | 233 | +/* ------ Display mode list device property ------ */ |
223 | + | 234 | + |
224 | +static void apple_gfx_get_display_mode(Object *obj, Visitor *v, | 235 | +static void apple_gfx_get_display_mode(Object *obj, Visitor *v, |
225 | + const char *name, void *opaque, | 236 | + const char *name, void *opaque, |
226 | + Error **errp) | 237 | + Error **errp) |
227 | +{ | 238 | +{ |
... | ... | ||
244 | + const char *name, void *opaque, | 255 | + const char *name, void *opaque, |
245 | + Error **errp) | 256 | + Error **errp) |
246 | +{ | 257 | +{ |
247 | + Property *prop = opaque; | 258 | + Property *prop = opaque; |
248 | + AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop); | 259 | + AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop); |
249 | + Error *local_err = NULL; | ||
250 | + const char *endptr; | 260 | + const char *endptr; |
251 | + g_autofree char *str = NULL; | 261 | + g_autofree char *str = NULL; |
252 | + int ret; | 262 | + int ret; |
253 | + int val; | 263 | + int val; |
254 | + | 264 | + |
255 | + visit_type_str(v, name, &str, &local_err); | 265 | + if (!visit_type_str(v, name, &str, errp)) { |
256 | + if (local_err) { | ||
257 | + error_propagate(errp, local_err); | ||
258 | + return; | 266 | + return; |
259 | + } | 267 | + } |
260 | + | 268 | + |
261 | + endptr = str; | 269 | + endptr = str; |
262 | + | 270 | + |
... | ... | ||
284 | + | 292 | + |
285 | + ret = qemu_strtoi(endptr + 1, &endptr, 10, &val); | 293 | + ret = qemu_strtoi(endptr + 1, &endptr, 10, &val); |
286 | + if (ret || val > UINT16_MAX || val <= 0) { | 294 | + if (ret || val > UINT16_MAX || val <= 0) { |
287 | + error_setg(errp, "refresh rate in '%s'" | 295 | + error_setg(errp, "refresh rate in '%s'" |
288 | + " must be a positive decimal integer (Hertz)", name); | 296 | + " must be a positive decimal integer (Hertz)", name); |
297 | + return; | ||
289 | + } | 298 | + } |
290 | + mode->refresh_rate_hz = val; | 299 | + mode->refresh_rate_hz = val; |
291 | + return; | 300 | + return; |
292 | + | 301 | + |
293 | +separator_error: | 302 | +separator_error: |
... | ... | ||
315 | +apple_gfx_display_mode(uint32_t mode_idx, uint16_t width_px, uint16_t height_px) "mode %2"PRIu32": %4"PRIu16"x%4"PRIu16 | 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 |
316 | 325 | ||
317 | # apple-gfx-mmio.m | 326 | # apple-gfx-mmio.m |
318 | apple_gfx_mmio_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 | 327 | apple_gfx_mmio_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 |
319 | -- | 328 | -- |
320 | 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 |
... | ... | ||
---|---|---|---|
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> | 9 | Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> |
10 | Tested-by: Akihiko Odaki <akihiko.odaki@daynix.com> | ||
10 | --- | 11 | --- |
11 | MAINTAINERS | 7 +++++++ | 12 | MAINTAINERS | 7 +++++++ |
12 | hw/Kconfig | 1 + | 13 | hw/Kconfig | 1 + |
13 | hw/meson.build | 1 + | 14 | hw/meson.build | 1 + |
14 | hw/vmapple/Kconfig | 1 + | 15 | hw/vmapple/Kconfig | 1 + |
... | ... | ||
100 | + 'hw/vmapple', | 101 | + 'hw/vmapple', |
101 | 'hw/watchdog', | 102 | 'hw/watchdog', |
102 | 'hw/xen', | 103 | 'hw/xen', |
103 | 'hw/gpio', | 104 | 'hw/gpio', |
104 | -- | 105 | -- |
105 | 2.39.3 (Apple Git-145) | 106 | 2.39.5 (Apple Git-154) | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
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> | 10 | Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> |
11 | Tested-by: Akihiko Odaki <akihiko.odaki@daynix.com> | ||
11 | --- | 12 | --- |
12 | 13 | ||
13 | v3: | 14 | v3: |
14 | * Rebased on upstream, updated a header path | 15 | * Rebased on upstream, updated a header path |
15 | 16 | ||
... | ... | ||
125 | +#define TYPE_PVPANIC_MMIO_DEVICE "pvpanic-mmio" | 126 | +#define TYPE_PVPANIC_MMIO_DEVICE "pvpanic-mmio" |
126 | 127 | ||
127 | #define PVPANIC_IOPORT_PROP "ioport" | 128 | #define PVPANIC_IOPORT_PROP "ioport" |
128 | 129 | ||
129 | -- | 130 | -- |
130 | 2.39.3 (Apple Git-145) | 131 | 2.39.5 (Apple Git-154) |
131 | 132 | ||
132 | 133 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
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> | 9 | Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> |
10 | Tested-by: Akihiko Odaki <akihiko.odaki@daynix.com> | ||
10 | --- | 11 | --- |
11 | target/arm/hvf/hvf.c | 9 +++++++++ | 12 | target/arm/hvf/hvf.c | 9 +++++++++ |
12 | 1 file changed, 9 insertions(+) | 13 | 1 file changed, 9 insertions(+) |
13 | 14 | ||
14 | 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 |
... | ... | ||
44 | + return 0; | 45 | + return 0; |
45 | case SYSREG_OSDLR_EL1: | 46 | case SYSREG_OSDLR_EL1: |
46 | /* Dummy register */ | 47 | /* Dummy register */ |
47 | return 0; | 48 | return 0; |
48 | -- | 49 | -- |
49 | 2.39.3 (Apple Git-145) | 50 | 2.39.5 (Apple Git-154) | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
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> | 11 | Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> |
12 | Tested-by: Akihiko Odaki <akihiko.odaki@daynix.com> | ||
12 | --- | 13 | --- |
13 | 14 | ||
14 | v4: | 15 | v4: |
15 | 16 | ||
16 | * Turned pair of IRQ arrays into array of structs. | 17 | * Turned pair of IRQ arrays into array of structs. |
17 | * Simplified swizzling logic selection. | 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. | ||
18 | 24 | ||
19 | hw/arm/sbsa-ref.c | 2 +- | 25 | hw/arm/sbsa-ref.c | 2 +- |
20 | hw/arm/virt.c | 2 +- | 26 | hw/arm/virt.c | 2 +- |
21 | hw/i386/microvm.c | 2 +- | 27 | hw/i386/microvm.c | 2 +- |
22 | hw/loongarch/virt.c | 2 +- | 28 | hw/loongarch/virt.c | 12 +++++------ |
23 | hw/mips/loongson3_virt.c | 2 +- | 29 | hw/mips/loongson3_virt.c | 2 +- |
24 | hw/openrisc/virt.c | 12 +++++------ | 30 | hw/openrisc/virt.c | 12 +++++------ |
25 | hw/pci-host/gpex.c | 43 ++++++++++++++++++++++++++++++-------- | 31 | hw/pci-host/gpex.c | 43 ++++++++++++++++++++++++++++++-------- |
26 | hw/riscv/virt.c | 12 +++++------ | 32 | hw/riscv/virt.c | 12 +++++------ |
33 | hw/xen/xen-pvh-common.c | 2 +- | ||
27 | hw/xtensa/virt.c | 2 +- | 34 | hw/xtensa/virt.c | 2 +- |
28 | include/hw/pci-host/gpex.h | 7 +++---- | 35 | include/hw/pci-host/gpex.h | 7 +++---- |
29 | 10 files changed, 55 insertions(+), 31 deletions(-) | 36 | 11 files changed, 61 insertions(+), 37 deletions(-) |
30 | 37 | ||
31 | 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 |
32 | index XXXXXXX..XXXXXXX 100644 | 39 | index XXXXXXX..XXXXXXX 100644 |
33 | --- a/hw/arm/sbsa-ref.c | 40 | --- a/hw/arm/sbsa-ref.c |
34 | +++ b/hw/arm/sbsa-ref.c | 41 | +++ b/hw/arm/sbsa-ref.c |
... | ... | ||
69 | } | 76 | } |
70 | 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 |
71 | index XXXXXXX..XXXXXXX 100644 | 78 | index XXXXXXX..XXXXXXX 100644 |
72 | --- a/hw/loongarch/virt.c | 79 | --- a/hw/loongarch/virt.c |
73 | +++ 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); | ||
74 | @@ -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, |
75 | memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE, | 115 | memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE, |
76 | pio_alias); | 116 | pio_alias); |
77 | 117 | ||
78 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | 118 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { |
... | ... | ||
303 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base); | 343 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base); |
304 | 344 | ||
305 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | 345 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { |
306 | + for (i = 0; i < PCI_NUM_PINS; i++) { | 346 | + for (i = 0; i < PCI_NUM_PINS; i++) { |
307 | 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); | ||
308 | 361 | ||
309 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); | 362 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); |
310 | 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 |
311 | index XXXXXXX..XXXXXXX 100644 | 364 | index XXXXXXX..XXXXXXX 100644 |
312 | --- a/hw/xtensa/virt.c | 365 | --- a/hw/xtensa/virt.c |
... | ... | ||
351 | + uint8_t num_irqs; | 404 | + uint8_t num_irqs; |
352 | 405 | ||
353 | bool allow_unmapped_accesses; | 406 | bool allow_unmapped_accesses; |
354 | 407 | ||
355 | -- | 408 | -- |
356 | 2.39.3 (Apple Git-145) | 409 | 2.39.5 (Apple Git-154) | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
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 | 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 | |||
12 | v3: | 15 | v3: |
13 | 16 | ||
14 | * Rebased on latest upstream and fixed minor breakages. | 17 | * Rebased on latest upstream and fixed minor breakages. |
15 | * Replaced legacy device reset method with Resettable method | 18 | * Replaced legacy device reset method with Resettable method |
16 | 19 | ||
... | ... | ||
23 | v5: | 26 | v5: |
24 | 27 | ||
25 | * More logging improvements | 28 | * More logging improvements |
26 | * Use xxx64_overflow() functions for hexdump buffer size calculations. | 29 | * Use xxx64_overflow() functions for hexdump buffer size calculations. |
27 | 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 | |||
28 | hw/vmapple/Kconfig | 2 + | 48 | hw/vmapple/Kconfig | 2 + |
29 | hw/vmapple/aes.c | 578 +++++++++++++++++++++++++++++++++++ | 49 | hw/vmapple/aes.c | 581 +++++++++++++++++++++++++++++++++++ |
30 | hw/vmapple/meson.build | 1 + | 50 | hw/vmapple/meson.build | 1 + |
31 | hw/vmapple/trace-events | 14 + | 51 | hw/vmapple/trace-events | 14 + |
32 | include/hw/vmapple/vmapple.h | 17 ++ | 52 | include/hw/vmapple/vmapple.h | 17 + |
33 | include/qemu/cutils.h | 15 + | 53 | include/qemu/cutils.h | 15 + |
34 | util/hexdump.c | 18 ++ | 54 | util/hexdump.c | 18 ++ |
35 | 7 files changed, 645 insertions(+) | 55 | 7 files changed, 648 insertions(+) |
36 | create mode 100644 hw/vmapple/aes.c | 56 | create mode 100644 hw/vmapple/aes.c |
37 | create mode 100644 include/hw/vmapple/vmapple.h | 57 | create mode 100644 include/hw/vmapple/vmapple.h |
38 | 58 | ||
39 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | 59 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig |
40 | index XXXXXXX..XXXXXXX 100644 | 60 | index XXXXXXX..XXXXXXX 100644 |
... | ... | ||
238 | + s->fifo[s->fifo_idx++] = val; | 258 | + s->fifo[s->fifo_idx++] = val; |
239 | +} | 259 | +} |
240 | + | 260 | + |
241 | +static bool has_payload(AESState *s, uint32_t elems) | 261 | +static bool has_payload(AESState *s, uint32_t elems) |
242 | +{ | 262 | +{ |
243 | + return s->fifo_idx >= (elems + 1); | 263 | + return s->fifo_idx >= elems + 1; |
244 | +} | 264 | +} |
245 | + | 265 | + |
246 | +static bool cmd_key(AESState *s) | 266 | +static bool cmd_key(AESState *s) |
247 | +{ | 267 | +{ |
248 | + uint32_t cmd = s->fifo[0]; | 268 | + uint32_t cmd = s->fifo[0]; |
... | ... | ||
260 | + default: | 280 | + default: |
261 | + return false; | 281 | + return false; |
262 | + } | 282 | + } |
263 | + | 283 | + |
264 | + s->is_encrypt = cmd & CMD_KEY_ENCRYPT_MASK; | 284 | + s->is_encrypt = cmd & CMD_KEY_ENCRYPT_MASK; |
265 | + 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]; |
266 | + | 286 | + |
267 | + if (key_select) { | 287 | + if (key_select) { |
268 | + trace_aes_cmd_key_select_builtin(ctxt, key_select, | 288 | + trace_aes_cmd_key_select_builtin(ctxt, key_select, |
269 | + s->is_encrypt ? "en" : "de", | 289 | + s->is_encrypt ? "en" : "de", |
270 | + QCryptoCipherMode_str(s->block_mode)); | 290 | + QCryptoCipherMode_str(s->block_mode)); |
... | ... | ||
341 | + return false; | 361 | + return false; |
342 | + } | 362 | + } |
343 | + | 363 | + |
344 | + if (ctxt_key >= ARRAY_SIZE(s->key) || | 364 | + if (ctxt_key >= ARRAY_SIZE(s->key) || |
345 | + ctxt_iv >= ARRAY_SIZE(s->iv)) { | 365 | + ctxt_iv >= ARRAY_SIZE(s->iv)) { |
346 | + /* Invalid input */ | ||
347 | + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key or iv\n", __func__); | 366 | + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key or iv\n", __func__); |
348 | + return false; | 367 | + return false; |
349 | + } | 368 | + } |
350 | + | 369 | + |
351 | + src = g_byte_array_sized_new(len); | 370 | + src = g_byte_array_sized_new(len); |
... | ... | ||
419 | +static bool cmd_store_iv(AESState *s) | 438 | +static bool cmd_store_iv(AESState *s) |
420 | +{ | 439 | +{ |
421 | + uint32_t cmd = s->fifo[0]; | 440 | + uint32_t cmd = s->fifo[0]; |
422 | + 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; |
423 | + uint64_t addr = s->fifo[1]; | 442 | + uint64_t addr = s->fifo[1]; |
443 | + MemTxResult dma_result; | ||
424 | + | 444 | + |
425 | + if (!has_payload(s, 1)) { | 445 | + if (!has_payload(s, 1)) { |
426 | + /* wait for payload */ | ||
427 | + qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__); | 446 | + qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__); |
428 | + return false; | 447 | + return false; |
429 | + } | 448 | + } |
430 | + | 449 | + |
431 | + if (ctxt >= ARRAY_SIZE(s->iv)) { | 450 | + if (ctxt >= ARRAY_SIZE(s->iv)) { |
432 | + /* 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); | ||
433 | + return false; | 454 | + return false; |
434 | + } | 455 | + } |
435 | + | 456 | + |
436 | + addr |= ((uint64_t)cmd << 32) & 0xff00000000ULL; | 457 | + addr |= ((uint64_t)cmd << 32) & 0xff00000000ULL; |
437 | + 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); | ||
438 | + | 461 | + |
439 | + 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], |
440 | + s->iv[ctxt].iv[2], s->iv[ctxt].iv[3]); | 463 | + s->iv[ctxt].iv[2], s->iv[ctxt].iv[3]); |
441 | + | 464 | + |
442 | + return true; | 465 | + return dma_result == MEMTX_OK; |
443 | +} | 466 | +} |
444 | + | 467 | + |
445 | +static bool cmd_flag(AESState *s) | 468 | +static bool cmd_flag(AESState *s) |
446 | +{ | 469 | +{ |
447 | + uint32_t cmd = s->fifo[0]; | 470 | + uint32_t cmd = s->fifo[0]; |
... | ... | ||
491 | + | 514 | + |
492 | + if (success) { | 515 | + if (success) { |
493 | + s->fifo_idx = 0; | 516 | + s->fifo_idx = 0; |
494 | + } | 517 | + } |
495 | + | 518 | + |
496 | + trace_aes_fifo_process(cmd, success ? 1 : 0); | 519 | + trace_aes_fifo_process(cmd, success); |
497 | +} | 520 | +} |
498 | + | 521 | + |
499 | +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) |
500 | +{ | 523 | +{ |
501 | + AESState *s = opaque; | 524 | + AESState *s = opaque; |
... | ... | ||
647 | +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" |
648 | +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" |
649 | +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" |
650 | +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" |
651 | +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" |
652 | +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" |
653 | +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 |
654 | +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 |
655 | +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 |
656 | +aes_dump_data(const char *desc, const char *hex) "%s%s" | 679 | +aes_dump_data(const char *desc, const char *hex) "%s%s" |
657 | + | 680 | + |
... | ... | ||
724 | +{ | 747 | +{ |
725 | + size_t i; | 748 | + size_t i; |
726 | + uint64_t required_buffer_size; | 749 | + uint64_t required_buffer_size; |
727 | + bool overflow = umul64_overflow(data_size, 2, &required_buffer_size); | 750 | + bool overflow = umul64_overflow(data_size, 2, &required_buffer_size); |
728 | + overflow |= uadd64_overflow(required_buffer_size, 1, &required_buffer_size); | 751 | + overflow |= uadd64_overflow(required_buffer_size, 1, &required_buffer_size); |
729 | + assert(buffer_size >= required_buffer_size && !overflow); | 752 | + assert(!overflow && buffer_size >= required_buffer_size); |
730 | + | 753 | + |
731 | + for (i = 0; i < data_size; i++) { | 754 | + for (i = 0; i < data_size; i++) { |
732 | + uint8_t val = data[i]; | 755 | + uint8_t val = data[i]; |
733 | + *(buffer++) = hexdump_nibble(val >> 4); | 756 | + *(buffer++) = hexdump_nibble(val >> 4); |
734 | + *(buffer++) = hexdump_nibble(val & 0xf); | 757 | + *(buffer++) = hexdump_nibble(val & 0xf); |
735 | + } | 758 | + } |
736 | + *buffer = '\0'; | 759 | + *buffer = '\0'; |
737 | +} | 760 | +} |
738 | -- | 761 | -- |
739 | 2.39.3 (Apple Git-145) | 762 | 2.39.5 (Apple Git-154) |
740 | 763 | ||
741 | 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 | 16 | ||
15 | v4: | 17 | v4: |
16 | 18 | ||
17 | * Moved most header code to .c, rest to vmapple.h | 19 | * Moved most header code to .c, rest to vmapple.h |
18 | * Better compliance with coding, naming, and formatting conventions. | 20 | * Better compliance with coding, naming, and formatting conventions. |
19 | 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 | |||
20 | hw/vmapple/Kconfig | 3 + | 31 | hw/vmapple/Kconfig | 3 + |
21 | hw/vmapple/bdif.c | 261 +++++++++++++++++++++++++++++++++++ | 32 | hw/vmapple/bdif.c | 275 +++++++++++++++++++++++++++++++++++ |
22 | hw/vmapple/meson.build | 1 + | 33 | hw/vmapple/meson.build | 1 + |
23 | hw/vmapple/trace-events | 5 + | 34 | hw/vmapple/trace-events | 5 + |
24 | include/hw/vmapple/vmapple.h | 2 + | 35 | include/hw/vmapple/vmapple.h | 2 + |
25 | 5 files changed, 272 insertions(+) | 36 | 5 files changed, 286 insertions(+) |
26 | create mode 100644 hw/vmapple/bdif.c | 37 | create mode 100644 hw/vmapple/bdif.c |
27 | 38 | ||
28 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | 39 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig |
29 | index XXXXXXX..XXXXXXX 100644 | 40 | index XXXXXXX..XXXXXXX 100644 |
30 | --- a/hw/vmapple/Kconfig | 41 | --- a/hw/vmapple/Kconfig |
... | ... | ||
61 | +#include "hw/vmapple/vmapple.h" | 72 | +#include "hw/vmapple/vmapple.h" |
62 | +#include "hw/sysbus.h" | 73 | +#include "hw/sysbus.h" |
63 | +#include "hw/block/block.h" | 74 | +#include "hw/block/block.h" |
64 | +#include "qapi/error.h" | 75 | +#include "qapi/error.h" |
65 | +#include "sysemu/block-backend.h" | 76 | +#include "sysemu/block-backend.h" |
77 | +#include "sysemu/dma.h" | ||
66 | + | 78 | + |
67 | +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF) | 79 | +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF) |
68 | + | 80 | + |
69 | +struct VMAppleBdifState { | 81 | +struct VMAppleBdifState { |
70 | + SysBusDevice parent_obj; | 82 | + SysBusDevice parent_obj; |
... | ... | ||
175 | + le2cpu_reqcmd(&req->sector); | 187 | + le2cpu_reqcmd(&req->sector); |
176 | + le2cpu_reqcmd(&req->data); | 188 | + le2cpu_reqcmd(&req->data); |
177 | + le2cpu_reqcmd(&req->retval); | 189 | + le2cpu_reqcmd(&req->retval); |
178 | +} | 190 | +} |
179 | + | 191 | + |
180 | +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, |
181 | + uint64_t static_off) | 193 | + uint64_t static_off) |
182 | +{ | 194 | +{ |
183 | + VblkReq req; | 195 | + VblkReq req; |
184 | + VblkSector sector; | 196 | + VblkSector sector; |
185 | + uint64_t off = 0; | 197 | + uint64_t off = 0; |
186 | + char *buf = NULL; | 198 | + g_autofree char *buf = NULL; |
187 | + uint8_t ret = VBLK_RET_FAILED; | 199 | + uint8_t ret = VBLK_RET_FAILED; |
188 | + int r; | 200 | + int r; |
189 | + | 201 | + MemTxResult dma_result; |
190 | + 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 | + | ||
191 | + le2cpu_req(&req); | 209 | + le2cpu_req(&req); |
192 | + | 210 | + |
193 | + if (req.sector.len != sizeof(sector)) { | 211 | + if (req.sector.len != sizeof(sector)) { |
194 | + ret = VBLK_RET_FAILED; | ||
195 | + goto out; | 212 | + goto out; |
196 | + } | 213 | + } |
197 | + | 214 | + |
198 | + /* Read the vblk command */ | 215 | + /* Read the vblk command */ |
199 | + cpu_physical_memory_read(req.sector.addr, §or, sizeof(sector)); | 216 | + dma_result = dma_memory_read(&address_space_memory, req.sector.addr, |
217 | + §or, sizeof(sector), | ||
218 | + MEMTXATTRS_UNSPECIFIED); | ||
219 | + if (dma_result != MEMTX_OK) { | ||
220 | + goto out; | ||
221 | + } | ||
200 | + le2cpu_sector(§or); | 222 | + le2cpu_sector(§or); |
201 | + | 223 | + |
202 | + off = sector.sector * 512ULL + static_off; | 224 | + off = sector.sector * 512ULL + static_off; |
203 | + | 225 | + |
204 | + /* Sanity check that we're not allocating bogus sizes */ | 226 | + /* Sanity check that we're not allocating bogus sizes */ |
... | ... | ||
213 | + trace_bdif_vblk_read(devid == DEVID_AUX ? "aux" : "root", | 235 | + trace_bdif_vblk_read(devid == DEVID_AUX ? "aux" : "root", |
214 | + req.data.addr, off, req.data.len, r); | 236 | + req.data.addr, off, req.data.len, r); |
215 | + if (r < 0) { | 237 | + if (r < 0) { |
216 | + goto out; | 238 | + goto out; |
217 | + } | 239 | + } |
218 | + cpu_physical_memory_write(req.data.addr, buf, req.data.len); | 240 | + dma_result = dma_memory_write(&address_space_memory, req.data.addr, buf, |
219 | + ret = VBLK_RET_SUCCESS; | 241 | + req.data.len, MEMTXATTRS_UNSPECIFIED); |
242 | + if (dma_result == MEMTX_OK) { | ||
243 | + ret = VBLK_RET_SUCCESS; | ||
244 | + } | ||
220 | + break; | 245 | + break; |
221 | + case VBLK_DATA_FLAGS_WRITE: | 246 | + case VBLK_DATA_FLAGS_WRITE: |
222 | + /* Not needed, iBoot only reads */ | 247 | + /* Not needed, iBoot only reads */ |
223 | + break; | 248 | + break; |
224 | + default: | 249 | + default: |
225 | + break; | 250 | + break; |
226 | + } | 251 | + } |
227 | + | 252 | + |
228 | +out: | 253 | +out: |
229 | + g_free(buf); | 254 | + dma_memory_write(&address_space_memory, req.retval.addr, &ret, 1, |
230 | + cpu_physical_memory_write(req.retval.addr, &ret, 1); | 255 | + MEMTXATTRS_UNSPECIFIED); |
231 | +} | 256 | +} |
232 | + | 257 | + |
233 | +static void bdif_write(void *opaque, hwaddr offset, | 258 | +static void bdif_write(void *opaque, hwaddr offset, |
234 | + uint64_t value, unsigned size) | 259 | + uint64_t value, unsigned size) |
235 | +{ | 260 | +{ |
... | ... | ||
333 | 358 | ||
334 | +#define TYPE_VMAPPLE_BDIF "vmapple-bdif" | 359 | +#define TYPE_VMAPPLE_BDIF "vmapple-bdif" |
335 | + | 360 | + |
336 | #endif /* HW_VMAPPLE_VMAPPLE_H */ | 361 | #endif /* HW_VMAPPLE_VMAPPLE_H */ |
337 | -- | 362 | -- |
338 | 2.39.3 (Apple Git-145) | 363 | 2.39.5 (Apple Git-154) |
339 | 364 | ||
340 | 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 | Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> | ||
14 | Tested-by: Akihiko Odaki <akihiko.odaki@daynix.com> | ||
13 | --- | 15 | --- |
16 | |||
14 | v3: | 17 | v3: |
15 | 18 | ||
16 | * Replaced legacy device reset method with Resettable method | 19 | * Replaced legacy device reset method with Resettable method |
17 | 20 | ||
18 | v4: | 21 | v4: |
... | ... | ||
23 | 26 | ||
24 | v5: | 27 | v5: |
25 | 28 | ||
26 | * Improved error reporting in case of string property buffer overflow. | 29 | * Improved error reporting in case of string property buffer overflow. |
27 | 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 | |||
28 | hw/vmapple/Kconfig | 3 + | 44 | hw/vmapple/Kconfig | 3 + |
29 | hw/vmapple/cfg.c | 203 +++++++++++++++++++++++++++++++++++ | 45 | hw/vmapple/cfg.c | 196 +++++++++++++++++++++++++++++++++++ |
30 | hw/vmapple/meson.build | 1 + | 46 | hw/vmapple/meson.build | 1 + |
31 | include/hw/vmapple/vmapple.h | 2 + | 47 | include/hw/vmapple/vmapple.h | 2 + |
32 | 4 files changed, 209 insertions(+) | 48 | 4 files changed, 202 insertions(+) |
33 | create mode 100644 hw/vmapple/cfg.c | 49 | create mode 100644 hw/vmapple/cfg.c |
34 | 50 | ||
35 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | 51 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig |
36 | index XXXXXXX..XXXXXXX 100644 | 52 | index XXXXXXX..XXXXXXX 100644 |
37 | --- a/hw/vmapple/Kconfig | 53 | --- a/hw/vmapple/Kconfig |
... | ... | ||
124 | + cfg = memory_region_get_ram_ptr(&s->mem); | 140 | + cfg = memory_region_get_ram_ptr(&s->mem); |
125 | + memset(cfg, 0, VMAPPLE_CFG_SIZE); | 141 | + memset(cfg, 0, VMAPPLE_CFG_SIZE); |
126 | + *cfg = s->cfg; | 142 | + *cfg = s->cfg; |
127 | +} | 143 | +} |
128 | + | 144 | + |
129 | +static bool strlcpy_set_error(char *restrict dst, const char *restrict src, | 145 | +static bool set_fixlen_property_or_error(char *restrict dst, |
130 | + size_t dst_size, Error **errp, | 146 | + const char *restrict src, |
131 | + const char *parent_func, const char *location, | 147 | + size_t dst_size, Error **errp, |
132 | + const char *buffer_name, const char *extra_info) | 148 | + const char *property_name) |
133 | +{ | 149 | +{ |
150 | + ERRP_GUARD(); | ||
134 | + size_t len; | 151 | + size_t len; |
135 | + | 152 | + |
136 | + len = g_strlcpy(dst, src, dst_size); | 153 | + len = g_strlcpy(dst, src, dst_size); |
137 | + if (len < dst_size) { /* len does not count nul terminator */ | 154 | + if (len < dst_size) { /* len does not count nul terminator */ |
138 | + return true; | 155 | + return true; |
139 | + } | 156 | + } |
140 | + | 157 | + |
141 | + error_setg(errp, | 158 | + error_setg(errp, "Provided value too long for property '%s'", property_name); |
142 | + "%s (%s) strlcpy error: Destination buffer %s too small " | 159 | + error_append_hint(errp, "length (%zu) exceeds maximum of %zu\n", |
143 | + "(need %zu, have %zu) %s", | 160 | + len, dst_size - 1); |
144 | + parent_func, location, buffer_name, len + 1, dst_size, extra_info); | ||
145 | + return false; | 161 | + return false; |
146 | +} | 162 | +} |
147 | + | 163 | + |
148 | +/* | 164 | +#define set_fixlen_property_or_return(dst_array, src, errp, property_name) \ |
149 | + * String copying wrapper that returns and reports a runtime error in | ||
150 | + * case of truncation due to insufficient destination buffer space. | ||
151 | + */ | ||
152 | +#define strlcpy_array_return_error(dst_array, src, errp, extra_info) \ | ||
153 | + do { \ | 165 | + do { \ |
154 | + if (!strlcpy_set_error((dst_array), (src), ARRAY_SIZE(dst_array), \ | 166 | + if (!set_fixlen_property_or_error((dst_array), (src), \ |
155 | + (errp), __func__, stringify(__LINE__), \ | 167 | + ARRAY_SIZE(dst_array), \ |
156 | + # dst_array, extra_info)) { \ | 168 | + (errp), (property_name))) { \ |
157 | + return; \ | 169 | + return; \ |
158 | + } \ | 170 | + } \ |
159 | + } while (0) | 171 | + } while (0) |
160 | + | 172 | + |
161 | +static void vmapple_cfg_realize(DeviceState *dev, Error **errp) | 173 | +static void vmapple_cfg_realize(DeviceState *dev, Error **errp) |
... | ... | ||
171 | + } | 183 | + } |
172 | + if (!s->soc_name) { | 184 | + if (!s->soc_name) { |
173 | + s->soc_name = g_strdup("Apple M1 (Virtual)"); | 185 | + s->soc_name = g_strdup("Apple M1 (Virtual)"); |
174 | + } | 186 | + } |
175 | + | 187 | + |
176 | + strlcpy_array_return_error(s->cfg.serial, s->serial, errp, | 188 | + set_fixlen_property_or_return(s->cfg.serial, s->serial, errp, "serial"); |
177 | + "setting 'serial' property on VMApple cfg device"); | 189 | + set_fixlen_property_or_return(s->cfg.model, s->model, errp, "model"); |
178 | + strlcpy_array_return_error(s->cfg.model, s->model, errp, | 190 | + set_fixlen_property_or_return(s->cfg.soc_name, s->soc_name, errp, "soc_name"); |
179 | + "setting 'model' property on VMApple cfg device"); | 191 | + set_fixlen_property_or_return(s->cfg.unk8, "D/A", errp, "unk8"); |
180 | + strlcpy_array_return_error(s->cfg.soc_name, s->soc_name, errp, | ||
181 | + "setting 'soc_name' property on VMApple cfg device"); | ||
182 | + strlcpy_array_return_error(s->cfg.unk8, "D/A", errp, ""); | ||
183 | + s->cfg.version = 2; | 192 | + s->cfg.version = 2; |
184 | + s->cfg.unk1 = 1; | 193 | + s->cfg.unk1 = 1; |
185 | + s->cfg.unk2 = 1; | 194 | + s->cfg.unk2 = 1; |
186 | + s->cfg.unk3 = 0x20; | 195 | + s->cfg.unk3 = 0x20; |
187 | + s->cfg.unk4 = 0; | 196 | + s->cfg.unk4 = 0; |
... | ... | ||
270 | 279 | ||
271 | +#define TYPE_VMAPPLE_CFG "vmapple-cfg" | 280 | +#define TYPE_VMAPPLE_CFG "vmapple-cfg" |
272 | + | 281 | + |
273 | #endif /* HW_VMAPPLE_VMAPPLE_H */ | 282 | #endif /* HW_VMAPPLE_VMAPPLE_H */ |
274 | -- | 283 | -- |
275 | 2.39.3 (Apple Git-145) | 284 | 2.39.5 (Apple Git-154) |
276 | 285 | ||
277 | 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 | 20 | ||
20 | v4: | 21 | v4: |
21 | 22 | ||
22 | * Use recommended object type declaration pattern. | 23 | * Use recommended object type declaration pattern. |
... | ... | ||
26 | v5: | 27 | v5: |
27 | 28 | ||
28 | * Corrected handling of potentially unaligned writes to virtio config area. | 29 | * Corrected handling of potentially unaligned writes to virtio config area. |
29 | * Simplified passing through device variant type to subobject. | 30 | * Simplified passing through device variant type to subobject. |
30 | 31 | ||
31 | hw/block/virtio-blk.c | 19 ++- | 32 | v9: |
32 | hw/vmapple/Kconfig | 3 + | 33 | |
33 | hw/vmapple/meson.build | 1 + | 34 | * Correctly specify class_size for VMAppleVirtIOBlkClass |
34 | hw/vmapple/virtio-blk.c | 226 +++++++++++++++++++++++++++++++++ | 35 | |
35 | include/hw/pci/pci_ids.h | 1 + | 36 | v10: |
36 | include/hw/virtio/virtio-blk.h | 12 +- | 37 | |
37 | include/hw/vmapple/vmapple.h | 4 + | 38 | * Folded v9 patch 16/16 into this one, changing the device type design to |
38 | 7 files changed, 261 insertions(+), 5 deletions(-) | 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(-) | ||
39 | create mode 100644 hw/vmapple/virtio-blk.c | 54 | create mode 100644 hw/vmapple/virtio-blk.c |
40 | 55 | ||
41 | 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 |
42 | index XXXXXXX..XXXXXXX 100644 | 57 | index XXXXXXX..XXXXXXX 100644 |
43 | --- a/hw/block/virtio-blk.c | 58 | --- a/hw/block/virtio-blk.c |
... | ... | ||
84 | .class_init = virtio_blk_class_init, | 99 | .class_init = virtio_blk_class_init, |
85 | + .class_size = sizeof(VirtIOBlkClass), | 100 | + .class_size = sizeof(VirtIOBlkClass), |
86 | }; | 101 | }; |
87 | 102 | ||
88 | 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 | +}; | ||
89 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | 120 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig |
90 | index XXXXXXX..XXXXXXX 100644 | 121 | index XXXXXXX..XXXXXXX 100644 |
91 | --- a/hw/vmapple/Kconfig | 122 | --- a/hw/vmapple/Kconfig |
92 | +++ b/hw/vmapple/Kconfig | 123 | +++ b/hw/vmapple/Kconfig |
93 | @@ -XXX,XX +XXX,XX @@ config VMAPPLE_BDIF | 124 | @@ -XXX,XX +XXX,XX @@ config VMAPPLE_BDIF |
... | ... | ||
136 | +#include "qemu/bswap.h" | 167 | +#include "qemu/bswap.h" |
137 | +#include "qemu/log.h" | 168 | +#include "qemu/log.h" |
138 | +#include "qemu/module.h" | 169 | +#include "qemu/module.h" |
139 | +#include "qapi/error.h" | 170 | +#include "qapi/error.h" |
140 | + | 171 | + |
172 | +#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk" | ||
141 | +OBJECT_DECLARE_TYPE(VMAppleVirtIOBlk, VMAppleVirtIOBlkClass, VMAPPLE_VIRTIO_BLK) | 173 | +OBJECT_DECLARE_TYPE(VMAppleVirtIOBlk, VMAppleVirtIOBlkClass, VMAPPLE_VIRTIO_BLK) |
142 | + | 174 | + |
143 | +typedef struct VMAppleVirtIOBlkClass { | 175 | +typedef struct VMAppleVirtIOBlkClass { |
144 | + VirtIOBlkClass parent; | 176 | + VirtIOBlkClass parent; |
145 | + | 177 | + |
... | ... | ||
153 | +} VMAppleVirtIOBlk; | 185 | +} VMAppleVirtIOBlk; |
154 | + | 186 | + |
155 | +/* | 187 | +/* |
156 | + * vmapple-virtio-blk-pci: This extends VirtioPCIProxy. | 188 | + * vmapple-virtio-blk-pci: This extends VirtioPCIProxy. |
157 | + */ | 189 | + */ |
158 | +#define TYPE_VMAPPLE_VIRTIO_BLK_PCI "vmapple-virtio-blk-pci-base" | ||
159 | +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleVirtIOBlkPCI, VMAPPLE_VIRTIO_BLK_PCI) | 190 | +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleVirtIOBlkPCI, VMAPPLE_VIRTIO_BLK_PCI) |
160 | + | 191 | + |
161 | +#define VIRTIO_BLK_T_APPLE_BARRIER 0x10000 | 192 | +#define VIRTIO_BLK_T_APPLE_BARRIER 0x10000 |
162 | + | ||
163 | +#define VIRTIO_APPLE_TYPE_ROOT 1 | ||
164 | +#define VIRTIO_APPLE_TYPE_AUX 2 | ||
165 | + | 193 | + |
166 | +static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req, | 194 | +static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req, |
167 | + MultiReqBuffer *mrb, | 195 | + MultiReqBuffer *mrb, |
168 | + uint32_t type) | 196 | + uint32_t type) |
169 | +{ | 197 | +{ |
... | ... | ||
212 | + | 240 | + |
213 | +static const TypeInfo vmapple_virtio_blk_info = { | 241 | +static const TypeInfo vmapple_virtio_blk_info = { |
214 | + .name = TYPE_VMAPPLE_VIRTIO_BLK, | 242 | + .name = TYPE_VMAPPLE_VIRTIO_BLK, |
215 | + .parent = TYPE_VIRTIO_BLK, | 243 | + .parent = TYPE_VIRTIO_BLK, |
216 | + .instance_size = sizeof(VMAppleVirtIOBlk), | 244 | + .instance_size = sizeof(VMAppleVirtIOBlk), |
245 | + .class_size = sizeof(VMAppleVirtIOBlkClass), | ||
217 | + .class_init = vmapple_virtio_blk_class_init, | 246 | + .class_init = vmapple_virtio_blk_class_init, |
218 | +}; | 247 | +}; |
219 | + | 248 | + |
220 | +/* PCI Devices */ | 249 | +/* PCI Devices */ |
221 | + | 250 | + |
222 | +struct VMAppleVirtIOBlkPCI { | 251 | +struct VMAppleVirtIOBlkPCI { |
223 | + VirtIOPCIProxy parent_obj; | 252 | + VirtIOPCIProxy parent_obj; |
224 | + VMAppleVirtIOBlk vdev; | 253 | + VMAppleVirtIOBlk vdev; |
225 | + uint32_t apple_type; | 254 | + VMAppleVirtioBlkVariant variant; |
226 | +}; | 255 | +}; |
227 | + | 256 | + |
228 | + | 257 | + |
229 | +static Property vmapple_virtio_blk_pci_properties[] = { | 258 | +static Property vmapple_virtio_blk_pci_properties[] = { |
230 | + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), | 259 | + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), |
231 | + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, | 260 | + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, |
232 | + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), | 261 | + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), |
233 | + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, | 262 | + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, |
234 | + DEV_NVECTORS_UNSPECIFIED), | 263 | + DEV_NVECTORS_UNSPECIFIED), |
264 | + DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT("variant", VMAppleVirtIOBlkPCI, variant, | ||
265 | + VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED), | ||
235 | + DEFINE_PROP_END_OF_LIST(), | 266 | + DEFINE_PROP_END_OF_LIST(), |
236 | +}; | 267 | +}; |
237 | + | 268 | + |
238 | +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) |
239 | +{ | 270 | +{ |
271 | + ERRP_GUARD(); | ||
240 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev); | 272 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev); |
241 | + DeviceState *vdev = DEVICE(&dev->vdev); | 273 | + DeviceState *vdev = DEVICE(&dev->vdev); |
242 | + 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 | + } | ||
243 | + | 284 | + |
244 | + if (conf->num_queues == VIRTIO_BLK_AUTO_NUM_QUEUES) { | 285 | + if (conf->num_queues == VIRTIO_BLK_AUTO_NUM_QUEUES) { |
245 | + conf->num_queues = virtio_pci_optimal_num_queues(0); | 286 | + conf->num_queues = virtio_pci_optimal_num_queues(0); |
246 | + } | 287 | + } |
247 | + | 288 | + |
... | ... | ||
254 | + * 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 |
255 | + * allocates enough space for us. The guest will ignore zones anyway. | 296 | + * allocates enough space for us. The guest will ignore zones anyway. |
256 | + */ | 297 | + */ |
257 | + 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); |
258 | + /* Propagate the apple type down to the virtio-blk device */ | 299 | + /* Propagate the apple type down to the virtio-blk device */ |
259 | + dev->vdev.apple_type = dev->apple_type; | 300 | + dev->vdev.apple_type = dev->variant; |
260 | + /* and spawn the virtio-blk device */ | 301 | + /* and spawn the virtio-blk device */ |
261 | + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); | 302 | + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); |
262 | + | 303 | + |
263 | + /* | 304 | + /* |
264 | + * 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 |
... | ... | ||
292 | + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), | 333 | + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), |
293 | + TYPE_VMAPPLE_VIRTIO_BLK); | 334 | + TYPE_VMAPPLE_VIRTIO_BLK); |
294 | +} | 335 | +} |
295 | + | 336 | + |
296 | +static const VirtioPCIDeviceTypeInfo vmapple_virtio_blk_pci_info = { | 337 | +static const VirtioPCIDeviceTypeInfo vmapple_virtio_blk_pci_info = { |
297 | + .base_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI, | 338 | + .generic_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI, |
298 | + .generic_name = "vmapple-virtio-blk-pci", | ||
299 | + .instance_size = sizeof(VMAppleVirtIOBlkPCI), | 339 | + .instance_size = sizeof(VMAppleVirtIOBlkPCI), |
300 | + .instance_init = vmapple_virtio_blk_pci_instance_init, | 340 | + .instance_init = vmapple_virtio_blk_pci_instance_init, |
301 | + .class_init = vmapple_virtio_blk_pci_class_init, | 341 | + .class_init = vmapple_virtio_blk_pci_class_init, |
302 | +}; | 342 | +}; |
303 | + | 343 | + |
304 | +static void vmapple_virtio_root_instance_init(Object *obj) | ||
305 | +{ | ||
306 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj); | ||
307 | + | ||
308 | + dev->apple_type = VIRTIO_APPLE_TYPE_ROOT; | ||
309 | +} | ||
310 | + | ||
311 | +static const TypeInfo vmapple_virtio_root_info = { | ||
312 | + .name = TYPE_VMAPPLE_VIRTIO_ROOT, | ||
313 | + .parent = "vmapple-virtio-blk-pci", | ||
314 | + .instance_size = sizeof(VMAppleVirtIOBlkPCI), | ||
315 | + .instance_init = vmapple_virtio_root_instance_init, | ||
316 | +}; | ||
317 | + | ||
318 | +static void vmapple_virtio_aux_instance_init(Object *obj) | ||
319 | +{ | ||
320 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj); | ||
321 | + | ||
322 | + dev->apple_type = VIRTIO_APPLE_TYPE_AUX; | ||
323 | +} | ||
324 | + | ||
325 | +static const TypeInfo vmapple_virtio_aux_info = { | ||
326 | + .name = TYPE_VMAPPLE_VIRTIO_AUX, | ||
327 | + .parent = "vmapple-virtio-blk-pci", | ||
328 | + .instance_size = sizeof(VMAppleVirtIOBlkPCI), | ||
329 | + .instance_init = vmapple_virtio_aux_instance_init, | ||
330 | +}; | ||
331 | + | ||
332 | +static void vmapple_virtio_blk_register_types(void) | 344 | +static void vmapple_virtio_blk_register_types(void) |
333 | +{ | 345 | +{ |
334 | + type_register_static(&vmapple_virtio_blk_info); | 346 | + type_register_static(&vmapple_virtio_blk_info); |
335 | + virtio_pci_types_register(&vmapple_virtio_blk_pci_info); | 347 | + virtio_pci_types_register(&vmapple_virtio_blk_pci_info); |
336 | + type_register_static(&vmapple_virtio_root_info); | ||
337 | + type_register_static(&vmapple_virtio_aux_info); | ||
338 | +} | 348 | +} |
339 | + | 349 | + |
340 | +type_init(vmapple_virtio_blk_register_types) | 350 | +type_init(vmapple_virtio_blk_register_types) |
341 | 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 |
342 | index XXXXXXX..XXXXXXX 100644 | 352 | index XXXXXXX..XXXXXXX 100644 |
... | ... | ||
348 | #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 | 358 | #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 |
349 | +#define PCI_DEVICE_ID_APPLE_VIRTIO_BLK 0x1a00 | 359 | +#define PCI_DEVICE_ID_APPLE_VIRTIO_BLK 0x1a00 |
350 | 360 | ||
351 | #define PCI_VENDOR_ID_SUN 0x108e | 361 | #define PCI_VENDOR_ID_SUN 0x108e |
352 | #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 | ||
353 | 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 |
354 | index XXXXXXX..XXXXXXX 100644 | 385 | index XXXXXXX..XXXXXXX 100644 |
355 | --- a/include/hw/virtio/virtio-blk.h | 386 | --- a/include/hw/virtio/virtio-blk.h |
356 | +++ b/include/hw/virtio/virtio-blk.h | 387 | +++ b/include/hw/virtio/virtio-blk.h |
357 | @@ -XXX,XX +XXX,XX @@ | 388 | @@ -XXX,XX +XXX,XX @@ |
... | ... | ||
386 | +++ b/include/hw/vmapple/vmapple.h | 417 | +++ b/include/hw/vmapple/vmapple.h |
387 | @@ -XXX,XX +XXX,XX @@ | 418 | @@ -XXX,XX +XXX,XX @@ |
388 | 419 | ||
389 | #define TYPE_VMAPPLE_CFG "vmapple-cfg" | 420 | #define TYPE_VMAPPLE_CFG "vmapple-cfg" |
390 | 421 | ||
391 | +#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk" | 422 | +#define TYPE_VMAPPLE_VIRTIO_BLK_PCI "vmapple-virtio-blk-pci" |
392 | +#define TYPE_VMAPPLE_VIRTIO_ROOT "vmapple-virtio-root" | ||
393 | +#define TYPE_VMAPPLE_VIRTIO_AUX "vmapple-virtio-aux" | ||
394 | + | 423 | + |
395 | #endif /* HW_VMAPPLE_VMAPPLE_H */ | 424 | #endif /* HW_VMAPPLE_VMAPPLE_H */ |
425 | diff --git a/qapi/virtio.json b/qapi/virtio.json | ||
426 | index XXXXXXX..XXXXXXX 100644 | ||
427 | --- a/qapi/virtio.json | ||
428 | +++ b/qapi/virtio.json | ||
429 | @@ -XXX,XX +XXX,XX @@ | ||
430 | ## | ||
431 | { 'enum': 'GranuleMode', | ||
432 | 'data': [ '4k', '8k', '16k', '64k', 'host' ] } | ||
433 | + | ||
434 | +## | ||
435 | +# @VMAppleVirtioBlkVariant: | ||
436 | +# | ||
437 | +# @unspecified: The default, not a valid setting. | ||
438 | +# | ||
439 | +# @root: Block device holding the root volume | ||
440 | +# | ||
441 | +# @aux: Block device holding auxiliary data required for boot | ||
442 | +# | ||
443 | +# Since: 9.2 | ||
444 | +## | ||
445 | +{ 'enum': 'VMAppleVirtioBlkVariant', | ||
446 | + 'data': [ 'unspecified', 'root', 'aux' ] } | ||
396 | -- | 447 | -- |
397 | 2.39.3 (Apple Git-145) | 448 | 2.39.5 (Apple Git-154) |
398 | 449 | ||
399 | 450 | diff view generated by jsdifflib |
1 | The virtio_blk_free_request() function has been a 1-liner forwarding | 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 | 2 | to g_free() for a while now. We may as well call g_free on the request |
3 | pointer directly. | 3 | pointer directly. |
4 | 4 | ||
5 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | 5 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> |
6 | Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> | 6 | Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> |
7 | Tested-by: Akihiko Odaki <akihiko.odaki@daynix.com> | ||
7 | --- | 8 | --- |
8 | hw/block/virtio-blk.c | 43 +++++++++++++++------------------- | 9 | hw/block/virtio-blk.c | 43 +++++++++++++++------------------- |
9 | hw/vmapple/virtio-blk.c | 2 +- | 10 | hw/vmapple/virtio-blk.c | 2 +- |
10 | include/hw/virtio/virtio-blk.h | 1 - | 11 | include/hw/virtio/virtio-blk.h | 1 - |
11 | 3 files changed, 20 insertions(+), 26 deletions(-) | 12 | 3 files changed, 20 insertions(+), 26 deletions(-) |
... | ... | ||
221 | -void virtio_blk_free_request(VirtIOBlockReq *req); | 222 | -void virtio_blk_free_request(VirtIOBlockReq *req); |
222 | void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status); | 223 | void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status); |
223 | 224 | ||
224 | #endif | 225 | #endif |
225 | -- | 226 | -- |
226 | 2.39.3 (Apple Git-145) | 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> |
37 | Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> | ||
38 | Tested-by: Akihiko Odaki <akihiko.odaki@daynix.com> | ||
38 | --- | 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. | ||
46 | 48 | ||
47 | v4: | 49 | v4: |
48 | * Fixups for v4 changes to the other patches in the set. | 50 | * Fixups for v4 changes to the other patches in the set. |
49 | * Corrected the assert macro to use | 51 | * Corrected the assert macro to use |
50 | * Removed superfluous endian conversions corresponding to cfg's. | 52 | * Removed superfluous endian conversions corresponding to cfg's. |
... | ... | ||
57 | * Fixed memory reservation for ecam alias region. | 59 | * Fixed memory reservation for ecam alias region. |
58 | * Better error handling setting properties on devices. | 60 | * Better error handling setting properties on devices. |
59 | * Simplified the machine ECID/UUID extraction script and actually created a | 61 | * Simplified the machine ECID/UUID extraction script and actually created a |
60 | file for it rather than quoting its code in documentation. | 62 | file for it rather than quoting its code in documentation. |
61 | 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. | ||
85 | |||
62 | MAINTAINERS | 1 + | 86 | MAINTAINERS | 1 + |
63 | contrib/vmapple/uuid.sh | 9 + | 87 | contrib/vmapple/uuid.sh | 9 + |
64 | docs/system/arm/vmapple.rst | 60 ++++ | 88 | docs/system/arm/vmapple.rst | 63 ++++ |
65 | docs/system/target-arm.rst | 1 + | 89 | docs/system/target-arm.rst | 1 + |
66 | hw/vmapple/Kconfig | 20 ++ | 90 | hw/vmapple/Kconfig | 20 ++ |
67 | hw/vmapple/meson.build | 1 + | 91 | hw/vmapple/meson.build | 1 + |
68 | hw/vmapple/vmapple.c | 659 ++++++++++++++++++++++++++++++++++++ | 92 | hw/vmapple/vmapple.c | 646 ++++++++++++++++++++++++++++++++++++ |
69 | 7 files changed, 751 insertions(+) | 93 | 7 files changed, 741 insertions(+) |
70 | create mode 100755 contrib/vmapple/uuid.sh | 94 | create mode 100755 contrib/vmapple/uuid.sh |
71 | create mode 100644 docs/system/arm/vmapple.rst | 95 | create mode 100644 docs/system/arm/vmapple.rst |
72 | create mode 100644 hw/vmapple/vmapple.c | 96 | create mode 100644 hw/vmapple/vmapple.c |
73 | 97 | ||
74 | diff --git a/MAINTAINERS b/MAINTAINERS | 98 | diff --git a/MAINTAINERS b/MAINTAINERS |
... | ... | ||
116 | + | 140 | + |
117 | +To run the vmapple machine model, you need to | 141 | +To run the vmapple machine model, you need to |
118 | + | 142 | + |
119 | + * Run on Apple Silicon | 143 | + * Run on Apple Silicon |
120 | + * Run on macOS 12.0 or above | 144 | + * Run on macOS 12.0 or above |
121 | + * 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 |
122 | + 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. | ||
123 | + | 149 | + |
124 | +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 |
125 | +by running the shell script in contrib/vmapple/uuid.sh on the macosvm.json file. | 151 | +by running the shell script in contrib/vmapple/uuid.sh on the macosvm.json file. |
126 | + | 152 | + |
127 | +.. code-block:: bash | 153 | +.. code-block:: bash |
... | ... | ||
144 | +to get better interactive access into the target system: | 170 | +to get better interactive access into the target system: |
145 | + | 171 | + |
146 | +.. code-block:: bash | 172 | +.. code-block:: bash |
147 | + :caption: Example execution command line | 173 | + :caption: Example execution command line |
148 | + | 174 | + |
149 | + $ UUID=$(uuid.sh macosvm.json) | 175 | + $ UUID="$(contrib/vmapple/uuid.sh 'macosvm.json')" |
150 | + $ AVPBOOTER=/System/Library/Frameworks/Virtualization.framework/Resources/AVPBooter.vmapple2.bin | 176 | + $ AVPBOOTER="/System/Library/Frameworks/Virtualization.framework/Resources/AVPBooter.vmapple2.bin" |
151 | + $ AUX=aux.img.trimmed | 177 | + $ AUX="aux.img.trimmed" |
152 | + $ DISK=disk.img | 178 | + $ DISK="disk.img" |
153 | + $ qemu-system-aarch64 \ | 179 | + $ qemu-system-aarch64 \ |
154 | + -serial mon:stdio \ | 180 | + -serial mon:stdio \ |
155 | + -m 4G \ | 181 | + -m 4G \ |
156 | + -accel hvf \ | 182 | + -accel hvf \ |
157 | + -M vmapple,uuid=$UUID \ | 183 | + -M vmapple,uuid="$UUID" \ |
158 | + -bios $AVPBOOTER \ | 184 | + -bios "$AVPBOOTER" \ |
159 | + -drive file="$AUX",if=pflash,format=raw \ | 185 | + -drive file="$AUX",if=pflash,format=raw \ |
160 | + -drive file="$DISK",if=pflash,format=raw \ | 186 | + -drive file="$DISK",if=pflash,format=raw \ |
161 | + -drive file="$AUX",if=none,id=aux,format=raw \ | 187 | + -drive file="$AUX",if=none,id=aux,format=raw \ |
162 | + -drive file="$DISK",if=none,id=root,format=raw \ | 188 | + -drive file="$DISK",if=none,id=root,format=raw \ |
163 | + -device vmapple-virtio-aux,drive=aux \ | 189 | + -device vmapple-virtio-blk-pci,variant=aux,drive=aux \ |
164 | + -device vmapple-virtio-root,drive=root \ | 190 | + -device vmapple-virtio-blk-pci,variant=root,drive=root \ |
165 | + -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 \ |
166 | + -net nic,model=virtio-net-pci \ | 192 | + -device virtio-net-pci,netdev=net0 |
193 | + | ||
167 | 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 |
168 | index XXXXXXX..XXXXXXX 100644 | 195 | index XXXXXXX..XXXXXXX 100644 |
169 | --- a/docs/system/target-arm.rst | 196 | --- a/docs/system/target-arm.rst |
170 | +++ b/docs/system/target-arm.rst | 197 | +++ b/docs/system/target-arm.rst |
171 | @@ -XXX,XX +XXX,XX @@ undocumented; you can get a complete list by running | 198 | @@ -XXX,XX +XXX,XX @@ Board-specific documentation |
172 | arm/stellaris | 199 | arm/stellaris |
173 | arm/stm32 | 200 | arm/stm32 |
174 | arm/virt | 201 | arm/virt |
175 | + arm/vmapple | 202 | + arm/vmapple |
176 | arm/xenpvh | 203 | arm/xenpvh |
... | ... | ||
287 | + struct arm_boot_info bootinfo; | 314 | + struct arm_boot_info bootinfo; |
288 | + MemMapEntry *memmap; | 315 | + MemMapEntry *memmap; |
289 | + const int *irqmap; | 316 | + const int *irqmap; |
290 | + DeviceState *gic; | 317 | + DeviceState *gic; |
291 | + DeviceState *cfg; | 318 | + DeviceState *cfg; |
319 | + DeviceState *pvpanic; | ||
292 | + Notifier powerdown_notifier; | 320 | + Notifier powerdown_notifier; |
293 | + PCIBus *bus; | 321 | + PCIBus *bus; |
294 | + MemoryRegion fw_mr; | 322 | + MemoryRegion fw_mr; |
295 | + MemoryRegion ecam_alias; | 323 | + MemoryRegion ecam_alias; |
296 | + uint64_t uuid; | 324 | + uint64_t uuid; |
... | ... | ||
418 | + sysbus_realize_and_unref(bdif_sb, &error_fatal); | 446 | + sysbus_realize_and_unref(bdif_sb, &error_fatal); |
419 | +} | 447 | +} |
420 | + | 448 | + |
421 | +static void create_pvpanic(VMAppleMachineState *vms, MemoryRegion *mem) | 449 | +static void create_pvpanic(VMAppleMachineState *vms, MemoryRegion *mem) |
422 | +{ | 450 | +{ |
423 | + SysBusDevice *cfg; | 451 | + SysBusDevice *pvpanic; |
424 | + | 452 | + |
425 | + vms->cfg = qdev_new(TYPE_PVPANIC_MMIO_DEVICE); | 453 | + vms->pvpanic = qdev_new(TYPE_PVPANIC_MMIO_DEVICE); |
426 | + cfg = SYS_BUS_DEVICE(vms->cfg); | 454 | + pvpanic = SYS_BUS_DEVICE(vms->pvpanic); |
427 | + sysbus_mmio_map(cfg, 0, vms->memmap[VMAPPLE_PVPANIC].base); | 455 | + sysbus_mmio_map(pvpanic, 0, vms->memmap[VMAPPLE_PVPANIC].base); |
428 | + | 456 | + |
429 | + sysbus_realize_and_unref(cfg, &error_fatal); | 457 | + sysbus_realize_and_unref(pvpanic, &error_fatal); |
430 | +} | 458 | +} |
431 | + | 459 | + |
432 | +static void create_cfg(VMAppleMachineState *vms, MemoryRegion *mem) | 460 | +static bool create_cfg(VMAppleMachineState *vms, MemoryRegion *mem, |
433 | +{ | 461 | + Error **errp) |
462 | +{ | ||
463 | + ERRP_GUARD(); | ||
434 | + SysBusDevice *cfg; | 464 | + SysBusDevice *cfg; |
435 | + MachineState *machine = MACHINE(vms); | 465 | + MachineState *machine = MACHINE(vms); |
436 | + uint32_t rnd = 1; | 466 | + uint32_t rnd = 1; |
437 | + | 467 | + |
438 | + vms->cfg = qdev_new(TYPE_VMAPPLE_CFG); | 468 | + vms->cfg = qdev_new(TYPE_VMAPPLE_CFG); |
... | ... | ||
444 | + qdev_prop_set_uint32(vms->cfg, "nr-cpus", machine->smp.cpus); | 474 | + qdev_prop_set_uint32(vms->cfg, "nr-cpus", machine->smp.cpus); |
445 | + qdev_prop_set_uint64(vms->cfg, "ecid", vms->uuid); | 475 | + qdev_prop_set_uint64(vms->cfg, "ecid", vms->uuid); |
446 | + qdev_prop_set_uint64(vms->cfg, "ram-size", machine->ram_size); | 476 | + qdev_prop_set_uint64(vms->cfg, "ram-size", machine->ram_size); |
447 | + qdev_prop_set_uint32(vms->cfg, "rnd", rnd); | 477 | + qdev_prop_set_uint32(vms->cfg, "rnd", rnd); |
448 | + | 478 | + |
449 | + 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; | ||
450 | +} | 485 | +} |
451 | + | 486 | + |
452 | +static void create_gfx(VMAppleMachineState *vms, MemoryRegion *mem) | 487 | +static void create_gfx(VMAppleMachineState *vms, MemoryRegion *mem) |
453 | +{ | 488 | +{ |
454 | + int irq_gfx = vms->irqmap[VMAPPLE_APV_GFX]; | 489 | + int irq_gfx = vms->irqmap[VMAPPLE_APV_GFX]; |
... | ... | ||
473 | + sysbus_mmio_map(aes, 1, vms->memmap[VMAPPLE_AES_2].base); | 508 | + sysbus_mmio_map(aes, 1, vms->memmap[VMAPPLE_AES_2].base); |
474 | + 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)); |
475 | + sysbus_realize_and_unref(aes, &error_fatal); | 510 | + sysbus_realize_and_unref(aes, &error_fatal); |
476 | +} | 511 | +} |
477 | + | 512 | + |
478 | +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) |
479 | +{ | 514 | +{ |
480 | + return NUM_IRQS + cpu_nr * GIC_INTERNAL + ppi_index; | 515 | + return NUM_IRQS + cpu_nr * GIC_INTERNAL + ppi_index; |
481 | +} | 516 | +} |
482 | + | 517 | + |
483 | +static void create_gic(VMAppleMachineState *vms, MemoryRegion *mem) | 518 | +static void create_gic(VMAppleMachineState *vms, MemoryRegion *mem) |
... | ... | ||
720 | + } | 755 | + } |
721 | + if (object_property_find(cpu, "has_el2")) { | 756 | + if (object_property_find(cpu, "has_el2")) { |
722 | + object_property_set_bool(cpu, "has_el2", false, &error_fatal); | 757 | + object_property_set_bool(cpu, "has_el2", false, &error_fatal); |
723 | + } | 758 | + } |
724 | + object_property_set_int(cpu, "psci-conduit", QEMU_PSCI_CONDUIT_HVC, | 759 | + object_property_set_int(cpu, "psci-conduit", QEMU_PSCI_CONDUIT_HVC, |
725 | + NULL); | 760 | + &error_fatal); |
726 | + | 761 | + |
727 | + /* Secondary CPUs start in PSCI powered-down state */ | 762 | + /* Secondary CPUs start in PSCI powered-down state */ |
728 | + if (n > 0) { | 763 | + if (n > 0) { |
729 | + object_property_set_bool(cpu, "start-powered-off", true, | 764 | + object_property_set_bool(cpu, "start-powered-off", true, |
730 | + &error_fatal); | 765 | + &error_fatal); |
... | ... | ||
748 | + create_pcie(vms); | 783 | + create_pcie(vms); |
749 | + | 784 | + |
750 | + create_gpio_devices(vms, VMAPPLE_GPIO, sysmem); | 785 | + create_gpio_devices(vms, VMAPPLE_GPIO, sysmem); |
751 | + | 786 | + |
752 | + vmapple_firmware_init(vms, sysmem); | 787 | + vmapple_firmware_init(vms, sysmem); |
753 | + create_cfg(vms, sysmem); | 788 | + create_cfg(vms, sysmem, &error_fatal); |
754 | + | 789 | + |
755 | + /* connect powerdown request */ | 790 | + /* connect powerdown request */ |
756 | + vms->powerdown_notifier.notify = vmapple_powerdown_req; | 791 | + vms->powerdown_notifier.notify = vmapple_powerdown_req; |
757 | + qemu_register_powerdown_notifier(&vms->powerdown_notifier); | 792 | + qemu_register_powerdown_notifier(&vms->powerdown_notifier); |
758 | + | 793 | + |
... | ... | ||
801 | + arm_build_mp_affinity(n, GICV3_TARGETLIST_BITS); | 836 | + arm_build_mp_affinity(n, GICV3_TARGETLIST_BITS); |
802 | + ms->possible_cpus->cpus[n].props.has_thread_id = true; | 837 | + ms->possible_cpus->cpus[n].props.has_thread_id = true; |
803 | + ms->possible_cpus->cpus[n].props.thread_id = n; | 838 | + ms->possible_cpus->cpus[n].props.thread_id = n; |
804 | + } | 839 | + } |
805 | + return ms->possible_cpus; | 840 | + return ms->possible_cpus; |
806 | +} | ||
807 | + | ||
808 | +static void vmapple_get_uuid(Object *obj, Visitor *v, const char *name, | ||
809 | + void *opaque, Error **errp) | ||
810 | +{ | ||
811 | + VMAppleMachineState *vms = VMAPPLE_MACHINE(obj); | ||
812 | + | ||
813 | + visit_type_uint64(v, name, &vms->uuid, errp); | ||
814 | +} | ||
815 | + | ||
816 | +static void vmapple_set_uuid(Object *obj, Visitor *v, const char *name, | ||
817 | + void *opaque, Error **errp) | ||
818 | +{ | ||
819 | + VMAppleMachineState *vms = VMAPPLE_MACHINE(obj); | ||
820 | + Error *error = NULL; | ||
821 | + | ||
822 | + visit_type_uint64(v, name, &vms->uuid, &error); | ||
823 | + if (error) { | ||
824 | + error_propagate(errp, error); | ||
825 | + return; | ||
826 | + } | ||
827 | +} | 841 | +} |
828 | + | 842 | + |
829 | +static void vmapple_machine_class_init(ObjectClass *oc, void *data) | 843 | +static void vmapple_machine_class_init(ObjectClass *oc, void *data) |
830 | +{ | 844 | +{ |
831 | + MachineClass *mc = MACHINE_CLASS(oc); | 845 | + MachineClass *mc = MACHINE_CLASS(oc); |
... | ... | ||
842 | + 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; |
843 | + mc->default_ram_id = "mach-vmapple.ram"; | 857 | + mc->default_ram_id = "mach-vmapple.ram"; |
844 | + | 858 | + |
845 | + object_register_sugar_prop(TYPE_VIRTIO_PCI, "disable-legacy", | 859 | + object_register_sugar_prop(TYPE_VIRTIO_PCI, "disable-legacy", |
846 | + "on", true); | 860 | + "on", true); |
847 | + | ||
848 | + object_class_property_add(oc, "uuid", "uint64", vmapple_get_uuid, | ||
849 | + vmapple_set_uuid, NULL, NULL); | ||
850 | + object_class_property_set_description(oc, "uuid", "Machine UUID (SDOM)"); | ||
851 | +} | 861 | +} |
852 | + | 862 | + |
853 | +static void vmapple_instance_init(Object *obj) | 863 | +static void vmapple_instance_init(Object *obj) |
854 | +{ | 864 | +{ |
855 | + VMAppleMachineState *vms = VMAPPLE_MACHINE(obj); | 865 | + VMAppleMachineState *vms = VMAPPLE_MACHINE(obj); |
856 | + | 866 | + |
857 | + 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)"); | ||
858 | +} | 872 | +} |
859 | + | 873 | + |
860 | +static const TypeInfo vmapple_machine_info = { | 874 | +static const TypeInfo vmapple_machine_info = { |
861 | + .name = TYPE_VMAPPLE_MACHINE, | 875 | + .name = TYPE_VMAPPLE_MACHINE, |
862 | + .parent = TYPE_MACHINE, | 876 | + .parent = TYPE_MACHINE, |
... | ... | ||
871 | +{ | 885 | +{ |
872 | + type_register_static(&vmapple_machine_info); | 886 | + type_register_static(&vmapple_machine_info); |
873 | +} | 887 | +} |
874 | +type_init(machvmapple_machine_init); | 888 | +type_init(machvmapple_machine_init); |
875 | + | 889 | + |
876 | +static void vmapple_machine_9_2_options(MachineClass *mc) | 890 | +static void vmapple_machine_10_0_options(MachineClass *mc) |
877 | +{ | 891 | +{ |
878 | +} | 892 | +} |
879 | +DEFINE_VMAPPLE_MACHINE_AS_LATEST(9, 2) | 893 | +DEFINE_VMAPPLE_MACHINE_AS_LATEST(10, 0) |
880 | + | 894 | + |
881 | -- | 895 | -- |
882 | 2.39.3 (Apple Git-145) | 896 | 2.39.5 (Apple Git-154) |
883 | 897 | ||
884 | 898 | diff view generated by jsdifflib |