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 23OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXPCIState, APPLE_GFX_PCI) 24 25struct AppleGFXPCIState { 26 PCIDevice parent_obj; 27 28 AppleGFXState common; 29}; 30 31static const char *apple_gfx_pci_option_rom_path = NULL; 32 33static 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 41static 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 58typedef struct AppleGFXPCIInterruptJob { 59 PCIDevice *device; 60 uint32_t vector; 61} AppleGFXPCIInterruptJob; 62 63static 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 73static 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 85static 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 112static void apple_gfx_pci_reset(Object *obj, ResetType type) 113{ 114 AppleGFXPCIState *s = APPLE_GFX_PCI(obj); 115 [s->common.pgdev reset]; 116} 117 118static 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 124static 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 143static 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}; 156DEFINE_TYPES(apple_gfx_pci_types) 157 158