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