1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
/*
* 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 const Property apple_gfx_pci_properties[] = {
DEFINE_PROP_ARRAY("display-modes", AppleGFXPCIState,
common.num_display_modes, common.display_modes,
qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode),
};
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;
device_class_set_props(dc, apple_gfx_pci_properties);
}
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)
|