master
m 157 lines 4.56 KB
Raw
1 /*
2 * QEMU Apple ParavirtualizedGraphics.framework device, PCI variant
3 *
4 * Copyright © 2023-2024 Phil Dennis-Jordan
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 *
8 * ParavirtualizedGraphics.framework is a set of libraries that macOS provides
9 * which implements 3d graphics passthrough to the host as well as a
10 * proprietary guest communication channel to drive it. This device model
11 * implements support to drive that library from within QEMU as a PCI device
12 * aimed primarily at x86-64 macOS VMs.
13 */
14
15 #include "qemu/osdep.h"
16 #include "hw/pci/pci_device.h"
17 #include "hw/pci/msi.h"
18 #include "apple-gfx.h"
19 #include "trace.h"
20
21 #import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
22
23 OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXPCIState, APPLE_GFX_PCI)
24
25 struct AppleGFXPCIState {
26 PCIDevice parent_obj;
27
28 AppleGFXState common;
29 };
30
31 static const char *apple_gfx_pci_option_rom_path = NULL;
32
33 static void apple_gfx_init_option_rom_path(void)
34 {
35 NSURL *option_rom_url = PGCopyOptionROMURL();
36 const char *option_rom_path = option_rom_url.fileSystemRepresentation;
37 apple_gfx_pci_option_rom_path = g_strdup(option_rom_path);
38 [option_rom_url release];
39 }
40
41 static void apple_gfx_pci_init(Object *obj)
42 {
43 AppleGFXPCIState *s = APPLE_GFX_PCI(obj);
44
45 if (!apple_gfx_pci_option_rom_path) {
46 /*
47 * The following is done on device not class init to avoid running
48 * ObjC code before fork() in -daemonize mode.
49 */
50 PCIDeviceClass *pci = PCI_DEVICE_CLASS(object_get_class(obj));
51 apple_gfx_init_option_rom_path();
52 pci->romfile = apple_gfx_pci_option_rom_path;
53 }
54
55 apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_PCI);
56 }
57
58 typedef struct AppleGFXPCIInterruptJob {
59 PCIDevice *device;
60 uint32_t vector;
61 } AppleGFXPCIInterruptJob;
62
63 static void apple_gfx_pci_raise_interrupt(void *opaque)
64 {
65 AppleGFXPCIInterruptJob *job = opaque;
66
67 if (msi_enabled(job->device)) {
68 msi_notify(job->device, job->vector);
69 }
70 g_free(job);
71 }
72
73 static void apple_gfx_pci_interrupt(PCIDevice *dev, uint32_t vector)
74 {
75 AppleGFXPCIInterruptJob *job;
76
77 trace_apple_gfx_raise_irq(vector);
78 job = g_malloc0(sizeof(*job));
79 job->device = dev;
80 job->vector = vector;
81 aio_bh_schedule_oneshot(qemu_get_aio_context(),
82 apple_gfx_pci_raise_interrupt, job);
83 }
84
85 static void apple_gfx_pci_realize(PCIDevice *dev, Error **errp)
86 {
87 AppleGFXPCIState *s = APPLE_GFX_PCI(dev);
88 int ret;
89
90 pci_register_bar(dev, PG_PCI_BAR_MMIO,
91 PCI_BASE_ADDRESS_SPACE_MEMORY, &s->common.iomem_gfx);
92
93 ret = msi_init(dev, 0x0 /* config offset; 0 = find space */,
94 PG_PCI_MAX_MSI_VECTORS, true /* msi64bit */,
95 false /* msi_per_vector_mask */, errp);
96 if (ret != 0) {
97 return;
98 }
99
100 @autoreleasepool {
101 PGDeviceDescriptor *desc = [PGDeviceDescriptor new];
102 desc.raiseInterrupt = ^(uint32_t vector) {
103 apple_gfx_pci_interrupt(dev, vector);
104 };
105
106 apple_gfx_common_realize(&s->common, DEVICE(dev), desc, errp);
107 [desc release];
108 desc = nil;
109 }
110 }
111
112 static void apple_gfx_pci_reset(Object *obj, ResetType type)
113 {
114 AppleGFXPCIState *s = APPLE_GFX_PCI(obj);
115 [s->common.pgdev reset];
116 }
117
118 static const Property apple_gfx_pci_properties[] = {
119 DEFINE_PROP_ARRAY("display-modes", AppleGFXPCIState,
120 common.num_display_modes, common.display_modes,
121 qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode),
122 };
123
124 static void apple_gfx_pci_class_init(ObjectClass *klass, const void *data)
125 {
126 DeviceClass *dc = DEVICE_CLASS(klass);
127 PCIDeviceClass *pci = PCI_DEVICE_CLASS(klass);
128 ResettableClass *rc = RESETTABLE_CLASS(klass);
129
130 rc->phases.hold = apple_gfx_pci_reset;
131 dc->desc = "macOS Paravirtualized Graphics PCI Display Controller";
132 dc->hotpluggable = false;
133 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
134
135 pci->vendor_id = PG_PCI_VENDOR_ID;
136 pci->device_id = PG_PCI_DEVICE_ID;
137 pci->class_id = PCI_CLASS_DISPLAY_OTHER;
138 pci->realize = apple_gfx_pci_realize;
139
140 device_class_set_props(dc, apple_gfx_pci_properties);
141 }
142
143 static const TypeInfo apple_gfx_pci_types[] = {
144 {
145 .name = TYPE_APPLE_GFX_PCI,
146 .parent = TYPE_PCI_DEVICE,
147 .instance_size = sizeof(AppleGFXPCIState),
148 .class_init = apple_gfx_pci_class_init,
149 .instance_init = apple_gfx_pci_init,
150 .interfaces = (const InterfaceInfo[]) {
151 { INTERFACE_PCIE_DEVICE },
152 { },
153 },
154 }
155 };
156 DEFINE_TYPES(apple_gfx_pci_types)
157