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 void apple_gfx_pci_class_init(ObjectClass *klass, void *data) 119{ 120 DeviceClass *dc = DEVICE_CLASS(klass); 121 PCIDeviceClass *pci = PCI_DEVICE_CLASS(klass); 122 ResettableClass *rc = RESETTABLE_CLASS(klass); 123 124 rc->phases.hold = apple_gfx_pci_reset; 125 dc->desc = "macOS Paravirtualized Graphics PCI Display Controller"; 126 dc->hotpluggable = false; 127 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 128 129 pci->vendor_id = PG_PCI_VENDOR_ID; 130 pci->device_id = PG_PCI_DEVICE_ID; 131 pci->class_id = PCI_CLASS_DISPLAY_OTHER; 132 pci->realize = apple_gfx_pci_realize; 133 134 /* TODO: Property for setting mode list */ 135} 136 137static const TypeInfo apple_gfx_pci_types[] = { 138 { 139 .name = TYPE_APPLE_GFX_PCI, 140 .parent = TYPE_PCI_DEVICE, 141 .instance_size = sizeof(AppleGFXPCIState), 142 .class_init = apple_gfx_pci_class_init, 143 .instance_init = apple_gfx_pci_init, 144 .interfaces = (InterfaceInfo[]) { 145 { INTERFACE_PCIE_DEVICE }, 146 { }, 147 }, 148 } 149}; 150DEFINE_TYPES(apple_gfx_pci_types) 151 152