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, ®ion); 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