xref: /qemu/hw/display/apple-gfx-mmio.m (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
12352159cSPhil Dennis-Jordan/*
22352159cSPhil Dennis-Jordan * QEMU Apple ParavirtualizedGraphics.framework device, MMIO (arm64) variant
32352159cSPhil Dennis-Jordan *
42352159cSPhil Dennis-Jordan * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
52352159cSPhil Dennis-Jordan *
62352159cSPhil Dennis-Jordan * SPDX-License-Identifier: GPL-2.0-or-later
72352159cSPhil Dennis-Jordan *
82352159cSPhil Dennis-Jordan * ParavirtualizedGraphics.framework is a set of libraries that macOS provides
92352159cSPhil Dennis-Jordan * which implements 3d graphics passthrough to the host as well as a
102352159cSPhil Dennis-Jordan * proprietary guest communication channel to drive it. This device model
112352159cSPhil Dennis-Jordan * implements support to drive that library from within QEMU as an MMIO-based
122352159cSPhil Dennis-Jordan * system device for macOS on arm64 VMs.
132352159cSPhil Dennis-Jordan */
142352159cSPhil Dennis-Jordan
152352159cSPhil Dennis-Jordan#include "qemu/osdep.h"
162352159cSPhil Dennis-Jordan#include "qemu/log.h"
172352159cSPhil Dennis-Jordan#include "block/aio-wait.h"
182352159cSPhil Dennis-Jordan#include "hw/sysbus.h"
192352159cSPhil Dennis-Jordan#include "hw/irq.h"
202352159cSPhil Dennis-Jordan#include "apple-gfx.h"
212352159cSPhil Dennis-Jordan#include "trace.h"
222352159cSPhil Dennis-Jordan
232352159cSPhil Dennis-Jordan#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
242352159cSPhil Dennis-Jordan
252352159cSPhil Dennis-JordanOBJECT_DECLARE_SIMPLE_TYPE(AppleGFXMMIOState, APPLE_GFX_MMIO)
262352159cSPhil Dennis-Jordan
272352159cSPhil Dennis-Jordan/*
282352159cSPhil Dennis-Jordan * ParavirtualizedGraphics.Framework only ships header files for the PCI
292352159cSPhil Dennis-Jordan * variant which does not include IOSFC descriptors and host devices. We add
302352159cSPhil Dennis-Jordan * their definitions here so that we can also work with the ARM version.
312352159cSPhil Dennis-Jordan */
322352159cSPhil Dennis-Jordantypedef bool(^IOSFCRaiseInterrupt)(uint32_t vector);
332352159cSPhil Dennis-Jordantypedef bool(^IOSFCUnmapMemory)(void *, void *, void *, void *, void *, void *);
342352159cSPhil Dennis-Jordantypedef bool(^IOSFCMapMemory)(uint64_t phys, uint64_t len, bool ro, void **va,
352352159cSPhil Dennis-Jordan                              void *, void *);
362352159cSPhil Dennis-Jordan
372352159cSPhil Dennis-Jordan@interface PGDeviceDescriptor (IOSurfaceMapper)
382352159cSPhil Dennis-Jordan@property (readwrite, nonatomic) bool usingIOSurfaceMapper;
392352159cSPhil Dennis-Jordan@end
402352159cSPhil Dennis-Jordan
412352159cSPhil Dennis-Jordan@interface PGIOSurfaceHostDeviceDescriptor : NSObject
422352159cSPhil Dennis-Jordan-(PGIOSurfaceHostDeviceDescriptor *)init;
432352159cSPhil Dennis-Jordan@property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory;
442352159cSPhil Dennis-Jordan@property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory;
452352159cSPhil Dennis-Jordan@property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt;
462352159cSPhil Dennis-Jordan@end
472352159cSPhil Dennis-Jordan
482352159cSPhil Dennis-Jordan@interface PGIOSurfaceHostDevice : NSObject
492352159cSPhil Dennis-Jordan-(instancetype)initWithDescriptor:(PGIOSurfaceHostDeviceDescriptor *)desc;
502352159cSPhil Dennis-Jordan-(uint32_t)mmioReadAtOffset:(size_t)offset;
512352159cSPhil Dennis-Jordan-(void)mmioWriteAtOffset:(size_t)offset value:(uint32_t)value;
522352159cSPhil Dennis-Jordan@end
532352159cSPhil Dennis-Jordan
542352159cSPhil Dennis-Jordanstruct AppleGFXMapSurfaceMemoryJob;
552352159cSPhil Dennis-Jordanstruct AppleGFXMMIOState {
562352159cSPhil Dennis-Jordan    SysBusDevice parent_obj;
572352159cSPhil Dennis-Jordan
582352159cSPhil Dennis-Jordan    AppleGFXState common;
592352159cSPhil Dennis-Jordan
602352159cSPhil Dennis-Jordan    qemu_irq irq_gfx;
612352159cSPhil Dennis-Jordan    qemu_irq irq_iosfc;
622352159cSPhil Dennis-Jordan    MemoryRegion iomem_iosfc;
632352159cSPhil Dennis-Jordan    PGIOSurfaceHostDevice *pgiosfc;
642352159cSPhil Dennis-Jordan};
652352159cSPhil Dennis-Jordan
662352159cSPhil Dennis-Jordantypedef struct AppleGFXMMIOJob {
672352159cSPhil Dennis-Jordan    AppleGFXMMIOState *state;
682352159cSPhil Dennis-Jordan    uint64_t offset;
692352159cSPhil Dennis-Jordan    uint64_t value;
702352159cSPhil Dennis-Jordan    bool completed;
712352159cSPhil Dennis-Jordan} AppleGFXMMIOJob;
722352159cSPhil Dennis-Jordan
732352159cSPhil Dennis-Jordanstatic void iosfc_do_read(void *opaque)
742352159cSPhil Dennis-Jordan{
752352159cSPhil Dennis-Jordan    AppleGFXMMIOJob *job = opaque;
762352159cSPhil Dennis-Jordan    job->value = [job->state->pgiosfc mmioReadAtOffset:job->offset];
772352159cSPhil Dennis-Jordan    qatomic_set(&job->completed, true);
782352159cSPhil Dennis-Jordan    aio_wait_kick();
792352159cSPhil Dennis-Jordan}
802352159cSPhil Dennis-Jordan
812352159cSPhil Dennis-Jordanstatic uint64_t iosfc_read(void *opaque, hwaddr offset, unsigned size)
822352159cSPhil Dennis-Jordan{
832352159cSPhil Dennis-Jordan    AppleGFXMMIOJob job = {
842352159cSPhil Dennis-Jordan        .state = opaque,
852352159cSPhil Dennis-Jordan        .offset = offset,
862352159cSPhil Dennis-Jordan        .completed = false,
872352159cSPhil Dennis-Jordan    };
882352159cSPhil Dennis-Jordan    dispatch_queue_t queue =
892352159cSPhil Dennis-Jordan        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
902352159cSPhil Dennis-Jordan
912352159cSPhil Dennis-Jordan    dispatch_async_f(queue, &job, iosfc_do_read);
922352159cSPhil Dennis-Jordan    AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
932352159cSPhil Dennis-Jordan
942352159cSPhil Dennis-Jordan    trace_apple_gfx_mmio_iosfc_read(offset, job.value);
952352159cSPhil Dennis-Jordan    return job.value;
962352159cSPhil Dennis-Jordan}
972352159cSPhil Dennis-Jordan
982352159cSPhil Dennis-Jordanstatic void iosfc_do_write(void *opaque)
992352159cSPhil Dennis-Jordan{
1002352159cSPhil Dennis-Jordan    AppleGFXMMIOJob *job = opaque;
1012352159cSPhil Dennis-Jordan    [job->state->pgiosfc mmioWriteAtOffset:job->offset value:job->value];
1022352159cSPhil Dennis-Jordan    qatomic_set(&job->completed, true);
1032352159cSPhil Dennis-Jordan    aio_wait_kick();
1042352159cSPhil Dennis-Jordan}
1052352159cSPhil Dennis-Jordan
1062352159cSPhil Dennis-Jordanstatic void iosfc_write(void *opaque, hwaddr offset, uint64_t val,
1072352159cSPhil Dennis-Jordan                        unsigned size)
1082352159cSPhil Dennis-Jordan{
1092352159cSPhil Dennis-Jordan    AppleGFXMMIOJob job = {
1102352159cSPhil Dennis-Jordan        .state = opaque,
1112352159cSPhil Dennis-Jordan        .offset = offset,
1122352159cSPhil Dennis-Jordan        .value = val,
1132352159cSPhil Dennis-Jordan        .completed = false,
1142352159cSPhil Dennis-Jordan    };
1152352159cSPhil Dennis-Jordan    dispatch_queue_t queue =
1162352159cSPhil Dennis-Jordan        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1172352159cSPhil Dennis-Jordan
1182352159cSPhil Dennis-Jordan    dispatch_async_f(queue, &job, iosfc_do_write);
1192352159cSPhil Dennis-Jordan    AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
1202352159cSPhil Dennis-Jordan
1212352159cSPhil Dennis-Jordan    trace_apple_gfx_mmio_iosfc_write(offset, val);
1222352159cSPhil Dennis-Jordan}
1232352159cSPhil Dennis-Jordan
1242352159cSPhil Dennis-Jordanstatic const MemoryRegionOps apple_iosfc_ops = {
1252352159cSPhil Dennis-Jordan    .read = iosfc_read,
1262352159cSPhil Dennis-Jordan    .write = iosfc_write,
1272352159cSPhil Dennis-Jordan    .endianness = DEVICE_LITTLE_ENDIAN,
1282352159cSPhil Dennis-Jordan    .valid = {
1292352159cSPhil Dennis-Jordan        .min_access_size = 4,
1302352159cSPhil Dennis-Jordan        .max_access_size = 8,
1312352159cSPhil Dennis-Jordan    },
1322352159cSPhil Dennis-Jordan    .impl = {
1332352159cSPhil Dennis-Jordan        .min_access_size = 4,
1342352159cSPhil Dennis-Jordan        .max_access_size = 8,
1352352159cSPhil Dennis-Jordan    },
1362352159cSPhil Dennis-Jordan};
1372352159cSPhil Dennis-Jordan
1382352159cSPhil Dennis-Jordanstatic void raise_irq_bh(void *opaque)
1392352159cSPhil Dennis-Jordan{
1402352159cSPhil Dennis-Jordan    qemu_irq *irq = opaque;
1412352159cSPhil Dennis-Jordan
1422352159cSPhil Dennis-Jordan    qemu_irq_pulse(*irq);
1432352159cSPhil Dennis-Jordan}
1442352159cSPhil Dennis-Jordan
1452352159cSPhil Dennis-Jordanstatic void *apple_gfx_mmio_map_surface_memory(uint64_t guest_physical_address,
1462352159cSPhil Dennis-Jordan                                               uint64_t length, bool read_only)
1472352159cSPhil Dennis-Jordan{
1482352159cSPhil Dennis-Jordan    void *mem;
1492352159cSPhil Dennis-Jordan    MemoryRegion *region = NULL;
1502352159cSPhil Dennis-Jordan
1512352159cSPhil Dennis-Jordan    RCU_READ_LOCK_GUARD();
1522352159cSPhil Dennis-Jordan    mem = apple_gfx_host_ptr_for_gpa_range(guest_physical_address,
1532352159cSPhil Dennis-Jordan                                           length, read_only, &region);
1542352159cSPhil Dennis-Jordan    if (mem) {
1552352159cSPhil Dennis-Jordan        memory_region_ref(region);
1562352159cSPhil Dennis-Jordan    }
1572352159cSPhil Dennis-Jordan    return mem;
1582352159cSPhil Dennis-Jordan}
1592352159cSPhil Dennis-Jordan
1602352159cSPhil Dennis-Jordanstatic bool apple_gfx_mmio_unmap_surface_memory(void *ptr)
1612352159cSPhil Dennis-Jordan{
1622352159cSPhil Dennis-Jordan    MemoryRegion *region;
1632352159cSPhil Dennis-Jordan    ram_addr_t offset = 0;
1642352159cSPhil Dennis-Jordan
1652352159cSPhil Dennis-Jordan    RCU_READ_LOCK_GUARD();
1662352159cSPhil Dennis-Jordan    region = memory_region_from_host(ptr, &offset);
1672352159cSPhil Dennis-Jordan    if (!region) {
1682352159cSPhil Dennis-Jordan        qemu_log_mask(LOG_GUEST_ERROR,
1692352159cSPhil Dennis-Jordan                      "%s: memory at %p to be unmapped not found.\n",
1702352159cSPhil Dennis-Jordan                      __func__, ptr);
1712352159cSPhil Dennis-Jordan        return false;
1722352159cSPhil Dennis-Jordan    }
1732352159cSPhil Dennis-Jordan
1742352159cSPhil Dennis-Jordan    trace_apple_gfx_iosfc_unmap_memory_region(ptr, region);
1752352159cSPhil Dennis-Jordan    memory_region_unref(region);
1762352159cSPhil Dennis-Jordan    return true;
1772352159cSPhil Dennis-Jordan}
1782352159cSPhil Dennis-Jordan
1792352159cSPhil Dennis-Jordanstatic PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device(
1802352159cSPhil Dennis-Jordan    AppleGFXMMIOState *s)
1812352159cSPhil Dennis-Jordan{
1822352159cSPhil Dennis-Jordan    PGIOSurfaceHostDeviceDescriptor *iosfc_desc =
1832352159cSPhil Dennis-Jordan        [PGIOSurfaceHostDeviceDescriptor new];
1842352159cSPhil Dennis-Jordan    PGIOSurfaceHostDevice *iosfc_host_dev;
1852352159cSPhil Dennis-Jordan
1862352159cSPhil Dennis-Jordan    iosfc_desc.mapMemory =
1872352159cSPhil Dennis-Jordan        ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
1882352159cSPhil Dennis-Jordan            *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
1892352159cSPhil Dennis-Jordan
1902352159cSPhil Dennis-Jordan            trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
1912352159cSPhil Dennis-Jordan
1922352159cSPhil Dennis-Jordan            return *va != NULL;
1932352159cSPhil Dennis-Jordan        };
1942352159cSPhil Dennis-Jordan
1952352159cSPhil Dennis-Jordan    iosfc_desc.unmapMemory =
1962352159cSPhil Dennis-Jordan        ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
1972352159cSPhil Dennis-Jordan            return apple_gfx_mmio_unmap_surface_memory(va);
1982352159cSPhil Dennis-Jordan        };
1992352159cSPhil Dennis-Jordan
2002352159cSPhil Dennis-Jordan    iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) {
2012352159cSPhil Dennis-Jordan        trace_apple_gfx_iosfc_raise_irq(vector);
2022352159cSPhil Dennis-Jordan        aio_bh_schedule_oneshot(qemu_get_aio_context(),
2032352159cSPhil Dennis-Jordan                                raise_irq_bh, &s->irq_iosfc);
2042352159cSPhil Dennis-Jordan        return true;
2052352159cSPhil Dennis-Jordan    };
2062352159cSPhil Dennis-Jordan
2072352159cSPhil Dennis-Jordan    iosfc_host_dev =
2082352159cSPhil Dennis-Jordan        [[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc];
2092352159cSPhil Dennis-Jordan    [iosfc_desc release];
2102352159cSPhil Dennis-Jordan    return iosfc_host_dev;
2112352159cSPhil Dennis-Jordan}
2122352159cSPhil Dennis-Jordan
2132352159cSPhil Dennis-Jordanstatic void apple_gfx_mmio_realize(DeviceState *dev, Error **errp)
2142352159cSPhil Dennis-Jordan{
2152352159cSPhil Dennis-Jordan    @autoreleasepool {
2162352159cSPhil Dennis-Jordan        AppleGFXMMIOState *s = APPLE_GFX_MMIO(dev);
2172352159cSPhil Dennis-Jordan        PGDeviceDescriptor *desc = [PGDeviceDescriptor new];
2182352159cSPhil Dennis-Jordan
2192352159cSPhil Dennis-Jordan        desc.raiseInterrupt = ^(uint32_t vector) {
2202352159cSPhil Dennis-Jordan            trace_apple_gfx_raise_irq(vector);
2212352159cSPhil Dennis-Jordan            aio_bh_schedule_oneshot(qemu_get_aio_context(),
2222352159cSPhil Dennis-Jordan                                    raise_irq_bh, &s->irq_gfx);
2232352159cSPhil Dennis-Jordan        };
2242352159cSPhil Dennis-Jordan
2252352159cSPhil Dennis-Jordan        desc.usingIOSurfaceMapper = true;
2262352159cSPhil Dennis-Jordan        s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
2272352159cSPhil Dennis-Jordan
2282352159cSPhil Dennis-Jordan        if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) {
2292352159cSPhil Dennis-Jordan            [s->pgiosfc release];
2302352159cSPhil Dennis-Jordan            s->pgiosfc = nil;
2312352159cSPhil Dennis-Jordan        }
2322352159cSPhil Dennis-Jordan
2332352159cSPhil Dennis-Jordan        [desc release];
2342352159cSPhil Dennis-Jordan        desc = nil;
2352352159cSPhil Dennis-Jordan    }
2362352159cSPhil Dennis-Jordan}
2372352159cSPhil Dennis-Jordan
2382352159cSPhil Dennis-Jordanstatic void apple_gfx_mmio_init(Object *obj)
2392352159cSPhil Dennis-Jordan{
2402352159cSPhil Dennis-Jordan    AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj);
2412352159cSPhil Dennis-Jordan
2422352159cSPhil Dennis-Jordan    apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_MMIO);
2432352159cSPhil Dennis-Jordan
2442352159cSPhil Dennis-Jordan    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->common.iomem_gfx);
2452352159cSPhil Dennis-Jordan    memory_region_init_io(&s->iomem_iosfc, obj, &apple_iosfc_ops, s,
2462352159cSPhil Dennis-Jordan                          TYPE_APPLE_GFX_MMIO, 0x10000);
2472352159cSPhil Dennis-Jordan    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem_iosfc);
2482352159cSPhil Dennis-Jordan    sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_gfx);
2492352159cSPhil Dennis-Jordan    sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_iosfc);
2502352159cSPhil Dennis-Jordan}
2512352159cSPhil Dennis-Jordan
2522352159cSPhil Dennis-Jordanstatic void apple_gfx_mmio_reset(Object *obj, ResetType type)
2532352159cSPhil Dennis-Jordan{
2542352159cSPhil Dennis-Jordan    AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj);
2552352159cSPhil Dennis-Jordan    [s->common.pgdev reset];
2562352159cSPhil Dennis-Jordan}
2572352159cSPhil Dennis-Jordan
258bb43a234SPhil Dennis-Jordanstatic const Property apple_gfx_mmio_properties[] = {
259bb43a234SPhil Dennis-Jordan    DEFINE_PROP_ARRAY("display-modes", AppleGFXMMIOState,
260bb43a234SPhil Dennis-Jordan                      common.num_display_modes, common.display_modes,
261bb43a234SPhil Dennis-Jordan                      qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode),
262bb43a234SPhil Dennis-Jordan};
2632352159cSPhil Dennis-Jordan
264*12d1a768SPhilippe Mathieu-Daudéstatic void apple_gfx_mmio_class_init(ObjectClass *klass, const void *data)
2652352159cSPhil Dennis-Jordan{
2662352159cSPhil Dennis-Jordan    DeviceClass *dc = DEVICE_CLASS(klass);
2672352159cSPhil Dennis-Jordan    ResettableClass *rc = RESETTABLE_CLASS(klass);
2682352159cSPhil Dennis-Jordan
2692352159cSPhil Dennis-Jordan    rc->phases.hold = apple_gfx_mmio_reset;
2702352159cSPhil Dennis-Jordan    dc->hotpluggable = false;
2712352159cSPhil Dennis-Jordan    dc->realize = apple_gfx_mmio_realize;
272bb43a234SPhil Dennis-Jordan
273bb43a234SPhil Dennis-Jordan    device_class_set_props(dc, apple_gfx_mmio_properties);
2742352159cSPhil Dennis-Jordan}
2752352159cSPhil Dennis-Jordan
2762352159cSPhil Dennis-Jordanstatic const TypeInfo apple_gfx_mmio_types[] = {
2772352159cSPhil Dennis-Jordan    {
2782352159cSPhil Dennis-Jordan        .name          = TYPE_APPLE_GFX_MMIO,
2792352159cSPhil Dennis-Jordan        .parent        = TYPE_SYS_BUS_DEVICE,
2802352159cSPhil Dennis-Jordan        .instance_size = sizeof(AppleGFXMMIOState),
2812352159cSPhil Dennis-Jordan        .class_init    = apple_gfx_mmio_class_init,
2822352159cSPhil Dennis-Jordan        .instance_init = apple_gfx_mmio_init,
2832352159cSPhil Dennis-Jordan    }
2842352159cSPhil Dennis-Jordan};
2852352159cSPhil Dennis-JordanDEFINE_TYPES(apple_gfx_mmio_types)
286