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