|
@@ -0,0 +1,151 @@
|
|
|
|
+/*
|
|
|
|
+ * QEMU Apple ParavirtualizedGraphics.framework device, PCI variant
|
|
|
|
+ *
|
|
|
|
+ * Copyright © 2023-2024 Phil Dennis-Jordan
|
|
|
|
+ *
|
|
|
|
+ * SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
+ *
|
|
|
|
+ * ParavirtualizedGraphics.framework is a set of libraries that macOS provides
|
|
|
|
+ * which implements 3d graphics passthrough to the host as well as a
|
|
|
|
+ * proprietary guest communication channel to drive it. This device model
|
|
|
|
+ * implements support to drive that library from within QEMU as a PCI device
|
|
|
|
+ * aimed primarily at x86-64 macOS VMs.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include "qemu/osdep.h"
|
|
|
|
+#include "hw/pci/pci_device.h"
|
|
|
|
+#include "hw/pci/msi.h"
|
|
|
|
+#include "apple-gfx.h"
|
|
|
|
+#include "trace.h"
|
|
|
|
+
|
|
|
|
+#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
|
|
|
|
+
|
|
|
|
+OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXPCIState, APPLE_GFX_PCI)
|
|
|
|
+
|
|
|
|
+struct AppleGFXPCIState {
|
|
|
|
+ PCIDevice parent_obj;
|
|
|
|
+
|
|
|
|
+ AppleGFXState common;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const char *apple_gfx_pci_option_rom_path = NULL;
|
|
|
|
+
|
|
|
|
+static void apple_gfx_init_option_rom_path(void)
|
|
|
|
+{
|
|
|
|
+ NSURL *option_rom_url = PGCopyOptionROMURL();
|
|
|
|
+ const char *option_rom_path = option_rom_url.fileSystemRepresentation;
|
|
|
|
+ apple_gfx_pci_option_rom_path = g_strdup(option_rom_path);
|
|
|
|
+ [option_rom_url release];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void apple_gfx_pci_init(Object *obj)
|
|
|
|
+{
|
|
|
|
+ AppleGFXPCIState *s = APPLE_GFX_PCI(obj);
|
|
|
|
+
|
|
|
|
+ if (!apple_gfx_pci_option_rom_path) {
|
|
|
|
+ /*
|
|
|
|
+ * The following is done on device not class init to avoid running
|
|
|
|
+ * ObjC code before fork() in -daemonize mode.
|
|
|
|
+ */
|
|
|
|
+ PCIDeviceClass *pci = PCI_DEVICE_CLASS(object_get_class(obj));
|
|
|
|
+ apple_gfx_init_option_rom_path();
|
|
|
|
+ pci->romfile = apple_gfx_pci_option_rom_path;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_PCI);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+typedef struct AppleGFXPCIInterruptJob {
|
|
|
|
+ PCIDevice *device;
|
|
|
|
+ uint32_t vector;
|
|
|
|
+} AppleGFXPCIInterruptJob;
|
|
|
|
+
|
|
|
|
+static void apple_gfx_pci_raise_interrupt(void *opaque)
|
|
|
|
+{
|
|
|
|
+ AppleGFXPCIInterruptJob *job = opaque;
|
|
|
|
+
|
|
|
|
+ if (msi_enabled(job->device)) {
|
|
|
|
+ msi_notify(job->device, job->vector);
|
|
|
|
+ }
|
|
|
|
+ g_free(job);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void apple_gfx_pci_interrupt(PCIDevice *dev, uint32_t vector)
|
|
|
|
+{
|
|
|
|
+ AppleGFXPCIInterruptJob *job;
|
|
|
|
+
|
|
|
|
+ trace_apple_gfx_raise_irq(vector);
|
|
|
|
+ job = g_malloc0(sizeof(*job));
|
|
|
|
+ job->device = dev;
|
|
|
|
+ job->vector = vector;
|
|
|
|
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
|
|
|
|
+ apple_gfx_pci_raise_interrupt, job);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void apple_gfx_pci_realize(PCIDevice *dev, Error **errp)
|
|
|
|
+{
|
|
|
|
+ AppleGFXPCIState *s = APPLE_GFX_PCI(dev);
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ pci_register_bar(dev, PG_PCI_BAR_MMIO,
|
|
|
|
+ PCI_BASE_ADDRESS_SPACE_MEMORY, &s->common.iomem_gfx);
|
|
|
|
+
|
|
|
|
+ ret = msi_init(dev, 0x0 /* config offset; 0 = find space */,
|
|
|
|
+ PG_PCI_MAX_MSI_VECTORS, true /* msi64bit */,
|
|
|
|
+ false /* msi_per_vector_mask */, errp);
|
|
|
|
+ if (ret != 0) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @autoreleasepool {
|
|
|
|
+ PGDeviceDescriptor *desc = [PGDeviceDescriptor new];
|
|
|
|
+ desc.raiseInterrupt = ^(uint32_t vector) {
|
|
|
|
+ apple_gfx_pci_interrupt(dev, vector);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ apple_gfx_common_realize(&s->common, DEVICE(dev), desc, errp);
|
|
|
|
+ [desc release];
|
|
|
|
+ desc = nil;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void apple_gfx_pci_reset(Object *obj, ResetType type)
|
|
|
|
+{
|
|
|
|
+ AppleGFXPCIState *s = APPLE_GFX_PCI(obj);
|
|
|
|
+ [s->common.pgdev reset];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void apple_gfx_pci_class_init(ObjectClass *klass, void *data)
|
|
|
|
+{
|
|
|
|
+ DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
+ PCIDeviceClass *pci = PCI_DEVICE_CLASS(klass);
|
|
|
|
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
|
|
|
|
+
|
|
|
|
+ rc->phases.hold = apple_gfx_pci_reset;
|
|
|
|
+ dc->desc = "macOS Paravirtualized Graphics PCI Display Controller";
|
|
|
|
+ dc->hotpluggable = false;
|
|
|
|
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
|
|
|
|
+
|
|
|
|
+ pci->vendor_id = PG_PCI_VENDOR_ID;
|
|
|
|
+ pci->device_id = PG_PCI_DEVICE_ID;
|
|
|
|
+ pci->class_id = PCI_CLASS_DISPLAY_OTHER;
|
|
|
|
+ pci->realize = apple_gfx_pci_realize;
|
|
|
|
+
|
|
|
|
+ /* TODO: Property for setting mode list */
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const TypeInfo apple_gfx_pci_types[] = {
|
|
|
|
+ {
|
|
|
|
+ .name = TYPE_APPLE_GFX_PCI,
|
|
|
|
+ .parent = TYPE_PCI_DEVICE,
|
|
|
|
+ .instance_size = sizeof(AppleGFXPCIState),
|
|
|
|
+ .class_init = apple_gfx_pci_class_init,
|
|
|
|
+ .instance_init = apple_gfx_pci_init,
|
|
|
|
+ .interfaces = (InterfaceInfo[]) {
|
|
|
|
+ { INTERFACE_PCIE_DEVICE },
|
|
|
|
+ { },
|
|
|
|
+ },
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+DEFINE_TYPES(apple_gfx_pci_types)
|
|
|
|
+
|