1 | (Apologies to anyone who has received more than one version of this | 1 | <meta> |
---|---|---|---|
2 | series of emails; my git-send-email was misconfigured and this is | 2 | This patch series has been through months of review and |
3 | a new attempt.) | 3 | refinement. It now has end-to-end Reviewed-by: tags and |
4 | all code patches but one have Tested-by: tags. No significant | ||
5 | issues have been found via review for some weeks. | ||
6 | |||
7 | The patch set creates two new subsystems: | ||
8 | hw/display/apple-gfx | ||
9 | hw/vmapple | ||
10 | so it doesn't fall within the responsibility of existing | ||
11 | maintainers. How do we proceed to get this merged now that | ||
12 | 10.0 development is open? | ||
13 | </meta> | ||
14 | |||
4 | 15 | ||
5 | This patch set introduces a new ARM and macOS HVF specific machine type | 16 | This patch set introduces a new ARM and macOS HVF specific machine type |
6 | called "vmapple", as well as a family of display devices based on the | 17 | called "vmapple", as well as a family of display devices based on the |
7 | ParavirtualizedGraphics.framework in macOS. One of the display adapter | 18 | ParavirtualizedGraphics.framework in macOS. One of the display adapter |
8 | variants, apple-gfx-vmapple, is required for the new machine type, while | 19 | variants, apple-gfx-mmio, is required for the new machine type, while |
9 | apple-gfx-pci can be used to enable 3D graphics acceleration with x86-64 | 20 | apple-gfx-pci can be used to enable 3D graphics acceleration with x86-64 |
10 | macOS guest OSes. | 21 | macOS guest OSes. |
11 | 22 | ||
12 | Previous versions of this patch set were submitted semi-separately: | 23 | Previous versions of this patch set were submitted semi-separately: |
13 | the original vmapple patch set by Alexander Graf included a monolithic | 24 | the original vmapple patch set by Alexander Graf included a monolithic |
14 | implementation of apple-gfx-vmapple. I subsequently reviewed and reworked | 25 | implementation of apple-gfx-mmio. I subsequently reviewed and reworked |
15 | the latter to support the PCI variant of the device as well and submitted | 26 | the latter to support the PCI variant of the device as well and submitted |
16 | the result in isolation. As requested in subsequent review, I have now | 27 | the result in isolation. As requested in subsequent review, I have now |
17 | recombined this with the original vmapple patch set, which I have updated | 28 | recombined this with the original vmapple patch set, which I have updated |
18 | and improved in a few ways as well. | 29 | and improved in a few ways as well. |
19 | 30 | ||
... | ... | ||
35 | hosts only because ParavirtualizedGraphics.framework is a black box | 46 | hosts only because ParavirtualizedGraphics.framework is a black box |
36 | implementing most of the logic behind the apple-gfx device.) | 47 | implementing most of the logic behind the apple-gfx device.) |
37 | * PCI devices use legacy IRQs, not MSI/MSI-X. As far as I can tell, | 48 | * PCI devices use legacy IRQs, not MSI/MSI-X. As far as I can tell, |
38 | we'd need to include the GICv3 ITS, but it's unclear to me what | 49 | we'd need to include the GICv3 ITS, but it's unclear to me what |
39 | exactly needs wiring up. | 50 | exactly needs wiring up. |
40 | * Due to lack of MSI(-X), event delivery from USB devices to the guest | 51 | * Due to a quirk (bug?) in the macOS XHCI driver when MSI-X is not |
41 | macOS isn't working correctly. My current conclusion is that the | 52 | available, correct functioning of the USB controller (and thus |
42 | OS's XHCI driver simply was never designed to work with legacy IRQs. | 53 | keyboard/tablet) requires a small workaround in the XHCI controller |
43 | The upshot is that keyboard and mouse/tablet input is very laggy. | 54 | device. This is part of another patch series: |
44 | The solution would be to implement MSI(-X) support or figure out how | 55 | https://patchew.org/QEMU/20241208191646.64857-1-phil@philjordan.eu/ |
45 | to make hcd-xhci-sysbus work with the macOS guest, if at all possible. | ||
46 | (EHCI and UHCI/OHCI controllers are not an option as the VMAPPLE | ||
47 | guest kernel does not include drivers for these.) | ||
48 | * The guest OS must first be provisioned using Virtualization.framework; | 56 | * The guest OS must first be provisioned using Virtualization.framework; |
49 | the disk images can subsequently be used in Qemu. (See docs.) | 57 | the disk images can subsequently be used in Qemu. (See docs.) |
50 | 58 | ||
51 | The apple-gfx device can be used independently from the vmapple machine | 59 | The apple-gfx device can be used independently from the vmapple machine |
52 | type, at least in the PCI variant. It mainly targets x86-64 macOS guests | 60 | type, at least in the PCI variant. It mainly targets x86-64 macOS guests |
... | ... | ||
72 | framebuffer is copied to system memory and uses Qemu's usual | 80 | framebuffer is copied to system memory and uses Qemu's usual |
73 | CPU-based drawing. For maximum efficiency, the Metal texture | 81 | CPU-based drawing. For maximum efficiency, the Metal texture |
74 | containing the guest framebuffer could be drawn directly to | 82 | containing the guest framebuffer could be drawn directly to |
75 | a Metal view in the host window, staying on the GPU. (Similar | 83 | a Metal view in the host window, staying on the GPU. (Similar |
76 | to the OpenGL/virgl render path on other platforms.) | 84 | to the OpenGL/virgl render path on other platforms.) |
85 | |||
86 | Some of my part of this work has been sponsored by Sauce Labs Inc. | ||
77 | 87 | ||
78 | --- | 88 | --- |
79 | 89 | ||
80 | v2 -> v3: | 90 | v2 -> v3: |
81 | 91 | ||
... | ... | ||
96 | virtio HID devices, at least not in version 12's vmapple kernel. | 106 | virtio HID devices, at least not in version 12's vmapple kernel. |
97 | So input now sort of works (interrupt issues) rather than not | 107 | So input now sort of works (interrupt issues) rather than not |
98 | at all. Use network-based remote access to the guest OS as a | 108 | at all. Use network-based remote access to the guest OS as a |
99 | work-around. | 109 | work-around. |
100 | 110 | ||
111 | v3 -> v4: | ||
112 | |||
113 | * Complete rework of the mechanism for handling runloop/libdispatch | ||
114 | events on the main thread. PV graphics now work with the SDL UI. | ||
115 | * Renamed 'apple-gfx-vmapple' device to 'apple-gfx-mmio' | ||
116 | * hw/display/apple-gfx: threading model overhaul to be more consistent, | ||
117 | safer, and more QEMU-idiomatic. | ||
118 | * display-modes property on the apple-gfx devices now uses the | ||
119 | native array property mechanism and works on both device variants. | ||
120 | * hw/vmapple/aes: Improvements to logging and error handling. | ||
121 | * hw/vmapple/cfg: Bug fixes around device property default values. | ||
122 | * hw/vmapple/{aes,cfg,virtio-blk/vmapple}: Most header code moved into | ||
123 | .c files, only a single vmapple.h now contains the #defines for the | ||
124 | vmapple machine model-specific device type names. | ||
125 | * hw/block/virtio-blk: New patch for replacing virtio_blk_free_request | ||
126 | with g_free. (Optional) | ||
127 | * Various smaller changes following comments in v3 code review in | ||
128 | apple-gfx, aes, cfg, bdif, virtio-blk-vmapple, and the vmapple | ||
129 | machine type itself. See patch-specific v4 change notes for details. | ||
130 | |||
131 | v4 -> v5: | ||
132 | |||
133 | * Simplified the main thread runloop mechanism. Back to setting | ||
134 | qemu_main directly, but narrowing the scope of what it needs to do, | ||
135 | and it can now be NULL. (Meaning run the QEMU main event loop on | ||
136 | the main thread as is traditional.) | ||
137 | * hw/display/apple-gfx: Further improvements to the BH based job code bridging | ||
138 | the libdispatch & QEMU thread synchronisation impedance mismatch. | ||
139 | * hw/display/apple-gfx: Thread safety and object lifetime improvements. | ||
140 | * hw/display/apple-gfx-*: Better buffer and error handling in display mode | ||
141 | property setters and getters. | ||
142 | * hw/vmapple/aes: More consistent and safer logging/tracing | ||
143 | * hw/vmapple/cfg: Better error reporting on overlong property strings. | ||
144 | * hw/vmapple/virtio-blk: Fixed theoretically-unaligned write to config buffer. | ||
145 | * vmapple machine type: Moved ecam region into machine state, improved device | ||
146 | property setting error handling, improved ECID/UUID extraction script and | ||
147 | docs. | ||
148 | * Various smaller fixes in apple-gfx/-mmio, apple-gfx-pci, vmapple/aes, | ||
149 | vmapple/cfg, vmapple/virtio-blk, and vmapple machine type. | ||
150 | * Added SPDX license identifiers where they were missing. | ||
151 | |||
152 | v5 -> v6: | ||
153 | |||
154 | * 01/15 (main/Cocoa/runloop): Combined functions, fixed whitespace | ||
155 | * 02/15 (apple-gfx): Further refinement of PVG threading: reduced some callback | ||
156 | tasks from BHs to merely acquiring RCU read lock; replaced some libdispatch | ||
157 | tasks with BHs; last remaining synchronous BH now uses emphemeral | ||
158 | QemuSemaphore. | ||
159 | * 02/15 (apple-gfx): Readability improvements and other smaller tweaks | ||
160 | (see patch change notes for details) | ||
161 | * 04/15 (display modes): Replaced use of alloca() with NSMutableArray. | ||
162 | |||
163 | v6 -> v7: | ||
164 | |||
165 | * 02/15 (apple-gfx): Use g_ptr_array_find() helper function, coding style tweak | ||
166 | * 03/15 (apple-gfx-pci): Removed an unused function parameter | ||
167 | * 04/15 (apple-gfx display mode property): Simplified error handling in | ||
168 | property parsing. | ||
169 | * 10/15 (vmapple/aes): Coding style tweaks. | ||
170 | * 12/15 (vmapple/cfg): Changed error messages for overrun of properties with | ||
171 | fixed-length strings to be more useful to users than developers. | ||
172 | * 15/15 (vmapple machine type): Tiny error handling fix, un-inlined function | ||
173 | |||
174 | v7 -> v8: | ||
175 | |||
176 | * 02/15 (apple-gfx): Naming and type use improvements, fixes for a bug and a | ||
177 | leak. | ||
178 | * 04/15 (apple-gfx display mode property): Type use improvement | ||
179 | * 10/15 (vmapple/aes): Guest error logging tweaks. | ||
180 | * 11/15 (vmapple/bdif): Replaced uses of cpu_physical_memory_read with | ||
181 | dma_memory_read, and a g_free call with g_autofree. | ||
182 | * 12/15 (vmapple/cfg): Macro hygiene fix: consistently enclosing arguments in | ||
183 | parens. | ||
184 | * 15/15 (vmapple machine type): Use less verbose pattern for defining uuid | ||
185 | property. | ||
186 | |||
187 | v8 -> v9: | ||
188 | |||
189 | * 01/16 (ui & main loop): Set qemu_main to NULL for GTK UI as well. | ||
190 | * 02/16 (apple-gfx): Pass device pointer to graphic_console_init(), various | ||
191 | non-functional changes. | ||
192 | * 03/16 (apple-gfx-pci): Fixup of changed common call, whitespace and comment | ||
193 | formatting tweaks. | ||
194 | * 04/16 (apple-gfx display modes): Re-ordered type definitions so we can drop | ||
195 | a 'struct' keyword. | ||
196 | * 10/16 (vmapple/aes): Replaced a use of cpu_physical_memory_write with | ||
197 | dma_memory_write, minor style tweak. | ||
198 | * 11/16 (vmapple/bdif): Replaced uses of cpu_physical_memory_write with | ||
199 | dma_memory_write. | ||
200 | * 13/16 (vmapple/virtio-blk): Correctly specify class_size for | ||
201 | VMAppleVirtIOBlkClass. | ||
202 | * 15/16 (vmapple machine type): Documentation improvements, fixed variable | ||
203 | name and struct field used during pvpanic device creation. | ||
204 | * 16/16 (NEW/RFC vmapple/virtio-blk): Proposed change to replace type hierarchy | ||
205 | with a variant property. This seems cleaner and less confusing than the | ||
206 | original approach to me, but I'm not sure if it warrants creation of a new | ||
207 | QAPI enum and property type definition. | ||
208 | |||
209 | v9 -> v10: | ||
210 | |||
211 | * 01/15 (ui & main loop): Added comments to qemu_main declaration and GTK. | ||
212 | * 02/15 (apple-gfx): Reworked the way frame rendering code is threaded to use | ||
213 | BHs for sections requiring BQL. | ||
214 | * 02/15 (apple-gfx): Fixed ./configure error on non-macOS platforms. | ||
215 | * 10/15 (vmapple/aes): Code style and comment improvements. | ||
216 | * 12/15 (vmapple/cfg): Slightly tidier error reporting for overlong property | ||
217 | values. | ||
218 | * 13/15 (vmapple/virtio-blk): Folded v9 patch 16/16 into this one, changing | ||
219 | the device type design to provide a single device type with a variant | ||
220 | property instead of 2 different subtypes for aux and root volumes. | ||
221 | * 15/15 (vmapple machine type): Documentation fixup for changed virtio-blk | ||
222 | device type; small improvements to shell commands in documentation; | ||
223 | improved propagation of errors during cfg device instantiation. | ||
224 | |||
225 | v10 -> v11: | ||
226 | |||
227 | * 01/15 (ui & main loop): Simplified main.c, better comments & commit message | ||
228 | * 02/15 (apple-gfx): Give each PV display instance a unique serial number. | ||
229 | * 02 & 03/15 (apple-gfx, -pci): Formatting/style tweaks | ||
230 | * 15/15 (vmapple machine type): Improvements to shell code in docs | ||
231 | |||
232 | v11 -> v12: | ||
233 | |||
234 | * 01/15 (ui & main loop): More precise wording of code comments. | ||
235 | * 02/15 (apple-gfx): Fixed memory management regressions introduced in v10; | ||
236 | improved error handling; various more conmetic code adjustments | ||
237 | * 09/15 (GPEX): Fixed uses of deleted GPEX_NUM_IRQS constant that have been | ||
238 | added to QEMU since this patch was originally written. | ||
239 | |||
240 | v12 -> v13: | ||
241 | |||
242 | * 15/15 (vmapple machine type): Bumped the machine type version from 9.2 | ||
243 | to 10.0. | ||
244 | * All patches in the series now have been positively reviewed and received | ||
245 | corresponding reviewed-by tags. | ||
246 | |||
247 | v13 -> v14: | ||
248 | |||
249 | * 6/15 (hw/vmapple directory): Changed myself from reviewer | ||
250 | to maintainer, as that seemed appropriate at this point. | ||
251 | * 15/15 (vmapple machine type): Gate creation of XHCI and | ||
252 | USB HID devices behind if (defaults_enabled()). | ||
101 | 253 | ||
102 | Alexander Graf (9): | 254 | Alexander Graf (9): |
103 | hw: Add vmapple subdir | 255 | hw: Add vmapple subdir |
104 | hw/misc/pvpanic: Add MMIO interface | 256 | hw/misc/pvpanic: Add MMIO interface |
105 | hvf: arm: Ignore writes to CNTP_CTL_EL0 | 257 | hvf: arm: Ignore writes to CNTP_CTL_EL0 |
... | ... | ||
108 | hw/vmapple/bdif: Introduce vmapple backdoor interface | 260 | hw/vmapple/bdif: Introduce vmapple backdoor interface |
109 | hw/vmapple/cfg: Introduce vmapple cfg region | 261 | hw/vmapple/cfg: Introduce vmapple cfg region |
110 | hw/vmapple/virtio-blk: Add support for apple virtio-blk | 262 | hw/vmapple/virtio-blk: Add support for apple virtio-blk |
111 | hw/vmapple/vmapple: Add vmapple machine type | 263 | hw/vmapple/vmapple: Add vmapple machine type |
112 | 264 | ||
113 | Phil Dennis-Jordan (5): | 265 | Phil Dennis-Jordan (6): |
266 | ui & main loop: Redesign of system-specific main thread event handling | ||
114 | hw/display/apple-gfx: Introduce ParavirtualizedGraphics.Framework | 267 | hw/display/apple-gfx: Introduce ParavirtualizedGraphics.Framework |
115 | support | 268 | support |
116 | hw/display/apple-gfx: Adds PCI implementation | 269 | hw/display/apple-gfx: Adds PCI implementation |
117 | ui/cocoa: Adds non-app runloop on main thread mode | ||
118 | hw/display/apple-gfx: Adds configurable mode list | 270 | hw/display/apple-gfx: Adds configurable mode list |
119 | MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF | 271 | MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF |
120 | 272 | hw/block/virtio-blk: Replaces request free function with g_free | |
121 | MAINTAINERS | 15 + | 273 | |
122 | docs/system/arm/vmapple.rst | 63 +++ | 274 | MAINTAINERS | 15 + |
123 | docs/system/target-arm.rst | 1 + | 275 | contrib/vmapple/uuid.sh | 9 + |
124 | hw/Kconfig | 1 + | 276 | docs/system/arm/vmapple.rst | 63 ++ |
125 | hw/arm/sbsa-ref.c | 2 +- | 277 | docs/system/target-arm.rst | 1 + |
126 | hw/arm/virt.c | 2 +- | 278 | hw/Kconfig | 1 + |
127 | hw/block/virtio-blk.c | 19 +- | 279 | hw/arm/sbsa-ref.c | 2 +- |
128 | hw/display/Kconfig | 14 + | 280 | hw/arm/virt.c | 2 +- |
129 | hw/display/apple-gfx-pci.m | 179 +++++++++ | 281 | hw/block/virtio-blk.c | 58 +- |
130 | hw/display/apple-gfx-vmapple.m | 215 ++++++++++ | 282 | hw/core/qdev-properties-system.c | 8 + |
131 | hw/display/apple-gfx.h | 72 ++++ | 283 | hw/display/Kconfig | 13 + |
132 | hw/display/apple-gfx.m | 668 ++++++++++++++++++++++++++++++++ | 284 | hw/display/apple-gfx-mmio.m | 289 +++++++++ |
133 | hw/display/meson.build | 3 + | 285 | hw/display/apple-gfx-pci.m | 157 +++++ |
134 | hw/display/trace-events | 26 ++ | 286 | hw/display/apple-gfx.h | 77 +++ |
135 | hw/i386/microvm.c | 2 +- | 287 | hw/display/apple-gfx.m | 880 ++++++++++++++++++++++++++++ |
136 | hw/loongarch/virt.c | 2 +- | 288 | hw/display/meson.build | 7 + |
137 | hw/meson.build | 1 + | 289 | hw/display/trace-events | 30 + |
138 | hw/mips/loongson3_virt.c | 2 +- | 290 | hw/i386/microvm.c | 2 +- |
139 | hw/misc/Kconfig | 4 + | 291 | hw/loongarch/virt.c | 12 +- |
140 | hw/misc/meson.build | 1 + | 292 | hw/meson.build | 1 + |
141 | hw/misc/pvpanic-mmio.c | 61 +++ | 293 | hw/mips/loongson3_virt.c | 2 +- |
142 | hw/openrisc/virt.c | 12 +- | 294 | hw/misc/Kconfig | 4 + |
143 | hw/pci-host/gpex.c | 36 +- | 295 | hw/misc/meson.build | 1 + |
144 | hw/riscv/virt.c | 12 +- | 296 | hw/misc/pvpanic-mmio.c | 61 ++ |
145 | hw/vmapple/Kconfig | 32 ++ | 297 | hw/openrisc/virt.c | 12 +- |
146 | hw/vmapple/aes.c | 584 ++++++++++++++++++++++++++++ | 298 | hw/pci-host/gpex.c | 43 +- |
147 | hw/vmapple/bdif.c | 245 ++++++++++++ | 299 | hw/riscv/virt.c | 12 +- |
148 | hw/vmapple/cfg.c | 106 +++++ | 300 | hw/vmapple/Kconfig | 32 + |
149 | hw/vmapple/meson.build | 5 + | 301 | hw/vmapple/aes.c | 581 ++++++++++++++++++ |
150 | hw/vmapple/trace-events | 26 ++ | 302 | hw/vmapple/bdif.c | 275 +++++++++ |
151 | hw/vmapple/trace.h | 1 + | 303 | hw/vmapple/cfg.c | 196 +++++++ |
152 | hw/vmapple/virtio-blk.c | 212 ++++++++++ | 304 | hw/vmapple/meson.build | 5 + |
153 | hw/vmapple/vmapple.c | 661 +++++++++++++++++++++++++++++++ | 305 | hw/vmapple/trace-events | 21 + |
154 | hw/xtensa/virt.c | 2 +- | 306 | hw/vmapple/trace.h | 1 + |
155 | include/hw/misc/pvpanic.h | 1 + | 307 | hw/vmapple/virtio-blk.c | 205 +++++++ |
156 | include/hw/pci-host/gpex.h | 7 +- | 308 | hw/vmapple/vmapple.c | 648 ++++++++++++++++++++ |
157 | include/hw/pci/pci_ids.h | 1 + | 309 | hw/xen/xen-pvh-common.c | 2 +- |
158 | include/hw/virtio/virtio-blk.h | 12 +- | 310 | hw/xtensa/virt.c | 2 +- |
159 | include/hw/vmapple/bdif.h | 31 ++ | 311 | include/hw/misc/pvpanic.h | 1 + |
160 | include/hw/vmapple/cfg.h | 68 ++++ | 312 | include/hw/pci-host/gpex.h | 7 +- |
161 | include/hw/vmapple/virtio-blk.h | 39 ++ | 313 | include/hw/pci/pci_ids.h | 1 + |
162 | include/qemu-main.h | 2 + | 314 | include/hw/qdev-properties-system.h | 5 + |
163 | meson.build | 5 + | 315 | include/hw/virtio/virtio-blk.h | 11 +- |
164 | target/arm/hvf/hvf.c | 9 + | 316 | include/hw/vmapple/vmapple.h | 23 + |
165 | ui/cocoa.m | 15 +- | 317 | include/qemu-main.h | 14 +- |
166 | 45 files changed, 3443 insertions(+), 34 deletions(-) | 318 | include/qemu/cutils.h | 15 + |
319 | meson.build | 5 + | ||
320 | qapi/virtio.json | 14 + | ||
321 | system/main.c | 37 +- | ||
322 | target/arm/hvf/hvf.c | 9 + | ||
323 | ui/cocoa.m | 54 +- | ||
324 | ui/gtk.c | 4 + | ||
325 | ui/sdl2.c | 4 + | ||
326 | util/hexdump.c | 18 + | ||
327 | 53 files changed, 3842 insertions(+), 110 deletions(-) | ||
328 | create mode 100755 contrib/vmapple/uuid.sh | ||
167 | create mode 100644 docs/system/arm/vmapple.rst | 329 | create mode 100644 docs/system/arm/vmapple.rst |
330 | create mode 100644 hw/display/apple-gfx-mmio.m | ||
168 | create mode 100644 hw/display/apple-gfx-pci.m | 331 | create mode 100644 hw/display/apple-gfx-pci.m |
169 | create mode 100644 hw/display/apple-gfx-vmapple.m | ||
170 | create mode 100644 hw/display/apple-gfx.h | 332 | create mode 100644 hw/display/apple-gfx.h |
171 | create mode 100644 hw/display/apple-gfx.m | 333 | create mode 100644 hw/display/apple-gfx.m |
172 | create mode 100644 hw/misc/pvpanic-mmio.c | 334 | create mode 100644 hw/misc/pvpanic-mmio.c |
173 | create mode 100644 hw/vmapple/Kconfig | 335 | create mode 100644 hw/vmapple/Kconfig |
174 | create mode 100644 hw/vmapple/aes.c | 336 | create mode 100644 hw/vmapple/aes.c |
... | ... | ||
177 | create mode 100644 hw/vmapple/meson.build | 339 | create mode 100644 hw/vmapple/meson.build |
178 | create mode 100644 hw/vmapple/trace-events | 340 | create mode 100644 hw/vmapple/trace-events |
179 | create mode 100644 hw/vmapple/trace.h | 341 | create mode 100644 hw/vmapple/trace.h |
180 | create mode 100644 hw/vmapple/virtio-blk.c | 342 | create mode 100644 hw/vmapple/virtio-blk.c |
181 | create mode 100644 hw/vmapple/vmapple.c | 343 | create mode 100644 hw/vmapple/vmapple.c |
182 | create mode 100644 include/hw/vmapple/bdif.h | 344 | create mode 100644 include/hw/vmapple/vmapple.h |
183 | create mode 100644 include/hw/vmapple/cfg.h | ||
184 | create mode 100644 include/hw/vmapple/virtio-blk.h | ||
185 | 345 | ||
186 | -- | 346 | -- |
187 | 2.39.3 (Apple Git-145) | 347 | 2.39.5 (Apple Git-154) |
348 | |||
349 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | MacOS provides a framework (library) that allows any vmm to implement a | ||
2 | paravirtualized 3d graphics passthrough to the host metal stack called | ||
3 | ParavirtualizedGraphics.Framework (PVG). The library abstracts away | ||
4 | almost every aspect of the paravirtualized device model and only provides | ||
5 | and receives callbacks on MMIO access as well as to share memory address | ||
6 | space between the VM and PVG. | ||
7 | 1 | ||
8 | This patch implements a QEMU device that drives PVG for the VMApple | ||
9 | variant of it. | ||
10 | |||
11 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
12 | Co-authored-by: Alexander Graf <graf@amazon.com> | ||
13 | |||
14 | Subsequent changes: | ||
15 | |||
16 | * Cherry-pick/rebase conflict fixes | ||
17 | * BQL function renaming | ||
18 | * Moved from hw/vmapple/ (useful outside that machine type) | ||
19 | * Code review comments: Switched to DEFINE_TYPES macro & little endian | ||
20 | MMIO. | ||
21 | * Removed some dead/superfluous code | ||
22 | * Mad set_mode thread & memory safe | ||
23 | * Added migration blocker due to lack of (de-)serialisation. | ||
24 | * Fixes to ObjC refcounting and autorelease pool usage. | ||
25 | * Fixed ObjC new/init misuse | ||
26 | * Switched to ObjC category extension for private property. | ||
27 | * Simplified task memory mapping and made it thread safe. | ||
28 | * Refactoring to split generic and vmapple MMIO variant specific | ||
29 | code. | ||
30 | * Switched to asynchronous MMIO writes on x86-64 | ||
31 | * Rendering and graphics update are now done asynchronously | ||
32 | * Fixed cursor handling | ||
33 | * Coding convention fixes | ||
34 | * Removed software cursor compositing | ||
35 | |||
36 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
37 | |||
38 | --- | ||
39 | |||
40 | v3: | ||
41 | |||
42 | * Rebased on latest upstream, fixed breakages including switching to Resettable methods. | ||
43 | * Squashed patches dealing with dGPUs, MMIO area size, and GPU picking. | ||
44 | * Allow re-entrant MMIO; this simplifies the code and solves the divergence | ||
45 | between x86-64 and arm64 variants. | ||
46 | |||
47 | hw/display/Kconfig | 9 + | ||
48 | hw/display/apple-gfx-vmapple.m | 215 +++++++++++++ | ||
49 | hw/display/apple-gfx.h | 57 ++++ | ||
50 | hw/display/apple-gfx.m | 536 +++++++++++++++++++++++++++++++++ | ||
51 | hw/display/meson.build | 2 + | ||
52 | hw/display/trace-events | 26 ++ | ||
53 | meson.build | 4 + | ||
54 | 7 files changed, 849 insertions(+) | ||
55 | create mode 100644 hw/display/apple-gfx-vmapple.m | ||
56 | create mode 100644 hw/display/apple-gfx.h | ||
57 | create mode 100644 hw/display/apple-gfx.m | ||
58 | |||
59 | diff --git a/hw/display/Kconfig b/hw/display/Kconfig | ||
60 | index XXXXXXX..XXXXXXX 100644 | ||
61 | --- a/hw/display/Kconfig | ||
62 | +++ b/hw/display/Kconfig | ||
63 | @@ -XXX,XX +XXX,XX @@ config XLNX_DISPLAYPORT | ||
64 | |||
65 | config DM163 | ||
66 | bool | ||
67 | + | ||
68 | +config MAC_PVG | ||
69 | + bool | ||
70 | + default y | ||
71 | + | ||
72 | +config MAC_PVG_VMAPPLE | ||
73 | + bool | ||
74 | + depends on MAC_PVG | ||
75 | + depends on ARM | ||
76 | diff --git a/hw/display/apple-gfx-vmapple.m b/hw/display/apple-gfx-vmapple.m | ||
77 | new file mode 100644 | ||
78 | index XXXXXXX..XXXXXXX | ||
79 | --- /dev/null | ||
80 | +++ b/hw/display/apple-gfx-vmapple.m | ||
81 | @@ -XXX,XX +XXX,XX @@ | ||
82 | +/* | ||
83 | + * QEMU Apple ParavirtualizedGraphics.framework device, vmapple (arm64) variant | ||
84 | + * | ||
85 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
86 | + * | ||
87 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
88 | + * See the COPYING file in the top-level directory. | ||
89 | + * | ||
90 | + * ParavirtualizedGraphics.framework is a set of libraries that macOS provides | ||
91 | + * which implements 3d graphics passthrough to the host as well as a | ||
92 | + * proprietary guest communication channel to drive it. This device model | ||
93 | + * implements support to drive that library from within QEMU as an MMIO-based | ||
94 | + * system device for macOS on arm64 VMs. | ||
95 | + */ | ||
96 | + | ||
97 | +#include "apple-gfx.h" | ||
98 | +#include "monitor/monitor.h" | ||
99 | +#include "hw/sysbus.h" | ||
100 | +#include "hw/irq.h" | ||
101 | +#include "trace.h" | ||
102 | +#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h> | ||
103 | + | ||
104 | +_Static_assert(__aarch64__, ""); | ||
105 | + | ||
106 | +/* | ||
107 | + * ParavirtualizedGraphics.Framework only ships header files for the PCI | ||
108 | + * variant which does not include IOSFC descriptors and host devices. We add | ||
109 | + * their definitions here so that we can also work with the ARM version. | ||
110 | + */ | ||
111 | +typedef bool(^IOSFCRaiseInterrupt)(uint32_t vector); | ||
112 | +typedef bool(^IOSFCUnmapMemory)( | ||
113 | + void *a, void *b, void *c, void *d, void *e, void *f); | ||
114 | +typedef bool(^IOSFCMapMemory)( | ||
115 | + uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f); | ||
116 | + | ||
117 | +@interface PGDeviceDescriptor (IOSurfaceMapper) | ||
118 | +@property (readwrite, nonatomic) bool usingIOSurfaceMapper; | ||
119 | +@end | ||
120 | + | ||
121 | +@interface PGIOSurfaceHostDeviceDescriptor : NSObject | ||
122 | +-(PGIOSurfaceHostDeviceDescriptor *)init; | ||
123 | +@property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory; | ||
124 | +@property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory; | ||
125 | +@property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt; | ||
126 | +@end | ||
127 | + | ||
128 | +@interface PGIOSurfaceHostDevice : NSObject | ||
129 | +-(instancetype)initWithDescriptor:(PGIOSurfaceHostDeviceDescriptor *) desc; | ||
130 | +-(uint32_t)mmioReadAtOffset:(size_t) offset; | ||
131 | +-(void)mmioWriteAtOffset:(size_t) offset value:(uint32_t)value; | ||
132 | +@end | ||
133 | + | ||
134 | +typedef struct AppleGFXVmappleState { | ||
135 | + SysBusDevice parent_obj; | ||
136 | + | ||
137 | + AppleGFXState common; | ||
138 | + | ||
139 | + qemu_irq irq_gfx; | ||
140 | + qemu_irq irq_iosfc; | ||
141 | + MemoryRegion iomem_iosfc; | ||
142 | + PGIOSurfaceHostDevice *pgiosfc; | ||
143 | +} AppleGFXVmappleState; | ||
144 | + | ||
145 | +OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXVmappleState, APPLE_GFX_VMAPPLE) | ||
146 | + | ||
147 | + | ||
148 | +static uint64_t apple_iosfc_read(void *opaque, hwaddr offset, unsigned size) | ||
149 | +{ | ||
150 | + AppleGFXVmappleState *s = opaque; | ||
151 | + uint64_t res = 0; | ||
152 | + | ||
153 | + bql_unlock(); | ||
154 | + res = [s->pgiosfc mmioReadAtOffset:offset]; | ||
155 | + bql_lock(); | ||
156 | + | ||
157 | + trace_apple_iosfc_read(offset, res); | ||
158 | + | ||
159 | + return res; | ||
160 | +} | ||
161 | + | ||
162 | +static void apple_iosfc_write( | ||
163 | + void *opaque, hwaddr offset, uint64_t val, unsigned size) | ||
164 | +{ | ||
165 | + AppleGFXVmappleState *s = opaque; | ||
166 | + | ||
167 | + trace_apple_iosfc_write(offset, val); | ||
168 | + | ||
169 | + [s->pgiosfc mmioWriteAtOffset:offset value:val]; | ||
170 | +} | ||
171 | + | ||
172 | +static const MemoryRegionOps apple_iosfc_ops = { | ||
173 | + .read = apple_iosfc_read, | ||
174 | + .write = apple_iosfc_write, | ||
175 | + .endianness = DEVICE_LITTLE_ENDIAN, | ||
176 | + .valid = { | ||
177 | + .min_access_size = 4, | ||
178 | + .max_access_size = 8, | ||
179 | + }, | ||
180 | + .impl = { | ||
181 | + .min_access_size = 4, | ||
182 | + .max_access_size = 8, | ||
183 | + }, | ||
184 | +}; | ||
185 | + | ||
186 | +static PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device( | ||
187 | + AppleGFXVmappleState *s) | ||
188 | +{ | ||
189 | + PGIOSurfaceHostDeviceDescriptor *iosfc_desc = | ||
190 | + [PGIOSurfaceHostDeviceDescriptor new]; | ||
191 | + PGIOSurfaceHostDevice *iosfc_host_dev = nil; | ||
192 | + | ||
193 | + iosfc_desc.mapMemory = | ||
194 | + ^(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) { | ||
195 | + trace_apple_iosfc_map_memory(phys, len, ro, va, e, f); | ||
196 | + MemoryRegion *tmp_mr; | ||
197 | + *va = gpa2hva(&tmp_mr, phys, len, NULL); | ||
198 | + return (bool)true; | ||
199 | + }; | ||
200 | + | ||
201 | + iosfc_desc.unmapMemory = | ||
202 | + ^(void *a, void *b, void *c, void *d, void *e, void *f) { | ||
203 | + trace_apple_iosfc_unmap_memory(a, b, c, d, e, f); | ||
204 | + return (bool)true; | ||
205 | + }; | ||
206 | + | ||
207 | + iosfc_desc.raiseInterrupt = ^(uint32_t vector) { | ||
208 | + trace_apple_iosfc_raise_irq(vector); | ||
209 | + bool locked = bql_locked(); | ||
210 | + if (!locked) { | ||
211 | + bql_lock(); | ||
212 | + } | ||
213 | + qemu_irq_pulse(s->irq_iosfc); | ||
214 | + if (!locked) { | ||
215 | + bql_unlock(); | ||
216 | + } | ||
217 | + return (bool)true; | ||
218 | + }; | ||
219 | + | ||
220 | + iosfc_host_dev = | ||
221 | + [[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc]; | ||
222 | + [iosfc_desc release]; | ||
223 | + return iosfc_host_dev; | ||
224 | +} | ||
225 | + | ||
226 | +static void apple_gfx_vmapple_realize(DeviceState *dev, Error **errp) | ||
227 | +{ | ||
228 | + @autoreleasepool { | ||
229 | + AppleGFXVmappleState *s = APPLE_GFX_VMAPPLE(dev); | ||
230 | + | ||
231 | + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; | ||
232 | + desc.usingIOSurfaceMapper = true; | ||
233 | + desc.raiseInterrupt = ^(uint32_t vector) { | ||
234 | + bool locked; | ||
235 | + | ||
236 | + trace_apple_gfx_raise_irq(vector); | ||
237 | + locked = bql_locked(); | ||
238 | + if (!locked) { | ||
239 | + bql_lock(); | ||
240 | + } | ||
241 | + qemu_irq_pulse(s->irq_gfx); | ||
242 | + if (!locked) { | ||
243 | + bql_unlock(); | ||
244 | + } | ||
245 | + }; | ||
246 | + | ||
247 | + s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s); | ||
248 | + | ||
249 | + apple_gfx_common_realize(&s->common, desc); | ||
250 | + [desc release]; | ||
251 | + desc = nil; | ||
252 | + } | ||
253 | +} | ||
254 | + | ||
255 | +static void apple_gfx_vmapple_init(Object *obj) | ||
256 | +{ | ||
257 | + AppleGFXVmappleState *s = APPLE_GFX_VMAPPLE(obj); | ||
258 | + | ||
259 | + apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_VMAPPLE); | ||
260 | + | ||
261 | + memory_region_init_io(&s->iomem_iosfc, obj, &apple_iosfc_ops, s, | ||
262 | + TYPE_APPLE_GFX_VMAPPLE, 0x10000); | ||
263 | + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->common.iomem_gfx); | ||
264 | + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem_iosfc); | ||
265 | + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_gfx); | ||
266 | + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_iosfc); | ||
267 | +} | ||
268 | + | ||
269 | +static void apple_gfx_vmapple_reset(Object *obj, ResetType type) | ||
270 | +{ | ||
271 | + AppleGFXVmappleState *s = APPLE_GFX_VMAPPLE(obj); | ||
272 | + [s->common.pgdev reset]; | ||
273 | +} | ||
274 | + | ||
275 | + | ||
276 | +static void apple_gfx_vmapple_class_init(ObjectClass *klass, void *data) | ||
277 | +{ | ||
278 | + DeviceClass *dc = DEVICE_CLASS(klass); | ||
279 | + ResettableClass *rc = RESETTABLE_CLASS(klass); | ||
280 | + | ||
281 | + assert(rc->phases.hold == NULL); | ||
282 | + rc->phases.hold = apple_gfx_vmapple_reset; | ||
283 | + | ||
284 | + dc->realize = apple_gfx_vmapple_realize; | ||
285 | +} | ||
286 | + | ||
287 | +static TypeInfo apple_gfx_vmapple_types[] = { | ||
288 | + { | ||
289 | + .name = TYPE_APPLE_GFX_VMAPPLE, | ||
290 | + .parent = TYPE_SYS_BUS_DEVICE, | ||
291 | + .instance_size = sizeof(AppleGFXVmappleState), | ||
292 | + .class_init = apple_gfx_vmapple_class_init, | ||
293 | + .instance_init = apple_gfx_vmapple_init, | ||
294 | + } | ||
295 | +}; | ||
296 | +DEFINE_TYPES(apple_gfx_vmapple_types) | ||
297 | diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h | ||
298 | new file mode 100644 | ||
299 | index XXXXXXX..XXXXXXX | ||
300 | --- /dev/null | ||
301 | +++ b/hw/display/apple-gfx.h | ||
302 | @@ -XXX,XX +XXX,XX @@ | ||
303 | +#ifndef QEMU_APPLE_GFX_H | ||
304 | +#define QEMU_APPLE_GFX_H | ||
305 | + | ||
306 | +#define TYPE_APPLE_GFX_VMAPPLE "apple-gfx-vmapple" | ||
307 | +#define TYPE_APPLE_GFX_PCI "apple-gfx-pci" | ||
308 | + | ||
309 | +#include "qemu/typedefs.h" | ||
310 | + | ||
311 | +typedef struct AppleGFXState AppleGFXState; | ||
312 | + | ||
313 | +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name); | ||
314 | + | ||
315 | +#ifdef __OBJC__ | ||
316 | + | ||
317 | +#include "qemu/osdep.h" | ||
318 | +#include "exec/memory.h" | ||
319 | +#include "ui/surface.h" | ||
320 | +#include <dispatch/dispatch.h> | ||
321 | + | ||
322 | +@class PGDeviceDescriptor; | ||
323 | +@protocol PGDevice; | ||
324 | +@protocol PGDisplay; | ||
325 | +@protocol MTLDevice; | ||
326 | +@protocol MTLTexture; | ||
327 | +@protocol MTLCommandQueue; | ||
328 | + | ||
329 | +typedef QTAILQ_HEAD(, PGTask_s) AppleGFXTaskList; | ||
330 | + | ||
331 | +struct AppleGFXState { | ||
332 | + MemoryRegion iomem_gfx; | ||
333 | + id<PGDevice> pgdev; | ||
334 | + id<PGDisplay> pgdisp; | ||
335 | + AppleGFXTaskList tasks; | ||
336 | + QemuConsole *con; | ||
337 | + id<MTLDevice> mtl; | ||
338 | + id<MTLCommandQueue> mtl_queue; | ||
339 | + bool handles_frames; | ||
340 | + bool new_frame; | ||
341 | + bool cursor_show; | ||
342 | + QEMUCursor *cursor; | ||
343 | + | ||
344 | + dispatch_queue_t render_queue; | ||
345 | + /* The following fields should only be accessed from render_queue: */ | ||
346 | + bool gfx_update_requested; | ||
347 | + bool new_frame_ready; | ||
348 | + bool using_managed_texture_storage; | ||
349 | + int32_t pending_frames; | ||
350 | + void *vram; | ||
351 | + DisplaySurface *surface; | ||
352 | + id<MTLTexture> texture; | ||
353 | +}; | ||
354 | + | ||
355 | +void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc); | ||
356 | + | ||
357 | +#endif /* __OBJC__ */ | ||
358 | + | ||
359 | +#endif | ||
360 | diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m | ||
361 | new file mode 100644 | ||
362 | index XXXXXXX..XXXXXXX | ||
363 | --- /dev/null | ||
364 | +++ b/hw/display/apple-gfx.m | ||
365 | @@ -XXX,XX +XXX,XX @@ | ||
366 | +/* | ||
367 | + * QEMU Apple ParavirtualizedGraphics.framework device | ||
368 | + * | ||
369 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
370 | + * | ||
371 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
372 | + * See the COPYING file in the top-level directory. | ||
373 | + * | ||
374 | + * ParavirtualizedGraphics.framework is a set of libraries that macOS provides | ||
375 | + * which implements 3d graphics passthrough to the host as well as a | ||
376 | + * proprietary guest communication channel to drive it. This device model | ||
377 | + * implements support to drive that library from within QEMU. | ||
378 | + */ | ||
379 | + | ||
380 | +#include "apple-gfx.h" | ||
381 | +#include "trace.h" | ||
382 | +#include "qemu/main-loop.h" | ||
383 | +#include "ui/console.h" | ||
384 | +#include "monitor/monitor.h" | ||
385 | +#include "qapi/error.h" | ||
386 | +#include "migration/blocker.h" | ||
387 | +#include <mach/mach_vm.h> | ||
388 | +#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h> | ||
389 | + | ||
390 | +static const PGDisplayCoord_t apple_gfx_modes[] = { | ||
391 | + { .x = 1440, .y = 1080 }, | ||
392 | + { .x = 1280, .y = 1024 }, | ||
393 | +}; | ||
394 | + | ||
395 | +typedef struct PGTask_s { // Name matches forward declaration in PG header | ||
396 | + QTAILQ_ENTRY(PGTask_s) node; | ||
397 | + mach_vm_address_t address; | ||
398 | + uint64_t len; | ||
399 | +} AppleGFXTask; | ||
400 | + | ||
401 | +static Error *apple_gfx_mig_blocker; | ||
402 | + | ||
403 | +static void apple_gfx_render_frame_completed(AppleGFXState *s, void *vram, | ||
404 | + id<MTLTexture> texture); | ||
405 | + | ||
406 | +static AppleGFXTask *apple_gfx_new_task(AppleGFXState *s, uint64_t len) | ||
407 | +{ | ||
408 | + mach_vm_address_t task_mem; | ||
409 | + AppleGFXTask *task; | ||
410 | + kern_return_t r; | ||
411 | + | ||
412 | + r = mach_vm_allocate(mach_task_self(), &task_mem, len, VM_FLAGS_ANYWHERE); | ||
413 | + if (r != KERN_SUCCESS || task_mem == 0) { | ||
414 | + return NULL; | ||
415 | + } | ||
416 | + | ||
417 | + task = g_new0(AppleGFXTask, 1); | ||
418 | + | ||
419 | + task->address = task_mem; | ||
420 | + task->len = len; | ||
421 | + QTAILQ_INSERT_TAIL(&s->tasks, task, node); | ||
422 | + | ||
423 | + return task; | ||
424 | +} | ||
425 | + | ||
426 | +static uint64_t apple_gfx_read(void *opaque, hwaddr offset, unsigned size) | ||
427 | +{ | ||
428 | + AppleGFXState *s = opaque; | ||
429 | + uint64_t res = 0; | ||
430 | + | ||
431 | + bql_unlock(); | ||
432 | + res = [s->pgdev mmioReadAtOffset:offset]; | ||
433 | + bql_lock(); | ||
434 | + | ||
435 | + trace_apple_gfx_read(offset, res); | ||
436 | + | ||
437 | + return res; | ||
438 | +} | ||
439 | + | ||
440 | +static void apple_gfx_write(void *opaque, hwaddr offset, uint64_t val, | ||
441 | + unsigned size) | ||
442 | +{ | ||
443 | + AppleGFXState *s = opaque; | ||
444 | + | ||
445 | + trace_apple_gfx_write(offset, val); | ||
446 | + | ||
447 | + bql_unlock(); | ||
448 | + [s->pgdev mmioWriteAtOffset:offset value:val]; | ||
449 | + bql_lock(); | ||
450 | +} | ||
451 | + | ||
452 | +static const MemoryRegionOps apple_gfx_ops = { | ||
453 | + .read = apple_gfx_read, | ||
454 | + .write = apple_gfx_write, | ||
455 | + .endianness = DEVICE_LITTLE_ENDIAN, | ||
456 | + .valid = { | ||
457 | + .min_access_size = 4, | ||
458 | + .max_access_size = 8, | ||
459 | + }, | ||
460 | + .impl = { | ||
461 | + .min_access_size = 4, | ||
462 | + .max_access_size = 4, | ||
463 | + }, | ||
464 | +}; | ||
465 | + | ||
466 | +static void apple_gfx_render_new_frame(AppleGFXState *s) | ||
467 | +{ | ||
468 | + BOOL r; | ||
469 | + void *vram = s->vram; | ||
470 | + uint32_t width = surface_width(s->surface); | ||
471 | + uint32_t height = surface_height(s->surface); | ||
472 | + MTLRegion region = MTLRegionMake2D(0, 0, width, height); | ||
473 | + id<MTLCommandBuffer> command_buffer = [s->mtl_queue commandBuffer]; | ||
474 | + id<MTLTexture> texture = s->texture; | ||
475 | + r = [s->pgdisp encodeCurrentFrameToCommandBuffer:command_buffer | ||
476 | + texture:texture | ||
477 | + region:region]; | ||
478 | + if (!r) { | ||
479 | + return; | ||
480 | + } | ||
481 | + [texture retain]; | ||
482 | + if (s->using_managed_texture_storage) { | ||
483 | + /* "Managed" textures exist in both VRAM and RAM and must be synced. */ | ||
484 | + id<MTLBlitCommandEncoder> blit = [command_buffer blitCommandEncoder]; | ||
485 | + [blit synchronizeResource:texture]; | ||
486 | + [blit endEncoding]; | ||
487 | + } | ||
488 | + [command_buffer retain]; | ||
489 | + [command_buffer addCompletedHandler: | ||
490 | + ^(id<MTLCommandBuffer> cb) | ||
491 | + { | ||
492 | + dispatch_async(s->render_queue, ^{ | ||
493 | + apple_gfx_render_frame_completed(s, vram, texture); | ||
494 | + [texture release]; | ||
495 | + }); | ||
496 | + [command_buffer release]; | ||
497 | + }]; | ||
498 | + [command_buffer commit]; | ||
499 | +} | ||
500 | + | ||
501 | +static void copy_mtl_texture_to_surface_mem(id<MTLTexture> texture, void *vram) | ||
502 | +{ | ||
503 | + /* TODO: Skip this entirely on a pure Metal or headless/guest-only | ||
504 | + * rendering path, else use a blit command encoder? Needs careful | ||
505 | + * (double?) buffering design. */ | ||
506 | + size_t width = texture.width, height = texture.height; | ||
507 | + MTLRegion region = MTLRegionMake2D(0, 0, width, height); | ||
508 | + [texture getBytes:vram | ||
509 | + bytesPerRow:(width * 4) | ||
510 | + bytesPerImage:(width * height * 4) | ||
511 | + fromRegion:region | ||
512 | + mipmapLevel:0 | ||
513 | + slice:0]; | ||
514 | +} | ||
515 | + | ||
516 | +static void apple_gfx_render_frame_completed(AppleGFXState *s, void *vram, | ||
517 | + id<MTLTexture> texture) | ||
518 | +{ | ||
519 | + --s->pending_frames; | ||
520 | + assert(s->pending_frames >= 0); | ||
521 | + | ||
522 | + if (vram != s->vram) { | ||
523 | + /* Display mode has changed, drop this old frame. */ | ||
524 | + assert(texture != s->texture); | ||
525 | + g_free(vram); | ||
526 | + } else { | ||
527 | + copy_mtl_texture_to_surface_mem(texture, vram); | ||
528 | + if (s->gfx_update_requested) { | ||
529 | + s->gfx_update_requested = false; | ||
530 | + dpy_gfx_update_full(s->con); | ||
531 | + graphic_hw_update_done(s->con); | ||
532 | + s->new_frame_ready = false; | ||
533 | + } else { | ||
534 | + s->new_frame_ready = true; | ||
535 | + } | ||
536 | + } | ||
537 | + if (s->pending_frames > 0) { | ||
538 | + apple_gfx_render_new_frame(s); | ||
539 | + } | ||
540 | +} | ||
541 | + | ||
542 | +static void apple_gfx_fb_update_display(void *opaque) | ||
543 | +{ | ||
544 | + AppleGFXState *s = opaque; | ||
545 | + | ||
546 | + dispatch_async(s->render_queue, ^{ | ||
547 | + if (s->pending_frames > 0) { | ||
548 | + s->gfx_update_requested = true; | ||
549 | + } else { | ||
550 | + if (s->new_frame_ready) { | ||
551 | + dpy_gfx_update_full(s->con); | ||
552 | + s->new_frame_ready = false; | ||
553 | + } | ||
554 | + graphic_hw_update_done(s->con); | ||
555 | + } | ||
556 | + }); | ||
557 | +} | ||
558 | + | ||
559 | +static const GraphicHwOps apple_gfx_fb_ops = { | ||
560 | + .gfx_update = apple_gfx_fb_update_display, | ||
561 | + .gfx_update_async = true, | ||
562 | +}; | ||
563 | + | ||
564 | +static void update_cursor(AppleGFXState *s) | ||
565 | +{ | ||
566 | + dpy_mouse_set(s->con, s->pgdisp.cursorPosition.x, | ||
567 | + s->pgdisp.cursorPosition.y, s->cursor_show); | ||
568 | +} | ||
569 | + | ||
570 | +static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height) | ||
571 | +{ | ||
572 | + void *vram = NULL; | ||
573 | + DisplaySurface *surface; | ||
574 | + MTLTextureDescriptor *textureDescriptor; | ||
575 | + id<MTLTexture> texture = nil; | ||
576 | + __block bool no_change = false; | ||
577 | + | ||
578 | + dispatch_sync(s->render_queue, | ||
579 | + ^{ | ||
580 | + if (s->surface && | ||
581 | + width == surface_width(s->surface) && | ||
582 | + height == surface_height(s->surface)) { | ||
583 | + no_change = true; | ||
584 | + } | ||
585 | + }); | ||
586 | + | ||
587 | + if (no_change) { | ||
588 | + return; | ||
589 | + } | ||
590 | + | ||
591 | + vram = g_malloc0(width * height * 4); | ||
592 | + surface = qemu_create_displaysurface_from(width, height, PIXMAN_LE_a8r8g8b8, | ||
593 | + width * 4, vram); | ||
594 | + | ||
595 | + @autoreleasepool { | ||
596 | + textureDescriptor = | ||
597 | + [MTLTextureDescriptor | ||
598 | + texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm | ||
599 | + width:width | ||
600 | + height:height | ||
601 | + mipmapped:NO]; | ||
602 | + textureDescriptor.usage = s->pgdisp.minimumTextureUsage; | ||
603 | + texture = [s->mtl newTextureWithDescriptor:textureDescriptor]; | ||
604 | + } | ||
605 | + | ||
606 | + s->using_managed_texture_storage = | ||
607 | + (texture.storageMode == MTLStorageModeManaged); | ||
608 | + | ||
609 | + dispatch_sync(s->render_queue, | ||
610 | + ^{ | ||
611 | + id<MTLTexture> old_texture = nil; | ||
612 | + void *old_vram = s->vram; | ||
613 | + s->vram = vram; | ||
614 | + s->surface = surface; | ||
615 | + | ||
616 | + dpy_gfx_replace_surface(s->con, surface); | ||
617 | + | ||
618 | + old_texture = s->texture; | ||
619 | + s->texture = texture; | ||
620 | + [old_texture release]; | ||
621 | + | ||
622 | + if (s->pending_frames == 0) { | ||
623 | + g_free(old_vram); | ||
624 | + } | ||
625 | + }); | ||
626 | +} | ||
627 | + | ||
628 | +static void create_fb(AppleGFXState *s) | ||
629 | +{ | ||
630 | + s->con = graphic_console_init(NULL, 0, &apple_gfx_fb_ops, s); | ||
631 | + set_mode(s, 1440, 1080); | ||
632 | + | ||
633 | + s->cursor_show = true; | ||
634 | +} | ||
635 | + | ||
636 | +static size_t apple_gfx_get_default_mmio_range_size(void) | ||
637 | +{ | ||
638 | + size_t mmio_range_size; | ||
639 | + @autoreleasepool { | ||
640 | + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; | ||
641 | + mmio_range_size = desc.mmioLength; | ||
642 | + [desc release]; | ||
643 | + } | ||
644 | + return mmio_range_size; | ||
645 | +} | ||
646 | + | ||
647 | +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name) | ||
648 | +{ | ||
649 | + Error *local_err = NULL; | ||
650 | + int r; | ||
651 | + size_t mmio_range_size = apple_gfx_get_default_mmio_range_size(); | ||
652 | + | ||
653 | + trace_apple_gfx_common_init(obj_name, mmio_range_size); | ||
654 | + memory_region_init_io(&s->iomem_gfx, obj, &apple_gfx_ops, s, obj_name, | ||
655 | + mmio_range_size); | ||
656 | + s->iomem_gfx.disable_reentrancy_guard = true; | ||
657 | + | ||
658 | + /* TODO: PVG framework supports serialising device state: integrate it! */ | ||
659 | + if (apple_gfx_mig_blocker == NULL) { | ||
660 | + error_setg(&apple_gfx_mig_blocker, | ||
661 | + "Migration state blocked by apple-gfx display device"); | ||
662 | + r = migrate_add_blocker(&apple_gfx_mig_blocker, &local_err); | ||
663 | + if (r < 0) { | ||
664 | + error_report_err(local_err); | ||
665 | + } | ||
666 | + } | ||
667 | +} | ||
668 | + | ||
669 | +static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, | ||
670 | + PGDeviceDescriptor *desc) | ||
671 | +{ | ||
672 | + desc.createTask = ^(uint64_t vmSize, void * _Nullable * _Nonnull baseAddress) { | ||
673 | + AppleGFXTask *task = apple_gfx_new_task(s, vmSize); | ||
674 | + *baseAddress = (void*)task->address; | ||
675 | + trace_apple_gfx_create_task(vmSize, *baseAddress); | ||
676 | + return task; | ||
677 | + }; | ||
678 | + | ||
679 | + desc.destroyTask = ^(AppleGFXTask * _Nonnull task) { | ||
680 | + trace_apple_gfx_destroy_task(task); | ||
681 | + QTAILQ_REMOVE(&s->tasks, task, node); | ||
682 | + mach_vm_deallocate(mach_task_self(), task->address, task->len); | ||
683 | + g_free(task); | ||
684 | + }; | ||
685 | + | ||
686 | + desc.mapMemory = ^(AppleGFXTask * _Nonnull task, uint32_t rangeCount, | ||
687 | + uint64_t virtualOffset, bool readOnly, | ||
688 | + PGPhysicalMemoryRange_t * _Nonnull ranges) { | ||
689 | + kern_return_t r; | ||
690 | + mach_vm_address_t target, source; | ||
691 | + trace_apple_gfx_map_memory(task, rangeCount, virtualOffset, readOnly); | ||
692 | + for (int i = 0; i < rangeCount; i++) { | ||
693 | + PGPhysicalMemoryRange_t *range = &ranges[i]; | ||
694 | + MemoryRegion *tmp_mr; | ||
695 | + /* TODO: Bounds checks? r/o? */ | ||
696 | + bql_lock(); | ||
697 | + | ||
698 | + trace_apple_gfx_map_memory_range(i, range->physicalAddress, | ||
699 | + range->physicalLength); | ||
700 | + | ||
701 | + target = task->address + virtualOffset; | ||
702 | + source = (mach_vm_address_t)gpa2hva(&tmp_mr, | ||
703 | + range->physicalAddress, | ||
704 | + range->physicalLength, NULL); | ||
705 | + vm_prot_t cur_protection = 0; | ||
706 | + vm_prot_t max_protection = 0; | ||
707 | + // Map guest RAM at range->physicalAddress into PG task memory range | ||
708 | + r = mach_vm_remap(mach_task_self(), | ||
709 | + &target, range->physicalLength, vm_page_size - 1, | ||
710 | + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, | ||
711 | + mach_task_self(), | ||
712 | + source, false /* shared mapping, no copy */, | ||
713 | + &cur_protection, &max_protection, | ||
714 | + VM_INHERIT_COPY); | ||
715 | + trace_apple_gfx_remap(r, source, target); | ||
716 | + g_assert(r == KERN_SUCCESS); | ||
717 | + | ||
718 | + bql_unlock(); | ||
719 | + | ||
720 | + virtualOffset += range->physicalLength; | ||
721 | + } | ||
722 | + return (bool)true; | ||
723 | + }; | ||
724 | + | ||
725 | + desc.unmapMemory = ^(AppleGFXTask * _Nonnull task, uint64_t virtualOffset, | ||
726 | + uint64_t length) { | ||
727 | + kern_return_t r; | ||
728 | + mach_vm_address_t range_address; | ||
729 | + | ||
730 | + trace_apple_gfx_unmap_memory(task, virtualOffset, length); | ||
731 | + | ||
732 | + /* Replace task memory range with fresh pages, undoing the mapping | ||
733 | + * from guest RAM. */ | ||
734 | + range_address = task->address + virtualOffset; | ||
735 | + r = mach_vm_allocate(mach_task_self(), &range_address, length, | ||
736 | + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); | ||
737 | + g_assert(r == KERN_SUCCESS); | ||
738 | + | ||
739 | + return (bool)true; | ||
740 | + }; | ||
741 | + | ||
742 | + desc.readMemory = ^(uint64_t physicalAddress, uint64_t length, | ||
743 | + void * _Nonnull dst) { | ||
744 | + trace_apple_gfx_read_memory(physicalAddress, length, dst); | ||
745 | + cpu_physical_memory_read(physicalAddress, dst, length); | ||
746 | + return (bool)true; | ||
747 | + }; | ||
748 | + | ||
749 | +} | ||
750 | + | ||
751 | +static PGDisplayDescriptor *apple_gfx_prepare_display_handlers(AppleGFXState *s) | ||
752 | +{ | ||
753 | + PGDisplayDescriptor *disp_desc = [PGDisplayDescriptor new]; | ||
754 | + | ||
755 | + disp_desc.name = @"QEMU display"; | ||
756 | + disp_desc.sizeInMillimeters = NSMakeSize(400., 300.); /* A 20" display */ | ||
757 | + disp_desc.queue = dispatch_get_main_queue(); | ||
758 | + disp_desc.newFrameEventHandler = ^(void) { | ||
759 | + trace_apple_gfx_new_frame(); | ||
760 | + dispatch_async(s->render_queue, ^{ | ||
761 | + /* Drop frames if we get too far ahead. */ | ||
762 | + if (s->pending_frames >= 2) | ||
763 | + return; | ||
764 | + ++s->pending_frames; | ||
765 | + if (s->pending_frames > 1) { | ||
766 | + return; | ||
767 | + } | ||
768 | + @autoreleasepool { | ||
769 | + apple_gfx_render_new_frame(s); | ||
770 | + } | ||
771 | + }); | ||
772 | + }; | ||
773 | + disp_desc.modeChangeHandler = ^(PGDisplayCoord_t sizeInPixels, | ||
774 | + OSType pixelFormat) { | ||
775 | + trace_apple_gfx_mode_change(sizeInPixels.x, sizeInPixels.y); | ||
776 | + set_mode(s, sizeInPixels.x, sizeInPixels.y); | ||
777 | + }; | ||
778 | + disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph, | ||
779 | + PGDisplayCoord_t hotSpot) { | ||
780 | + uint32_t bpp = glyph.bitsPerPixel; | ||
781 | + size_t width = glyph.pixelsWide; | ||
782 | + size_t height = glyph.pixelsHigh; | ||
783 | + size_t padding_bytes_per_row = glyph.bytesPerRow - width * 4; | ||
784 | + const uint8_t* px_data = glyph.bitmapData; | ||
785 | + | ||
786 | + trace_apple_gfx_cursor_set(bpp, width, height); | ||
787 | + | ||
788 | + if (s->cursor) { | ||
789 | + cursor_unref(s->cursor); | ||
790 | + s->cursor = NULL; | ||
791 | + } | ||
792 | + | ||
793 | + if (bpp == 32) { /* Shouldn't be anything else, but just to be safe...*/ | ||
794 | + s->cursor = cursor_alloc(width, height); | ||
795 | + s->cursor->hot_x = hotSpot.x; | ||
796 | + s->cursor->hot_y = hotSpot.y; | ||
797 | + | ||
798 | + uint32_t *dest_px = s->cursor->data; | ||
799 | + | ||
800 | + for (size_t y = 0; y < height; ++y) { | ||
801 | + for (size_t x = 0; x < width; ++x) { | ||
802 | + /* NSBitmapImageRep's red & blue channels are swapped | ||
803 | + * compared to QEMUCursor's. */ | ||
804 | + *dest_px = | ||
805 | + (px_data[0] << 16u) | | ||
806 | + (px_data[1] << 8u) | | ||
807 | + (px_data[2] << 0u) | | ||
808 | + (px_data[3] << 24u); | ||
809 | + ++dest_px; | ||
810 | + px_data += 4; | ||
811 | + } | ||
812 | + px_data += padding_bytes_per_row; | ||
813 | + } | ||
814 | + dpy_cursor_define(s->con, s->cursor); | ||
815 | + update_cursor(s); | ||
816 | + } | ||
817 | + }; | ||
818 | + disp_desc.cursorShowHandler = ^(BOOL show) { | ||
819 | + trace_apple_gfx_cursor_show(show); | ||
820 | + s->cursor_show = show; | ||
821 | + update_cursor(s); | ||
822 | + }; | ||
823 | + disp_desc.cursorMoveHandler = ^(void) { | ||
824 | + trace_apple_gfx_cursor_move(); | ||
825 | + update_cursor(s); | ||
826 | + }; | ||
827 | + | ||
828 | + return disp_desc; | ||
829 | +} | ||
830 | + | ||
831 | +static NSArray<PGDisplayMode*>* apple_gfx_prepare_display_mode_array(void) | ||
832 | +{ | ||
833 | + PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; | ||
834 | + NSArray<PGDisplayMode*>* mode_array = nil; | ||
835 | + int i; | ||
836 | + | ||
837 | + for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { | ||
838 | + modes[i] = | ||
839 | + [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.]; | ||
840 | + } | ||
841 | + | ||
842 | + mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)]; | ||
843 | + | ||
844 | + for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { | ||
845 | + [modes[i] release]; | ||
846 | + modes[i] = nil; | ||
847 | + } | ||
848 | + | ||
849 | + return mode_array; | ||
850 | +} | ||
851 | + | ||
852 | +static id<MTLDevice> copy_suitable_metal_device(void) | ||
853 | +{ | ||
854 | + id<MTLDevice> dev = nil; | ||
855 | + NSArray<id<MTLDevice>> *devs = MTLCopyAllDevices(); | ||
856 | + | ||
857 | + /* Prefer a unified memory GPU. Failing that, pick a non-removable GPU. */ | ||
858 | + for (size_t i = 0; i < devs.count; ++i) { | ||
859 | + if (devs[i].hasUnifiedMemory) { | ||
860 | + dev = devs[i]; | ||
861 | + break; | ||
862 | + } | ||
863 | + if (!devs[i].removable) { | ||
864 | + dev = devs[i]; | ||
865 | + } | ||
866 | + } | ||
867 | + | ||
868 | + if (dev != nil) { | ||
869 | + [dev retain]; | ||
870 | + } else { | ||
871 | + dev = MTLCreateSystemDefaultDevice(); | ||
872 | + } | ||
873 | + [devs release]; | ||
874 | + | ||
875 | + return dev; | ||
876 | +} | ||
877 | + | ||
878 | +void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc) | ||
879 | +{ | ||
880 | + PGDisplayDescriptor *disp_desc = nil; | ||
881 | + | ||
882 | + QTAILQ_INIT(&s->tasks); | ||
883 | + s->render_queue = dispatch_queue_create("apple-gfx.render", | ||
884 | + DISPATCH_QUEUE_SERIAL); | ||
885 | + s->mtl = copy_suitable_metal_device(); | ||
886 | + s->mtl_queue = [s->mtl newCommandQueue]; | ||
887 | + | ||
888 | + desc.device = s->mtl; | ||
889 | + | ||
890 | + apple_gfx_register_task_mapping_handlers(s, desc); | ||
891 | + | ||
892 | + s->pgdev = PGNewDeviceWithDescriptor(desc); | ||
893 | + | ||
894 | + disp_desc = apple_gfx_prepare_display_handlers(s); | ||
895 | + s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc | ||
896 | + port:0 serialNum:1234]; | ||
897 | + [disp_desc release]; | ||
898 | + s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); | ||
899 | + | ||
900 | + create_fb(s); | ||
901 | +} | ||
902 | diff --git a/hw/display/meson.build b/hw/display/meson.build | ||
903 | index XXXXXXX..XXXXXXX 100644 | ||
904 | --- a/hw/display/meson.build | ||
905 | +++ b/hw/display/meson.build | ||
906 | @@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) | ||
907 | |||
908 | system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) | ||
909 | |||
910 | +system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) | ||
911 | +system_ss.add(when: 'CONFIG_MAC_PVG_VMAPPLE', if_true: [files('apple-gfx-vmapple.m'), pvg, metal]) | ||
912 | |||
913 | if config_all_devices.has_key('CONFIG_VIRTIO_GPU') | ||
914 | virtio_gpu_ss = ss.source_set() | ||
915 | diff --git a/hw/display/trace-events b/hw/display/trace-events | ||
916 | index XXXXXXX..XXXXXXX 100644 | ||
917 | --- a/hw/display/trace-events | ||
918 | +++ b/hw/display/trace-events | ||
919 | @@ -XXX,XX +XXX,XX @@ dm163_bits_ppi(unsigned dest_width) "dest_width : %u" | ||
920 | dm163_leds(int led, uint32_t value) "led %d: 0x%x" | ||
921 | dm163_channels(int channel, uint8_t value) "channel %d: 0x%x" | ||
922 | dm163_refresh_rate(uint32_t rr) "refresh rate %d" | ||
923 | + | ||
924 | +# apple-gfx.m | ||
925 | +apple_gfx_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 | ||
926 | +apple_gfx_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 | ||
927 | +apple_gfx_create_task(uint32_t vm_size, void *va) "vm_size=0x%x base_addr=%p" | ||
928 | +apple_gfx_destroy_task(void *task) "task=%p" | ||
929 | +apple_gfx_map_memory(void *task, uint32_t range_count, uint64_t virtual_offset, uint32_t read_only) "task=%p range_count=0x%x virtual_offset=0x%"PRIx64" read_only=%d" | ||
930 | +apple_gfx_map_memory_range(uint32_t i, uint64_t phys_addr, uint64_t phys_len) "[%d] phys_addr=0x%"PRIx64" phys_len=0x%"PRIx64 | ||
931 | +apple_gfx_remap(uint64_t retval, uint64_t source, uint64_t target) "retval=%"PRId64" source=0x%"PRIx64" target=0x%"PRIx64 | ||
932 | +apple_gfx_unmap_memory(void *task, uint64_t virtual_offset, uint64_t length) "task=%p virtual_offset=0x%"PRIx64" length=0x%"PRIx64 | ||
933 | +apple_gfx_read_memory(uint64_t phys_address, uint64_t length, void *dst) "phys_addr=0x%"PRIx64" length=0x%"PRIx64" dest=%p" | ||
934 | +apple_gfx_raise_irq(uint32_t vector) "vector=0x%x" | ||
935 | +apple_gfx_new_frame(void) "" | ||
936 | +apple_gfx_mode_change(uint64_t x, uint64_t y) "x=%"PRId64" y=%"PRId64 | ||
937 | +apple_gfx_cursor_set(uint32_t bpp, uint64_t width, uint64_t height) "bpp=%d width=%"PRId64" height=0x%"PRId64 | ||
938 | +apple_gfx_cursor_show(uint32_t show) "show=%d" | ||
939 | +apple_gfx_cursor_move(void) "" | ||
940 | +apple_gfx_common_init(const char *device_name, size_t mmio_size) "device: %s; MMIO size: %zu bytes" | ||
941 | + | ||
942 | +# apple-gfx-vmapple.m | ||
943 | +apple_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 | ||
944 | +apple_iosfc_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 | ||
945 | +apple_iosfc_map_memory(uint64_t phys, uint64_t len, uint32_t ro, void *va, void *e, void *f) "phys=0x%"PRIx64" len=0x%"PRIx64" ro=%d va=%p e=%p f=%p" | ||
946 | +apple_iosfc_unmap_memory(void *a, void *b, void *c, void *d, void *e, void *f) "a=%p b=%p c=%p d=%p e=%p f=%p" | ||
947 | +apple_iosfc_raise_irq(uint32_t vector) "vector=0x%x" | ||
948 | + | ||
949 | diff --git a/meson.build b/meson.build | ||
950 | index XXXXXXX..XXXXXXX 100644 | ||
951 | --- a/meson.build | ||
952 | +++ b/meson.build | ||
953 | @@ -XXX,XX +XXX,XX @@ socket = [] | ||
954 | version_res = [] | ||
955 | coref = [] | ||
956 | iokit = [] | ||
957 | +pvg = [] | ||
958 | +metal = [] | ||
959 | emulator_link_args = [] | ||
960 | midl = not_found | ||
961 | widl = not_found | ||
962 | @@ -XXX,XX +XXX,XX @@ elif host_os == 'darwin' | ||
963 | coref = dependency('appleframeworks', modules: 'CoreFoundation') | ||
964 | iokit = dependency('appleframeworks', modules: 'IOKit', required: false) | ||
965 | host_dsosuf = '.dylib' | ||
966 | + pvg = dependency('appleframeworks', modules: 'ParavirtualizedGraphics') | ||
967 | + metal = dependency('appleframeworks', modules: 'Metal') | ||
968 | elif host_os == 'sunos' | ||
969 | socket = [cc.find_library('socket'), | ||
970 | cc.find_library('nsl'), | ||
971 | -- | ||
972 | 2.39.3 (Apple Git-145) | ||
973 | |||
974 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This change wires up the PCI variant of the paravirtualised | ||
2 | graphics device, mainly useful for x86-64 macOS guests, implemented | ||
3 | by macOS's ParavirtualizedGraphics.framework. It builds on code | ||
4 | shared with the vmapple/mmio variant of the PVG device. | ||
5 | 1 | ||
6 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
7 | --- | ||
8 | hw/display/Kconfig | 5 ++ | ||
9 | hw/display/apple-gfx-pci.m | 138 +++++++++++++++++++++++++++++++++++++ | ||
10 | hw/display/meson.build | 1 + | ||
11 | 3 files changed, 144 insertions(+) | ||
12 | create mode 100644 hw/display/apple-gfx-pci.m | ||
13 | |||
14 | diff --git a/hw/display/Kconfig b/hw/display/Kconfig | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/hw/display/Kconfig | ||
17 | +++ b/hw/display/Kconfig | ||
18 | @@ -XXX,XX +XXX,XX @@ config MAC_PVG_VMAPPLE | ||
19 | bool | ||
20 | depends on MAC_PVG | ||
21 | depends on ARM | ||
22 | + | ||
23 | +config MAC_PVG_PCI | ||
24 | + bool | ||
25 | + depends on MAC_PVG && PCI | ||
26 | + default y if PCI_DEVICES | ||
27 | diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m | ||
28 | new file mode 100644 | ||
29 | index XXXXXXX..XXXXXXX | ||
30 | --- /dev/null | ||
31 | +++ b/hw/display/apple-gfx-pci.m | ||
32 | @@ -XXX,XX +XXX,XX @@ | ||
33 | +/* | ||
34 | + * QEMU Apple ParavirtualizedGraphics.framework device, PCI variant | ||
35 | + * | ||
36 | + * Copyright © 2023-2024 Phil Dennis-Jordan | ||
37 | + * | ||
38 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
39 | + * See the COPYING file in the top-level directory. | ||
40 | + * | ||
41 | + * ParavirtualizedGraphics.framework is a set of libraries that macOS provides | ||
42 | + * which implements 3d graphics passthrough to the host as well as a | ||
43 | + * proprietary guest communication channel to drive it. This device model | ||
44 | + * implements support to drive that library from within QEMU as a PCI device | ||
45 | + * aimed primarily at x86-64 macOS VMs. | ||
46 | + */ | ||
47 | + | ||
48 | +#include "apple-gfx.h" | ||
49 | +#include "hw/pci/pci_device.h" | ||
50 | +#include "hw/pci/msi.h" | ||
51 | +#include "qapi/error.h" | ||
52 | +#include "trace.h" | ||
53 | +#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h> | ||
54 | + | ||
55 | +typedef struct AppleGFXPCIState { | ||
56 | + PCIDevice parent_obj; | ||
57 | + | ||
58 | + AppleGFXState common; | ||
59 | +} AppleGFXPCIState; | ||
60 | + | ||
61 | +OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXPCIState, APPLE_GFX_PCI) | ||
62 | + | ||
63 | +static const char* apple_gfx_pci_option_rom_path = NULL; | ||
64 | + | ||
65 | +static void apple_gfx_init_option_rom_path(void) | ||
66 | +{ | ||
67 | + NSURL *option_rom_url = PGCopyOptionROMURL(); | ||
68 | + const char *option_rom_path = option_rom_url.fileSystemRepresentation; | ||
69 | + if (option_rom_url.fileURL && option_rom_path != NULL) { | ||
70 | + apple_gfx_pci_option_rom_path = g_strdup(option_rom_path); | ||
71 | + } | ||
72 | + [option_rom_url release]; | ||
73 | +} | ||
74 | + | ||
75 | +static void apple_gfx_pci_init(Object *obj) | ||
76 | +{ | ||
77 | + AppleGFXPCIState *s = APPLE_GFX_PCI(obj); | ||
78 | + | ||
79 | + if (!apple_gfx_pci_option_rom_path) { | ||
80 | + /* Done on device not class init to avoid -daemonize ObjC fork crash */ | ||
81 | + PCIDeviceClass *pci = PCI_DEVICE_CLASS(object_get_class(obj)); | ||
82 | + apple_gfx_init_option_rom_path(); | ||
83 | + pci->romfile = apple_gfx_pci_option_rom_path; | ||
84 | + } | ||
85 | + | ||
86 | + apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_PCI); | ||
87 | +} | ||
88 | + | ||
89 | +static void apple_gfx_pci_interrupt(PCIDevice *dev, AppleGFXPCIState *s, | ||
90 | + uint32_t vector) | ||
91 | +{ | ||
92 | + bool msi_ok; | ||
93 | + trace_apple_gfx_raise_irq(vector); | ||
94 | + | ||
95 | + msi_ok = msi_enabled(dev); | ||
96 | + if (msi_ok) { | ||
97 | + msi_notify(dev, vector); | ||
98 | + } | ||
99 | +} | ||
100 | + | ||
101 | +static void apple_gfx_pci_realize(PCIDevice *dev, Error **errp) | ||
102 | +{ | ||
103 | + AppleGFXPCIState *s = APPLE_GFX_PCI(dev); | ||
104 | + Error *err = NULL; | ||
105 | + int ret; | ||
106 | + | ||
107 | + pci_register_bar(dev, PG_PCI_BAR_MMIO, | ||
108 | + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->common.iomem_gfx); | ||
109 | + | ||
110 | + ret = msi_init(dev, 0x0 /* config offset; 0 = find space */, | ||
111 | + PG_PCI_MAX_MSI_VECTORS, true /* msi64bit */, | ||
112 | + false /*msi_per_vector_mask*/, &err); | ||
113 | + if (ret != 0) { | ||
114 | + error_propagate(errp, err); | ||
115 | + return; | ||
116 | + } | ||
117 | + | ||
118 | + @autoreleasepool { | ||
119 | + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; | ||
120 | + desc.raiseInterrupt = ^(uint32_t vector) { | ||
121 | + apple_gfx_pci_interrupt(dev, s, vector); | ||
122 | + }; | ||
123 | + | ||
124 | + apple_gfx_common_realize(&s->common, desc); | ||
125 | + [desc release]; | ||
126 | + desc = nil; | ||
127 | + } | ||
128 | +} | ||
129 | + | ||
130 | +static void apple_gfx_pci_reset(Object *obj, ResetType type) | ||
131 | +{ | ||
132 | + AppleGFXPCIState *s = APPLE_GFX_PCI(obj); | ||
133 | + [s->common.pgdev reset]; | ||
134 | +} | ||
135 | + | ||
136 | +static void apple_gfx_pci_class_init(ObjectClass *klass, void *data) | ||
137 | +{ | ||
138 | + DeviceClass *dc = DEVICE_CLASS(klass); | ||
139 | + PCIDeviceClass *pci = PCI_DEVICE_CLASS(klass); | ||
140 | + ResettableClass *rc = RESETTABLE_CLASS(klass); | ||
141 | + | ||
142 | + assert(rc->phases.hold == NULL); | ||
143 | + rc->phases.hold = apple_gfx_pci_reset; | ||
144 | + dc->desc = "macOS Paravirtualized Graphics PCI Display Controller"; | ||
145 | + dc->hotpluggable = false; | ||
146 | + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); | ||
147 | + | ||
148 | + pci->vendor_id = PG_PCI_VENDOR_ID; | ||
149 | + pci->device_id = PG_PCI_DEVICE_ID; | ||
150 | + pci->class_id = PCI_CLASS_DISPLAY_OTHER; | ||
151 | + pci->realize = apple_gfx_pci_realize; | ||
152 | + | ||
153 | + // TODO: Property for setting mode list | ||
154 | +} | ||
155 | + | ||
156 | +static TypeInfo apple_gfx_pci_types[] = { | ||
157 | + { | ||
158 | + .name = TYPE_APPLE_GFX_PCI, | ||
159 | + .parent = TYPE_PCI_DEVICE, | ||
160 | + .instance_size = sizeof(AppleGFXPCIState), | ||
161 | + .class_init = apple_gfx_pci_class_init, | ||
162 | + .instance_init = apple_gfx_pci_init, | ||
163 | + .interfaces = (InterfaceInfo[]) { | ||
164 | + { INTERFACE_PCIE_DEVICE }, | ||
165 | + { }, | ||
166 | + }, | ||
167 | + } | ||
168 | +}; | ||
169 | +DEFINE_TYPES(apple_gfx_pci_types) | ||
170 | + | ||
171 | diff --git a/hw/display/meson.build b/hw/display/meson.build | ||
172 | index XXXXXXX..XXXXXXX 100644 | ||
173 | --- a/hw/display/meson.build | ||
174 | +++ b/hw/display/meson.build | ||
175 | @@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_ | ||
176 | |||
177 | system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) | ||
178 | system_ss.add(when: 'CONFIG_MAC_PVG_VMAPPLE', if_true: [files('apple-gfx-vmapple.m'), pvg, metal]) | ||
179 | +system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx-pci.m'), pvg, metal]) | ||
180 | |||
181 | if config_all_devices.has_key('CONFIG_VIRTIO_GPU') | ||
182 | virtio_gpu_ss = ss.source_set() | ||
183 | -- | ||
184 | 2.39.3 (Apple Git-145) | ||
185 | |||
186 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Various system frameworks on macOS and other Apple platforms | ||
2 | require a main runloop to be processing events on the process’s | ||
3 | main thread. The Cocoa UI’s requirement to run the process as a | ||
4 | Cocoa application automatically enables this runloop, but it | ||
5 | can be useful to have the runloop handling events even without | ||
6 | the Cocoa UI active. | ||
7 | 1 | ||
8 | This change adds a non-app runloop mode to the cocoa_main | ||
9 | function. This can be requested by other code, while the Cocoa UI | ||
10 | additionally enables app mode. This arrangement ensures there is | ||
11 | only one qemu_main function switcheroo, and the Cocoa UI’s app | ||
12 | mode requirement and other subsystems’ runloop requests don’t | ||
13 | conflict with each other. | ||
14 | |||
15 | The main runloop is required for the AppleGFX PV graphics device, | ||
16 | so the runloop request call has been added to its initialisation. | ||
17 | |||
18 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
19 | --- | ||
20 | hw/display/apple-gfx.m | 3 +++ | ||
21 | include/qemu-main.h | 2 ++ | ||
22 | ui/cocoa.m | 15 +++++++++++++-- | ||
23 | 3 files changed, 18 insertions(+), 2 deletions(-) | ||
24 | |||
25 | diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m | ||
26 | index XXXXXXX..XXXXXXX 100644 | ||
27 | --- a/hw/display/apple-gfx.m | ||
28 | +++ b/hw/display/apple-gfx.m | ||
29 | @@ -XXX,XX +XXX,XX @@ | ||
30 | |||
31 | #include "apple-gfx.h" | ||
32 | #include "trace.h" | ||
33 | +#include "qemu-main.h" | ||
34 | #include "qemu/main-loop.h" | ||
35 | #include "ui/console.h" | ||
36 | #include "monitor/monitor.h" | ||
37 | @@ -XXX,XX +XXX,XX @@ void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name) | ||
38 | error_report_err(local_err); | ||
39 | } | ||
40 | } | ||
41 | + | ||
42 | + cocoa_enable_runloop_on_main_thread(); | ||
43 | } | ||
44 | |||
45 | static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, | ||
46 | diff --git a/include/qemu-main.h b/include/qemu-main.h | ||
47 | index XXXXXXX..XXXXXXX 100644 | ||
48 | --- a/include/qemu-main.h | ||
49 | +++ b/include/qemu-main.h | ||
50 | @@ -XXX,XX +XXX,XX @@ | ||
51 | int qemu_default_main(void); | ||
52 | extern int (*qemu_main)(void); | ||
53 | |||
54 | +void cocoa_enable_runloop_on_main_thread(void); | ||
55 | + | ||
56 | #endif /* QEMU_MAIN_H */ | ||
57 | diff --git a/ui/cocoa.m b/ui/cocoa.m | ||
58 | index XXXXXXX..XXXXXXX 100644 | ||
59 | --- a/ui/cocoa.m | ||
60 | +++ b/ui/cocoa.m | ||
61 | @@ -XXX,XX +XXX,XX @@ static void cocoa_clipboard_request(QemuClipboardInfo *info, | ||
62 | exit(status); | ||
63 | } | ||
64 | |||
65 | +static bool run_as_cocoa_app = false; | ||
66 | static int cocoa_main(void) | ||
67 | { | ||
68 | QemuThread thread; | ||
69 | @@ -XXX,XX +XXX,XX @@ static int cocoa_main(void) | ||
70 | |||
71 | // Start the main event loop | ||
72 | COCOA_DEBUG("Main thread: entering OSX run loop\n"); | ||
73 | - [NSApp run]; | ||
74 | + if (run_as_cocoa_app) { | ||
75 | + [NSApp run]; | ||
76 | + } else { | ||
77 | + CFRunLoopRun(); | ||
78 | + } | ||
79 | COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n"); | ||
80 | |||
81 | abort(); | ||
82 | @@ -XXX,XX +XXX,XX @@ static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor) | ||
83 | }); | ||
84 | } | ||
85 | |||
86 | +void cocoa_enable_runloop_on_main_thread(void) | ||
87 | +{ | ||
88 | + qemu_main = cocoa_main; | ||
89 | +} | ||
90 | + | ||
91 | static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) | ||
92 | { | ||
93 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | ||
94 | |||
95 | COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); | ||
96 | |||
97 | - qemu_main = cocoa_main; | ||
98 | + run_as_cocoa_app = true; | ||
99 | + cocoa_enable_runloop_on_main_thread(); | ||
100 | |||
101 | // Pull this console process up to being a fully-fledged graphical | ||
102 | // app with a menubar and Dock icon | ||
103 | -- | ||
104 | 2.39.3 (Apple Git-145) | ||
105 | |||
106 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This change adds a property 'display_modes' on the graphics device | ||
2 | which permits specifying a list of display modes. (screen resolution | ||
3 | and refresh rate) | ||
4 | 1 | ||
5 | PCI variant of apple-gfx only for the moment. | ||
6 | |||
7 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
8 | --- | ||
9 | hw/display/apple-gfx-pci.m | 43 ++++++++++- | ||
10 | hw/display/apple-gfx.h | 17 ++++- | ||
11 | hw/display/apple-gfx.m | 151 ++++++++++++++++++++++++++++++++++--- | ||
12 | 3 files changed, 198 insertions(+), 13 deletions(-) | ||
13 | |||
14 | diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/hw/display/apple-gfx-pci.m | ||
17 | +++ b/hw/display/apple-gfx-pci.m | ||
18 | @@ -XXX,XX +XXX,XX @@ | ||
19 | #include "apple-gfx.h" | ||
20 | #include "hw/pci/pci_device.h" | ||
21 | #include "hw/pci/msi.h" | ||
22 | +#include "hw/qdev-properties.h" | ||
23 | #include "qapi/error.h" | ||
24 | #include "trace.h" | ||
25 | #import <ParavirtualizedGraphics/ParavirtualizedGraphics.h> | ||
26 | @@ -XXX,XX +XXX,XX @@ static void apple_gfx_pci_reset(Object *obj, ResetType type) | ||
27 | [s->common.pgdev reset]; | ||
28 | } | ||
29 | |||
30 | +static void apple_gfx_pci_get_display_modes(Object *obj, Visitor *v, | ||
31 | + const char *name, void *opaque, | ||
32 | + Error **errp) | ||
33 | +{ | ||
34 | + Property *prop = opaque; | ||
35 | + AppleGFXDisplayModeList *mode_list = object_field_prop_ptr(obj, prop); | ||
36 | + | ||
37 | + apple_gfx_get_display_modes(mode_list, v, name, errp); | ||
38 | +} | ||
39 | + | ||
40 | +static void apple_gfx_pci_set_display_modes(Object *obj, Visitor *v, | ||
41 | + const char *name, void *opaque, | ||
42 | + Error **errp) | ||
43 | +{ | ||
44 | + Property *prop = opaque; | ||
45 | + AppleGFXDisplayModeList *mode_list = object_field_prop_ptr(obj, prop); | ||
46 | + | ||
47 | + apple_gfx_set_display_modes(mode_list, v, name, errp); | ||
48 | +} | ||
49 | + | ||
50 | +const PropertyInfo apple_gfx_pci_prop_display_modes = { | ||
51 | + .name = "display_modes", | ||
52 | + .description = | ||
53 | + "Colon-separated list of display modes; " | ||
54 | + "<width>x<height>@<refresh-rate>; the first mode is considered " | ||
55 | + "'native'. Example: 3840x2160@60:2560x1440@60:1920x1080@60", | ||
56 | + .get = apple_gfx_pci_get_display_modes, | ||
57 | + .set = apple_gfx_pci_set_display_modes, | ||
58 | +}; | ||
59 | + | ||
60 | +#define DEFINE_PROP_DISPLAY_MODES(_name, _state, _field) \ | ||
61 | + DEFINE_PROP(_name, _state, _field, apple_gfx_pci_prop_display_modes, \ | ||
62 | + AppleGFXDisplayModeList) | ||
63 | + | ||
64 | +static Property apple_gfx_pci_properties[] = { | ||
65 | + DEFINE_PROP_DISPLAY_MODES("display-modes", AppleGFXPCIState, | ||
66 | + common.display_modes), | ||
67 | + DEFINE_PROP_END_OF_LIST(), | ||
68 | +}; | ||
69 | + | ||
70 | static void apple_gfx_pci_class_init(ObjectClass *klass, void *data) | ||
71 | { | ||
72 | DeviceClass *dc = DEVICE_CLASS(klass); | ||
73 | @@ -XXX,XX +XXX,XX @@ static void apple_gfx_pci_class_init(ObjectClass *klass, void *data) | ||
74 | pci->class_id = PCI_CLASS_DISPLAY_OTHER; | ||
75 | pci->realize = apple_gfx_pci_realize; | ||
76 | |||
77 | - // TODO: Property for setting mode list | ||
78 | + device_class_set_props(dc, apple_gfx_pci_properties); | ||
79 | } | ||
80 | |||
81 | static TypeInfo apple_gfx_pci_types[] = { | ||
82 | diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h | ||
83 | index XXXXXXX..XXXXXXX 100644 | ||
84 | --- a/hw/display/apple-gfx.h | ||
85 | +++ b/hw/display/apple-gfx.h | ||
86 | @@ -XXX,XX +XXX,XX @@ | ||
87 | #define TYPE_APPLE_GFX_PCI "apple-gfx-pci" | ||
88 | |||
89 | #include "qemu/typedefs.h" | ||
90 | +#include "qemu/osdep.h" | ||
91 | |||
92 | typedef struct AppleGFXState AppleGFXState; | ||
93 | |||
94 | +typedef struct AppleGFXDisplayMode { | ||
95 | + uint16_t width_px; | ||
96 | + uint16_t height_px; | ||
97 | + uint16_t refresh_rate_hz; | ||
98 | +} AppleGFXDisplayMode; | ||
99 | + | ||
100 | +typedef struct AppleGFXDisplayModeList { | ||
101 | + GArray *modes; | ||
102 | +} AppleGFXDisplayModeList; | ||
103 | + | ||
104 | void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name); | ||
105 | +void apple_gfx_get_display_modes(AppleGFXDisplayModeList *mode_list, Visitor *v, | ||
106 | + const char *name, Error **errp); | ||
107 | +void apple_gfx_set_display_modes(AppleGFXDisplayModeList *mode_list, Visitor *v, | ||
108 | + const char *name, Error **errp); | ||
109 | |||
110 | #ifdef __OBJC__ | ||
111 | |||
112 | -#include "qemu/osdep.h" | ||
113 | #include "exec/memory.h" | ||
114 | #include "ui/surface.h" | ||
115 | #include <dispatch/dispatch.h> | ||
116 | @@ -XXX,XX +XXX,XX @@ struct AppleGFXState { | ||
117 | bool new_frame; | ||
118 | bool cursor_show; | ||
119 | QEMUCursor *cursor; | ||
120 | + AppleGFXDisplayModeList display_modes; | ||
121 | |||
122 | dispatch_queue_t render_queue; | ||
123 | /* The following fields should only be accessed from render_queue: */ | ||
124 | diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m | ||
125 | index XXXXXXX..XXXXXXX 100644 | ||
126 | --- a/hw/display/apple-gfx.m | ||
127 | +++ b/hw/display/apple-gfx.m | ||
128 | @@ -XXX,XX +XXX,XX @@ | ||
129 | #include "trace.h" | ||
130 | #include "qemu-main.h" | ||
131 | #include "qemu/main-loop.h" | ||
132 | +#include "qemu/cutils.h" | ||
133 | +#include "qapi/visitor.h" | ||
134 | +#include "qapi/error.h" | ||
135 | #include "ui/console.h" | ||
136 | #include "monitor/monitor.h" | ||
137 | #include "qapi/error.h" | ||
138 | @@ -XXX,XX +XXX,XX @@ | ||
139 | #include <mach/mach_vm.h> | ||
140 | #import <ParavirtualizedGraphics/ParavirtualizedGraphics.h> | ||
141 | |||
142 | -static const PGDisplayCoord_t apple_gfx_modes[] = { | ||
143 | - { .x = 1440, .y = 1080 }, | ||
144 | - { .x = 1280, .y = 1024 }, | ||
145 | +static const AppleGFXDisplayMode apple_gfx_default_modes[] = { | ||
146 | + { 1920, 1080, 60 }, | ||
147 | + { 1440, 1080, 60 }, | ||
148 | + { 1280, 1024, 60 }, | ||
149 | }; | ||
150 | |||
151 | typedef struct PGTask_s { // Name matches forward declaration in PG header | ||
152 | @@ -XXX,XX +XXX,XX @@ static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height) | ||
153 | static void create_fb(AppleGFXState *s) | ||
154 | { | ||
155 | s->con = graphic_console_init(NULL, 0, &apple_gfx_fb_ops, s); | ||
156 | - set_mode(s, 1440, 1080); | ||
157 | |||
158 | s->cursor_show = true; | ||
159 | } | ||
160 | @@ -XXX,XX +XXX,XX @@ static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, | ||
161 | return disp_desc; | ||
162 | } | ||
163 | |||
164 | -static NSArray<PGDisplayMode*>* apple_gfx_prepare_display_mode_array(void) | ||
165 | +static NSArray<PGDisplayMode*>* apple_gfx_create_display_mode_array( | ||
166 | + const AppleGFXDisplayMode display_modes[], int display_mode_count) | ||
167 | { | ||
168 | - PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; | ||
169 | + PGDisplayMode **modes = alloca(sizeof(modes[0]) * display_mode_count); | ||
170 | NSArray<PGDisplayMode*>* mode_array = nil; | ||
171 | int i; | ||
172 | |||
173 | - for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { | ||
174 | + for (i = 0; i < display_mode_count; i++) { | ||
175 | + const AppleGFXDisplayMode *mode = &display_modes[i]; | ||
176 | + PGDisplayCoord_t mode_size = { mode->width_px, mode->height_px }; | ||
177 | modes[i] = | ||
178 | - [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.]; | ||
179 | + [[PGDisplayMode alloc] initWithSizeInPixels:mode_size | ||
180 | + refreshRateInHz:mode->refresh_rate_hz]; | ||
181 | } | ||
182 | |||
183 | - mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)]; | ||
184 | + mode_array = [NSArray arrayWithObjects:modes count:display_mode_count]; | ||
185 | |||
186 | - for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { | ||
187 | + for (i = 0; i < display_mode_count; i++) { | ||
188 | [modes[i] release]; | ||
189 | modes[i] = nil; | ||
190 | } | ||
191 | @@ -XXX,XX +XXX,XX @@ static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, | ||
192 | void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc) | ||
193 | { | ||
194 | PGDisplayDescriptor *disp_desc = nil; | ||
195 | + const AppleGFXDisplayMode *display_modes = apple_gfx_default_modes; | ||
196 | + int num_display_modes = ARRAY_SIZE(apple_gfx_default_modes); | ||
197 | |||
198 | QTAILQ_INIT(&s->tasks); | ||
199 | s->render_queue = dispatch_queue_create("apple-gfx.render", | ||
200 | @@ -XXX,XX +XXX,XX @@ void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc) | ||
201 | s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc | ||
202 | port:0 serialNum:1234]; | ||
203 | [disp_desc release]; | ||
204 | - s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); | ||
205 | + | ||
206 | + if (s->display_modes.modes != NULL && s->display_modes.modes->len > 0) { | ||
207 | + display_modes = | ||
208 | + &g_array_index(s->display_modes.modes, AppleGFXDisplayMode, 0); | ||
209 | + num_display_modes = s->display_modes.modes->len; | ||
210 | + } | ||
211 | + s->pgdisp.modeList = | ||
212 | + apple_gfx_create_display_mode_array(display_modes, num_display_modes); | ||
213 | |||
214 | create_fb(s); | ||
215 | } | ||
216 | + | ||
217 | +void apple_gfx_get_display_modes(AppleGFXDisplayModeList *mode_list, Visitor *v, | ||
218 | + const char *name, Error **errp) | ||
219 | +{ | ||
220 | + GArray *modes = mode_list->modes; | ||
221 | + /* 3 uint16s (max 5 digits) and 3 separator characters per mode + nul. */ | ||
222 | + size_t buffer_size = (5 + 1) * 3 * modes->len + 1; | ||
223 | + | ||
224 | + char *buffer = alloca(buffer_size); | ||
225 | + char *pos = buffer; | ||
226 | + | ||
227 | + unsigned used = 0; | ||
228 | + buffer[0] = '\0'; | ||
229 | + for (guint i = 0; i < modes->len; ++i) | ||
230 | + { | ||
231 | + AppleGFXDisplayMode *mode = | ||
232 | + &g_array_index(modes, AppleGFXDisplayMode, i); | ||
233 | + int rc = snprintf(pos, buffer_size - used, | ||
234 | + "%s%"PRIu16"x%"PRIu16"@%"PRIu16, | ||
235 | + i > 0 ? ":" : "", | ||
236 | + mode->width_px, mode->height_px, | ||
237 | + mode->refresh_rate_hz); | ||
238 | + used += rc; | ||
239 | + pos += rc; | ||
240 | + assert(used < buffer_size); | ||
241 | + } | ||
242 | + | ||
243 | + pos = buffer; | ||
244 | + visit_type_str(v, name, &pos, errp); | ||
245 | +} | ||
246 | + | ||
247 | +void apple_gfx_set_display_modes(AppleGFXDisplayModeList *mode_list, Visitor *v, | ||
248 | + const char *name, Error **errp) | ||
249 | +{ | ||
250 | + Error *local_err = NULL; | ||
251 | + const char *endptr; | ||
252 | + char *str; | ||
253 | + int ret; | ||
254 | + unsigned int val; | ||
255 | + uint32_t num_modes; | ||
256 | + GArray *modes; | ||
257 | + uint32_t mode_idx; | ||
258 | + | ||
259 | + visit_type_str(v, name, &str, &local_err); | ||
260 | + if (local_err) { | ||
261 | + error_propagate(errp, local_err); | ||
262 | + return; | ||
263 | + } | ||
264 | + | ||
265 | + // Count colons to estimate modes. No leading/trailing colons so start at 1. | ||
266 | + num_modes = 1; | ||
267 | + for (size_t i = 0; str[i] != '\0'; ++i) | ||
268 | + { | ||
269 | + if (str[i] == ':') { | ||
270 | + ++num_modes; | ||
271 | + } | ||
272 | + } | ||
273 | + | ||
274 | + modes = g_array_sized_new(false, true, sizeof(AppleGFXDisplayMode), num_modes); | ||
275 | + | ||
276 | + endptr = str; | ||
277 | + for (mode_idx = 0; mode_idx < num_modes; ++mode_idx) | ||
278 | + { | ||
279 | + AppleGFXDisplayMode mode = {}; | ||
280 | + if (mode_idx > 0) | ||
281 | + { | ||
282 | + if (*endptr != ':') { | ||
283 | + goto separator_error; | ||
284 | + } | ||
285 | + ++endptr; | ||
286 | + } | ||
287 | + | ||
288 | + ret = qemu_strtoui(endptr, &endptr, 10, &val); | ||
289 | + if (ret || val > UINT16_MAX || val == 0) { | ||
290 | + error_setg(errp, "width of '%s' must be a decimal integer number " | ||
291 | + "of pixels in the range 1..65535", name); | ||
292 | + goto out; | ||
293 | + } | ||
294 | + mode.width_px = val; | ||
295 | + if (*endptr != 'x') { | ||
296 | + goto separator_error; | ||
297 | + } | ||
298 | + | ||
299 | + ret = qemu_strtoui(endptr + 1, &endptr, 10, &val); | ||
300 | + if (ret || val > UINT16_MAX || val == 0) { | ||
301 | + error_setg(errp, "height of '%s' must be a decimal integer number " | ||
302 | + "of pixels in the range 1..65535", name); | ||
303 | + goto out; | ||
304 | + } | ||
305 | + mode.height_px = val; | ||
306 | + if (*endptr != '@') { | ||
307 | + goto separator_error; | ||
308 | + } | ||
309 | + | ||
310 | + ret = qemu_strtoui(endptr + 1, &endptr, 10, &val); | ||
311 | + if (ret) { | ||
312 | + error_setg(errp, "refresh rate of '%s'" | ||
313 | + " must be a non-negative decimal integer (Hertz)", name); | ||
314 | + } | ||
315 | + mode.refresh_rate_hz = val; | ||
316 | + g_array_append_val(modes, mode); | ||
317 | + } | ||
318 | + | ||
319 | + mode_list->modes = modes; | ||
320 | + goto out; | ||
321 | + | ||
322 | +separator_error: | ||
323 | + error_setg(errp, "Each display mode takes the format " | ||
324 | + "'<width>x<height>@<rate>', modes are separated by colons. (:)"); | ||
325 | +out: | ||
326 | + g_free(str); | ||
327 | + return; | ||
328 | +} | ||
329 | -- | ||
330 | 2.39.3 (Apple Git-145) | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | I'm happy to take responsibility for the macOS PV graphics code. As | ||
2 | HVF patches don't seem to get much attention at the moment, I'm also | ||
3 | adding myself as designated reviewer for HVF and x86 HVF to try and | ||
4 | improve that. | ||
5 | 1 | ||
6 | I anticipate that the resulting workload should be covered by the | ||
7 | funding I'm receiving for improving Qemu in combination with macOS. As | ||
8 | of right now this runs out at the end of 2024; I expect the workload on | ||
9 | apple-gfx should be relatively minor and manageable in my spare time | ||
10 | beyond that. I may have to remove myself from more general HVF duties | ||
11 | once the contract runs out if it's more than I can manage. | ||
12 | |||
13 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
14 | --- | ||
15 | MAINTAINERS | 7 +++++++ | ||
16 | 1 file changed, 7 insertions(+) | ||
17 | |||
18 | diff --git a/MAINTAINERS b/MAINTAINERS | ||
19 | index XXXXXXX..XXXXXXX 100644 | ||
20 | --- a/MAINTAINERS | ||
21 | +++ b/MAINTAINERS | ||
22 | @@ -XXX,XX +XXX,XX @@ F: target/arm/hvf/ | ||
23 | X86 HVF CPUs | ||
24 | M: Cameron Esfahani <dirty@apple.com> | ||
25 | M: Roman Bolshakov <rbolshakov@ddn.com> | ||
26 | +R: Phil Dennis-Jordan <phil@philjordan.eu> | ||
27 | W: https://wiki.qemu.org/Features/HVF | ||
28 | S: Maintained | ||
29 | F: target/i386/hvf/ | ||
30 | @@ -XXX,XX +XXX,XX @@ F: target/i386/hvf/ | ||
31 | HVF | ||
32 | M: Cameron Esfahani <dirty@apple.com> | ||
33 | M: Roman Bolshakov <rbolshakov@ddn.com> | ||
34 | +R: Phil Dennis-Jordan <phil@philjordan.eu> | ||
35 | W: https://wiki.qemu.org/Features/HVF | ||
36 | S: Maintained | ||
37 | F: accel/hvf/ | ||
38 | @@ -XXX,XX +XXX,XX @@ F: hw/display/edid* | ||
39 | F: include/hw/display/edid.h | ||
40 | F: qemu-edid.c | ||
41 | |||
42 | +macOS PV Graphics (apple-gfx) | ||
43 | +M: Phil Dennis-Jordan <phil@philjordan.eu> | ||
44 | +S: Maintained | ||
45 | +F: hw/display/apple-gfx* | ||
46 | + | ||
47 | PIIX4 South Bridge (i82371AB) | ||
48 | M: Hervé Poussineau <hpoussin@reactos.org> | ||
49 | M: Philippe Mathieu-Daudé <philmd@linaro.org> | ||
50 | -- | ||
51 | 2.39.3 (Apple Git-145) | ||
52 | |||
53 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alexander Graf <graf@amazon.com> | ||
2 | 1 | ||
3 | We will introduce a number of devices that are specific to the vmapple | ||
4 | target machine. To keep them all tidily together, let's put them into | ||
5 | a single target directory. | ||
6 | |||
7 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
8 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
9 | --- | ||
10 | MAINTAINERS | 7 +++++++ | ||
11 | hw/Kconfig | 1 + | ||
12 | hw/meson.build | 1 + | ||
13 | hw/vmapple/Kconfig | 1 + | ||
14 | hw/vmapple/meson.build | 0 | ||
15 | hw/vmapple/trace-events | 2 ++ | ||
16 | hw/vmapple/trace.h | 1 + | ||
17 | meson.build | 1 + | ||
18 | 8 files changed, 14 insertions(+) | ||
19 | create mode 100644 hw/vmapple/Kconfig | ||
20 | create mode 100644 hw/vmapple/meson.build | ||
21 | create mode 100644 hw/vmapple/trace-events | ||
22 | create mode 100644 hw/vmapple/trace.h | ||
23 | |||
24 | diff --git a/MAINTAINERS b/MAINTAINERS | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/MAINTAINERS | ||
27 | +++ b/MAINTAINERS | ||
28 | @@ -XXX,XX +XXX,XX @@ F: hw/hyperv/hv-balloon*.h | ||
29 | F: include/hw/hyperv/dynmem-proto.h | ||
30 | F: include/hw/hyperv/hv-balloon.h | ||
31 | |||
32 | +VMapple | ||
33 | +M: Alexander Graf <agraf@csgraf.de> | ||
34 | +R: Phil Dennis-Jordan <phil@philjordan.eu> | ||
35 | +S: Maintained | ||
36 | +F: hw/vmapple/* | ||
37 | +F: include/hw/vmapple/* | ||
38 | + | ||
39 | Subsystems | ||
40 | ---------- | ||
41 | Overall Audio backends | ||
42 | diff --git a/hw/Kconfig b/hw/Kconfig | ||
43 | index XXXXXXX..XXXXXXX 100644 | ||
44 | --- a/hw/Kconfig | ||
45 | +++ b/hw/Kconfig | ||
46 | @@ -XXX,XX +XXX,XX @@ source ufs/Kconfig | ||
47 | source usb/Kconfig | ||
48 | source virtio/Kconfig | ||
49 | source vfio/Kconfig | ||
50 | +source vmapple/Kconfig | ||
51 | source xen/Kconfig | ||
52 | source watchdog/Kconfig | ||
53 | |||
54 | diff --git a/hw/meson.build b/hw/meson.build | ||
55 | index XXXXXXX..XXXXXXX 100644 | ||
56 | --- a/hw/meson.build | ||
57 | +++ b/hw/meson.build | ||
58 | @@ -XXX,XX +XXX,XX @@ subdir('ufs') | ||
59 | subdir('usb') | ||
60 | subdir('vfio') | ||
61 | subdir('virtio') | ||
62 | +subdir('vmapple') | ||
63 | subdir('watchdog') | ||
64 | subdir('xen') | ||
65 | subdir('xenpv') | ||
66 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | ||
67 | new file mode 100644 | ||
68 | index XXXXXXX..XXXXXXX | ||
69 | --- /dev/null | ||
70 | +++ b/hw/vmapple/Kconfig | ||
71 | @@ -0,0 +1 @@ | ||
72 | + | ||
73 | diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build | ||
74 | new file mode 100644 | ||
75 | index XXXXXXX..XXXXXXX | ||
76 | diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events | ||
77 | new file mode 100644 | ||
78 | index XXXXXXX..XXXXXXX | ||
79 | --- /dev/null | ||
80 | +++ b/hw/vmapple/trace-events | ||
81 | @@ -XXX,XX +XXX,XX @@ | ||
82 | +# See docs/devel/tracing.rst for syntax documentation. | ||
83 | + | ||
84 | diff --git a/hw/vmapple/trace.h b/hw/vmapple/trace.h | ||
85 | new file mode 100644 | ||
86 | index XXXXXXX..XXXXXXX | ||
87 | --- /dev/null | ||
88 | +++ b/hw/vmapple/trace.h | ||
89 | @@ -0,0 +1 @@ | ||
90 | +#include "trace/trace-hw_vmapple.h" | ||
91 | diff --git a/meson.build b/meson.build | ||
92 | index XXXXXXX..XXXXXXX 100644 | ||
93 | --- a/meson.build | ||
94 | +++ b/meson.build | ||
95 | @@ -XXX,XX +XXX,XX @@ if have_system | ||
96 | 'hw/usb', | ||
97 | 'hw/vfio', | ||
98 | 'hw/virtio', | ||
99 | + 'hw/vmapple', | ||
100 | 'hw/watchdog', | ||
101 | 'hw/xen', | ||
102 | 'hw/gpio', | ||
103 | -- | ||
104 | 2.39.3 (Apple Git-145) | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alexander Graf <graf@amazon.com> | ||
2 | 1 | ||
3 | In addition to the ISA and PCI variants of pvpanic, let's add an MMIO | ||
4 | platform device that we can use in embedded arm environments. | ||
5 | |||
6 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
7 | Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> | ||
8 | Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> | ||
9 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
10 | |||
11 | --- | ||
12 | v3: | ||
13 | * Rebased on upstream, updated a header path | ||
14 | |||
15 | hw/misc/Kconfig | 4 +++ | ||
16 | hw/misc/meson.build | 1 + | ||
17 | hw/misc/pvpanic-mmio.c | 61 +++++++++++++++++++++++++++++++++++++++ | ||
18 | include/hw/misc/pvpanic.h | 1 + | ||
19 | 4 files changed, 67 insertions(+) | ||
20 | create mode 100644 hw/misc/pvpanic-mmio.c | ||
21 | |||
22 | diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig | ||
23 | index XXXXXXX..XXXXXXX 100644 | ||
24 | --- a/hw/misc/Kconfig | ||
25 | +++ b/hw/misc/Kconfig | ||
26 | @@ -XXX,XX +XXX,XX @@ config PVPANIC_ISA | ||
27 | depends on ISA_BUS | ||
28 | select PVPANIC_COMMON | ||
29 | |||
30 | +config PVPANIC_MMIO | ||
31 | + bool | ||
32 | + select PVPANIC_COMMON | ||
33 | + | ||
34 | config AUX | ||
35 | bool | ||
36 | select I2C | ||
37 | diff --git a/hw/misc/meson.build b/hw/misc/meson.build | ||
38 | index XXXXXXX..XXXXXXX 100644 | ||
39 | --- a/hw/misc/meson.build | ||
40 | +++ b/hw/misc/meson.build | ||
41 | @@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c')) | ||
42 | |||
43 | system_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c')) | ||
44 | system_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c')) | ||
45 | +system_ss.add(when: 'CONFIG_PVPANIC_MMIO', if_true: files('pvpanic-mmio.c')) | ||
46 | system_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c')) | ||
47 | system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( | ||
48 | 'aspeed_hace.c', | ||
49 | diff --git a/hw/misc/pvpanic-mmio.c b/hw/misc/pvpanic-mmio.c | ||
50 | new file mode 100644 | ||
51 | index XXXXXXX..XXXXXXX | ||
52 | --- /dev/null | ||
53 | +++ b/hw/misc/pvpanic-mmio.c | ||
54 | @@ -XXX,XX +XXX,XX @@ | ||
55 | +/* | ||
56 | + * QEMU simulated pvpanic device (MMIO frontend) | ||
57 | + * | ||
58 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
59 | + * | ||
60 | + * SPDX-License-Identifier: GPL-2.0-or-later | ||
61 | + */ | ||
62 | + | ||
63 | +#include "qemu/osdep.h" | ||
64 | + | ||
65 | +#include "hw/qdev-properties.h" | ||
66 | +#include "hw/misc/pvpanic.h" | ||
67 | +#include "hw/sysbus.h" | ||
68 | +#include "standard-headers/misc/pvpanic.h" | ||
69 | + | ||
70 | +OBJECT_DECLARE_SIMPLE_TYPE(PVPanicMMIOState, PVPANIC_MMIO_DEVICE) | ||
71 | + | ||
72 | +#define PVPANIC_MMIO_SIZE 0x2 | ||
73 | + | ||
74 | +struct PVPanicMMIOState { | ||
75 | + SysBusDevice parent_obj; | ||
76 | + | ||
77 | + PVPanicState pvpanic; | ||
78 | +}; | ||
79 | + | ||
80 | +static void pvpanic_mmio_initfn(Object *obj) | ||
81 | +{ | ||
82 | + PVPanicMMIOState *s = PVPANIC_MMIO_DEVICE(obj); | ||
83 | + | ||
84 | + pvpanic_setup_io(&s->pvpanic, DEVICE(s), PVPANIC_MMIO_SIZE); | ||
85 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->pvpanic.mr); | ||
86 | +} | ||
87 | + | ||
88 | +static Property pvpanic_mmio_properties[] = { | ||
89 | + DEFINE_PROP_UINT8("events", PVPanicMMIOState, pvpanic.events, | ||
90 | + PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), | ||
91 | + DEFINE_PROP_END_OF_LIST(), | ||
92 | +}; | ||
93 | + | ||
94 | +static void pvpanic_mmio_class_init(ObjectClass *klass, void *data) | ||
95 | +{ | ||
96 | + DeviceClass *dc = DEVICE_CLASS(klass); | ||
97 | + | ||
98 | + device_class_set_props(dc, pvpanic_mmio_properties); | ||
99 | + set_bit(DEVICE_CATEGORY_MISC, dc->categories); | ||
100 | +} | ||
101 | + | ||
102 | +static const TypeInfo pvpanic_mmio_info = { | ||
103 | + .name = TYPE_PVPANIC_MMIO_DEVICE, | ||
104 | + .parent = TYPE_SYS_BUS_DEVICE, | ||
105 | + .instance_size = sizeof(PVPanicMMIOState), | ||
106 | + .instance_init = pvpanic_mmio_initfn, | ||
107 | + .class_init = pvpanic_mmio_class_init, | ||
108 | +}; | ||
109 | + | ||
110 | +static void pvpanic_register_types(void) | ||
111 | +{ | ||
112 | + type_register_static(&pvpanic_mmio_info); | ||
113 | +} | ||
114 | + | ||
115 | +type_init(pvpanic_register_types) | ||
116 | diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h | ||
117 | index XXXXXXX..XXXXXXX 100644 | ||
118 | --- a/include/hw/misc/pvpanic.h | ||
119 | +++ b/include/hw/misc/pvpanic.h | ||
120 | @@ -XXX,XX +XXX,XX @@ | ||
121 | |||
122 | #define TYPE_PVPANIC_ISA_DEVICE "pvpanic" | ||
123 | #define TYPE_PVPANIC_PCI_DEVICE "pvpanic-pci" | ||
124 | +#define TYPE_PVPANIC_MMIO_DEVICE "pvpanic-mmio" | ||
125 | |||
126 | #define PVPANIC_IOPORT_PROP "ioport" | ||
127 | |||
128 | -- | ||
129 | 2.39.3 (Apple Git-145) | ||
130 | |||
131 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alexander Graf <graf@amazon.com> | ||
2 | 1 | ||
3 | MacOS unconditionally disables interrupts of the physical timer on boot | ||
4 | and then continues to use the virtual one. We don't really want to support | ||
5 | a full physical timer emulation, so let's just ignore those writes. | ||
6 | |||
7 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
8 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
9 | --- | ||
10 | target/arm/hvf/hvf.c | 9 +++++++++ | ||
11 | 1 file changed, 9 insertions(+) | ||
12 | |||
13 | diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/target/arm/hvf/hvf.c | ||
16 | +++ b/target/arm/hvf/hvf.c | ||
17 | @@ -XXX,XX +XXX,XX @@ | ||
18 | |||
19 | #include "qemu/osdep.h" | ||
20 | #include "qemu/error-report.h" | ||
21 | +#include "qemu/log.h" | ||
22 | |||
23 | #include "sysemu/runstate.h" | ||
24 | #include "sysemu/hvf.h" | ||
25 | @@ -XXX,XX +XXX,XX @@ void hvf_arm_init_debug(void) | ||
26 | #define SYSREG_OSLSR_EL1 SYSREG(2, 0, 1, 1, 4) | ||
27 | #define SYSREG_OSDLR_EL1 SYSREG(2, 0, 1, 3, 4) | ||
28 | #define SYSREG_CNTPCT_EL0 SYSREG(3, 3, 14, 0, 1) | ||
29 | +#define SYSREG_CNTP_CTL_EL0 SYSREG(3, 3, 14, 2, 1) | ||
30 | #define SYSREG_PMCR_EL0 SYSREG(3, 3, 9, 12, 0) | ||
31 | #define SYSREG_PMUSERENR_EL0 SYSREG(3, 3, 9, 14, 0) | ||
32 | #define SYSREG_PMCNTENSET_EL0 SYSREG(3, 3, 9, 12, 1) | ||
33 | @@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) | ||
34 | case SYSREG_OSLAR_EL1: | ||
35 | env->cp15.oslsr_el1 = val & 1; | ||
36 | return 0; | ||
37 | + case SYSREG_CNTP_CTL_EL0: | ||
38 | + /* | ||
39 | + * Guests should not rely on the physical counter, but macOS emits | ||
40 | + * disable writes to it. Let it do so, but ignore the requests. | ||
41 | + */ | ||
42 | + qemu_log_mask(LOG_UNIMP, "Unsupported write to CNTP_CTL_EL0\n"); | ||
43 | + return 0; | ||
44 | case SYSREG_OSDLR_EL1: | ||
45 | /* Dummy register */ | ||
46 | return 0; | ||
47 | -- | ||
48 | 2.39.3 (Apple Git-145) | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alexander Graf <graf@amazon.com> | ||
2 | 1 | ||
3 | Some boards such as vmapple don't do real legacy PCI IRQ swizzling. | ||
4 | Instead, they just keep allocating more board IRQ lines for each new | ||
5 | legacy IRQ. Let's support that mode by giving instantiators a new | ||
6 | "nr_irqs" property they can use to support more than 4 legacy IRQ lines. | ||
7 | In this mode, GPEX will export more IRQ lines, one for each device. | ||
8 | |||
9 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
10 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
11 | --- | ||
12 | hw/arm/sbsa-ref.c | 2 +- | ||
13 | hw/arm/virt.c | 2 +- | ||
14 | hw/i386/microvm.c | 2 +- | ||
15 | hw/loongarch/virt.c | 2 +- | ||
16 | hw/mips/loongson3_virt.c | 2 +- | ||
17 | hw/openrisc/virt.c | 12 ++++++------ | ||
18 | hw/pci-host/gpex.c | 36 +++++++++++++++++++++++++++++++----- | ||
19 | hw/riscv/virt.c | 12 ++++++------ | ||
20 | hw/xtensa/virt.c | 2 +- | ||
21 | include/hw/pci-host/gpex.h | 7 +++---- | ||
22 | 10 files changed, 52 insertions(+), 27 deletions(-) | ||
23 | |||
24 | diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/hw/arm/sbsa-ref.c | ||
27 | +++ b/hw/arm/sbsa-ref.c | ||
28 | @@ -XXX,XX +XXX,XX @@ static void create_pcie(SBSAMachineState *sms) | ||
29 | /* Map IO port space */ | ||
30 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); | ||
31 | |||
32 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
33 | + for (i = 0; i < PCI_NUM_PINS; i++) { | ||
34 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, | ||
35 | qdev_get_gpio_in(sms->gic, irq + i)); | ||
36 | gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); | ||
37 | diff --git a/hw/arm/virt.c b/hw/arm/virt.c | ||
38 | index XXXXXXX..XXXXXXX 100644 | ||
39 | --- a/hw/arm/virt.c | ||
40 | +++ b/hw/arm/virt.c | ||
41 | @@ -XXX,XX +XXX,XX @@ static void create_pcie(VirtMachineState *vms) | ||
42 | /* Map IO port space */ | ||
43 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); | ||
44 | |||
45 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
46 | + for (i = 0; i < PCI_NUM_PINS; i++) { | ||
47 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, | ||
48 | qdev_get_gpio_in(vms->gic, irq + i)); | ||
49 | gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); | ||
50 | diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c | ||
51 | index XXXXXXX..XXXXXXX 100644 | ||
52 | --- a/hw/i386/microvm.c | ||
53 | +++ b/hw/i386/microvm.c | ||
54 | @@ -XXX,XX +XXX,XX @@ static void create_gpex(MicrovmMachineState *mms) | ||
55 | mms->gpex.mmio64.base, mmio64_alias); | ||
56 | } | ||
57 | |||
58 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
59 | + for (i = 0; i < PCI_NUM_PINS; i++) { | ||
60 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, | ||
61 | x86ms->gsi[mms->gpex.irq + i]); | ||
62 | } | ||
63 | diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c | ||
64 | index XXXXXXX..XXXXXXX 100644 | ||
65 | --- a/hw/loongarch/virt.c | ||
66 | +++ b/hw/loongarch/virt.c | ||
67 | @@ -XXX,XX +XXX,XX @@ static void virt_devices_init(DeviceState *pch_pic, | ||
68 | memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE, | ||
69 | pio_alias); | ||
70 | |||
71 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
72 | + for (i = 0; i < PCI_NUM_PINS; i++) { | ||
73 | sysbus_connect_irq(d, i, | ||
74 | qdev_get_gpio_in(pch_pic, 16 + i)); | ||
75 | gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i); | ||
76 | diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c | ||
77 | index XXXXXXX..XXXXXXX 100644 | ||
78 | --- a/hw/mips/loongson3_virt.c | ||
79 | +++ b/hw/mips/loongson3_virt.c | ||
80 | @@ -XXX,XX +XXX,XX @@ static inline void loongson3_virt_devices_init(MachineState *machine, | ||
81 | virt_memmap[VIRT_PCIE_PIO].base, s->pio_alias); | ||
82 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, virt_memmap[VIRT_PCIE_PIO].base); | ||
83 | |||
84 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
85 | + for (i = 0; i < PCI_NUM_PINS; i++) { | ||
86 | irq = qdev_get_gpio_in(pic, PCIE_IRQ_BASE + i); | ||
87 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); | ||
88 | gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ_BASE + i); | ||
89 | diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c | ||
90 | index XXXXXXX..XXXXXXX 100644 | ||
91 | --- a/hw/openrisc/virt.c | ||
92 | +++ b/hw/openrisc/virt.c | ||
93 | @@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, | ||
94 | { | ||
95 | int pin, dev; | ||
96 | uint32_t irq_map_stride = 0; | ||
97 | - uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * 6] = {}; | ||
98 | + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 6] = {}; | ||
99 | uint32_t *irq_map = full_irq_map; | ||
100 | |||
101 | /* | ||
102 | @@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, | ||
103 | * possible slot) seeing the interrupt-map-mask will allow the table | ||
104 | * to wrap to any number of devices. | ||
105 | */ | ||
106 | - for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { | ||
107 | + for (dev = 0; dev < PCI_NUM_PINS; dev++) { | ||
108 | int devfn = dev << 3; | ||
109 | |||
110 | - for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { | ||
111 | - int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); | ||
112 | + for (pin = 0; pin < PCI_NUM_PINS; pin++) { | ||
113 | + int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); | ||
114 | int i = 0; | ||
115 | |||
116 | /* Fill PCI address cells */ | ||
117 | @@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, | ||
118 | } | ||
119 | |||
120 | qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, | ||
121 | - GPEX_NUM_IRQS * GPEX_NUM_IRQS * | ||
122 | + PCI_NUM_PINS * PCI_NUM_PINS * | ||
123 | irq_map_stride * sizeof(uint32_t)); | ||
124 | |||
125 | qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", | ||
126 | @@ -XXX,XX +XXX,XX @@ static void openrisc_virt_pcie_init(OR1KVirtState *state, | ||
127 | memory_region_add_subregion(get_system_memory(), pio_base, alias); | ||
128 | |||
129 | /* Connect IRQ lines. */ | ||
130 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
131 | + for (i = 0; i < PCI_NUM_PINS; i++) { | ||
132 | pcie_irq = get_per_cpu_irq(cpus, num_cpus, irq_base + i); | ||
133 | |||
134 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pcie_irq); | ||
135 | diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c | ||
136 | index XXXXXXX..XXXXXXX 100644 | ||
137 | --- a/hw/pci-host/gpex.c | ||
138 | +++ b/hw/pci-host/gpex.c | ||
139 | @@ -XXX,XX +XXX,XX @@ | ||
140 | #include "qemu/osdep.h" | ||
141 | #include "qapi/error.h" | ||
142 | #include "hw/irq.h" | ||
143 | +#include "hw/pci/pci_bus.h" | ||
144 | #include "hw/pci-host/gpex.h" | ||
145 | #include "hw/qdev-properties.h" | ||
146 | #include "migration/vmstate.h" | ||
147 | @@ -XXX,XX +XXX,XX @@ static void gpex_set_irq(void *opaque, int irq_num, int level) | ||
148 | |||
149 | int gpex_set_irq_num(GPEXHost *s, int index, int gsi) | ||
150 | { | ||
151 | - if (index >= GPEX_NUM_IRQS) { | ||
152 | + if (index >= s->nr_irqs) { | ||
153 | return -EINVAL; | ||
154 | } | ||
155 | |||
156 | @@ -XXX,XX +XXX,XX @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin) | ||
157 | return route; | ||
158 | } | ||
159 | |||
160 | +static int gpex_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin) | ||
161 | +{ | ||
162 | + PCIBus *bus = pci_device_root_bus(pci_dev); | ||
163 | + | ||
164 | + return (PCI_SLOT(pci_dev->devfn) + pin) % bus->nirq; | ||
165 | +} | ||
166 | + | ||
167 | static void gpex_host_realize(DeviceState *dev, Error **errp) | ||
168 | { | ||
169 | PCIHostState *pci = PCI_HOST_BRIDGE(dev); | ||
170 | GPEXHost *s = GPEX_HOST(dev); | ||
171 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | ||
172 | PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev); | ||
173 | + pci_map_irq_fn map_irq_fn = pci_swizzle_map_irq_fn; | ||
174 | int i; | ||
175 | |||
176 | + s->irq = g_malloc0(s->nr_irqs * sizeof(*s->irq)); | ||
177 | + s->irq_num = g_malloc0(s->nr_irqs * sizeof(*s->irq_num)); | ||
178 | + | ||
179 | + if (s->nr_irqs != PCI_NUM_PINS) { | ||
180 | + map_irq_fn = gpex_swizzle_map_irq_fn; | ||
181 | + } | ||
182 | + | ||
183 | pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX); | ||
184 | sysbus_init_mmio(sbd, &pex->mmio); | ||
185 | |||
186 | @@ -XXX,XX +XXX,XX @@ static void gpex_host_realize(DeviceState *dev, Error **errp) | ||
187 | sysbus_init_mmio(sbd, &s->io_ioport); | ||
188 | } | ||
189 | |||
190 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
191 | + for (i = 0; i < s->nr_irqs; i++) { | ||
192 | sysbus_init_irq(sbd, &s->irq[i]); | ||
193 | s->irq_num[i] = -1; | ||
194 | } | ||
195 | |||
196 | - pci->bus = pci_register_root_bus(dev, "pcie.0", gpex_set_irq, | ||
197 | - pci_swizzle_map_irq_fn, s, &s->io_mmio, | ||
198 | - &s->io_ioport, 0, 4, TYPE_PCIE_BUS); | ||
199 | + pci->bus = pci_register_root_bus(dev, "pcie.0", gpex_set_irq, map_irq_fn, | ||
200 | + s, &s->io_mmio, &s->io_ioport, 0, | ||
201 | + s->nr_irqs, TYPE_PCIE_BUS); | ||
202 | |||
203 | pci_bus_set_route_irq_fn(pci->bus, gpex_route_intx_pin_to_irq); | ||
204 | qdev_realize(DEVICE(&s->gpex_root), BUS(pci->bus), &error_fatal); | ||
205 | } | ||
206 | |||
207 | +static void gpex_host_unrealize(DeviceState *dev) | ||
208 | +{ | ||
209 | + GPEXHost *s = GPEX_HOST(dev); | ||
210 | + | ||
211 | + g_free(s->irq); | ||
212 | + g_free(s->irq_num); | ||
213 | +} | ||
214 | + | ||
215 | static const char *gpex_host_root_bus_path(PCIHostState *host_bridge, | ||
216 | PCIBus *rootbus) | ||
217 | { | ||
218 | @@ -XXX,XX +XXX,XX @@ static Property gpex_host_properties[] = { | ||
219 | gpex_cfg.mmio64.base, 0), | ||
220 | DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MMIO_SIZE, GPEXHost, | ||
221 | gpex_cfg.mmio64.size, 0), | ||
222 | + DEFINE_PROP_UINT32("nr-irqs", GPEXHost, nr_irqs, PCI_NUM_PINS), | ||
223 | DEFINE_PROP_END_OF_LIST(), | ||
224 | }; | ||
225 | |||
226 | @@ -XXX,XX +XXX,XX @@ static void gpex_host_class_init(ObjectClass *klass, void *data) | ||
227 | |||
228 | hc->root_bus_path = gpex_host_root_bus_path; | ||
229 | dc->realize = gpex_host_realize; | ||
230 | + dc->unrealize = gpex_host_unrealize; | ||
231 | set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); | ||
232 | dc->fw_name = "pci"; | ||
233 | device_class_set_props(dc, gpex_host_properties); | ||
234 | diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c | ||
235 | index XXXXXXX..XXXXXXX 100644 | ||
236 | --- a/hw/riscv/virt.c | ||
237 | +++ b/hw/riscv/virt.c | ||
238 | @@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, | ||
239 | { | ||
240 | int pin, dev; | ||
241 | uint32_t irq_map_stride = 0; | ||
242 | - uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * | ||
243 | + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * | ||
244 | FDT_MAX_INT_MAP_WIDTH] = {}; | ||
245 | uint32_t *irq_map = full_irq_map; | ||
246 | |||
247 | @@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, | ||
248 | * possible slot) seeing the interrupt-map-mask will allow the table | ||
249 | * to wrap to any number of devices. | ||
250 | */ | ||
251 | - for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { | ||
252 | + for (dev = 0; dev < PCI_NUM_PINS; dev++) { | ||
253 | int devfn = dev * 0x8; | ||
254 | |||
255 | - for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { | ||
256 | - int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); | ||
257 | + for (pin = 0; pin < PCI_NUM_PINS; pin++) { | ||
258 | + int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); | ||
259 | int i = 0; | ||
260 | |||
261 | /* Fill PCI address cells */ | ||
262 | @@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, | ||
263 | } | ||
264 | |||
265 | qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, | ||
266 | - GPEX_NUM_IRQS * GPEX_NUM_IRQS * | ||
267 | + PCI_NUM_PINS * PCI_NUM_PINS * | ||
268 | irq_map_stride * sizeof(uint32_t)); | ||
269 | |||
270 | qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", | ||
271 | @@ -XXX,XX +XXX,XX @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, | ||
272 | |||
273 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base); | ||
274 | |||
275 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
276 | + for (i = 0; i < PCI_NUM_PINS; i++) { | ||
277 | irq = qdev_get_gpio_in(irqchip, PCIE_IRQ + i); | ||
278 | |||
279 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); | ||
280 | diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c | ||
281 | index XXXXXXX..XXXXXXX 100644 | ||
282 | --- a/hw/xtensa/virt.c | ||
283 | +++ b/hw/xtensa/virt.c | ||
284 | @@ -XXX,XX +XXX,XX @@ static void create_pcie(MachineState *ms, CPUXtensaState *env, int irq_base, | ||
285 | /* Connect IRQ lines. */ | ||
286 | extints = xtensa_get_extints(env); | ||
287 | |||
288 | - for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
289 | + for (i = 0; i < PCI_NUM_PINS; i++) { | ||
290 | void *q = extints[irq_base + i]; | ||
291 | |||
292 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, q); | ||
293 | diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h | ||
294 | index XXXXXXX..XXXXXXX 100644 | ||
295 | --- a/include/hw/pci-host/gpex.h | ||
296 | +++ b/include/hw/pci-host/gpex.h | ||
297 | @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(GPEXHost, GPEX_HOST) | ||
298 | #define TYPE_GPEX_ROOT_DEVICE "gpex-root" | ||
299 | OBJECT_DECLARE_SIMPLE_TYPE(GPEXRootState, GPEX_ROOT_DEVICE) | ||
300 | |||
301 | -#define GPEX_NUM_IRQS 4 | ||
302 | - | ||
303 | struct GPEXRootState { | ||
304 | /*< private >*/ | ||
305 | PCIDevice parent_obj; | ||
306 | @@ -XXX,XX +XXX,XX @@ struct GPEXHost { | ||
307 | MemoryRegion io_mmio; | ||
308 | MemoryRegion io_ioport_window; | ||
309 | MemoryRegion io_mmio_window; | ||
310 | - qemu_irq irq[GPEX_NUM_IRQS]; | ||
311 | - int irq_num[GPEX_NUM_IRQS]; | ||
312 | + uint32_t nr_irqs; | ||
313 | + qemu_irq *irq; | ||
314 | + int *irq_num; | ||
315 | |||
316 | bool allow_unmapped_accesses; | ||
317 | |||
318 | -- | ||
319 | 2.39.3 (Apple Git-145) | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alexander Graf <graf@amazon.com> | ||
2 | 1 | ||
3 | VMApple contains an "aes" engine device that it uses to encrypt and | ||
4 | decrypt its nvram. It has trivial hard coded keys it uses for that | ||
5 | purpose. | ||
6 | |||
7 | Add device emulation for this device model. | ||
8 | |||
9 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
10 | Co-authored-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
11 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
12 | |||
13 | --- | ||
14 | v3: | ||
15 | |||
16 | * Rebased on latest upstream and fixed minor breakages. | ||
17 | * Replaced legacy device reset method with Resettable method | ||
18 | |||
19 | hw/vmapple/Kconfig | 2 + | ||
20 | hw/vmapple/aes.c | 584 ++++++++++++++++++++++++++++++++++++++++ | ||
21 | hw/vmapple/meson.build | 1 + | ||
22 | hw/vmapple/trace-events | 19 ++ | ||
23 | 4 files changed, 606 insertions(+) | ||
24 | create mode 100644 hw/vmapple/aes.c | ||
25 | |||
26 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | ||
27 | index XXXXXXX..XXXXXXX 100644 | ||
28 | --- a/hw/vmapple/Kconfig | ||
29 | +++ b/hw/vmapple/Kconfig | ||
30 | @@ -1 +1,3 @@ | ||
31 | +config VMAPPLE_AES | ||
32 | + bool | ||
33 | |||
34 | diff --git a/hw/vmapple/aes.c b/hw/vmapple/aes.c | ||
35 | new file mode 100644 | ||
36 | index XXXXXXX..XXXXXXX | ||
37 | --- /dev/null | ||
38 | +++ b/hw/vmapple/aes.c | ||
39 | @@ -XXX,XX +XXX,XX @@ | ||
40 | +/* | ||
41 | + * QEMU Apple AES device emulation | ||
42 | + * | ||
43 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
44 | + * | ||
45 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
46 | + * See the COPYING file in the top-level directory. | ||
47 | + */ | ||
48 | + | ||
49 | +#include "qemu/osdep.h" | ||
50 | +#include "hw/irq.h" | ||
51 | +#include "migration/vmstate.h" | ||
52 | +#include "qemu/log.h" | ||
53 | +#include "qemu/module.h" | ||
54 | +#include "trace.h" | ||
55 | +#include "hw/sysbus.h" | ||
56 | +#include "crypto/hash.h" | ||
57 | +#include "crypto/aes.h" | ||
58 | +#include "crypto/cipher.h" | ||
59 | + | ||
60 | +#define TYPE_AES "apple-aes" | ||
61 | +#define MAX_FIFO_SIZE 9 | ||
62 | + | ||
63 | +#define CMD_KEY 0x1 | ||
64 | +#define CMD_KEY_CONTEXT_SHIFT 27 | ||
65 | +#define CMD_KEY_CONTEXT_MASK (0x1 << CMD_KEY_CONTEXT_SHIFT) | ||
66 | +#define CMD_KEY_SELECT_SHIFT 24 | ||
67 | +#define CMD_KEY_SELECT_MASK (0x7 << CMD_KEY_SELECT_SHIFT) | ||
68 | +#define CMD_KEY_KEY_LEN_SHIFT 22 | ||
69 | +#define CMD_KEY_KEY_LEN_MASK (0x3 << CMD_KEY_KEY_LEN_SHIFT) | ||
70 | +#define CMD_KEY_ENCRYPT_SHIFT 20 | ||
71 | +#define CMD_KEY_ENCRYPT_MASK (0x1 << CMD_KEY_ENCRYPT_SHIFT) | ||
72 | +#define CMD_KEY_BLOCK_MODE_SHIFT 16 | ||
73 | +#define CMD_KEY_BLOCK_MODE_MASK (0x3 << CMD_KEY_BLOCK_MODE_SHIFT) | ||
74 | +#define CMD_IV 0x2 | ||
75 | +#define CMD_IV_CONTEXT_SHIFT 26 | ||
76 | +#define CMD_IV_CONTEXT_MASK (0x3 << CMD_KEY_CONTEXT_SHIFT) | ||
77 | +#define CMD_DSB 0x3 | ||
78 | +#define CMD_SKG 0x4 | ||
79 | +#define CMD_DATA 0x5 | ||
80 | +#define CMD_DATA_KEY_CTX_SHIFT 27 | ||
81 | +#define CMD_DATA_KEY_CTX_MASK (0x1 << CMD_DATA_KEY_CTX_SHIFT) | ||
82 | +#define CMD_DATA_IV_CTX_SHIFT 25 | ||
83 | +#define CMD_DATA_IV_CTX_MASK (0x3 << CMD_DATA_IV_CTX_SHIFT) | ||
84 | +#define CMD_DATA_LEN_MASK 0xffffff | ||
85 | +#define CMD_STORE_IV 0x6 | ||
86 | +#define CMD_STORE_IV_ADDR_MASK 0xffffff | ||
87 | +#define CMD_WRITE_REG 0x7 | ||
88 | +#define CMD_FLAG 0x8 | ||
89 | +#define CMD_FLAG_STOP_MASK BIT(26) | ||
90 | +#define CMD_FLAG_RAISE_IRQ_MASK BIT(27) | ||
91 | +#define CMD_FLAG_INFO_MASK 0xff | ||
92 | +#define CMD_MAX 0x10 | ||
93 | + | ||
94 | +#define CMD_SHIFT 28 | ||
95 | + | ||
96 | +#define REG_STATUS 0xc | ||
97 | +#define REG_STATUS_DMA_READ_RUNNING BIT(0) | ||
98 | +#define REG_STATUS_DMA_READ_PENDING BIT(1) | ||
99 | +#define REG_STATUS_DMA_WRITE_RUNNING BIT(2) | ||
100 | +#define REG_STATUS_DMA_WRITE_PENDING BIT(3) | ||
101 | +#define REG_STATUS_BUSY BIT(4) | ||
102 | +#define REG_STATUS_EXECUTING BIT(5) | ||
103 | +#define REG_STATUS_READY BIT(6) | ||
104 | +#define REG_STATUS_TEXT_DPA_SEEDED BIT(7) | ||
105 | +#define REG_STATUS_UNWRAP_DPA_SEEDED BIT(8) | ||
106 | + | ||
107 | +#define REG_IRQ_STATUS 0x18 | ||
108 | +#define REG_IRQ_STATUS_INVALID_CMD BIT(2) | ||
109 | +#define REG_IRQ_STATUS_FLAG BIT(5) | ||
110 | +#define REG_IRQ_ENABLE 0x1c | ||
111 | +#define REG_WATERMARK 0x20 | ||
112 | +#define REG_Q_STATUS 0x24 | ||
113 | +#define REG_FLAG_INFO 0x30 | ||
114 | +#define REG_FIFO 0x200 | ||
115 | + | ||
116 | +static const uint32_t key_lens[4] = { | ||
117 | + [0] = 16, | ||
118 | + [1] = 24, | ||
119 | + [2] = 32, | ||
120 | + [3] = 64, | ||
121 | +}; | ||
122 | + | ||
123 | +struct key { | ||
124 | + uint32_t key_len; | ||
125 | + uint32_t key[8]; | ||
126 | +}; | ||
127 | + | ||
128 | +struct iv { | ||
129 | + uint32_t iv[4]; | ||
130 | +}; | ||
131 | + | ||
132 | +struct context { | ||
133 | + struct key key; | ||
134 | + struct iv iv; | ||
135 | +}; | ||
136 | + | ||
137 | +static struct key builtin_keys[7] = { | ||
138 | + [1] = { | ||
139 | + .key_len = 32, | ||
140 | + .key = { 0x1 }, | ||
141 | + }, | ||
142 | + [2] = { | ||
143 | + .key_len = 32, | ||
144 | + .key = { 0x2 }, | ||
145 | + }, | ||
146 | + [3] = { | ||
147 | + .key_len = 32, | ||
148 | + .key = { 0x3 }, | ||
149 | + } | ||
150 | +}; | ||
151 | + | ||
152 | +typedef struct AESState { | ||
153 | + /* Private */ | ||
154 | + SysBusDevice parent_obj; | ||
155 | + | ||
156 | + /* Public */ | ||
157 | + qemu_irq irq; | ||
158 | + MemoryRegion iomem1; | ||
159 | + MemoryRegion iomem2; | ||
160 | + | ||
161 | + uint32_t status; | ||
162 | + uint32_t q_status; | ||
163 | + uint32_t irq_status; | ||
164 | + uint32_t irq_enable; | ||
165 | + uint32_t watermark; | ||
166 | + uint32_t flag_info; | ||
167 | + uint32_t fifo[MAX_FIFO_SIZE]; | ||
168 | + uint32_t fifo_idx; | ||
169 | + struct key key[2]; | ||
170 | + struct iv iv[4]; | ||
171 | + bool is_encrypt; | ||
172 | + QCryptoCipherMode block_mode; | ||
173 | +} AESState; | ||
174 | + | ||
175 | +OBJECT_DECLARE_SIMPLE_TYPE(AESState, AES) | ||
176 | + | ||
177 | +static void aes_update_irq(AESState *s) | ||
178 | +{ | ||
179 | + qemu_set_irq(s->irq, !!(s->irq_status & s->irq_enable)); | ||
180 | +} | ||
181 | + | ||
182 | +static uint64_t aes1_read(void *opaque, hwaddr offset, unsigned size) | ||
183 | +{ | ||
184 | + AESState *s = opaque; | ||
185 | + uint64_t res = 0; | ||
186 | + | ||
187 | + switch (offset) { | ||
188 | + case REG_STATUS: | ||
189 | + res = s->status; | ||
190 | + break; | ||
191 | + case REG_IRQ_STATUS: | ||
192 | + res = s->irq_status; | ||
193 | + break; | ||
194 | + case REG_IRQ_ENABLE: | ||
195 | + res = s->irq_enable; | ||
196 | + break; | ||
197 | + case REG_WATERMARK: | ||
198 | + res = s->watermark; | ||
199 | + break; | ||
200 | + case REG_Q_STATUS: | ||
201 | + res = s->q_status; | ||
202 | + break; | ||
203 | + case REG_FLAG_INFO: | ||
204 | + res = s->flag_info; | ||
205 | + break; | ||
206 | + | ||
207 | + default: | ||
208 | + trace_aes_read_unknown(offset); | ||
209 | + break; | ||
210 | + } | ||
211 | + | ||
212 | + trace_aes_read(offset, res); | ||
213 | + | ||
214 | + return res; | ||
215 | +} | ||
216 | + | ||
217 | +static void fifo_append(AESState *s, uint64_t val) | ||
218 | +{ | ||
219 | + if (s->fifo_idx == MAX_FIFO_SIZE) { | ||
220 | + /* Exceeded the FIFO. Bail out */ | ||
221 | + return; | ||
222 | + } | ||
223 | + | ||
224 | + s->fifo[s->fifo_idx++] = val; | ||
225 | +} | ||
226 | + | ||
227 | +static bool has_payload(AESState *s, uint32_t elems) | ||
228 | +{ | ||
229 | + return s->fifo_idx >= (elems + 1); | ||
230 | +} | ||
231 | + | ||
232 | +static bool cmd_key(AESState *s) | ||
233 | +{ | ||
234 | + uint32_t cmd = s->fifo[0]; | ||
235 | + uint32_t key_select = (cmd & CMD_KEY_SELECT_MASK) >> CMD_KEY_SELECT_SHIFT; | ||
236 | + uint32_t ctxt = (cmd & CMD_KEY_CONTEXT_MASK) >> CMD_KEY_CONTEXT_SHIFT; | ||
237 | + uint32_t key_len; | ||
238 | + | ||
239 | + switch ((cmd & CMD_KEY_BLOCK_MODE_MASK) >> CMD_KEY_BLOCK_MODE_SHIFT) { | ||
240 | + case 0: | ||
241 | + s->block_mode = QCRYPTO_CIPHER_MODE_ECB; | ||
242 | + break; | ||
243 | + case 1: | ||
244 | + s->block_mode = QCRYPTO_CIPHER_MODE_CBC; | ||
245 | + break; | ||
246 | + default: | ||
247 | + return false; | ||
248 | + } | ||
249 | + | ||
250 | + s->is_encrypt = !!((cmd & CMD_KEY_ENCRYPT_MASK) >> CMD_KEY_ENCRYPT_SHIFT); | ||
251 | + key_len = key_lens[((cmd & CMD_KEY_KEY_LEN_MASK) >> CMD_KEY_KEY_LEN_SHIFT)]; | ||
252 | + | ||
253 | + if (key_select) { | ||
254 | + trace_aes_cmd_key_select_builtin(ctxt, key_select, | ||
255 | + s->is_encrypt ? "en" : "de", | ||
256 | + QCryptoCipherMode_str(s->block_mode)); | ||
257 | + s->key[ctxt] = builtin_keys[key_select]; | ||
258 | + } else { | ||
259 | + trace_aes_cmd_key_select_new(ctxt, key_len, | ||
260 | + s->is_encrypt ? "en" : "de", | ||
261 | + QCryptoCipherMode_str(s->block_mode)); | ||
262 | + if (key_len > sizeof(s->key[ctxt].key)) { | ||
263 | + return false; | ||
264 | + } | ||
265 | + if (!has_payload(s, key_len / sizeof(uint32_t))) { | ||
266 | + /* wait for payload */ | ||
267 | + return false; | ||
268 | + } | ||
269 | + memcpy(&s->key[ctxt].key, &s->fifo[1], key_len); | ||
270 | + s->key[ctxt].key_len = key_len; | ||
271 | + } | ||
272 | + | ||
273 | + return true; | ||
274 | +} | ||
275 | + | ||
276 | +static bool cmd_iv(AESState *s) | ||
277 | +{ | ||
278 | + uint32_t cmd = s->fifo[0]; | ||
279 | + uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT; | ||
280 | + | ||
281 | + if (!has_payload(s, 4)) { | ||
282 | + /* wait for payload */ | ||
283 | + return false; | ||
284 | + } | ||
285 | + memcpy(&s->iv[ctxt].iv, &s->fifo[1], sizeof(s->iv[ctxt].iv)); | ||
286 | + trace_aes_cmd_iv(ctxt, s->fifo[1], s->fifo[2], s->fifo[3], s->fifo[4]); | ||
287 | + | ||
288 | + return true; | ||
289 | +} | ||
290 | + | ||
291 | +static char hexdigit2str(uint8_t val) | ||
292 | +{ | ||
293 | + g_assert(val < 0x10); | ||
294 | + if (val >= 0xa) { | ||
295 | + return 'a' + (val - 0xa); | ||
296 | + } else { | ||
297 | + return '0' + val; | ||
298 | + } | ||
299 | +} | ||
300 | + | ||
301 | +static void dump_data(const char *desc, const void *p, size_t len) | ||
302 | +{ | ||
303 | + char *hex = alloca((len * 2) + 1); | ||
304 | + const uint8_t *data = p; | ||
305 | + char *hexp = hex; | ||
306 | + size_t i; | ||
307 | + | ||
308 | + if (len > 0x1000) { | ||
309 | + /* Too large buffer, let's bail out */ | ||
310 | + return; | ||
311 | + } | ||
312 | + | ||
313 | + for (i = 0; i < len; i++) { | ||
314 | + uint8_t val = data[i]; | ||
315 | + *(hexp++) = hexdigit2str(val >> 4); | ||
316 | + *(hexp++) = hexdigit2str(val & 0xf); | ||
317 | + } | ||
318 | + *hexp = '\0'; | ||
319 | + | ||
320 | + trace_aes_dump_data(desc, hex); | ||
321 | +} | ||
322 | + | ||
323 | +static bool cmd_data(AESState *s) | ||
324 | +{ | ||
325 | + uint32_t cmd = s->fifo[0]; | ||
326 | + uint32_t ctxt_iv = 0; | ||
327 | + uint32_t ctxt_key = (cmd & CMD_DATA_KEY_CTX_MASK) >> CMD_DATA_KEY_CTX_SHIFT; | ||
328 | + uint32_t len = cmd & CMD_DATA_LEN_MASK; | ||
329 | + uint64_t src_addr = s->fifo[2]; | ||
330 | + uint64_t dst_addr = s->fifo[3]; | ||
331 | + QCryptoCipherAlgo alg; | ||
332 | + QCryptoCipher *cipher; | ||
333 | + char *src; | ||
334 | + char *dst; | ||
335 | + | ||
336 | + src_addr |= ((uint64_t)s->fifo[1] << 16) & 0xffff00000000ULL; | ||
337 | + dst_addr |= ((uint64_t)s->fifo[1] << 32) & 0xffff00000000ULL; | ||
338 | + | ||
339 | + trace_aes_cmd_data(ctxt_key, ctxt_iv, src_addr, dst_addr, len); | ||
340 | + | ||
341 | + if (!has_payload(s, 3)) { | ||
342 | + /* wait for payload */ | ||
343 | + trace_aes_cmd_data_error("No payload"); | ||
344 | + return false; | ||
345 | + } | ||
346 | + | ||
347 | + if (ctxt_key >= ARRAY_SIZE(s->key) || | ||
348 | + ctxt_iv >= ARRAY_SIZE(s->iv)) { | ||
349 | + /* Invalid input */ | ||
350 | + trace_aes_cmd_data_error("Invalid key or iv"); | ||
351 | + return false; | ||
352 | + } | ||
353 | + | ||
354 | + src = g_malloc0(len); | ||
355 | + dst = g_malloc0(len); | ||
356 | + | ||
357 | + cpu_physical_memory_read(src_addr, src, len); | ||
358 | + | ||
359 | + dump_data("cmd_data(): src_data=", src, len); | ||
360 | + | ||
361 | + switch (s->key[ctxt_key].key_len) { | ||
362 | + case 128 / 8: | ||
363 | + alg = QCRYPTO_CIPHER_ALGO_AES_128; | ||
364 | + break; | ||
365 | + case 192 / 8: | ||
366 | + alg = QCRYPTO_CIPHER_ALGO_AES_192; | ||
367 | + break; | ||
368 | + case 256 / 8: | ||
369 | + alg = QCRYPTO_CIPHER_ALGO_AES_256; | ||
370 | + break; | ||
371 | + default: | ||
372 | + trace_aes_cmd_data_error("Invalid key len"); | ||
373 | + goto err_free; | ||
374 | + } | ||
375 | + cipher = qcrypto_cipher_new(alg, s->block_mode, | ||
376 | + (void *)s->key[ctxt_key].key, | ||
377 | + s->key[ctxt_key].key_len, NULL); | ||
378 | + g_assert(cipher != NULL); | ||
379 | + if (s->block_mode != QCRYPTO_CIPHER_MODE_ECB) { | ||
380 | + if (qcrypto_cipher_setiv(cipher, (void *)s->iv[ctxt_iv].iv, | ||
381 | + sizeof(s->iv[ctxt_iv].iv), NULL) != 0) { | ||
382 | + trace_aes_cmd_data_error("Failed to set IV"); | ||
383 | + goto err_free_cipher; | ||
384 | + } | ||
385 | + } | ||
386 | + if (s->is_encrypt) { | ||
387 | + if (qcrypto_cipher_encrypt(cipher, src, dst, len, NULL) != 0) { | ||
388 | + trace_aes_cmd_data_error("Encrypt failed"); | ||
389 | + goto err_free_cipher; | ||
390 | + } | ||
391 | + } else { | ||
392 | + if (qcrypto_cipher_decrypt(cipher, src, dst, len, NULL) != 0) { | ||
393 | + trace_aes_cmd_data_error("Decrypt failed"); | ||
394 | + goto err_free_cipher; | ||
395 | + } | ||
396 | + } | ||
397 | + qcrypto_cipher_free(cipher); | ||
398 | + | ||
399 | + dump_data("cmd_data(): dst_data=", dst, len); | ||
400 | + cpu_physical_memory_write(dst_addr, dst, len); | ||
401 | + g_free(src); | ||
402 | + g_free(dst); | ||
403 | + | ||
404 | + return true; | ||
405 | + | ||
406 | +err_free_cipher: | ||
407 | + qcrypto_cipher_free(cipher); | ||
408 | +err_free: | ||
409 | + g_free(src); | ||
410 | + g_free(dst); | ||
411 | + return false; | ||
412 | +} | ||
413 | + | ||
414 | +static bool cmd_store_iv(AESState *s) | ||
415 | +{ | ||
416 | + uint32_t cmd = s->fifo[0]; | ||
417 | + uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT; | ||
418 | + uint64_t addr = s->fifo[1]; | ||
419 | + | ||
420 | + if (!has_payload(s, 1)) { | ||
421 | + /* wait for payload */ | ||
422 | + return false; | ||
423 | + } | ||
424 | + | ||
425 | + if (ctxt >= ARRAY_SIZE(s->iv)) { | ||
426 | + /* Invalid context selected */ | ||
427 | + return false; | ||
428 | + } | ||
429 | + | ||
430 | + addr |= ((uint64_t)cmd << 32) & 0xff00000000ULL; | ||
431 | + cpu_physical_memory_write(addr, &s->iv[ctxt].iv, sizeof(s->iv[ctxt].iv)); | ||
432 | + | ||
433 | + trace_aes_cmd_store_iv(ctxt, addr, s->iv[ctxt].iv[0], s->iv[ctxt].iv[1], | ||
434 | + s->iv[ctxt].iv[2], s->iv[ctxt].iv[3]); | ||
435 | + | ||
436 | + return true; | ||
437 | +} | ||
438 | + | ||
439 | +static bool cmd_flag(AESState *s) | ||
440 | +{ | ||
441 | + uint32_t cmd = s->fifo[0]; | ||
442 | + uint32_t raise_irq = cmd & CMD_FLAG_RAISE_IRQ_MASK; | ||
443 | + | ||
444 | + /* We always process data when it's coming in, so fire an IRQ immediately */ | ||
445 | + if (raise_irq) { | ||
446 | + s->irq_status |= REG_IRQ_STATUS_FLAG; | ||
447 | + } | ||
448 | + | ||
449 | + s->flag_info = cmd & CMD_FLAG_INFO_MASK; | ||
450 | + | ||
451 | + trace_aes_cmd_flag(!!raise_irq, s->flag_info); | ||
452 | + | ||
453 | + return true; | ||
454 | +} | ||
455 | + | ||
456 | +static void fifo_process(AESState *s) | ||
457 | +{ | ||
458 | + uint32_t cmd = s->fifo[0] >> CMD_SHIFT; | ||
459 | + bool success = false; | ||
460 | + | ||
461 | + if (!s->fifo_idx) { | ||
462 | + return; | ||
463 | + } | ||
464 | + | ||
465 | + switch (cmd) { | ||
466 | + case CMD_KEY: | ||
467 | + success = cmd_key(s); | ||
468 | + break; | ||
469 | + case CMD_IV: | ||
470 | + success = cmd_iv(s); | ||
471 | + break; | ||
472 | + case CMD_DATA: | ||
473 | + success = cmd_data(s); | ||
474 | + break; | ||
475 | + case CMD_STORE_IV: | ||
476 | + success = cmd_store_iv(s); | ||
477 | + break; | ||
478 | + case CMD_FLAG: | ||
479 | + success = cmd_flag(s); | ||
480 | + break; | ||
481 | + default: | ||
482 | + s->irq_status |= REG_IRQ_STATUS_INVALID_CMD; | ||
483 | + break; | ||
484 | + } | ||
485 | + | ||
486 | + if (success) { | ||
487 | + s->fifo_idx = 0; | ||
488 | + } | ||
489 | + | ||
490 | + trace_aes_fifo_process(cmd, success ? 1 : 0); | ||
491 | +} | ||
492 | + | ||
493 | +static void aes1_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) | ||
494 | +{ | ||
495 | + AESState *s = opaque; | ||
496 | + | ||
497 | + trace_aes_write(offset, val); | ||
498 | + | ||
499 | + switch (offset) { | ||
500 | + case REG_IRQ_STATUS: | ||
501 | + s->irq_status &= ~val; | ||
502 | + break; | ||
503 | + case REG_IRQ_ENABLE: | ||
504 | + s->irq_enable = val; | ||
505 | + break; | ||
506 | + case REG_FIFO: | ||
507 | + fifo_append(s, val); | ||
508 | + fifo_process(s); | ||
509 | + break; | ||
510 | + default: | ||
511 | + trace_aes_write_unknown(offset); | ||
512 | + return; | ||
513 | + } | ||
514 | + | ||
515 | + aes_update_irq(s); | ||
516 | +} | ||
517 | + | ||
518 | +static const MemoryRegionOps aes1_ops = { | ||
519 | + .read = aes1_read, | ||
520 | + .write = aes1_write, | ||
521 | + .endianness = DEVICE_NATIVE_ENDIAN, | ||
522 | + .valid = { | ||
523 | + .min_access_size = 4, | ||
524 | + .max_access_size = 8, | ||
525 | + }, | ||
526 | + .impl = { | ||
527 | + .min_access_size = 4, | ||
528 | + .max_access_size = 4, | ||
529 | + }, | ||
530 | +}; | ||
531 | + | ||
532 | +static uint64_t aes2_read(void *opaque, hwaddr offset, unsigned size) | ||
533 | +{ | ||
534 | + uint64_t res = 0; | ||
535 | + | ||
536 | + switch (offset) { | ||
537 | + case 0: | ||
538 | + res = 0; | ||
539 | + break; | ||
540 | + default: | ||
541 | + trace_aes_2_read_unknown(offset); | ||
542 | + break; | ||
543 | + } | ||
544 | + | ||
545 | + trace_aes_2_read(offset, res); | ||
546 | + | ||
547 | + return res; | ||
548 | +} | ||
549 | + | ||
550 | +static void aes2_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) | ||
551 | +{ | ||
552 | + trace_aes_2_write(offset, val); | ||
553 | + | ||
554 | + switch (offset) { | ||
555 | + default: | ||
556 | + trace_aes_2_write_unknown(offset); | ||
557 | + return; | ||
558 | + } | ||
559 | +} | ||
560 | + | ||
561 | +static const MemoryRegionOps aes2_ops = { | ||
562 | + .read = aes2_read, | ||
563 | + .write = aes2_write, | ||
564 | + .endianness = DEVICE_NATIVE_ENDIAN, | ||
565 | + .valid = { | ||
566 | + .min_access_size = 4, | ||
567 | + .max_access_size = 8, | ||
568 | + }, | ||
569 | + .impl = { | ||
570 | + .min_access_size = 4, | ||
571 | + .max_access_size = 4, | ||
572 | + }, | ||
573 | +}; | ||
574 | + | ||
575 | +static void aes_reset(Object *obj, ResetType type) | ||
576 | +{ | ||
577 | + AESState *s = AES(obj); | ||
578 | + | ||
579 | + s->status = 0x3f80; | ||
580 | + s->q_status = 2; | ||
581 | + s->irq_status = 0; | ||
582 | + s->irq_enable = 0; | ||
583 | + s->watermark = 0; | ||
584 | +} | ||
585 | + | ||
586 | +static void aes_init(Object *obj) | ||
587 | +{ | ||
588 | + AESState *s = AES(obj); | ||
589 | + | ||
590 | + memory_region_init_io(&s->iomem1, obj, &aes1_ops, s, TYPE_AES, 0x4000); | ||
591 | + memory_region_init_io(&s->iomem2, obj, &aes2_ops, s, TYPE_AES, 0x4000); | ||
592 | + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem1); | ||
593 | + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem2); | ||
594 | + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); | ||
595 | +} | ||
596 | + | ||
597 | +static void aes_realize(DeviceState *dev, Error **errp) | ||
598 | +{ | ||
599 | +} | ||
600 | + | ||
601 | +static void aes_class_init(ObjectClass *klass, void *data) | ||
602 | +{ | ||
603 | + DeviceClass *dc = DEVICE_CLASS(klass); | ||
604 | + ResettableClass *rc = RESETTABLE_CLASS(klass); | ||
605 | + | ||
606 | + rc->phases.hold = aes_reset; | ||
607 | + dc->realize = aes_realize; | ||
608 | +} | ||
609 | + | ||
610 | +static const TypeInfo aes_info = { | ||
611 | + .name = TYPE_AES, | ||
612 | + .parent = TYPE_SYS_BUS_DEVICE, | ||
613 | + .instance_size = sizeof(AESState), | ||
614 | + .class_init = aes_class_init, | ||
615 | + .instance_init = aes_init, | ||
616 | +}; | ||
617 | + | ||
618 | +static void aes_register_types(void) | ||
619 | +{ | ||
620 | + type_register_static(&aes_info); | ||
621 | +} | ||
622 | + | ||
623 | +type_init(aes_register_types) | ||
624 | diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build | ||
625 | index XXXXXXX..XXXXXXX 100644 | ||
626 | --- a/hw/vmapple/meson.build | ||
627 | +++ b/hw/vmapple/meson.build | ||
628 | @@ -0,0 +1 @@ | ||
629 | +system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) | ||
630 | diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events | ||
631 | index XXXXXXX..XXXXXXX 100644 | ||
632 | --- a/hw/vmapple/trace-events | ||
633 | +++ b/hw/vmapple/trace-events | ||
634 | @@ -XXX,XX +XXX,XX @@ | ||
635 | # See docs/devel/tracing.rst for syntax documentation. | ||
636 | |||
637 | +# aes.c | ||
638 | +aes_read_unknown(uint64_t offset) "offset=0x%"PRIx64 | ||
639 | +aes_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 | ||
640 | +aes_cmd_key_select_builtin(uint32_t ctx, uint32_t key_id, const char *direction, const char *cipher) "[%d] Selecting builtin key %d to %scrypt with %s" | ||
641 | +aes_cmd_key_select_new(uint32_t ctx, uint32_t key_len, const char *direction, const char *cipher) "[%d] Selecting new key size=%d to %scrypt with %s" | ||
642 | +aes_cmd_iv(uint32_t ctx, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] 0x%08x 0x%08x 0x%08x 0x%08x" | ||
643 | +aes_cmd_data(uint32_t key, uint32_t iv, uint64_t src, uint64_t dst, uint32_t len) "[key=%d iv=%d] src=0x%"PRIx64" dst=0x%"PRIx64" len=0x%x" | ||
644 | +aes_cmd_data_error(const char *reason) "reason=%s" | ||
645 | +aes_cmd_store_iv(uint32_t ctx, uint64_t addr, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] addr=0x%"PRIx64"x -> 0x%08x 0x%08x 0x%08x 0x%08x" | ||
646 | +aes_cmd_flag(uint32_t raise, uint32_t flag_info) "raise=%d flag_info=0x%x" | ||
647 | +aes_fifo_process(uint32_t cmd, uint32_t success) "cmd=%d success=%d" | ||
648 | +aes_write_unknown(uint64_t offset) "offset=0x%"PRIx64 | ||
649 | +aes_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 | ||
650 | +aes_2_read_unknown(uint64_t offset) "offset=0x%"PRIx64 | ||
651 | +aes_2_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 | ||
652 | +aes_2_write_unknown(uint64_t offset) "offset=0x%"PRIx64 | ||
653 | +aes_2_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 | ||
654 | +aes_dump_data(const char *desc, const char *hex) "%s%s" | ||
655 | + | ||
656 | -- | ||
657 | 2.39.3 (Apple Git-145) | ||
658 | |||
659 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alexander Graf <graf@amazon.com> | ||
2 | 1 | ||
3 | The VMApple machine exposes AUX and ROOT block devices (as well as USB OTG | ||
4 | emulation) via virtio-pci as well as a special, simple backdoor platform | ||
5 | device. | ||
6 | |||
7 | This patch implements this backdoor platform device to the best of my | ||
8 | understanding. I left out any USB OTG parts; they're only needed for | ||
9 | guest recovery and I don't understand the protocol yet. | ||
10 | |||
11 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
12 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
13 | --- | ||
14 | hw/vmapple/Kconfig | 3 + | ||
15 | hw/vmapple/bdif.c | 245 ++++++++++++++++++++++++++++++++++++++ | ||
16 | hw/vmapple/meson.build | 1 + | ||
17 | hw/vmapple/trace-events | 5 + | ||
18 | include/hw/vmapple/bdif.h | 31 +++++ | ||
19 | 5 files changed, 285 insertions(+) | ||
20 | create mode 100644 hw/vmapple/bdif.c | ||
21 | create mode 100644 include/hw/vmapple/bdif.h | ||
22 | |||
23 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | ||
24 | index XXXXXXX..XXXXXXX 100644 | ||
25 | --- a/hw/vmapple/Kconfig | ||
26 | +++ b/hw/vmapple/Kconfig | ||
27 | @@ -XXX,XX +XXX,XX @@ | ||
28 | config VMAPPLE_AES | ||
29 | bool | ||
30 | |||
31 | +config VMAPPLE_BDIF | ||
32 | + bool | ||
33 | + | ||
34 | diff --git a/hw/vmapple/bdif.c b/hw/vmapple/bdif.c | ||
35 | new file mode 100644 | ||
36 | index XXXXXXX..XXXXXXX | ||
37 | --- /dev/null | ||
38 | +++ b/hw/vmapple/bdif.c | ||
39 | @@ -XXX,XX +XXX,XX @@ | ||
40 | +/* | ||
41 | + * VMApple Backdoor Interface | ||
42 | + * | ||
43 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
44 | + * | ||
45 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
46 | + * See the COPYING file in the top-level directory. | ||
47 | + */ | ||
48 | + | ||
49 | +#include "qemu/osdep.h" | ||
50 | +#include "hw/vmapple/bdif.h" | ||
51 | +#include "qemu/log.h" | ||
52 | +#include "qemu/module.h" | ||
53 | +#include "qapi/error.h" | ||
54 | +#include "trace.h" | ||
55 | +#include "hw/block/block.h" | ||
56 | +#include "sysemu/block-backend.h" | ||
57 | + | ||
58 | +#define REG_DEVID_MASK 0xffff0000 | ||
59 | +#define DEVID_ROOT 0x00000000 | ||
60 | +#define DEVID_AUX 0x00010000 | ||
61 | +#define DEVID_USB 0x00100000 | ||
62 | + | ||
63 | +#define REG_STATUS 0x0 | ||
64 | +#define REG_STATUS_ACTIVE BIT(0) | ||
65 | +#define REG_CFG 0x4 | ||
66 | +#define REG_CFG_ACTIVE BIT(1) | ||
67 | +#define REG_UNK1 0x8 | ||
68 | +#define REG_BUSY 0x10 | ||
69 | +#define REG_BUSY_READY BIT(0) | ||
70 | +#define REG_UNK2 0x400 | ||
71 | +#define REG_CMD 0x408 | ||
72 | +#define REG_NEXT_DEVICE 0x420 | ||
73 | +#define REG_UNK3 0x434 | ||
74 | + | ||
75 | +typedef struct vblk_sector { | ||
76 | + uint32_t pad; | ||
77 | + uint32_t pad2; | ||
78 | + uint32_t sector; | ||
79 | + uint32_t pad3; | ||
80 | +} VblkSector; | ||
81 | + | ||
82 | +typedef struct vblk_req_cmd { | ||
83 | + uint64_t addr; | ||
84 | + uint32_t len; | ||
85 | + uint32_t flags; | ||
86 | +} VblkReqCmd; | ||
87 | + | ||
88 | +typedef struct vblk_req { | ||
89 | + VblkReqCmd sector; | ||
90 | + VblkReqCmd data; | ||
91 | + VblkReqCmd retval; | ||
92 | +} VblkReq; | ||
93 | + | ||
94 | +#define VBLK_DATA_FLAGS_READ 0x00030001 | ||
95 | +#define VBLK_DATA_FLAGS_WRITE 0x00010001 | ||
96 | + | ||
97 | +#define VBLK_RET_SUCCESS 0 | ||
98 | +#define VBLK_RET_FAILED 1 | ||
99 | + | ||
100 | +static uint64_t bdif_read(void *opaque, hwaddr offset, unsigned size) | ||
101 | +{ | ||
102 | + uint64_t ret = -1; | ||
103 | + uint64_t devid = (offset & REG_DEVID_MASK); | ||
104 | + | ||
105 | + switch (offset & ~REG_DEVID_MASK) { | ||
106 | + case REG_STATUS: | ||
107 | + ret = REG_STATUS_ACTIVE; | ||
108 | + break; | ||
109 | + case REG_CFG: | ||
110 | + ret = REG_CFG_ACTIVE; | ||
111 | + break; | ||
112 | + case REG_UNK1: | ||
113 | + ret = 0x420; | ||
114 | + break; | ||
115 | + case REG_BUSY: | ||
116 | + ret = REG_BUSY_READY; | ||
117 | + break; | ||
118 | + case REG_UNK2: | ||
119 | + ret = 0x1; | ||
120 | + break; | ||
121 | + case REG_UNK3: | ||
122 | + ret = 0x0; | ||
123 | + break; | ||
124 | + case REG_NEXT_DEVICE: | ||
125 | + switch (devid) { | ||
126 | + case DEVID_ROOT: | ||
127 | + ret = 0x8000000; | ||
128 | + break; | ||
129 | + case DEVID_AUX: | ||
130 | + ret = 0x10000; | ||
131 | + break; | ||
132 | + } | ||
133 | + break; | ||
134 | + } | ||
135 | + | ||
136 | + trace_bdif_read(offset, size, ret); | ||
137 | + return ret; | ||
138 | +} | ||
139 | + | ||
140 | +static void le2cpu_sector(VblkSector *sector) | ||
141 | +{ | ||
142 | + sector->sector = le32_to_cpu(sector->sector); | ||
143 | +} | ||
144 | + | ||
145 | +static void le2cpu_reqcmd(VblkReqCmd *cmd) | ||
146 | +{ | ||
147 | + cmd->addr = le64_to_cpu(cmd->addr); | ||
148 | + cmd->len = le32_to_cpu(cmd->len); | ||
149 | + cmd->flags = le32_to_cpu(cmd->flags); | ||
150 | +} | ||
151 | + | ||
152 | +static void le2cpu_req(VblkReq *req) | ||
153 | +{ | ||
154 | + le2cpu_reqcmd(&req->sector); | ||
155 | + le2cpu_reqcmd(&req->data); | ||
156 | + le2cpu_reqcmd(&req->retval); | ||
157 | +} | ||
158 | + | ||
159 | +static void vblk_cmd(uint64_t devid, BlockBackend *blk, uint64_t value, | ||
160 | + uint64_t static_off) | ||
161 | +{ | ||
162 | + VblkReq req; | ||
163 | + VblkSector sector; | ||
164 | + uint64_t off = 0; | ||
165 | + char *buf = NULL; | ||
166 | + uint8_t ret = VBLK_RET_FAILED; | ||
167 | + int r; | ||
168 | + | ||
169 | + cpu_physical_memory_read(value, &req, sizeof(req)); | ||
170 | + le2cpu_req(&req); | ||
171 | + | ||
172 | + if (req.sector.len != sizeof(sector)) { | ||
173 | + ret = VBLK_RET_FAILED; | ||
174 | + goto out; | ||
175 | + } | ||
176 | + | ||
177 | + /* Read the vblk command */ | ||
178 | + cpu_physical_memory_read(req.sector.addr, §or, sizeof(sector)); | ||
179 | + le2cpu_sector(§or); | ||
180 | + | ||
181 | + off = sector.sector * 512ULL + static_off; | ||
182 | + | ||
183 | + /* Sanity check that we're not allocating bogus sizes */ | ||
184 | + if (req.data.len > (128 * 1024 * 1024)) { | ||
185 | + goto out; | ||
186 | + } | ||
187 | + | ||
188 | + buf = g_malloc0(req.data.len); | ||
189 | + switch (req.data.flags) { | ||
190 | + case VBLK_DATA_FLAGS_READ: | ||
191 | + r = blk_pread(blk, off, req.data.len, buf, 0); | ||
192 | + trace_bdif_vblk_read(devid == DEVID_AUX ? "aux" : "root", | ||
193 | + req.data.addr, off, req.data.len, r); | ||
194 | + if (r < 0) { | ||
195 | + goto out; | ||
196 | + } | ||
197 | + cpu_physical_memory_write(req.data.addr, buf, req.data.len); | ||
198 | + ret = VBLK_RET_SUCCESS; | ||
199 | + break; | ||
200 | + case VBLK_DATA_FLAGS_WRITE: | ||
201 | + /* Not needed, iBoot only reads */ | ||
202 | + break; | ||
203 | + default: | ||
204 | + break; | ||
205 | + } | ||
206 | + | ||
207 | +out: | ||
208 | + g_free(buf); | ||
209 | + cpu_physical_memory_write(req.retval.addr, &ret, 1); | ||
210 | +} | ||
211 | + | ||
212 | +static void bdif_write(void *opaque, hwaddr offset, | ||
213 | + uint64_t value, unsigned size) | ||
214 | +{ | ||
215 | + VMAppleBdifState *s = opaque; | ||
216 | + uint64_t devid = (offset & REG_DEVID_MASK); | ||
217 | + | ||
218 | + trace_bdif_write(offset, size, value); | ||
219 | + | ||
220 | + switch (offset & ~REG_DEVID_MASK) { | ||
221 | + case REG_CMD: | ||
222 | + switch (devid) { | ||
223 | + case DEVID_ROOT: | ||
224 | + vblk_cmd(devid, s->root, value, 0x0); | ||
225 | + break; | ||
226 | + case DEVID_AUX: | ||
227 | + vblk_cmd(devid, s->aux, value, 0x0); | ||
228 | + break; | ||
229 | + } | ||
230 | + break; | ||
231 | + } | ||
232 | +} | ||
233 | + | ||
234 | +static const MemoryRegionOps bdif_ops = { | ||
235 | + .read = bdif_read, | ||
236 | + .write = bdif_write, | ||
237 | + .endianness = DEVICE_NATIVE_ENDIAN, | ||
238 | + .valid = { | ||
239 | + .min_access_size = 1, | ||
240 | + .max_access_size = 8, | ||
241 | + }, | ||
242 | + .impl = { | ||
243 | + .min_access_size = 1, | ||
244 | + .max_access_size = 8, | ||
245 | + }, | ||
246 | +}; | ||
247 | + | ||
248 | +static void bdif_init(Object *obj) | ||
249 | +{ | ||
250 | + VMAppleBdifState *s = VMAPPLE_BDIF(obj); | ||
251 | + | ||
252 | + memory_region_init_io(&s->mmio, obj, &bdif_ops, obj, | ||
253 | + "VMApple Backdoor Interface", VMAPPLE_BDIF_SIZE); | ||
254 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); | ||
255 | +} | ||
256 | + | ||
257 | +static Property bdif_properties[] = { | ||
258 | + DEFINE_PROP_DRIVE("aux", VMAppleBdifState, aux), | ||
259 | + DEFINE_PROP_DRIVE("root", VMAppleBdifState, root), | ||
260 | + DEFINE_PROP_END_OF_LIST(), | ||
261 | +}; | ||
262 | + | ||
263 | +static void bdif_class_init(ObjectClass *klass, void *data) | ||
264 | +{ | ||
265 | + DeviceClass *dc = DEVICE_CLASS(klass); | ||
266 | + | ||
267 | + dc->desc = "VMApple Backdoor Interface"; | ||
268 | + device_class_set_props(dc, bdif_properties); | ||
269 | +} | ||
270 | + | ||
271 | +static const TypeInfo bdif_info = { | ||
272 | + .name = TYPE_VMAPPLE_BDIF, | ||
273 | + .parent = TYPE_SYS_BUS_DEVICE, | ||
274 | + .instance_size = sizeof(VMAppleBdifState), | ||
275 | + .instance_init = bdif_init, | ||
276 | + .class_init = bdif_class_init, | ||
277 | +}; | ||
278 | + | ||
279 | +static void bdif_register_types(void) | ||
280 | +{ | ||
281 | + type_register_static(&bdif_info); | ||
282 | +} | ||
283 | + | ||
284 | +type_init(bdif_register_types) | ||
285 | diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build | ||
286 | index XXXXXXX..XXXXXXX 100644 | ||
287 | --- a/hw/vmapple/meson.build | ||
288 | +++ b/hw/vmapple/meson.build | ||
289 | @@ -1 +1,2 @@ | ||
290 | system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) | ||
291 | +system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c')) | ||
292 | diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events | ||
293 | index XXXXXXX..XXXXXXX 100644 | ||
294 | --- a/hw/vmapple/trace-events | ||
295 | +++ b/hw/vmapple/trace-events | ||
296 | @@ -XXX,XX +XXX,XX @@ aes_2_write_unknown(uint64_t offset) "offset=0x%"PRIx64 | ||
297 | aes_2_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 | ||
298 | aes_dump_data(const char *desc, const char *hex) "%s%s" | ||
299 | |||
300 | +# bdif.c | ||
301 | +bdif_read(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64 | ||
302 | +bdif_write(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64 | ||
303 | +bdif_vblk_read(const char *dev, uint64_t addr, uint64_t offset, uint32_t len, int r) "dev=%s addr=0x%"PRIx64" off=0x%"PRIx64" size=0x%x r=%d" | ||
304 | + | ||
305 | diff --git a/include/hw/vmapple/bdif.h b/include/hw/vmapple/bdif.h | ||
306 | new file mode 100644 | ||
307 | index XXXXXXX..XXXXXXX | ||
308 | --- /dev/null | ||
309 | +++ b/include/hw/vmapple/bdif.h | ||
310 | @@ -XXX,XX +XXX,XX @@ | ||
311 | +/* | ||
312 | + * VMApple Backdoor Interface | ||
313 | + * | ||
314 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
315 | + * | ||
316 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
317 | + * See the COPYING file in the top-level directory. | ||
318 | + */ | ||
319 | + | ||
320 | +#ifndef HW_VMAPPLE_BDIF_H | ||
321 | +#define HW_VMAPPLE_BDIF_H | ||
322 | + | ||
323 | +#include "hw/sysbus.h" | ||
324 | +#include "qom/object.h" | ||
325 | + | ||
326 | +#define TYPE_VMAPPLE_BDIF "vmapple-bdif" | ||
327 | +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF) | ||
328 | + | ||
329 | +struct VMAppleBdifState { | ||
330 | + /* <private> */ | ||
331 | + SysBusDevice parent_obj; | ||
332 | + | ||
333 | + /* <public> */ | ||
334 | + BlockBackend *aux; | ||
335 | + BlockBackend *root; | ||
336 | + MemoryRegion mmio; | ||
337 | +}; | ||
338 | + | ||
339 | +#define VMAPPLE_BDIF_SIZE 0x00200000 | ||
340 | + | ||
341 | +#endif /* HW_VMAPPLE_BDIF_H */ | ||
342 | -- | ||
343 | 2.39.3 (Apple Git-145) | ||
344 | |||
345 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alexander Graf <graf@amazon.com> | ||
2 | 1 | ||
3 | Instead of device tree or other more standardized means, VMApple passes | ||
4 | platform configuration to the first stage boot loader in a binary encoded | ||
5 | format that resides at a dedicated RAM region in physical address space. | ||
6 | |||
7 | This patch models this configuration space as a qdev device which we can | ||
8 | then map at the fixed location in the address space. That way, we can | ||
9 | influence and annotate all configuration fields easily. | ||
10 | |||
11 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
12 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
13 | |||
14 | --- | ||
15 | v3: | ||
16 | |||
17 | * Replaced legacy device reset method with Resettable method | ||
18 | |||
19 | hw/vmapple/Kconfig | 3 ++ | ||
20 | hw/vmapple/cfg.c | 106 +++++++++++++++++++++++++++++++++++++++ | ||
21 | hw/vmapple/meson.build | 1 + | ||
22 | include/hw/vmapple/cfg.h | 68 +++++++++++++++++++++++++ | ||
23 | 4 files changed, 178 insertions(+) | ||
24 | create mode 100644 hw/vmapple/cfg.c | ||
25 | create mode 100644 include/hw/vmapple/cfg.h | ||
26 | |||
27 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | ||
28 | index XXXXXXX..XXXXXXX 100644 | ||
29 | --- a/hw/vmapple/Kconfig | ||
30 | +++ b/hw/vmapple/Kconfig | ||
31 | @@ -XXX,XX +XXX,XX @@ config VMAPPLE_AES | ||
32 | config VMAPPLE_BDIF | ||
33 | bool | ||
34 | |||
35 | +config VMAPPLE_CFG | ||
36 | + bool | ||
37 | + | ||
38 | diff --git a/hw/vmapple/cfg.c b/hw/vmapple/cfg.c | ||
39 | new file mode 100644 | ||
40 | index XXXXXXX..XXXXXXX | ||
41 | --- /dev/null | ||
42 | +++ b/hw/vmapple/cfg.c | ||
43 | @@ -XXX,XX +XXX,XX @@ | ||
44 | +/* | ||
45 | + * VMApple Configuration Region | ||
46 | + * | ||
47 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
48 | + * | ||
49 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
50 | + * See the COPYING file in the top-level directory. | ||
51 | + */ | ||
52 | + | ||
53 | +#include "qemu/osdep.h" | ||
54 | +#include "hw/vmapple/cfg.h" | ||
55 | +#include "qemu/log.h" | ||
56 | +#include "qemu/module.h" | ||
57 | +#include "qapi/error.h" | ||
58 | + | ||
59 | +static void vmapple_cfg_reset(Object *obj, ResetType type) | ||
60 | +{ | ||
61 | + VMAppleCfgState *s = VMAPPLE_CFG(obj); | ||
62 | + VMAppleCfg *cfg; | ||
63 | + | ||
64 | + cfg = memory_region_get_ram_ptr(&s->mem); | ||
65 | + memset((void *)cfg, 0, VMAPPLE_CFG_SIZE); | ||
66 | + *cfg = s->cfg; | ||
67 | +} | ||
68 | + | ||
69 | +static void vmapple_cfg_realize(DeviceState *dev, Error **errp) | ||
70 | +{ | ||
71 | + VMAppleCfgState *s = VMAPPLE_CFG(dev); | ||
72 | + uint32_t i; | ||
73 | + | ||
74 | + strncpy(s->cfg.serial, s->serial, sizeof(s->cfg.serial)); | ||
75 | + strncpy(s->cfg.model, s->model, sizeof(s->cfg.model)); | ||
76 | + strncpy(s->cfg.soc_name, s->soc_name, sizeof(s->cfg.soc_name)); | ||
77 | + strncpy(s->cfg.unk8, "D/A", sizeof(s->cfg.soc_name)); | ||
78 | + s->cfg.ecid = cpu_to_be64(s->cfg.ecid); | ||
79 | + s->cfg.version = 2; | ||
80 | + s->cfg.unk1 = 1; | ||
81 | + s->cfg.unk2 = 1; | ||
82 | + s->cfg.unk3 = 0x20; | ||
83 | + s->cfg.unk4 = 0; | ||
84 | + s->cfg.unk5 = 1; | ||
85 | + s->cfg.unk6 = 1; | ||
86 | + s->cfg.unk7 = 0; | ||
87 | + s->cfg.unk10 = 1; | ||
88 | + | ||
89 | + g_assert(s->cfg.nr_cpus < ARRAY_SIZE(s->cfg.cpu_ids)); | ||
90 | + for (i = 0; i < s->cfg.nr_cpus; i++) { | ||
91 | + s->cfg.cpu_ids[i] = i; | ||
92 | + } | ||
93 | +} | ||
94 | + | ||
95 | +static void vmapple_cfg_init(Object *obj) | ||
96 | +{ | ||
97 | + VMAppleCfgState *s = VMAPPLE_CFG(obj); | ||
98 | + | ||
99 | + memory_region_init_ram(&s->mem, obj, "VMApple Config", VMAPPLE_CFG_SIZE, | ||
100 | + &error_fatal); | ||
101 | + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mem); | ||
102 | + | ||
103 | + s->serial = (char *)"1234"; | ||
104 | + s->model = (char *)"VM0001"; | ||
105 | + s->soc_name = (char *)"Apple M1 (Virtual)"; | ||
106 | +} | ||
107 | + | ||
108 | +static Property vmapple_cfg_properties[] = { | ||
109 | + DEFINE_PROP_UINT32("nr-cpus", VMAppleCfgState, cfg.nr_cpus, 1), | ||
110 | + DEFINE_PROP_UINT64("ecid", VMAppleCfgState, cfg.ecid, 0), | ||
111 | + DEFINE_PROP_UINT64("ram-size", VMAppleCfgState, cfg.ram_size, 0), | ||
112 | + DEFINE_PROP_UINT32("run_installer1", VMAppleCfgState, cfg.run_installer1, 0), | ||
113 | + DEFINE_PROP_UINT32("run_installer2", VMAppleCfgState, cfg.run_installer2, 0), | ||
114 | + DEFINE_PROP_UINT32("rnd", VMAppleCfgState, cfg.rnd, 0), | ||
115 | + DEFINE_PROP_MACADDR("mac-en0", VMAppleCfgState, cfg.mac_en0), | ||
116 | + DEFINE_PROP_MACADDR("mac-en1", VMAppleCfgState, cfg.mac_en1), | ||
117 | + DEFINE_PROP_MACADDR("mac-wifi0", VMAppleCfgState, cfg.mac_wifi0), | ||
118 | + DEFINE_PROP_MACADDR("mac-bt0", VMAppleCfgState, cfg.mac_bt0), | ||
119 | + DEFINE_PROP_STRING("serial", VMAppleCfgState, serial), | ||
120 | + DEFINE_PROP_STRING("model", VMAppleCfgState, model), | ||
121 | + DEFINE_PROP_STRING("soc_name", VMAppleCfgState, soc_name), | ||
122 | + DEFINE_PROP_END_OF_LIST(), | ||
123 | +}; | ||
124 | + | ||
125 | +static void vmapple_cfg_class_init(ObjectClass *klass, void *data) | ||
126 | +{ | ||
127 | + DeviceClass *dc = DEVICE_CLASS(klass); | ||
128 | + ResettableClass *rc = RESETTABLE_CLASS(klass); | ||
129 | + | ||
130 | + dc->realize = vmapple_cfg_realize; | ||
131 | + dc->desc = "VMApple Configuration Region"; | ||
132 | + device_class_set_props(dc, vmapple_cfg_properties); | ||
133 | + rc->phases.hold = vmapple_cfg_reset; | ||
134 | +} | ||
135 | + | ||
136 | +static const TypeInfo vmapple_cfg_info = { | ||
137 | + .name = TYPE_VMAPPLE_CFG, | ||
138 | + .parent = TYPE_SYS_BUS_DEVICE, | ||
139 | + .instance_size = sizeof(VMAppleCfgState), | ||
140 | + .instance_init = vmapple_cfg_init, | ||
141 | + .class_init = vmapple_cfg_class_init, | ||
142 | +}; | ||
143 | + | ||
144 | +static void vmapple_cfg_register_types(void) | ||
145 | +{ | ||
146 | + type_register_static(&vmapple_cfg_info); | ||
147 | +} | ||
148 | + | ||
149 | +type_init(vmapple_cfg_register_types) | ||
150 | diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build | ||
151 | index XXXXXXX..XXXXXXX 100644 | ||
152 | --- a/hw/vmapple/meson.build | ||
153 | +++ b/hw/vmapple/meson.build | ||
154 | @@ -XXX,XX +XXX,XX @@ | ||
155 | system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) | ||
156 | system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c')) | ||
157 | +system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c')) | ||
158 | diff --git a/include/hw/vmapple/cfg.h b/include/hw/vmapple/cfg.h | ||
159 | new file mode 100644 | ||
160 | index XXXXXXX..XXXXXXX | ||
161 | --- /dev/null | ||
162 | +++ b/include/hw/vmapple/cfg.h | ||
163 | @@ -XXX,XX +XXX,XX @@ | ||
164 | +/* | ||
165 | + * VMApple Configuration Region | ||
166 | + * | ||
167 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
168 | + * | ||
169 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
170 | + * See the COPYING file in the top-level directory. | ||
171 | + */ | ||
172 | + | ||
173 | +#ifndef HW_VMAPPLE_CFG_H | ||
174 | +#define HW_VMAPPLE_CFG_H | ||
175 | + | ||
176 | +#include "hw/sysbus.h" | ||
177 | +#include "qom/object.h" | ||
178 | +#include "net/net.h" | ||
179 | + | ||
180 | +typedef struct VMAppleCfg { | ||
181 | + uint32_t version; /* 0x000 */ | ||
182 | + uint32_t nr_cpus; /* 0x004 */ | ||
183 | + uint32_t unk1; /* 0x008 */ | ||
184 | + uint32_t unk2; /* 0x00c */ | ||
185 | + uint32_t unk3; /* 0x010 */ | ||
186 | + uint32_t unk4; /* 0x014 */ | ||
187 | + uint64_t ecid; /* 0x018 */ | ||
188 | + uint64_t ram_size; /* 0x020 */ | ||
189 | + uint32_t run_installer1; /* 0x028 */ | ||
190 | + uint32_t unk5; /* 0x02c */ | ||
191 | + uint32_t unk6; /* 0x030 */ | ||
192 | + uint32_t run_installer2; /* 0x034 */ | ||
193 | + uint32_t rnd; /* 0x038 */ | ||
194 | + uint32_t unk7; /* 0x03c */ | ||
195 | + MACAddr mac_en0; /* 0x040 */ | ||
196 | + uint8_t pad1[2]; | ||
197 | + MACAddr mac_en1; /* 0x048 */ | ||
198 | + uint8_t pad2[2]; | ||
199 | + MACAddr mac_wifi0; /* 0x050 */ | ||
200 | + uint8_t pad3[2]; | ||
201 | + MACAddr mac_bt0; /* 0x058 */ | ||
202 | + uint8_t pad4[2]; | ||
203 | + uint8_t reserved[0xa0]; /* 0x060 */ | ||
204 | + uint32_t cpu_ids[0x80]; /* 0x100 */ | ||
205 | + uint8_t scratch[0x200]; /* 0x180 */ | ||
206 | + char serial[32]; /* 0x380 */ | ||
207 | + char unk8[32]; /* 0x3a0 */ | ||
208 | + char model[32]; /* 0x3c0 */ | ||
209 | + uint8_t unk9[32]; /* 0x3e0 */ | ||
210 | + uint32_t unk10; /* 0x400 */ | ||
211 | + char soc_name[32]; /* 0x404 */ | ||
212 | +} VMAppleCfg; | ||
213 | + | ||
214 | +#define TYPE_VMAPPLE_CFG "vmapple-cfg" | ||
215 | +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleCfgState, VMAPPLE_CFG) | ||
216 | + | ||
217 | +struct VMAppleCfgState { | ||
218 | + /* <private> */ | ||
219 | + SysBusDevice parent_obj; | ||
220 | + VMAppleCfg cfg; | ||
221 | + | ||
222 | + /* <public> */ | ||
223 | + MemoryRegion mem; | ||
224 | + char *serial; | ||
225 | + char *model; | ||
226 | + char *soc_name; | ||
227 | +}; | ||
228 | + | ||
229 | +#define VMAPPLE_CFG_SIZE 0x00010000 | ||
230 | + | ||
231 | +#endif /* HW_VMAPPLE_CFG_H */ | ||
232 | -- | ||
233 | 2.39.3 (Apple Git-145) | ||
234 | |||
235 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alexander Graf <graf@amazon.com> | ||
2 | 1 | ||
3 | Apple has its own virtio-blk PCI device ID where it deviates from the | ||
4 | official virtio-pci spec slightly: It puts a new "apple type" | ||
5 | field at a static offset in config space and introduces a new barrier | ||
6 | command. | ||
7 | |||
8 | This patch first creates a mechanism for virtio-blk downstream classes to | ||
9 | handle unknown commands. It then creates such a downstream class and a new | ||
10 | vmapple-virtio-blk-pci class which support the additional apple type config | ||
11 | identifier as well as the barrier command. | ||
12 | |||
13 | It then exposes 2 subclasses from that that we can use to expose root and | ||
14 | aux virtio-blk devices: "vmapple-virtio-root" and "vmapple-virtio-aux". | ||
15 | |||
16 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
17 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
18 | --- | ||
19 | hw/block/virtio-blk.c | 19 ++- | ||
20 | hw/vmapple/Kconfig | 3 + | ||
21 | hw/vmapple/meson.build | 1 + | ||
22 | hw/vmapple/virtio-blk.c | 212 ++++++++++++++++++++++++++++++++ | ||
23 | include/hw/pci/pci_ids.h | 1 + | ||
24 | include/hw/virtio/virtio-blk.h | 12 +- | ||
25 | include/hw/vmapple/virtio-blk.h | 39 ++++++ | ||
26 | 7 files changed, 282 insertions(+), 5 deletions(-) | ||
27 | create mode 100644 hw/vmapple/virtio-blk.c | ||
28 | create mode 100644 include/hw/vmapple/virtio-blk.h | ||
29 | |||
30 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c | ||
31 | index XXXXXXX..XXXXXXX 100644 | ||
32 | --- a/hw/block/virtio-blk.c | ||
33 | +++ b/hw/block/virtio-blk.c | ||
34 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, | ||
35 | req->mr_next = NULL; | ||
36 | } | ||
37 | |||
38 | -static void virtio_blk_free_request(VirtIOBlockReq *req) | ||
39 | +void virtio_blk_free_request(VirtIOBlockReq *req) | ||
40 | { | ||
41 | g_free(req); | ||
42 | } | ||
43 | |||
44 | -static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) | ||
45 | +void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) | ||
46 | { | ||
47 | VirtIOBlock *s = req->dev; | ||
48 | VirtIODevice *vdev = VIRTIO_DEVICE(s); | ||
49 | @@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) | ||
50 | break; | ||
51 | } | ||
52 | default: | ||
53 | - virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); | ||
54 | - virtio_blk_free_request(req); | ||
55 | + { | ||
56 | + /* | ||
57 | + * Give subclasses a chance to handle unknown requests. This way the | ||
58 | + * class lookup is not in the hot path. | ||
59 | + */ | ||
60 | + VirtIOBlkClass *vbk = VIRTIO_BLK_GET_CLASS(s); | ||
61 | + if (!vbk->handle_unknown_request || | ||
62 | + !vbk->handle_unknown_request(req, mrb, type)) { | ||
63 | + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); | ||
64 | + virtio_blk_free_request(req); | ||
65 | + } | ||
66 | + } | ||
67 | } | ||
68 | return 0; | ||
69 | } | ||
70 | @@ -XXX,XX +XXX,XX @@ static const TypeInfo virtio_blk_info = { | ||
71 | .instance_size = sizeof(VirtIOBlock), | ||
72 | .instance_init = virtio_blk_instance_init, | ||
73 | .class_init = virtio_blk_class_init, | ||
74 | + .class_size = sizeof(VirtIOBlkClass), | ||
75 | }; | ||
76 | |||
77 | static void virtio_register_types(void) | ||
78 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | ||
79 | index XXXXXXX..XXXXXXX 100644 | ||
80 | --- a/hw/vmapple/Kconfig | ||
81 | +++ b/hw/vmapple/Kconfig | ||
82 | @@ -XXX,XX +XXX,XX @@ config VMAPPLE_BDIF | ||
83 | config VMAPPLE_CFG | ||
84 | bool | ||
85 | |||
86 | +config VMAPPLE_VIRTIO_BLK | ||
87 | + bool | ||
88 | + | ||
89 | diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build | ||
90 | index XXXXXXX..XXXXXXX 100644 | ||
91 | --- a/hw/vmapple/meson.build | ||
92 | +++ b/hw/vmapple/meson.build | ||
93 | @@ -XXX,XX +XXX,XX @@ | ||
94 | system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) | ||
95 | system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c')) | ||
96 | system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c')) | ||
97 | +system_ss.add(when: 'CONFIG_VMAPPLE_VIRTIO_BLK', if_true: files('virtio-blk.c')) | ||
98 | diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c | ||
99 | new file mode 100644 | ||
100 | index XXXXXXX..XXXXXXX | ||
101 | --- /dev/null | ||
102 | +++ b/hw/vmapple/virtio-blk.c | ||
103 | @@ -XXX,XX +XXX,XX @@ | ||
104 | +/* | ||
105 | + * VMApple specific VirtIO Block implementation | ||
106 | + * | ||
107 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
108 | + * | ||
109 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
110 | + * See the COPYING file in the top-level directory. | ||
111 | + * | ||
112 | + * VMApple uses almost standard VirtIO Block, but with a few key differences: | ||
113 | + * | ||
114 | + * - Different PCI device/vendor ID | ||
115 | + * - An additional "type" identifier to differentiate AUX and Root volumes | ||
116 | + * - An additional BARRIER command | ||
117 | + */ | ||
118 | + | ||
119 | +#include "qemu/osdep.h" | ||
120 | +#include "hw/vmapple/virtio-blk.h" | ||
121 | +#include "qemu/log.h" | ||
122 | +#include "qemu/module.h" | ||
123 | +#include "qapi/error.h" | ||
124 | + | ||
125 | +#define VIRTIO_BLK_T_APPLE_BARRIER 0x10000 | ||
126 | + | ||
127 | +#define VIRTIO_APPLE_TYPE_ROOT 1 | ||
128 | +#define VIRTIO_APPLE_TYPE_AUX 2 | ||
129 | + | ||
130 | +static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req, | ||
131 | + MultiReqBuffer *mrb, | ||
132 | + uint32_t type) | ||
133 | +{ | ||
134 | + switch (type) { | ||
135 | + case VIRTIO_BLK_T_APPLE_BARRIER: | ||
136 | + /* We ignore barriers for now. YOLO. */ | ||
137 | + virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); | ||
138 | + virtio_blk_free_request(req); | ||
139 | + return true; | ||
140 | + default: | ||
141 | + return false; | ||
142 | + } | ||
143 | +} | ||
144 | + | ||
145 | +/* | ||
146 | + * VMApple virtio-blk uses the same config format as normal virtio, with one | ||
147 | + * exception: It adds an "apple type" specififer at the same location that | ||
148 | + * the spec reserves for max_secure_erase_sectors. Let's hook into the | ||
149 | + * get_config code path here, run it as usual and then patch in the apple type. | ||
150 | + */ | ||
151 | +static void vmapple_virtio_blk_get_config(VirtIODevice *vdev, uint8_t *config) | ||
152 | +{ | ||
153 | + VMAppleVirtIOBlk *dev = VMAPPLE_VIRTIO_BLK(vdev); | ||
154 | + VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_GET_CLASS(dev); | ||
155 | + struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config; | ||
156 | + | ||
157 | + vvbk->get_config(vdev, config); | ||
158 | + | ||
159 | + g_assert(dev->parent_obj.config_size >= endof(struct virtio_blk_config, zoned)); | ||
160 | + | ||
161 | + /* Apple abuses the field for max_secure_erase_sectors as type id */ | ||
162 | + blkcfg->max_secure_erase_sectors = dev->apple_type; | ||
163 | +} | ||
164 | + | ||
165 | +static Property vmapple_virtio_blk_properties[] = { | ||
166 | + DEFINE_PROP_UINT32("apple-type", VMAppleVirtIOBlk, apple_type, 0), | ||
167 | + DEFINE_PROP_END_OF_LIST(), | ||
168 | +}; | ||
169 | + | ||
170 | +static void vmapple_virtio_blk_class_init(ObjectClass *klass, void *data) | ||
171 | +{ | ||
172 | + DeviceClass *dc = DEVICE_CLASS(klass); | ||
173 | + VirtIOBlkClass *vbk = VIRTIO_BLK_CLASS(klass); | ||
174 | + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | ||
175 | + VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_CLASS(klass); | ||
176 | + | ||
177 | + vbk->handle_unknown_request = vmapple_virtio_blk_handle_unknown_request; | ||
178 | + vvbk->get_config = vdc->get_config; | ||
179 | + vdc->get_config = vmapple_virtio_blk_get_config; | ||
180 | + device_class_set_props(dc, vmapple_virtio_blk_properties); | ||
181 | +} | ||
182 | + | ||
183 | +static const TypeInfo vmapple_virtio_blk_info = { | ||
184 | + .name = TYPE_VMAPPLE_VIRTIO_BLK, | ||
185 | + .parent = TYPE_VIRTIO_BLK, | ||
186 | + .instance_size = sizeof(VMAppleVirtIOBlk), | ||
187 | + .class_init = vmapple_virtio_blk_class_init, | ||
188 | +}; | ||
189 | + | ||
190 | +/* PCI Devices */ | ||
191 | + | ||
192 | +typedef struct VMAppleVirtIOBlkPCI { | ||
193 | + VirtIOPCIProxy parent_obj; | ||
194 | + VMAppleVirtIOBlk vdev; | ||
195 | + uint32_t apple_type; | ||
196 | +} VMAppleVirtIOBlkPCI; | ||
197 | + | ||
198 | +/* | ||
199 | + * vmapple-virtio-blk-pci: This extends VirtioPCIProxy. | ||
200 | + */ | ||
201 | +#define TYPE_VMAPPLE_VIRTIO_BLK_PCI "vmapple-virtio-blk-pci-base" | ||
202 | +DECLARE_INSTANCE_CHECKER(VMAppleVirtIOBlkPCI, VMAPPLE_VIRTIO_BLK_PCI, | ||
203 | + TYPE_VMAPPLE_VIRTIO_BLK_PCI) | ||
204 | + | ||
205 | +static Property vmapple_virtio_blk_pci_properties[] = { | ||
206 | + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), | ||
207 | + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, | ||
208 | + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), | ||
209 | + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, | ||
210 | + DEV_NVECTORS_UNSPECIFIED), | ||
211 | + DEFINE_PROP_END_OF_LIST(), | ||
212 | +}; | ||
213 | + | ||
214 | +static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) | ||
215 | +{ | ||
216 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev); | ||
217 | + DeviceState *vdev = DEVICE(&dev->vdev); | ||
218 | + VirtIOBlkConf *conf = &dev->vdev.parent_obj.conf; | ||
219 | + | ||
220 | + if (conf->num_queues == VIRTIO_BLK_AUTO_NUM_QUEUES) { | ||
221 | + conf->num_queues = virtio_pci_optimal_num_queues(0); | ||
222 | + } | ||
223 | + | ||
224 | + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { | ||
225 | + vpci_dev->nvectors = conf->num_queues + 1; | ||
226 | + } | ||
227 | + | ||
228 | + /* | ||
229 | + * We don't support zones, but we need the additional config space size. | ||
230 | + * Let's just expose the feature so the rest of the virtio-blk logic | ||
231 | + * allocates enough space for us. The guest will ignore zones anyway. | ||
232 | + */ | ||
233 | + virtio_add_feature(&dev->vdev.parent_obj.host_features, VIRTIO_BLK_F_ZONED); | ||
234 | + /* Propagate the apple type down to the virtio-blk device */ | ||
235 | + qdev_prop_set_uint32(DEVICE(&dev->vdev), "apple-type", dev->apple_type); | ||
236 | + /* and spawn the virtio-blk device */ | ||
237 | + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); | ||
238 | + | ||
239 | + /* | ||
240 | + * The virtio-pci machinery adjusts its vendor/device ID based on whether | ||
241 | + * we support modern or legacy virtio. Let's patch it back to the Apple | ||
242 | + * identifiers here. | ||
243 | + */ | ||
244 | + pci_config_set_vendor_id(vpci_dev->pci_dev.config, PCI_VENDOR_ID_APPLE); | ||
245 | + pci_config_set_device_id(vpci_dev->pci_dev.config, PCI_DEVICE_ID_APPLE_VIRTIO_BLK); | ||
246 | +} | ||
247 | + | ||
248 | +static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass, void *data) | ||
249 | +{ | ||
250 | + DeviceClass *dc = DEVICE_CLASS(klass); | ||
251 | + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); | ||
252 | + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); | ||
253 | + | ||
254 | + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); | ||
255 | + device_class_set_props(dc, vmapple_virtio_blk_pci_properties); | ||
256 | + k->realize = vmapple_virtio_blk_pci_realize; | ||
257 | + pcidev_k->vendor_id = PCI_VENDOR_ID_APPLE; | ||
258 | + pcidev_k->device_id = PCI_DEVICE_ID_APPLE_VIRTIO_BLK; | ||
259 | + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; | ||
260 | + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; | ||
261 | +} | ||
262 | + | ||
263 | +static void vmapple_virtio_blk_pci_instance_init(Object *obj) | ||
264 | +{ | ||
265 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj); | ||
266 | + | ||
267 | + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), | ||
268 | + TYPE_VMAPPLE_VIRTIO_BLK); | ||
269 | +} | ||
270 | + | ||
271 | +static const VirtioPCIDeviceTypeInfo vmapple_virtio_blk_pci_info = { | ||
272 | + .base_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI, | ||
273 | + .generic_name = "vmapple-virtio-blk-pci", | ||
274 | + .instance_size = sizeof(VMAppleVirtIOBlkPCI), | ||
275 | + .instance_init = vmapple_virtio_blk_pci_instance_init, | ||
276 | + .class_init = vmapple_virtio_blk_pci_class_init, | ||
277 | +}; | ||
278 | + | ||
279 | +static void vmapple_virtio_root_instance_init(Object *obj) | ||
280 | +{ | ||
281 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj); | ||
282 | + | ||
283 | + dev->apple_type = VIRTIO_APPLE_TYPE_ROOT; | ||
284 | +} | ||
285 | + | ||
286 | +static const TypeInfo vmapple_virtio_root_info = { | ||
287 | + .name = TYPE_VMAPPLE_VIRTIO_ROOT, | ||
288 | + .parent = "vmapple-virtio-blk-pci", | ||
289 | + .instance_size = sizeof(VMAppleVirtIOBlkPCI), | ||
290 | + .instance_init = vmapple_virtio_root_instance_init, | ||
291 | +}; | ||
292 | + | ||
293 | +static void vmapple_virtio_aux_instance_init(Object *obj) | ||
294 | +{ | ||
295 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj); | ||
296 | + | ||
297 | + dev->apple_type = VIRTIO_APPLE_TYPE_AUX; | ||
298 | +} | ||
299 | + | ||
300 | +static const TypeInfo vmapple_virtio_aux_info = { | ||
301 | + .name = TYPE_VMAPPLE_VIRTIO_AUX, | ||
302 | + .parent = "vmapple-virtio-blk-pci", | ||
303 | + .instance_size = sizeof(VMAppleVirtIOBlkPCI), | ||
304 | + .instance_init = vmapple_virtio_aux_instance_init, | ||
305 | +}; | ||
306 | + | ||
307 | +static void vmapple_virtio_blk_register_types(void) | ||
308 | +{ | ||
309 | + type_register_static(&vmapple_virtio_blk_info); | ||
310 | + virtio_pci_types_register(&vmapple_virtio_blk_pci_info); | ||
311 | + type_register_static(&vmapple_virtio_root_info); | ||
312 | + type_register_static(&vmapple_virtio_aux_info); | ||
313 | +} | ||
314 | + | ||
315 | +type_init(vmapple_virtio_blk_register_types) | ||
316 | diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h | ||
317 | index XXXXXXX..XXXXXXX 100644 | ||
318 | --- a/include/hw/pci/pci_ids.h | ||
319 | +++ b/include/hw/pci/pci_ids.h | ||
320 | @@ -XXX,XX +XXX,XX @@ | ||
321 | #define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 | ||
322 | #define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b | ||
323 | #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 | ||
324 | +#define PCI_DEVICE_ID_APPLE_VIRTIO_BLK 0x1a00 | ||
325 | |||
326 | #define PCI_VENDOR_ID_SUN 0x108e | ||
327 | #define PCI_DEVICE_ID_SUN_EBUS 0x1000 | ||
328 | diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h | ||
329 | index XXXXXXX..XXXXXXX 100644 | ||
330 | --- a/include/hw/virtio/virtio-blk.h | ||
331 | +++ b/include/hw/virtio/virtio-blk.h | ||
332 | @@ -XXX,XX +XXX,XX @@ | ||
333 | #include "qapi/qapi-types-virtio.h" | ||
334 | |||
335 | #define TYPE_VIRTIO_BLK "virtio-blk-device" | ||
336 | -OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBlock, VIRTIO_BLK) | ||
337 | +OBJECT_DECLARE_TYPE(VirtIOBlock, VirtIOBlkClass, VIRTIO_BLK) | ||
338 | |||
339 | /* This is the last element of the write scatter-gather list */ | ||
340 | struct virtio_blk_inhdr | ||
341 | @@ -XXX,XX +XXX,XX @@ typedef struct MultiReqBuffer { | ||
342 | bool is_write; | ||
343 | } MultiReqBuffer; | ||
344 | |||
345 | +typedef struct VirtIOBlkClass { | ||
346 | + /*< private >*/ | ||
347 | + VirtioDeviceClass parent; | ||
348 | + /*< public >*/ | ||
349 | + bool (*handle_unknown_request)(VirtIOBlockReq *req, MultiReqBuffer *mrb, | ||
350 | + uint32_t type); | ||
351 | +} VirtIOBlkClass; | ||
352 | + | ||
353 | void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq); | ||
354 | +void virtio_blk_free_request(VirtIOBlockReq *req); | ||
355 | +void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status); | ||
356 | |||
357 | #endif | ||
358 | diff --git a/include/hw/vmapple/virtio-blk.h b/include/hw/vmapple/virtio-blk.h | ||
359 | new file mode 100644 | ||
360 | index XXXXXXX..XXXXXXX | ||
361 | --- /dev/null | ||
362 | +++ b/include/hw/vmapple/virtio-blk.h | ||
363 | @@ -XXX,XX +XXX,XX @@ | ||
364 | +/* | ||
365 | + * VMApple specific VirtIO Block implementation | ||
366 | + * | ||
367 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
368 | + * | ||
369 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
370 | + * See the COPYING file in the top-level directory. | ||
371 | + */ | ||
372 | + | ||
373 | +#ifndef HW_VMAPPLE_CFG_H | ||
374 | +#define HW_VMAPPLE_CFG_H | ||
375 | + | ||
376 | +#include "hw/sysbus.h" | ||
377 | +#include "qom/object.h" | ||
378 | +#include "hw/virtio/virtio-pci.h" | ||
379 | +#include "hw/virtio/virtio-blk.h" | ||
380 | + | ||
381 | +#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk" | ||
382 | +#define TYPE_VMAPPLE_VIRTIO_ROOT "vmapple-virtio-root" | ||
383 | +#define TYPE_VMAPPLE_VIRTIO_AUX "vmapple-virtio-aux" | ||
384 | + | ||
385 | +OBJECT_DECLARE_TYPE(VMAppleVirtIOBlk, VMAppleVirtIOBlkClass, VMAPPLE_VIRTIO_BLK) | ||
386 | + | ||
387 | +typedef struct VMAppleVirtIOBlkClass { | ||
388 | + /*< private >*/ | ||
389 | + VirtIOBlkClass parent; | ||
390 | + /*< public >*/ | ||
391 | + void (*get_config)(VirtIODevice *vdev, uint8_t *config); | ||
392 | +} VMAppleVirtIOBlkClass; | ||
393 | + | ||
394 | +typedef struct VMAppleVirtIOBlk { | ||
395 | + /* <private> */ | ||
396 | + VirtIOBlock parent_obj; | ||
397 | + | ||
398 | + /* <public> */ | ||
399 | + uint32_t apple_type; | ||
400 | +} VMAppleVirtIOBlk; | ||
401 | + | ||
402 | +#endif /* HW_VMAPPLE_CFG_H */ | ||
403 | -- | ||
404 | 2.39.3 (Apple Git-145) | ||
405 | |||
406 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alexander Graf <graf@amazon.com> | ||
2 | 1 | ||
3 | Apple defines a new "vmapple" machine type as part of its proprietary | ||
4 | macOS Virtualization.Framework vmm. This machine type is similar to the | ||
5 | virt one, but with subtle differences in base devices, a few special | ||
6 | vmapple device additions and a vastly different boot chain. | ||
7 | |||
8 | This patch reimplements this machine type in QEMU. To use it, you | ||
9 | have to have a readily installed version of macOS for VMApple, | ||
10 | run on macOS with -accel hvf, pass the Virtualization.Framework | ||
11 | boot rom (AVPBooter) in via -bios, pass the aux and root volume as pflash | ||
12 | and pass aux and root volume as virtio drives. In addition, you also | ||
13 | need to find the machine UUID and pass that as -M vmapple,uuid= parameter: | ||
14 | |||
15 | $ qemu-system-aarch64 -accel hvf -M vmapple,uuid=0x1234 -m 4G \ | ||
16 | -bios /System/Library/Frameworks/Virtualization.framework/Versions/A/Resources/AVPBooter.vmapple2.bin | ||
17 | -drive file=aux,if=pflash,format=raw \ | ||
18 | -drive file=root,if=pflash,format=raw \ | ||
19 | -drive file=aux,if=none,id=aux,format=raw \ | ||
20 | -device vmapple-virtio-aux,drive=aux \ | ||
21 | -drive file=root,if=none,id=root,format=raw \ | ||
22 | -device vmapple-virtio-root,drive=root | ||
23 | |||
24 | With all these in place, you should be able to see macOS booting | ||
25 | successfully. | ||
26 | |||
27 | Known issues: | ||
28 | - Keyboard and mouse/tablet input is laggy. The reason for this is | ||
29 | either that macOS's XHCI driver is broken when the device/platform | ||
30 | does not support MSI/MSI-X, or there's some unfortunate interplay | ||
31 | with Qemu's XHCI implementation in this scenario. | ||
32 | - Currently only macOS 12 guests are supported. The boot process for | ||
33 | 13+ will need further investigation and adjustment. | ||
34 | |||
35 | Signed-off-by: Alexander Graf <graf@amazon.com> | ||
36 | Co-authored-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
37 | Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> | ||
38 | |||
39 | --- | ||
40 | v3: | ||
41 | * Rebased on latest upstream, updated affinity and NIC creation | ||
42 | API usage | ||
43 | * Included Apple-variant virtio-blk in build dependency | ||
44 | * Updated API usage for setting 'redist-region-count' array-typed property on GIC. | ||
45 | * Switched from virtio HID devices (for which macOS 12 does not contain drivers) to an XHCI USB controller and USB HID devices. | ||
46 | |||
47 | MAINTAINERS | 1 + | ||
48 | docs/system/arm/vmapple.rst | 63 ++++ | ||
49 | docs/system/target-arm.rst | 1 + | ||
50 | hw/vmapple/Kconfig | 20 ++ | ||
51 | hw/vmapple/meson.build | 1 + | ||
52 | hw/vmapple/vmapple.c | 661 ++++++++++++++++++++++++++++++++++++ | ||
53 | 6 files changed, 747 insertions(+) | ||
54 | create mode 100644 docs/system/arm/vmapple.rst | ||
55 | create mode 100644 hw/vmapple/vmapple.c | ||
56 | |||
57 | diff --git a/MAINTAINERS b/MAINTAINERS | ||
58 | index XXXXXXX..XXXXXXX 100644 | ||
59 | --- a/MAINTAINERS | ||
60 | +++ b/MAINTAINERS | ||
61 | @@ -XXX,XX +XXX,XX @@ R: Phil Dennis-Jordan <phil@philjordan.eu> | ||
62 | S: Maintained | ||
63 | F: hw/vmapple/* | ||
64 | F: include/hw/vmapple/* | ||
65 | +F: docs/system/arm/vmapple.rst | ||
66 | |||
67 | Subsystems | ||
68 | ---------- | ||
69 | diff --git a/docs/system/arm/vmapple.rst b/docs/system/arm/vmapple.rst | ||
70 | new file mode 100644 | ||
71 | index XXXXXXX..XXXXXXX | ||
72 | --- /dev/null | ||
73 | +++ b/docs/system/arm/vmapple.rst | ||
74 | @@ -XXX,XX +XXX,XX @@ | ||
75 | +VMApple machine emulation | ||
76 | +======================================================================================== | ||
77 | + | ||
78 | +VMApple is the device model that the macOS built-in hypervisor called "Virtualization.framework" | ||
79 | +exposes to Apple Silicon macOS guests. The "vmapple" machine model in QEMU implements the same | ||
80 | +device model, but does not use any code from Virtualization.Framework. | ||
81 | + | ||
82 | +Prerequisites | ||
83 | +------------- | ||
84 | + | ||
85 | +To run the vmapple machine model, you need to | ||
86 | + | ||
87 | + * Run on Apple Silicon | ||
88 | + * Run on macOS 12.0 or above | ||
89 | + * Have an already installed copy of a Virtualization.Framework macOS 12 virtual machine. I will | ||
90 | + assume that you installed it using the macosvm CLI. | ||
91 | + | ||
92 | +First, we need to extract the UUID from the virtual machine that you installed. You can do this | ||
93 | +by running the following shell script: | ||
94 | + | ||
95 | +.. code-block:: bash | ||
96 | + :caption: uuid.sh script to extract the UUID from a macosvm.json file | ||
97 | + | ||
98 | + #!/bin/bash | ||
99 | + | ||
100 | + MID=$(cat "$1" | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj["machineId"]);') | ||
101 | + echo "$MID" | base64 -d | plutil -extract ECID raw - | ||
102 | + | ||
103 | +Now we also need to trim the aux partition. It contains metadata that we can just discard: | ||
104 | + | ||
105 | +.. code-block:: bash | ||
106 | + :caption: Command to trim the aux file | ||
107 | + | ||
108 | + $ dd if="aux.img" of="aux.img.trimmed" bs=$(( 0x4000 )) skip=1 | ||
109 | + | ||
110 | +How to run | ||
111 | +---------- | ||
112 | + | ||
113 | +Then, we can launch QEMU with the Virtualization.Framework pre-boot environment and the readily | ||
114 | +installed target disk images. I recommend to port forward the VM's ssh and vnc ports to the host | ||
115 | +to get better interactive access into the target system: | ||
116 | + | ||
117 | +.. code-block:: bash | ||
118 | + :caption: Example execution command line | ||
119 | + | ||
120 | + $ UUID=$(uuid.sh macosvm.json) | ||
121 | + $ AVPBOOTER=/System/Library/Frameworks/Virtualization.framework/Resources/AVPBooter.vmapple2.bin | ||
122 | + $ AUX=aux.img.trimmed | ||
123 | + $ DISK=disk.img | ||
124 | + $ qemu-system-aarch64 \ | ||
125 | + -serial mon:stdio \ | ||
126 | + -m 4G \ | ||
127 | + -accel hvf \ | ||
128 | + -M vmapple,uuid=$UUID \ | ||
129 | + -bios $AVPBOOTER \ | ||
130 | + -drive file="$AUX",if=pflash,format=raw \ | ||
131 | + -drive file="$DISK",if=pflash,format=raw \ | ||
132 | + -drive file="$AUX",if=none,id=aux,format=raw \ | ||
133 | + -drive file="$DISK",if=none,id=root,format=raw \ | ||
134 | + -device vmapple-virtio-aux,drive=aux \ | ||
135 | + -device vmapple-virtio-root,drive=root \ | ||
136 | + -net user,ipv6=off,hostfwd=tcp::2222-:22,hostfwd=tcp::5901-:5900 \ | ||
137 | + -net nic,model=virtio-net-pci \ | ||
138 | diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst | ||
139 | index XXXXXXX..XXXXXXX 100644 | ||
140 | --- a/docs/system/target-arm.rst | ||
141 | +++ b/docs/system/target-arm.rst | ||
142 | @@ -XXX,XX +XXX,XX @@ undocumented; you can get a complete list by running | ||
143 | arm/stellaris | ||
144 | arm/stm32 | ||
145 | arm/virt | ||
146 | + arm/vmapple | ||
147 | arm/xenpvh | ||
148 | arm/xlnx-versal-virt | ||
149 | arm/xlnx-zynq | ||
150 | diff --git a/hw/vmapple/Kconfig b/hw/vmapple/Kconfig | ||
151 | index XXXXXXX..XXXXXXX 100644 | ||
152 | --- a/hw/vmapple/Kconfig | ||
153 | +++ b/hw/vmapple/Kconfig | ||
154 | @@ -XXX,XX +XXX,XX @@ config VMAPPLE_CFG | ||
155 | config VMAPPLE_VIRTIO_BLK | ||
156 | bool | ||
157 | |||
158 | +config VMAPPLE | ||
159 | + bool | ||
160 | + depends on ARM | ||
161 | + depends on HVF | ||
162 | + default y if ARM | ||
163 | + imply PCI_DEVICES | ||
164 | + select ARM_GIC | ||
165 | + select PLATFORM_BUS | ||
166 | + select PCI_EXPRESS | ||
167 | + select PCI_EXPRESS_GENERIC_BRIDGE | ||
168 | + select PL011 # UART | ||
169 | + select PL031 # RTC | ||
170 | + select PL061 # GPIO | ||
171 | + select GPIO_PWR | ||
172 | + select PVPANIC_MMIO | ||
173 | + select VMAPPLE_AES | ||
174 | + select VMAPPLE_BDIF | ||
175 | + select VMAPPLE_CFG | ||
176 | + select MAC_PVG_VMAPPLE | ||
177 | + select VMAPPLE_VIRTIO_BLK | ||
178 | diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build | ||
179 | index XXXXXXX..XXXXXXX 100644 | ||
180 | --- a/hw/vmapple/meson.build | ||
181 | +++ b/hw/vmapple/meson.build | ||
182 | @@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) | ||
183 | system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c')) | ||
184 | system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c')) | ||
185 | system_ss.add(when: 'CONFIG_VMAPPLE_VIRTIO_BLK', if_true: files('virtio-blk.c')) | ||
186 | +specific_ss.add(when: 'CONFIG_VMAPPLE', if_true: files('vmapple.c')) | ||
187 | diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c | ||
188 | new file mode 100644 | ||
189 | index XXXXXXX..XXXXXXX | ||
190 | --- /dev/null | ||
191 | +++ b/hw/vmapple/vmapple.c | ||
192 | @@ -XXX,XX +XXX,XX @@ | ||
193 | +/* | ||
194 | + * VMApple machine emulation | ||
195 | + * | ||
196 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
197 | + * | ||
198 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
199 | + * See the COPYING file in the top-level directory. | ||
200 | + * | ||
201 | + * VMApple is the device model that the macOS built-in hypervisor called | ||
202 | + * "Virtualization.framework" exposes to Apple Silicon macOS guests. The | ||
203 | + * machine model in this file implements the same device model in QEMU, but | ||
204 | + * does not use any code from Virtualization.Framework. | ||
205 | + */ | ||
206 | + | ||
207 | +#include "qemu/osdep.h" | ||
208 | +#include "qemu/help-texts.h" | ||
209 | +#include "qemu/datadir.h" | ||
210 | +#include "qemu/units.h" | ||
211 | +#include "qemu/option.h" | ||
212 | +#include "monitor/qdev.h" | ||
213 | +#include "hw/sysbus.h" | ||
214 | +#include "hw/arm/boot.h" | ||
215 | +#include "hw/arm/primecell.h" | ||
216 | +#include "hw/boards.h" | ||
217 | +#include "hw/usb.h" | ||
218 | +#include "net/net.h" | ||
219 | +#include "sysemu/sysemu.h" | ||
220 | +#include "sysemu/runstate.h" | ||
221 | +#include "sysemu/kvm.h" | ||
222 | +#include "sysemu/hvf.h" | ||
223 | +#include "hw/loader.h" | ||
224 | +#include "qapi/error.h" | ||
225 | +#include "qapi/qmp/qlist.h" | ||
226 | +#include "qemu/bitops.h" | ||
227 | +#include "qemu/error-report.h" | ||
228 | +#include "qemu/module.h" | ||
229 | +#include "hw/pci-host/gpex.h" | ||
230 | +#include "hw/virtio/virtio-pci.h" | ||
231 | +#include "hw/qdev-properties.h" | ||
232 | +#include "hw/intc/arm_gic.h" | ||
233 | +#include "hw/intc/arm_gicv3_common.h" | ||
234 | +#include "hw/irq.h" | ||
235 | +#include "hw/usb/xhci.h" | ||
236 | +#include "qapi/visitor.h" | ||
237 | +#include "qapi/qapi-visit-common.h" | ||
238 | +#include "standard-headers/linux/input.h" | ||
239 | +#include "target/arm/internals.h" | ||
240 | +#include "target/arm/kvm_arm.h" | ||
241 | +#include "hw/char/pl011.h" | ||
242 | +#include "qemu/guest-random.h" | ||
243 | +#include "sysemu/reset.h" | ||
244 | +#include "qemu/log.h" | ||
245 | +#include "hw/vmapple/cfg.h" | ||
246 | +#include "hw/misc/pvpanic.h" | ||
247 | +#include "hw/vmapple/bdif.h" | ||
248 | + | ||
249 | +struct VMAppleMachineClass { | ||
250 | + MachineClass parent; | ||
251 | +}; | ||
252 | + | ||
253 | +struct VMAppleMachineState { | ||
254 | + MachineState parent; | ||
255 | + | ||
256 | + Notifier machine_done; | ||
257 | + struct arm_boot_info bootinfo; | ||
258 | + MemMapEntry *memmap; | ||
259 | + const int *irqmap; | ||
260 | + DeviceState *gic; | ||
261 | + DeviceState *cfg; | ||
262 | + Notifier powerdown_notifier; | ||
263 | + PCIBus *bus; | ||
264 | + MemoryRegion fw_mr; | ||
265 | + uint64_t uuid; | ||
266 | +}; | ||
267 | + | ||
268 | +#define DEFINE_VMAPPLE_MACHINE_LATEST(major, minor, latest) \ | ||
269 | + static void vmapple##major##_##minor##_class_init(ObjectClass *oc, \ | ||
270 | + void *data) \ | ||
271 | + { \ | ||
272 | + MachineClass *mc = MACHINE_CLASS(oc); \ | ||
273 | + vmapple_machine_##major##_##minor##_options(mc); \ | ||
274 | + mc->desc = "QEMU " # major "." # minor " Apple Virtual Machine"; \ | ||
275 | + if (latest) { \ | ||
276 | + mc->alias = "vmapple"; \ | ||
277 | + } \ | ||
278 | + } \ | ||
279 | + static const TypeInfo machvmapple##major##_##minor##_info = { \ | ||
280 | + .name = MACHINE_TYPE_NAME("vmapple-" # major "." # minor), \ | ||
281 | + .parent = TYPE_VMAPPLE_MACHINE, \ | ||
282 | + .class_init = vmapple##major##_##minor##_class_init, \ | ||
283 | + }; \ | ||
284 | + static void machvmapple_machine_##major##_##minor##_init(void) \ | ||
285 | + { \ | ||
286 | + type_register_static(&machvmapple##major##_##minor##_info); \ | ||
287 | + } \ | ||
288 | + type_init(machvmapple_machine_##major##_##minor##_init); | ||
289 | + | ||
290 | +#define DEFINE_VMAPPLE_MACHINE_AS_LATEST(major, minor) \ | ||
291 | + DEFINE_VMAPPLE_MACHINE_LATEST(major, minor, true) | ||
292 | +#define DEFINE_VMAPPLE_MACHINE(major, minor) \ | ||
293 | + DEFINE_VMAPPLE_MACHINE_LATEST(major, minor, false) | ||
294 | + | ||
295 | +#define TYPE_VMAPPLE_MACHINE MACHINE_TYPE_NAME("vmapple") | ||
296 | +OBJECT_DECLARE_TYPE(VMAppleMachineState, VMAppleMachineClass, VMAPPLE_MACHINE) | ||
297 | + | ||
298 | +/* Number of external interrupt lines to configure the GIC with */ | ||
299 | +#define NUM_IRQS 256 | ||
300 | + | ||
301 | +enum { | ||
302 | + VMAPPLE_FIRMWARE, | ||
303 | + VMAPPLE_CONFIG, | ||
304 | + VMAPPLE_MEM, | ||
305 | + VMAPPLE_GIC_DIST, | ||
306 | + VMAPPLE_GIC_REDIST, | ||
307 | + VMAPPLE_UART, | ||
308 | + VMAPPLE_RTC, | ||
309 | + VMAPPLE_PCIE, | ||
310 | + VMAPPLE_PCIE_MMIO, | ||
311 | + VMAPPLE_PCIE_ECAM, | ||
312 | + VMAPPLE_GPIO, | ||
313 | + VMAPPLE_PVPANIC, | ||
314 | + VMAPPLE_APV_GFX, | ||
315 | + VMAPPLE_APV_IOSFC, | ||
316 | + VMAPPLE_AES_1, | ||
317 | + VMAPPLE_AES_2, | ||
318 | + VMAPPLE_BDOOR, | ||
319 | + VMAPPLE_MEMMAP_LAST, | ||
320 | +}; | ||
321 | + | ||
322 | +static MemMapEntry memmap[] = { | ||
323 | + [VMAPPLE_FIRMWARE] = { 0x00100000, 0x00100000 }, | ||
324 | + [VMAPPLE_CONFIG] = { 0x00400000, 0x00010000 }, | ||
325 | + | ||
326 | + [VMAPPLE_GIC_DIST] = { 0x10000000, 0x00010000 }, | ||
327 | + [VMAPPLE_GIC_REDIST] = { 0x10010000, 0x00400000 }, | ||
328 | + | ||
329 | + [VMAPPLE_UART] = { 0x20010000, 0x00010000 }, | ||
330 | + [VMAPPLE_RTC] = { 0x20050000, 0x00001000 }, | ||
331 | + [VMAPPLE_GPIO] = { 0x20060000, 0x00001000 }, | ||
332 | + [VMAPPLE_PVPANIC] = { 0x20070000, 0x00000002 }, | ||
333 | + [VMAPPLE_BDOOR] = { 0x30000000, 0x00200000 }, | ||
334 | + [VMAPPLE_APV_GFX] = { 0x30200000, 0x00010000 }, | ||
335 | + [VMAPPLE_APV_IOSFC] = { 0x30210000, 0x00010000 }, | ||
336 | + [VMAPPLE_AES_1] = { 0x30220000, 0x00004000 }, | ||
337 | + [VMAPPLE_AES_2] = { 0x30230000, 0x00004000 }, | ||
338 | + [VMAPPLE_PCIE_ECAM] = { 0x40000000, 0x10000000 }, | ||
339 | + [VMAPPLE_PCIE_MMIO] = { 0x50000000, 0x1fff0000 }, | ||
340 | + | ||
341 | + /* Actual RAM size depends on configuration */ | ||
342 | + [VMAPPLE_MEM] = { 0x70000000ULL, GiB}, | ||
343 | +}; | ||
344 | + | ||
345 | +static const int irqmap[] = { | ||
346 | + [VMAPPLE_UART] = 1, | ||
347 | + [VMAPPLE_RTC] = 2, | ||
348 | + [VMAPPLE_GPIO] = 0x5, | ||
349 | + [VMAPPLE_APV_IOSFC] = 0x10, | ||
350 | + [VMAPPLE_APV_GFX] = 0x11, | ||
351 | + [VMAPPLE_AES_1] = 0x12, | ||
352 | + [VMAPPLE_PCIE] = 0x20, | ||
353 | +}; | ||
354 | + | ||
355 | +#define GPEX_NUM_IRQS 16 | ||
356 | + | ||
357 | +static void create_bdif(VMAppleMachineState *vms, MemoryRegion *mem) | ||
358 | +{ | ||
359 | + DeviceState *bdif; | ||
360 | + SysBusDevice *bdif_sb; | ||
361 | + DriveInfo *di_aux = drive_get(IF_PFLASH, 0, 0); | ||
362 | + DriveInfo *di_root = drive_get(IF_PFLASH, 0, 1); | ||
363 | + | ||
364 | + if (!di_aux) { | ||
365 | + error_report("No AUX device found. Please specify one as pflash drive"); | ||
366 | + exit(1); | ||
367 | + } | ||
368 | + | ||
369 | + if (!di_root) { | ||
370 | + /* Fall back to the first IF_VIRTIO device as root device */ | ||
371 | + di_root = drive_get(IF_VIRTIO, 0, 0); | ||
372 | + } | ||
373 | + | ||
374 | + if (!di_root) { | ||
375 | + error_report("No root device found. Please specify one as virtio drive"); | ||
376 | + exit(1); | ||
377 | + } | ||
378 | + | ||
379 | + /* PV backdoor device */ | ||
380 | + bdif = qdev_new(TYPE_VMAPPLE_BDIF); | ||
381 | + bdif_sb = SYS_BUS_DEVICE(bdif); | ||
382 | + sysbus_mmio_map(bdif_sb, 0, vms->memmap[VMAPPLE_BDOOR].base); | ||
383 | + | ||
384 | + qdev_prop_set_drive(DEVICE(bdif), "aux", blk_by_legacy_dinfo(di_aux)); | ||
385 | + qdev_prop_set_drive(DEVICE(bdif), "root", blk_by_legacy_dinfo(di_root)); | ||
386 | + | ||
387 | + sysbus_realize_and_unref(bdif_sb, &error_fatal); | ||
388 | +} | ||
389 | + | ||
390 | +static void create_pvpanic(VMAppleMachineState *vms, MemoryRegion *mem) | ||
391 | +{ | ||
392 | + SysBusDevice *cfg; | ||
393 | + | ||
394 | + vms->cfg = qdev_new(TYPE_PVPANIC_MMIO_DEVICE); | ||
395 | + cfg = SYS_BUS_DEVICE(vms->cfg); | ||
396 | + sysbus_mmio_map(cfg, 0, vms->memmap[VMAPPLE_PVPANIC].base); | ||
397 | + | ||
398 | + sysbus_realize_and_unref(cfg, &error_fatal); | ||
399 | +} | ||
400 | + | ||
401 | +static void create_cfg(VMAppleMachineState *vms, MemoryRegion *mem) | ||
402 | +{ | ||
403 | + SysBusDevice *cfg; | ||
404 | + MachineState *machine = MACHINE(vms); | ||
405 | + uint32_t rnd = 1; | ||
406 | + | ||
407 | + vms->cfg = qdev_new(TYPE_VMAPPLE_CFG); | ||
408 | + cfg = SYS_BUS_DEVICE(vms->cfg); | ||
409 | + sysbus_mmio_map(cfg, 0, vms->memmap[VMAPPLE_CONFIG].base); | ||
410 | + | ||
411 | + qemu_guest_getrandom_nofail(&rnd, sizeof(rnd)); | ||
412 | + | ||
413 | + qdev_prop_set_uint32(vms->cfg, "nr-cpus", machine->smp.cpus); | ||
414 | + qdev_prop_set_uint64(vms->cfg, "ecid", vms->uuid); | ||
415 | + qdev_prop_set_uint64(vms->cfg, "ram-size", machine->ram_size); | ||
416 | + qdev_prop_set_uint32(vms->cfg, "rnd", rnd); | ||
417 | + | ||
418 | + sysbus_realize_and_unref(cfg, &error_fatal); | ||
419 | +} | ||
420 | + | ||
421 | +static void create_gfx(VMAppleMachineState *vms, MemoryRegion *mem) | ||
422 | +{ | ||
423 | + int irq_gfx = vms->irqmap[VMAPPLE_APV_GFX]; | ||
424 | + int irq_iosfc = vms->irqmap[VMAPPLE_APV_IOSFC]; | ||
425 | + SysBusDevice *aes; | ||
426 | + | ||
427 | + aes = SYS_BUS_DEVICE(qdev_new("apple-gfx-vmapple")); | ||
428 | + sysbus_mmio_map(aes, 0, vms->memmap[VMAPPLE_APV_GFX].base); | ||
429 | + sysbus_mmio_map(aes, 1, vms->memmap[VMAPPLE_APV_IOSFC].base); | ||
430 | + sysbus_connect_irq(aes, 0, qdev_get_gpio_in(vms->gic, irq_gfx)); | ||
431 | + sysbus_connect_irq(aes, 1, qdev_get_gpio_in(vms->gic, irq_iosfc)); | ||
432 | + sysbus_realize_and_unref(aes, &error_fatal); | ||
433 | +} | ||
434 | + | ||
435 | +static void create_aes(VMAppleMachineState *vms, MemoryRegion *mem) | ||
436 | +{ | ||
437 | + int irq = vms->irqmap[VMAPPLE_AES_1]; | ||
438 | + SysBusDevice *aes; | ||
439 | + | ||
440 | + aes = SYS_BUS_DEVICE(qdev_new("apple-aes")); | ||
441 | + sysbus_mmio_map(aes, 0, vms->memmap[VMAPPLE_AES_1].base); | ||
442 | + sysbus_mmio_map(aes, 1, vms->memmap[VMAPPLE_AES_2].base); | ||
443 | + sysbus_connect_irq(aes, 0, qdev_get_gpio_in(vms->gic, irq)); | ||
444 | + sysbus_realize_and_unref(aes, &error_fatal); | ||
445 | +} | ||
446 | + | ||
447 | +static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index) | ||
448 | +{ | ||
449 | + return NUM_IRQS + cpu_nr * GIC_INTERNAL + ppi_index; | ||
450 | +} | ||
451 | + | ||
452 | +static void create_gic(VMAppleMachineState *vms, MemoryRegion *mem) | ||
453 | +{ | ||
454 | + MachineState *ms = MACHINE(vms); | ||
455 | + /* We create a standalone GIC */ | ||
456 | + SysBusDevice *gicbusdev; | ||
457 | + QList *redist_region_count; | ||
458 | + int i; | ||
459 | + unsigned int smp_cpus = ms->smp.cpus; | ||
460 | + | ||
461 | + vms->gic = qdev_new(gicv3_class_name()); | ||
462 | + qdev_prop_set_uint32(vms->gic, "revision", 3); | ||
463 | + qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus); | ||
464 | + /* | ||
465 | + * Note that the num-irq property counts both internal and external | ||
466 | + * interrupts; there are always 32 of the former (mandated by GIC spec). | ||
467 | + */ | ||
468 | + qdev_prop_set_uint32(vms->gic, "num-irq", NUM_IRQS + 32); | ||
469 | + | ||
470 | + uint32_t redist0_capacity = | ||
471 | + vms->memmap[VMAPPLE_GIC_REDIST].size / GICV3_REDIST_SIZE; | ||
472 | + uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); | ||
473 | + | ||
474 | + redist_region_count = qlist_new(); | ||
475 | + qlist_append_int(redist_region_count, redist0_count); | ||
476 | + qdev_prop_set_array(vms->gic, "redist-region-count", redist_region_count); | ||
477 | + | ||
478 | + gicbusdev = SYS_BUS_DEVICE(vms->gic); | ||
479 | + sysbus_realize_and_unref(gicbusdev, &error_fatal); | ||
480 | + sysbus_mmio_map(gicbusdev, 0, vms->memmap[VMAPPLE_GIC_DIST].base); | ||
481 | + sysbus_mmio_map(gicbusdev, 1, vms->memmap[VMAPPLE_GIC_REDIST].base); | ||
482 | + | ||
483 | + /* | ||
484 | + * Wire the outputs from each CPU's generic timer and the GICv3 | ||
485 | + * maintenance interrupt signal to the appropriate GIC PPI inputs, | ||
486 | + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. | ||
487 | + */ | ||
488 | + for (i = 0; i < smp_cpus; i++) { | ||
489 | + DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); | ||
490 | + | ||
491 | + /* Map the virt timer to PPI 27 */ | ||
492 | + qdev_connect_gpio_out(cpudev, GTIMER_VIRT, | ||
493 | + qdev_get_gpio_in(vms->gic, | ||
494 | + arm_gic_ppi_index(i, 27))); | ||
495 | + | ||
496 | + /* Map the GIC IRQ and FIQ lines to CPU */ | ||
497 | + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); | ||
498 | + sysbus_connect_irq(gicbusdev, i + smp_cpus, | ||
499 | + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); | ||
500 | + } | ||
501 | +} | ||
502 | + | ||
503 | +static void create_uart(const VMAppleMachineState *vms, int uart, | ||
504 | + MemoryRegion *mem, Chardev *chr) | ||
505 | +{ | ||
506 | + hwaddr base = vms->memmap[uart].base; | ||
507 | + int irq = vms->irqmap[uart]; | ||
508 | + DeviceState *dev = qdev_new(TYPE_PL011); | ||
509 | + SysBusDevice *s = SYS_BUS_DEVICE(dev); | ||
510 | + | ||
511 | + qdev_prop_set_chr(dev, "chardev", chr); | ||
512 | + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); | ||
513 | + memory_region_add_subregion(mem, base, | ||
514 | + sysbus_mmio_get_region(s, 0)); | ||
515 | + sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); | ||
516 | +} | ||
517 | + | ||
518 | +static void create_rtc(const VMAppleMachineState *vms) | ||
519 | +{ | ||
520 | + hwaddr base = vms->memmap[VMAPPLE_RTC].base; | ||
521 | + int irq = vms->irqmap[VMAPPLE_RTC]; | ||
522 | + | ||
523 | + sysbus_create_simple("pl031", base, qdev_get_gpio_in(vms->gic, irq)); | ||
524 | +} | ||
525 | + | ||
526 | +static DeviceState *gpio_key_dev; | ||
527 | +static void vmapple_powerdown_req(Notifier *n, void *opaque) | ||
528 | +{ | ||
529 | + /* use gpio Pin 3 for power button event */ | ||
530 | + qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1); | ||
531 | +} | ||
532 | + | ||
533 | +static void create_gpio_devices(const VMAppleMachineState *vms, int gpio, | ||
534 | + MemoryRegion *mem) | ||
535 | +{ | ||
536 | + DeviceState *pl061_dev; | ||
537 | + hwaddr base = vms->memmap[gpio].base; | ||
538 | + int irq = vms->irqmap[gpio]; | ||
539 | + SysBusDevice *s; | ||
540 | + | ||
541 | + pl061_dev = qdev_new("pl061"); | ||
542 | + /* Pull lines down to 0 if not driven by the PL061 */ | ||
543 | + qdev_prop_set_uint32(pl061_dev, "pullups", 0); | ||
544 | + qdev_prop_set_uint32(pl061_dev, "pulldowns", 0xff); | ||
545 | + s = SYS_BUS_DEVICE(pl061_dev); | ||
546 | + sysbus_realize_and_unref(s, &error_fatal); | ||
547 | + memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); | ||
548 | + sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); | ||
549 | + gpio_key_dev = sysbus_create_simple("gpio-key", -1, | ||
550 | + qdev_get_gpio_in(pl061_dev, 3)); | ||
551 | +} | ||
552 | + | ||
553 | +static void vmapple_firmware_init(VMAppleMachineState *vms, | ||
554 | + MemoryRegion *sysmem) | ||
555 | +{ | ||
556 | + hwaddr size = vms->memmap[VMAPPLE_FIRMWARE].size; | ||
557 | + hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base; | ||
558 | + const char *bios_name; | ||
559 | + int image_size; | ||
560 | + char *fname; | ||
561 | + | ||
562 | + bios_name = MACHINE(vms)->firmware; | ||
563 | + if (!bios_name) { | ||
564 | + error_report("No firmware specified"); | ||
565 | + exit(1); | ||
566 | + } | ||
567 | + | ||
568 | + fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); | ||
569 | + if (!fname) { | ||
570 | + error_report("Could not find ROM image '%s'", bios_name); | ||
571 | + exit(1); | ||
572 | + } | ||
573 | + | ||
574 | + memory_region_init_ram(&vms->fw_mr, NULL, "firmware", size, NULL); | ||
575 | + image_size = load_image_mr(fname, &vms->fw_mr); | ||
576 | + | ||
577 | + g_free(fname); | ||
578 | + if (image_size < 0) { | ||
579 | + error_report("Could not load ROM image '%s'", bios_name); | ||
580 | + exit(1); | ||
581 | + } | ||
582 | + | ||
583 | + memory_region_add_subregion(get_system_memory(), base, &vms->fw_mr); | ||
584 | +} | ||
585 | + | ||
586 | +static void create_pcie(VMAppleMachineState *vms) | ||
587 | +{ | ||
588 | + hwaddr base_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].base; | ||
589 | + hwaddr size_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].size; | ||
590 | + hwaddr base_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].base; | ||
591 | + hwaddr size_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].size; | ||
592 | + int irq = vms->irqmap[VMAPPLE_PCIE]; | ||
593 | + MemoryRegion *mmio_alias; | ||
594 | + MemoryRegion *mmio_reg; | ||
595 | + MemoryRegion *ecam_alias; | ||
596 | + MemoryRegion *ecam_reg; | ||
597 | + DeviceState *dev; | ||
598 | + int i; | ||
599 | + PCIHostState *pci; | ||
600 | + DeviceState *usb_controller; | ||
601 | + USBBus *usb_bus; | ||
602 | + | ||
603 | + dev = qdev_new(TYPE_GPEX_HOST); | ||
604 | + qdev_prop_set_uint32(dev, "nr-irqs", GPEX_NUM_IRQS); | ||
605 | + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); | ||
606 | + | ||
607 | + /* Map only the first size_ecam bytes of ECAM space */ | ||
608 | + ecam_alias = g_new0(MemoryRegion, 1); | ||
609 | + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); | ||
610 | + memory_region_init_alias(ecam_alias, OBJECT(dev), "pcie-ecam", | ||
611 | + ecam_reg, 0, size_ecam); | ||
612 | + memory_region_add_subregion(get_system_memory(), base_ecam, ecam_alias); | ||
613 | + | ||
614 | + /* | ||
615 | + * Map the MMIO window from [0x50000000-0x7fff0000] in PCI space into | ||
616 | + * system address space at [0x50000000-0x7fff0000]. | ||
617 | + */ | ||
618 | + mmio_alias = g_new0(MemoryRegion, 1); | ||
619 | + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); | ||
620 | + memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio", | ||
621 | + mmio_reg, base_mmio, size_mmio); | ||
622 | + memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias); | ||
623 | + | ||
624 | + for (i = 0; i < GPEX_NUM_IRQS; i++) { | ||
625 | + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, | ||
626 | + qdev_get_gpio_in(vms->gic, irq + i)); | ||
627 | + gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); | ||
628 | + } | ||
629 | + | ||
630 | + pci = PCI_HOST_BRIDGE(dev); | ||
631 | + vms->bus = pci->bus; | ||
632 | + g_assert_nonnull(vms->bus); | ||
633 | + | ||
634 | + while ((dev = qemu_create_nic_device("virtio-net-pci", true, NULL))) { | ||
635 | + qdev_realize_and_unref(dev, BUS(vms->bus), &error_fatal); | ||
636 | + } | ||
637 | + | ||
638 | + usb_controller = qdev_new(TYPE_QEMU_XHCI); | ||
639 | + qdev_realize_and_unref(usb_controller, BUS(pci->bus), &error_fatal); | ||
640 | + | ||
641 | + usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS, | ||
642 | + &error_fatal)); | ||
643 | + usb_create_simple(usb_bus, "usb-kbd"); | ||
644 | + usb_create_simple(usb_bus, "usb-tablet"); | ||
645 | +} | ||
646 | + | ||
647 | +static void vmapple_reset(void *opaque) | ||
648 | +{ | ||
649 | + VMAppleMachineState *vms = opaque; | ||
650 | + hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base; | ||
651 | + | ||
652 | + cpu_set_pc(first_cpu, base); | ||
653 | +} | ||
654 | + | ||
655 | +static void mach_vmapple_init(MachineState *machine) | ||
656 | +{ | ||
657 | + VMAppleMachineState *vms = VMAPPLE_MACHINE(machine); | ||
658 | + MachineClass *mc = MACHINE_GET_CLASS(machine); | ||
659 | + const CPUArchIdList *possible_cpus; | ||
660 | + MemoryRegion *sysmem = get_system_memory(); | ||
661 | + int n; | ||
662 | + unsigned int smp_cpus = machine->smp.cpus; | ||
663 | + unsigned int max_cpus = machine->smp.max_cpus; | ||
664 | + | ||
665 | + vms->memmap = memmap; | ||
666 | + machine->usb = true; | ||
667 | + | ||
668 | + possible_cpus = mc->possible_cpu_arch_ids(machine); | ||
669 | + assert(possible_cpus->len == max_cpus); | ||
670 | + for (n = 0; n < possible_cpus->len; n++) { | ||
671 | + Object *cpu; | ||
672 | + CPUState *cs; | ||
673 | + | ||
674 | + if (n >= smp_cpus) { | ||
675 | + break; | ||
676 | + } | ||
677 | + | ||
678 | + cpu = object_new(possible_cpus->cpus[n].type); | ||
679 | + object_property_set_int(cpu, "mp-affinity", | ||
680 | + possible_cpus->cpus[n].arch_id, NULL); | ||
681 | + | ||
682 | + cs = CPU(cpu); | ||
683 | + cs->cpu_index = n; | ||
684 | + | ||
685 | + numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpu), | ||
686 | + &error_fatal); | ||
687 | + | ||
688 | + object_property_set_bool(cpu, "has_el3", false, NULL); | ||
689 | + object_property_set_bool(cpu, "has_el2", false, NULL); | ||
690 | + object_property_set_int(cpu, "psci-conduit", QEMU_PSCI_CONDUIT_HVC, | ||
691 | + NULL); | ||
692 | + | ||
693 | + /* Secondary CPUs start in PSCI powered-down state */ | ||
694 | + if (n > 0) { | ||
695 | + object_property_set_bool(cpu, "start-powered-off", true, NULL); | ||
696 | + } | ||
697 | + | ||
698 | + object_property_set_link(cpu, "memory", OBJECT(sysmem), &error_abort); | ||
699 | + qdev_realize(DEVICE(cpu), NULL, &error_fatal); | ||
700 | + object_unref(cpu); | ||
701 | + } | ||
702 | + | ||
703 | + memory_region_add_subregion(sysmem, vms->memmap[VMAPPLE_MEM].base, | ||
704 | + machine->ram); | ||
705 | + | ||
706 | + create_gic(vms, sysmem); | ||
707 | + create_bdif(vms, sysmem); | ||
708 | + create_pvpanic(vms, sysmem); | ||
709 | + create_aes(vms, sysmem); | ||
710 | + create_gfx(vms, sysmem); | ||
711 | + create_uart(vms, VMAPPLE_UART, sysmem, serial_hd(0)); | ||
712 | + create_rtc(vms); | ||
713 | + create_pcie(vms); | ||
714 | + | ||
715 | + create_gpio_devices(vms, VMAPPLE_GPIO, sysmem); | ||
716 | + | ||
717 | + vmapple_firmware_init(vms, sysmem); | ||
718 | + create_cfg(vms, sysmem); | ||
719 | + | ||
720 | + /* connect powerdown request */ | ||
721 | + vms->powerdown_notifier.notify = vmapple_powerdown_req; | ||
722 | + qemu_register_powerdown_notifier(&vms->powerdown_notifier); | ||
723 | + | ||
724 | + vms->bootinfo.ram_size = machine->ram_size; | ||
725 | + vms->bootinfo.board_id = -1; | ||
726 | + vms->bootinfo.loader_start = vms->memmap[VMAPPLE_MEM].base; | ||
727 | + vms->bootinfo.skip_dtb_autoload = true; | ||
728 | + vms->bootinfo.firmware_loaded = true; | ||
729 | + arm_load_kernel(ARM_CPU(first_cpu), machine, &vms->bootinfo); | ||
730 | + | ||
731 | + qemu_register_reset(vmapple_reset, vms); | ||
732 | +} | ||
733 | + | ||
734 | +static CpuInstanceProperties | ||
735 | +vmapple_cpu_index_to_props(MachineState *ms, unsigned cpu_index) | ||
736 | +{ | ||
737 | + MachineClass *mc = MACHINE_GET_CLASS(ms); | ||
738 | + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); | ||
739 | + | ||
740 | + assert(cpu_index < possible_cpus->len); | ||
741 | + return possible_cpus->cpus[cpu_index].props; | ||
742 | +} | ||
743 | + | ||
744 | + | ||
745 | +static int64_t vmapple_get_default_cpu_node_id(const MachineState *ms, int idx) | ||
746 | +{ | ||
747 | + return idx % ms->numa_state->num_nodes; | ||
748 | +} | ||
749 | + | ||
750 | +static const CPUArchIdList *vmapple_possible_cpu_arch_ids(MachineState *ms) | ||
751 | +{ | ||
752 | + int n; | ||
753 | + unsigned int max_cpus = ms->smp.max_cpus; | ||
754 | + | ||
755 | + if (ms->possible_cpus) { | ||
756 | + assert(ms->possible_cpus->len == max_cpus); | ||
757 | + return ms->possible_cpus; | ||
758 | + } | ||
759 | + | ||
760 | + ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + | ||
761 | + sizeof(CPUArchId) * max_cpus); | ||
762 | + ms->possible_cpus->len = max_cpus; | ||
763 | + for (n = 0; n < ms->possible_cpus->len; n++) { | ||
764 | + ms->possible_cpus->cpus[n].type = ms->cpu_type; | ||
765 | + ms->possible_cpus->cpus[n].arch_id = | ||
766 | + arm_build_mp_affinity(n, GICV3_TARGETLIST_BITS); | ||
767 | + ms->possible_cpus->cpus[n].props.has_thread_id = true; | ||
768 | + ms->possible_cpus->cpus[n].props.thread_id = n; | ||
769 | + } | ||
770 | + return ms->possible_cpus; | ||
771 | +} | ||
772 | + | ||
773 | +static void vmapple_get_uuid(Object *obj, Visitor *v, const char *name, | ||
774 | + void *opaque, Error **errp) | ||
775 | +{ | ||
776 | + VMAppleMachineState *vms = VMAPPLE_MACHINE(obj); | ||
777 | + uint64_t value = be64_to_cpu(vms->uuid); | ||
778 | + | ||
779 | + visit_type_uint64(v, name, &value, errp); | ||
780 | +} | ||
781 | + | ||
782 | +static void vmapple_set_uuid(Object *obj, Visitor *v, const char *name, | ||
783 | + void *opaque, Error **errp) | ||
784 | +{ | ||
785 | + VMAppleMachineState *vms = VMAPPLE_MACHINE(obj); | ||
786 | + Error *error = NULL; | ||
787 | + uint64_t value; | ||
788 | + | ||
789 | + visit_type_uint64(v, name, &value, &error); | ||
790 | + if (error) { | ||
791 | + error_propagate(errp, error); | ||
792 | + return; | ||
793 | + } | ||
794 | + | ||
795 | + vms->uuid = cpu_to_be64(value); | ||
796 | +} | ||
797 | + | ||
798 | +static void vmapple_machine_class_init(ObjectClass *oc, void *data) | ||
799 | +{ | ||
800 | + MachineClass *mc = MACHINE_CLASS(oc); | ||
801 | + | ||
802 | + mc->init = mach_vmapple_init; | ||
803 | + mc->max_cpus = 32; | ||
804 | + mc->block_default_type = IF_VIRTIO; | ||
805 | + mc->no_cdrom = 1; | ||
806 | + mc->pci_allow_0_address = true; | ||
807 | + mc->minimum_page_bits = 12; | ||
808 | + mc->possible_cpu_arch_ids = vmapple_possible_cpu_arch_ids; | ||
809 | + mc->cpu_index_to_instance_props = vmapple_cpu_index_to_props; | ||
810 | + if (hvf_enabled()) { | ||
811 | + mc->default_cpu_type = ARM_CPU_TYPE_NAME("host"); | ||
812 | + } else { | ||
813 | + mc->default_cpu_type = ARM_CPU_TYPE_NAME("max"); | ||
814 | + } | ||
815 | + mc->get_default_cpu_node_id = vmapple_get_default_cpu_node_id; | ||
816 | + mc->default_ram_id = "mach-vmapple.ram"; | ||
817 | + | ||
818 | + object_register_sugar_prop(TYPE_VIRTIO_PCI, "disable-legacy", | ||
819 | + "on", true); | ||
820 | + | ||
821 | + object_class_property_add(oc, "uuid", "uint64", vmapple_get_uuid, | ||
822 | + vmapple_set_uuid, NULL, NULL); | ||
823 | + object_class_property_set_description(oc, "uuid", "Machine UUID (SDOM)"); | ||
824 | +} | ||
825 | + | ||
826 | +static void vmapple_instance_init(Object *obj) | ||
827 | +{ | ||
828 | + VMAppleMachineState *vms = VMAPPLE_MACHINE(obj); | ||
829 | + | ||
830 | + vms->irqmap = irqmap; | ||
831 | +} | ||
832 | + | ||
833 | +static const TypeInfo vmapple_machine_info = { | ||
834 | + .name = TYPE_VMAPPLE_MACHINE, | ||
835 | + .parent = TYPE_MACHINE, | ||
836 | + .abstract = true, | ||
837 | + .instance_size = sizeof(VMAppleMachineState), | ||
838 | + .class_size = sizeof(VMAppleMachineClass), | ||
839 | + .class_init = vmapple_machine_class_init, | ||
840 | + .instance_init = vmapple_instance_init, | ||
841 | +}; | ||
842 | + | ||
843 | +static void machvmapple_machine_init(void) | ||
844 | +{ | ||
845 | + type_register_static(&vmapple_machine_info); | ||
846 | +} | ||
847 | +type_init(machvmapple_machine_init); | ||
848 | + | ||
849 | +static void vmapple_machine_8_1_options(MachineClass *mc) | ||
850 | +{ | ||
851 | +} | ||
852 | +DEFINE_VMAPPLE_MACHINE_AS_LATEST(8, 1) | ||
853 | + | ||
854 | -- | ||
855 | 2.39.3 (Apple Git-145) | ||
856 | |||
857 | diff view generated by jsdifflib |