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