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