From patchwork Wed Jul 17 11:03:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Phil Dennis-Jordan X-Patchwork-Id: 13735399 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 47976C3DA42 for ; Wed, 17 Jul 2024 11:14:36 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2TL-0004v8-CD; Wed, 17 Jul 2024 07:05:23 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sU2Sj-0001qR-3g for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:47 -0400 Received: from 89-104-8-17.customer.bnet.at ([89.104.8.17] helo=intel-mbp.local) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2Sa-0006zs-Al for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:43 -0400 Received: by intel-mbp.local (Postfix, from userid 501) id A9EE238F9CF; Wed, 17 Jul 2024 13:04:06 +0200 (CEST) From: Phil Dennis-Jordan To: qemu-devel@nongnu.org, agraf@csgraf.de, pbonzini@redhat.com, phil@philjordan.eu, marcandre.lureau@redhat.com, berrange@redhat.com, thuth@redhat.com, philmd@linaro.org, akihiko.odaki@daynix.com, peter.maydell@linaro.org Cc: Alexander Graf Subject: [PATCH v2 1/8] hw/display/apple-gfx: Introduce ParavirtualizedGraphics.Framework support Date: Wed, 17 Jul 2024 13:03:19 +0200 Message-Id: <20240717110326.45230-2-phil@philjordan.eu> X-Mailer: git-send-email 2.39.3 (Apple Git-146) In-Reply-To: <20240717110326.45230-1-phil@philjordan.eu> References: <20240717110326.45230-1-phil@philjordan.eu> MIME-Version: 1.0 Received-SPF: none client-ip=89.104.8.17; envelope-from=phil@intel-mbp.local; helo=intel-mbp.local X-Spam_score_int: -6 X-Spam_score: -0.7 X-Spam_bar: / X-Spam_report: (-0.7 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.001, KHOP_HELO_FCRDNS=0.261, NO_DNS_FOR_FROM=0.001, RDNS_DYNAMIC=0.982, SPF_HELO_NONE=0.001, SPF_NONE=0.001, TVD_RCVD_IP=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org MacOS provides a framework (library) that allows any vmm to implement a paravirtualized 3d graphics passthrough to the host metal stack called ParavirtualizedGraphics.Framework (PVG). The library abstracts away almost every aspect of the paravirtualized device model and only provides and receives callbacks on MMIO access as well as to share memory address space between the VM and PVG. This patch implements a QEMU device that drives PVG for the VMApple variant of it. Signed-off-by: Alexander Graf Co-authored-by: Alexander Graf Subsequent changes: * Cherry-pick/rebase conflict fixes * BQL function renaming * Moved from hw/vmapple/ (useful outside that machine type) * Code review comments: Switched to DEFINE_TYPES macro & little endian MMIO. * Removed some dead/superfluous code * Mad set_mode thread & memory safe * Added migration blocker due to lack of (de-)serialisation. * Fixes to ObjC refcounting and autorelease pool usage. * Fixed ObjC new/init misuse * Switched to ObjC category extension for private property. * Simplified task memory mapping and made it thread safe. * Refactoring to split generic and vmapple MMIO variant specific code. * Switched to asynchronous MMIO writes on x86-64 * Rendering and graphics update are now done asynchronously * Fixed cursor handling * Coding convention fixes * Removed software cursor compositing Signed-off-by: Phil Dennis-Jordan --- hw/display/Kconfig | 8 + hw/display/apple-gfx-vmapple.m | 194 +++++++++++++ hw/display/apple-gfx.h | 56 ++++ hw/display/apple-gfx.m | 501 +++++++++++++++++++++++++++++++++ hw/display/meson.build | 2 + hw/display/trace-events | 25 ++ meson.build | 4 + 7 files changed, 790 insertions(+) create mode 100644 hw/display/apple-gfx-vmapple.m create mode 100644 hw/display/apple-gfx.h create mode 100644 hw/display/apple-gfx.m diff --git a/hw/display/Kconfig b/hw/display/Kconfig index a4552c8ed7..e3d10bf6ff 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -143,3 +143,11 @@ config XLNX_DISPLAYPORT config DM163 bool + +config MAC_PVG + bool + default y + +config MAC_PVG_VMAPPLE + bool + depends on MAC_PVG diff --git a/hw/display/apple-gfx-vmapple.m b/hw/display/apple-gfx-vmapple.m new file mode 100644 index 0000000000..6af8b7a292 --- /dev/null +++ b/hw/display/apple-gfx-vmapple.m @@ -0,0 +1,194 @@ +#include "apple-gfx.h" +#include "monitor/monitor.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "trace.h" +#import + +_Static_assert(__aarch64__, ""); + +/* + * ParavirtualizedGraphics.Framework only ships header files for the x86 + * variant which does not include IOSFC descriptors and host devices. We add + * their definitions here so that we can also work with the ARM version. + */ +typedef bool(^IOSFCRaiseInterrupt)(uint32_t vector); +typedef bool(^IOSFCUnmapMemory)( + void *a, void *b, void *c, void *d, void *e, void *f); +typedef bool(^IOSFCMapMemory)( + uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f); + +@interface PGDeviceDescriptor (IOSurfaceMapper) +@property (readwrite, nonatomic) bool usingIOSurfaceMapper; +@end + +@interface PGIOSurfaceHostDeviceDescriptor : NSObject +-(PGIOSurfaceHostDeviceDescriptor *)init; +@property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory; +@property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory; +@property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt; +@end + +@interface PGIOSurfaceHostDevice : NSObject +-(instancetype)initWithDescriptor:(PGIOSurfaceHostDeviceDescriptor *) desc; +-(uint32_t)mmioReadAtOffset:(size_t) offset; +-(void)mmioWriteAtOffset:(size_t) offset value:(uint32_t)value; +@end + +typedef struct AppleGFXVmappleState { + SysBusDevice parent_obj; + + AppleGFXState common; + + qemu_irq irq_gfx; + qemu_irq irq_iosfc; + MemoryRegion iomem_iosfc; + PGIOSurfaceHostDevice *pgiosfc; +} AppleGFXVmappleState; + +OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXVmappleState, APPLE_GFX_VMAPPLE) + + +static uint64_t apple_iosfc_read(void *opaque, hwaddr offset, unsigned size) +{ + AppleGFXVmappleState *s = opaque; + uint64_t res = 0; + + bql_unlock(); + res = [s->pgiosfc mmioReadAtOffset:offset]; + bql_lock(); + + trace_apple_iosfc_read(offset, res); + + return res; +} + +static void apple_iosfc_write( + void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + AppleGFXVmappleState *s = opaque; + + trace_apple_iosfc_write(offset, val); + + [s->pgiosfc mmioWriteAtOffset:offset value:val]; +} + +static const MemoryRegionOps apple_iosfc_ops = { + .read = apple_iosfc_read, + .write = apple_iosfc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device( + AppleGFXVmappleState *s) +{ + PGIOSurfaceHostDeviceDescriptor *iosfc_desc = + [PGIOSurfaceHostDeviceDescriptor new]; + PGIOSurfaceHostDevice *iosfc_host_dev = nil; + + iosfc_desc.mapMemory = + ^(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) { + trace_apple_iosfc_map_memory(phys, len, ro, va, e, f); + MemoryRegion *tmp_mr; + *va = gpa2hva(&tmp_mr, phys, len, NULL); + return (bool)true; + }; + + iosfc_desc.unmapMemory = + ^(void *a, void *b, void *c, void *d, void *e, void *f) { + trace_apple_iosfc_unmap_memory(a, b, c, d, e, f); + return (bool)true; + }; + + iosfc_desc.raiseInterrupt = ^(uint32_t vector) { + trace_apple_iosfc_raise_irq(vector); + bool locked = bql_locked(); + if (!locked) { + bql_lock(); + } + qemu_irq_pulse(s->irq_iosfc); + if (!locked) { + bql_unlock(); + } + return (bool)true; + }; + + iosfc_host_dev = + [[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc]; + [iosfc_desc release]; + return iosfc_host_dev; +} + +static void apple_gfx_vmapple_realize(DeviceState *dev, Error **errp) +{ + @autoreleasepool { + AppleGFXVmappleState *s = APPLE_GFX_VMAPPLE(dev); + + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; + desc.usingIOSurfaceMapper = true; + desc.raiseInterrupt = ^(uint32_t vector) { + bool locked; + + trace_apple_gfx_raise_irq(vector); + locked = bql_locked(); + if (!locked) { + bql_lock(); + } + qemu_irq_pulse(s->irq_gfx); + if (!locked) { + bql_unlock(); + } + }; + + s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s); + + apple_gfx_common_realize(&s->common, desc); + [desc release]; + desc = nil; + } +} + +static void apple_gfx_vmapple_reset(DeviceState *d) +{ +} + +static void apple_gfx_vmapple_init(Object *obj) +{ + AppleGFXVmappleState *s = APPLE_GFX_VMAPPLE(obj); + + apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_VMAPPLE); + + memory_region_init_io(&s->iomem_iosfc, obj, &apple_iosfc_ops, s, + TYPE_APPLE_GFX_VMAPPLE, 0x10000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->common.iomem_gfx); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem_iosfc); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_gfx); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_iosfc); +} + +static void apple_gfx_vmapple_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = apple_gfx_vmapple_reset; + dc->realize = apple_gfx_vmapple_realize; +} + +static TypeInfo apple_gfx_vmapple_types[] = { + { + .name = TYPE_APPLE_GFX_VMAPPLE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AppleGFXVmappleState), + .class_init = apple_gfx_vmapple_class_init, + .instance_init = apple_gfx_vmapple_init, + } +}; +DEFINE_TYPES(apple_gfx_vmapple_types) diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h new file mode 100644 index 0000000000..9d6d40795e --- /dev/null +++ b/hw/display/apple-gfx.h @@ -0,0 +1,56 @@ +#ifndef QEMU_APPLE_GFX_H +#define QEMU_APPLE_GFX_H + +#define TYPE_APPLE_GFX_VMAPPLE "apple-gfx-vmapple" +#define TYPE_APPLE_GFX_PCI "apple-gfx-pci" + +#include "qemu/typedefs.h" + +typedef struct AppleGFXState AppleGFXState; + +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name); + +#ifdef __OBJC__ + +#include "qemu/osdep.h" +#include "exec/memory.h" +#include "ui/surface.h" +#include + +@class PGDeviceDescriptor; +@protocol PGDevice; +@protocol PGDisplay; +@protocol MTLDevice; +@protocol MTLTexture; +@protocol MTLCommandQueue; + +typedef QTAILQ_HEAD(, PGTask_s) AppleGFXTaskList; + +struct AppleGFXState { + MemoryRegion iomem_gfx; + id pgdev; + id pgdisp; + AppleGFXTaskList tasks; + QemuConsole *con; + id mtl; + id mtl_queue; + bool handles_frames; + bool new_frame; + bool cursor_show; + QEMUCursor *cursor; + + dispatch_queue_t render_queue; + /* The following fields should only be accessed from render_queue: */ + bool gfx_update_requested; + bool new_frame_ready; + int32_t pending_frames; + void *vram; + DisplaySurface *surface; + id texture; +}; + +void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc); + +#endif /* __OBJC__ */ + +#endif diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m new file mode 100644 index 0000000000..564f3df6fd --- /dev/null +++ b/hw/display/apple-gfx.m @@ -0,0 +1,501 @@ +/* + * QEMU Apple ParavirtualizedGraphics.framework device + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * 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. + */ + +#include "apple-gfx.h" +#include "trace.h" +#include "qemu/main-loop.h" +#include "ui/console.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "migration/blocker.h" +#include +#import + +static const PGDisplayCoord_t apple_gfx_modes[] = { + { .x = 1440, .y = 1080 }, + { .x = 1280, .y = 1024 }, +}; + +typedef struct PGTask_s { // Name matches forward declaration in PG header + QTAILQ_ENTRY(PGTask_s) node; + mach_vm_address_t address; + uint64_t len; +} AppleGFXTask; + +static Error *apple_gfx_mig_blocker; + +static void apple_gfx_render_frame_completed(AppleGFXState *s, void *vram, + id texture); + +static AppleGFXTask *apple_gfx_new_task(AppleGFXState *s, uint64_t len) +{ + mach_vm_address_t task_mem; + AppleGFXTask *task; + kern_return_t r; + + r = mach_vm_allocate(mach_task_self(), &task_mem, len, VM_FLAGS_ANYWHERE); + if (r != KERN_SUCCESS || task_mem == 0) { + return NULL; + } + + task = g_new0(AppleGFXTask, 1); + + task->address = task_mem; + task->len = len; + QTAILQ_INSERT_TAIL(&s->tasks, task, node); + + return task; +} + +static uint64_t apple_gfx_read(void *opaque, hwaddr offset, unsigned size) +{ + AppleGFXState *s = opaque; + uint64_t res = 0; + + res = [s->pgdev mmioReadAtOffset:offset]; + + trace_apple_gfx_read(offset, res); + + return res; +} + +static void apple_gfx_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + AppleGFXState *s = opaque; + + trace_apple_gfx_write(offset, val); + +#ifdef __x86_64__ + /* If we use this code on aarch64, the guest fails to bring up the + * device... */ + id dev = s->pgdev; + dispatch_queue_t bg_queue = NULL; + + bg_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul); + [dev retain]; + dispatch_async(bg_queue, ^{ + [dev mmioWriteAtOffset:offset value:val]; + [dev release]; + }); +#else + /* ... and if we use the following on x86-64, graphics output eventually + * hangs with warnings about reentrant MMIO. */ + bql_unlock(); + [s->pgdev mmioWriteAtOffset:offset value:val]; + bql_lock(); +#endif +} + +static const MemoryRegionOps apple_gfx_ops = { + .read = apple_gfx_read, + .write = apple_gfx_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void apple_gfx_render_new_frame(AppleGFXState *s) +{ + BOOL r; + void *vram = s->vram; + uint32_t width = surface_width(s->surface); + uint32_t height = surface_height(s->surface); + MTLRegion region = MTLRegionMake2D(0, 0, width, height); + id command_buffer = [s->mtl_queue commandBuffer]; + id texture = s->texture; + r = [s->pgdisp encodeCurrentFrameToCommandBuffer:command_buffer + texture:texture + region:region]; + if (!r) { + return; + } + [texture retain]; + + [command_buffer retain]; + [command_buffer addCompletedHandler: + ^(id cb) + { + dispatch_async(s->render_queue, ^{ + apple_gfx_render_frame_completed(s, vram, texture); + [texture release]; + }); + [command_buffer release]; + }]; + [command_buffer commit]; +} + +static void copy_mtl_texture_to_surface_mem(id texture, void *vram) +{ + /* TODO: Skip this entirely on a pure Metal or headless/guest-only + * rendering path, else use a blit command encoder? Needs careful + * (double?) buffering design. */ + size_t width = texture.width, height = texture.height; + MTLRegion region = MTLRegionMake2D(0, 0, width, height); + [texture getBytes:vram + bytesPerRow:(width * 4) + bytesPerImage:(width * height * 4) + fromRegion:region + mipmapLevel:0 + slice:0]; +} + +static void apple_gfx_render_frame_completed(AppleGFXState *s, void *vram, + id texture) +{ + --s->pending_frames; + assert(s->pending_frames >= 0); + + if (vram != s->vram) { + /* Display mode has changed, drop this old frame. */ + assert(texture != s->texture); + g_free(vram); + } else { + copy_mtl_texture_to_surface_mem(texture, vram); + if (s->gfx_update_requested) { + s->gfx_update_requested = false; + dpy_gfx_update_full(s->con); + graphic_hw_update_done(s->con); + s->new_frame_ready = false; + } else { + s->new_frame_ready = true; + } + } + if (s->pending_frames > 0) { + apple_gfx_render_new_frame(s); + } +} + +static void apple_gfx_fb_update_display(void *opaque) +{ + AppleGFXState *s = opaque; + + dispatch_async(s->render_queue, ^{ + if (s->pending_frames > 0) { + s->gfx_update_requested = true; + } else { + if (s->new_frame_ready) { + dpy_gfx_update_full(s->con); + s->new_frame_ready = false; + } + graphic_hw_update_done(s->con); + } + }); +} + +static const GraphicHwOps apple_gfx_fb_ops = { + .gfx_update = apple_gfx_fb_update_display, + .gfx_update_async = true, +}; + +static void update_cursor(AppleGFXState *s) +{ + dpy_mouse_set(s->con, s->pgdisp.cursorPosition.x, + s->pgdisp.cursorPosition.y, s->cursor_show); +} + +static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height) +{ + void *vram = NULL; + DisplaySurface *surface; + MTLTextureDescriptor *textureDescriptor; + id texture = nil; + __block bool no_change = false; + + dispatch_sync(s->render_queue, + ^{ + if (s->surface && + width == surface_width(s->surface) && + height == surface_height(s->surface)) { + no_change = true; + } + }); + + if (no_change) { + return; + } + + vram = g_malloc0(width * height * 4); + surface = qemu_create_displaysurface_from(width, height, PIXMAN_LE_a8r8g8b8, + width * 4, vram); + + @autoreleasepool { + textureDescriptor = + [MTLTextureDescriptor + texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:width + height:height + mipmapped:NO]; + textureDescriptor.usage = s->pgdisp.minimumTextureUsage; + texture = [s->mtl newTextureWithDescriptor:textureDescriptor]; + } + + dispatch_sync(s->render_queue, + ^{ + id old_texture = nil; + void *old_vram = s->vram; + s->vram = vram; + s->surface = surface; + + dpy_gfx_replace_surface(s->con, surface); + + old_texture = s->texture; + s->texture = texture; + [old_texture release]; + + if (s->pending_frames == 0) { + g_free(old_vram); + } + }); +} + +static void create_fb(AppleGFXState *s) +{ + s->con = graphic_console_init(NULL, 0, &apple_gfx_fb_ops, s); + set_mode(s, 1440, 1080); + + s->cursor_show = true; +} + +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name) +{ + Error *local_err = NULL; + int r; + + memory_region_init_io(&s->iomem_gfx, obj, &apple_gfx_ops, s, obj_name, 0x4000); + + /* TODO: PVG framework supports serialising device state: integrate it! */ + if (apple_gfx_mig_blocker == NULL) { + error_setg(&apple_gfx_mig_blocker, + "Migration state blocked by apple-gfx display device"); + r = migrate_add_blocker(&apple_gfx_mig_blocker, &local_err); + if (r < 0) { + error_report_err(local_err); + } + } +} + +static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, + PGDeviceDescriptor *desc) +{ + desc.createTask = ^(uint64_t vmSize, void * _Nullable * _Nonnull baseAddress) { + AppleGFXTask *task = apple_gfx_new_task(s, vmSize); + *baseAddress = (void*)task->address; + trace_apple_gfx_create_task(vmSize, *baseAddress); + return task; + }; + + desc.destroyTask = ^(AppleGFXTask * _Nonnull task) { + trace_apple_gfx_destroy_task(task); + QTAILQ_REMOVE(&s->tasks, task, node); + mach_vm_deallocate(mach_task_self(), task->address, task->len); + g_free(task); + }; + + desc.mapMemory = ^(AppleGFXTask * _Nonnull task, uint32_t rangeCount, + uint64_t virtualOffset, bool readOnly, + PGPhysicalMemoryRange_t * _Nonnull ranges) { + kern_return_t r; + mach_vm_address_t target, source; + trace_apple_gfx_map_memory(task, rangeCount, virtualOffset, readOnly); + for (int i = 0; i < rangeCount; i++) { + PGPhysicalMemoryRange_t *range = &ranges[i]; + MemoryRegion *tmp_mr; + /* TODO: Bounds checks? r/o? */ + bql_lock(); + + trace_apple_gfx_map_memory_range(i, range->physicalAddress, + range->physicalLength); + + target = task->address + virtualOffset; + source = (mach_vm_address_t)gpa2hva(&tmp_mr, + range->physicalAddress, + range->physicalLength, NULL); + vm_prot_t cur_protection = 0; + vm_prot_t max_protection = 0; + // Map guest RAM at range->physicalAddress into PG task memory range + r = mach_vm_remap(mach_task_self(), + &target, range->physicalLength, vm_page_size - 1, + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, + mach_task_self(), + source, false /* shared mapping, no copy */, + &cur_protection, &max_protection, + VM_INHERIT_COPY); + trace_apple_gfx_remap(r, source, target); + g_assert(r == KERN_SUCCESS); + + bql_unlock(); + + virtualOffset += range->physicalLength; + } + return (bool)true; + }; + + desc.unmapMemory = ^(AppleGFXTask * _Nonnull task, uint64_t virtualOffset, + uint64_t length) { + kern_return_t r; + mach_vm_address_t range_address; + + trace_apple_gfx_unmap_memory(task, virtualOffset, length); + + /* Replace task memory range with fresh pages, undoing the mapping + * from guest RAM. */ + range_address = task->address + virtualOffset; + r = mach_vm_allocate(mach_task_self(), &range_address, length, + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); + g_assert(r == KERN_SUCCESS); + + return (bool)true; + }; + + desc.readMemory = ^(uint64_t physicalAddress, uint64_t length, + void * _Nonnull dst) { + trace_apple_gfx_read_memory(physicalAddress, length, dst); + cpu_physical_memory_read(physicalAddress, dst, length); + return (bool)true; + }; + +} + +static PGDisplayDescriptor *apple_gfx_prepare_display_handlers(AppleGFXState *s) +{ + PGDisplayDescriptor *disp_desc = [PGDisplayDescriptor new]; + + disp_desc.name = @"QEMU display"; + disp_desc.sizeInMillimeters = NSMakeSize(400., 300.); /* A 20" display */ + disp_desc.queue = dispatch_get_main_queue(); + disp_desc.newFrameEventHandler = ^(void) { + trace_apple_gfx_new_frame(); + dispatch_async(s->render_queue, ^{ + /* Drop frames if we get too far ahead. */ + if (s->pending_frames >= 2) + return; + ++s->pending_frames; + if (s->pending_frames > 1) { + return; + } + @autoreleasepool { + apple_gfx_render_new_frame(s); + } + }); + }; + disp_desc.modeChangeHandler = ^(PGDisplayCoord_t sizeInPixels, + OSType pixelFormat) { + trace_apple_gfx_mode_change(sizeInPixels.x, sizeInPixels.y); + set_mode(s, sizeInPixels.x, sizeInPixels.y); + }; + disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph, + PGDisplayCoord_t hotSpot) { + uint32_t bpp = glyph.bitsPerPixel; + size_t width = glyph.pixelsWide; + size_t height = glyph.pixelsHigh; + size_t padding_bytes_per_row = glyph.bytesPerRow - width * 4; + const uint8_t* px_data = glyph.bitmapData; + + trace_apple_gfx_cursor_set(bpp, width, height); + + if (s->cursor) { + cursor_unref(s->cursor); + s->cursor = NULL; + } + + if (bpp == 32) { /* Shouldn't be anything else, but just to be safe...*/ + s->cursor = cursor_alloc(width, height); + s->cursor->hot_x = hotSpot.x; + s->cursor->hot_y = hotSpot.y; + + uint32_t *dest_px = s->cursor->data; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + /* NSBitmapImageRep's red & blue channels are swapped + * compared to QEMUCursor's. */ + *dest_px = + (px_data[0] << 16u) | + (px_data[1] << 8u) | + (px_data[2] << 0u) | + (px_data[3] << 24u); + ++dest_px; + px_data += 4; + } + px_data += padding_bytes_per_row; + } + dpy_cursor_define(s->con, s->cursor); + update_cursor(s); + } + }; + disp_desc.cursorShowHandler = ^(BOOL show) { + trace_apple_gfx_cursor_show(show); + s->cursor_show = show; + update_cursor(s); + }; + disp_desc.cursorMoveHandler = ^(void) { + trace_apple_gfx_cursor_move(); + update_cursor(s); + }; + + return disp_desc; +} + +static NSArray* apple_gfx_prepare_display_mode_array(void) +{ + PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; + NSArray* mode_array = nil; + int i; + + for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { + modes[i] = + [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.]; + } + + mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)]; + + for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { + [modes[i] release]; + modes[i] = nil; + } + + return mode_array; +} + +void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc) +{ + PGDisplayDescriptor *disp_desc = nil; + + QTAILQ_INIT(&s->tasks); + s->render_queue = dispatch_queue_create("apple-gfx.render", + DISPATCH_QUEUE_SERIAL); + s->mtl = MTLCreateSystemDefaultDevice(); + s->mtl_queue = [s->mtl newCommandQueue]; + + desc.device = s->mtl; + + apple_gfx_register_task_mapping_handlers(s, desc); + + s->pgdev = PGNewDeviceWithDescriptor(desc); + + disp_desc = apple_gfx_prepare_display_handlers(s); + s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc + port:0 serialNum:1234]; + [disp_desc release]; + s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); + + create_fb(s); +} diff --git a/hw/display/meson.build b/hw/display/meson.build index 7db05eace9..70d855749c 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -65,6 +65,8 @@ system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) +system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) +system_ss.add(when: 'CONFIG_MAC_PVG_VMAPPLE', if_true: [files('apple-gfx-vmapple.m'), pvg, metal]) if config_all_devices.has_key('CONFIG_VIRTIO_GPU') virtio_gpu_ss = ss.source_set() diff --git a/hw/display/trace-events b/hw/display/trace-events index 781f8a3320..e35582d659 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -191,3 +191,28 @@ dm163_bits_ppi(unsigned dest_width) "dest_width : %u" dm163_leds(int led, uint32_t value) "led %d: 0x%x" dm163_channels(int channel, uint8_t value) "channel %d: 0x%x" dm163_refresh_rate(uint32_t rr) "refresh rate %d" + +# apple-gfx.m +apple_gfx_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +apple_gfx_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +apple_gfx_create_task(uint32_t vm_size, void *va) "vm_size=0x%x base_addr=%p" +apple_gfx_destroy_task(void *task) "task=%p" +apple_gfx_map_memory(void *task, uint32_t range_count, uint64_t virtual_offset, uint32_t read_only) "task=%p range_count=0x%x virtual_offset=0x%"PRIx64" read_only=%d" +apple_gfx_map_memory_range(uint32_t i, uint64_t phys_addr, uint64_t phys_len) "[%d] phys_addr=0x%"PRIx64" phys_len=0x%"PRIx64 +apple_gfx_remap(uint64_t retval, uint64_t source, uint64_t target) "retval=%"PRId64" source=0x%"PRIx64" target=0x%"PRIx64 +apple_gfx_unmap_memory(void *task, uint64_t virtual_offset, uint64_t length) "task=%p virtual_offset=0x%"PRIx64" length=0x%"PRIx64 +apple_gfx_read_memory(uint64_t phys_address, uint64_t length, void *dst) "phys_addr=0x%"PRIx64" length=0x%"PRIx64" dest=%p" +apple_gfx_raise_irq(uint32_t vector) "vector=0x%x" +apple_gfx_new_frame(void) "" +apple_gfx_mode_change(uint64_t x, uint64_t y) "x=%"PRId64" y=%"PRId64 +apple_gfx_cursor_set(uint32_t bpp, uint64_t width, uint64_t height) "bpp=%d width=%"PRId64" height=0x%"PRId64 +apple_gfx_cursor_show(uint32_t show) "show=%d" +apple_gfx_cursor_move(void) "" + +# apple-gfx-vmapple.m +apple_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +apple_iosfc_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +apple_iosfc_map_memory(uint64_t phys, uint64_t len, uint32_t ro, void *va, void *e, void *f) "phys=0x%"PRIx64" len=0x%"PRIx64" ro=%d va=%p e=%p f=%p" +apple_iosfc_unmap_memory(void *a, void *b, void *c, void *d, void *e, void *f) "a=%p b=%p c=%p d=%p e=%p f=%p" +apple_iosfc_raise_irq(uint32_t vector) "vector=0x%x" + diff --git a/meson.build b/meson.build index a1e51277b0..4bd45cdbfe 100644 --- a/meson.build +++ b/meson.build @@ -710,6 +710,8 @@ socket = [] version_res = [] coref = [] iokit = [] +pvg = [] +metal = [] emulator_link_args = [] midl = not_found widl = not_found @@ -731,6 +733,8 @@ elif host_os == 'darwin' coref = dependency('appleframeworks', modules: 'CoreFoundation') iokit = dependency('appleframeworks', modules: 'IOKit', required: false) host_dsosuf = '.dylib' + pvg = dependency('appleframeworks', modules: 'ParavirtualizedGraphics') + metal = dependency('appleframeworks', modules: 'Metal') elif host_os == 'sunos' socket = [cc.find_library('socket'), cc.find_library('nsl'), From patchwork Wed Jul 17 11:03:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phil Dennis-Jordan X-Patchwork-Id: 13735357 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 99262C3DA42 for ; Wed, 17 Jul 2024 11:06:33 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2TS-0005a7-As; Wed, 17 Jul 2024 07:05:31 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sU2Sj-0001qP-47 for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:47 -0400 Received: from 89-104-8-17.customer.bnet.at ([89.104.8.17] helo=intel-mbp.local) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2SZ-0006zK-W1 for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:43 -0400 Received: by intel-mbp.local (Postfix, from userid 501) id A618B38F9CA; Wed, 17 Jul 2024 13:04:06 +0200 (CEST) From: Phil Dennis-Jordan To: qemu-devel@nongnu.org, agraf@csgraf.de, pbonzini@redhat.com, phil@philjordan.eu, marcandre.lureau@redhat.com, berrange@redhat.com, thuth@redhat.com, philmd@linaro.org, akihiko.odaki@daynix.com, peter.maydell@linaro.org Subject: [PATCH v2 2/8] hw/display/apple-gfx: Adds PCI implementation Date: Wed, 17 Jul 2024 13:03:20 +0200 Message-Id: <20240717110326.45230-3-phil@philjordan.eu> X-Mailer: git-send-email 2.39.3 (Apple Git-146) In-Reply-To: <20240717110326.45230-1-phil@philjordan.eu> References: <20240717110326.45230-1-phil@philjordan.eu> MIME-Version: 1.0 Received-SPF: none client-ip=89.104.8.17; envelope-from=phil@intel-mbp.local; helo=intel-mbp.local X-Spam_score_int: -5 X-Spam_score: -0.6 X-Spam_bar: / X-Spam_report: (-0.6 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.001, KHOP_HELO_FCRDNS=0.261, NO_DNS_FOR_FROM=0.001, RDNS_DYNAMIC=0.982, SPF_NONE=0.001, TVD_RCVD_IP=0.001, T_SPF_HELO_TEMPERROR=0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This change wires up the PCI variant of the paravirtualised graphics device, mainly useful for x86-64 macOS guests, implemented by macOS's ParavirtualizedGraphics.framework. It builds on code shared with the vmapple/mmio variant of the PVG device. Signed-off-by: Phil Dennis-Jordan --- hw/display/Kconfig | 5 ++ hw/display/apple-gfx-pci.m | 121 +++++++++++++++++++++++++++++++++++++ hw/display/meson.build | 1 + 3 files changed, 127 insertions(+) create mode 100644 hw/display/apple-gfx-pci.m diff --git a/hw/display/Kconfig b/hw/display/Kconfig index e3d10bf6ff..8a78a60670 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -151,3 +151,8 @@ config MAC_PVG config MAC_PVG_VMAPPLE bool depends on MAC_PVG + +config MAC_PVG_PCI + bool + depends on MAC_PVG && PCI + default y if PCI_DEVICES diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m new file mode 100644 index 0000000000..b3311e736c --- /dev/null +++ b/hw/display/apple-gfx-pci.m @@ -0,0 +1,121 @@ +#include "apple-gfx.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/msi.h" +#include "qapi/error.h" +#include "trace.h" +#import + +typedef struct AppleGFXPCIState { + PCIDevice parent_obj; + + AppleGFXState common; +} AppleGFXPCIState; + +OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXPCIState, APPLE_GFX_PCI) + +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; + if (option_rom_url.fileURL && option_rom_path != NULL) { + 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) { + /* Done on device not class init to avoid -daemonize ObjC fork crash */ + 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); +} + +static void apple_gfx_pci_interrupt(PCIDevice *dev, AppleGFXPCIState *s, + uint32_t vector) +{ + bool msi_ok; + trace_apple_gfx_raise_irq(vector); + + msi_ok = msi_enabled(dev); + if (msi_ok) { + msi_notify(dev, vector); + } +} + +static void apple_gfx_pci_realize(PCIDevice *dev, Error **errp) +{ + AppleGFXPCIState *s = APPLE_GFX_PCI(dev); + Error *err = NULL; + 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*/, &err); + if (ret != 0) { + error_propagate(errp, err); + return; + } + + @autoreleasepool { + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; + desc.raiseInterrupt = ^(uint32_t vector) { + apple_gfx_pci_interrupt(dev, s, vector); + }; + + apple_gfx_common_realize(&s->common, desc); + [desc release]; + desc = nil; + } +} + +static void apple_gfx_pci_reset(DeviceState *dev) +{ + AppleGFXPCIState *s = APPLE_GFX_PCI(dev); + [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); + + dc->reset = 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 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) + diff --git a/hw/display/meson.build b/hw/display/meson.build index 70d855749c..ceb7bb0761 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -67,6 +67,7 @@ system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_ system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) system_ss.add(when: 'CONFIG_MAC_PVG_VMAPPLE', if_true: [files('apple-gfx-vmapple.m'), pvg, metal]) +system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx-pci.m'), pvg, metal]) if config_all_devices.has_key('CONFIG_VIRTIO_GPU') virtio_gpu_ss = ss.source_set() From patchwork Wed Jul 17 11:03:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Phil Dennis-Jordan X-Patchwork-Id: 13735361 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 85AE9C3DA42 for ; Wed, 17 Jul 2024 11:07:54 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2TF-0004Mu-Pn; Wed, 17 Jul 2024 07:05:17 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sU2Sc-0001Pr-CV for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:38 -0400 Received: from 89-104-8-17.customer.bnet.at ([89.104.8.17] helo=intel-mbp.local) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2SZ-0006zM-Ip for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:38 -0400 Received: by intel-mbp.local (Postfix, from userid 501) id A536838F9C9; Wed, 17 Jul 2024 13:04:06 +0200 (CEST) From: Phil Dennis-Jordan To: qemu-devel@nongnu.org, agraf@csgraf.de, pbonzini@redhat.com, phil@philjordan.eu, marcandre.lureau@redhat.com, berrange@redhat.com, thuth@redhat.com, philmd@linaro.org, akihiko.odaki@daynix.com, peter.maydell@linaro.org Subject: [PATCH v2 3/8] ui/cocoa: Adds non-app runloop on main thread mode Date: Wed, 17 Jul 2024 13:03:21 +0200 Message-Id: <20240717110326.45230-4-phil@philjordan.eu> X-Mailer: git-send-email 2.39.3 (Apple Git-146) In-Reply-To: <20240717110326.45230-1-phil@philjordan.eu> References: <20240717110326.45230-1-phil@philjordan.eu> MIME-Version: 1.0 Received-SPF: none client-ip=89.104.8.17; envelope-from=phil@intel-mbp.local; helo=intel-mbp.local X-Spam_score_int: -6 X-Spam_score: -0.7 X-Spam_bar: / X-Spam_report: (-0.7 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.001, KHOP_HELO_FCRDNS=0.261, NO_DNS_FOR_FROM=0.001, RDNS_DYNAMIC=0.982, SPF_HELO_NONE=0.001, SPF_NONE=0.001, TVD_RCVD_IP=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Various system frameworks on macOS and other Apple platforms require a main runloop to be processing events on the process’s main thread. The Cocoa UI’s requirement to run the process as a Cocoa application automatically enables this runloop, but it can be useful to have the runloop handling events even without the Cocoa UI active. This change adds a non-app runloop mode to the cocoa_main function. This can be requested by other code, while the Cocoa UI additionally enables app mode. This arrangement ensures there is only one qemu_main function switcheroo, and the Cocoa UI’s app mode requirement and other subsystems’ runloop requests don’t conflict with each other. The main runloop is required for the AppleGFX PV graphics device, so the runloop request call has been added to its initialisation. Signed-off-by: Phil Dennis-Jordan --- hw/display/apple-gfx.m | 3 +++ include/qemu-main.h | 2 ++ ui/cocoa.m | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index 564f3df6fd..b1a8a9f649 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -14,6 +14,7 @@ #include "apple-gfx.h" #include "trace.h" +#include "qemu-main.h" #include "qemu/main-loop.h" #include "ui/console.h" #include "monitor/monitor.h" @@ -290,6 +291,8 @@ void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name) error_report_err(local_err); } } + + cocoa_enable_runloop_on_main_thread(); } static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, diff --git a/include/qemu-main.h b/include/qemu-main.h index 940960a7db..da4516e69e 100644 --- a/include/qemu-main.h +++ b/include/qemu-main.h @@ -8,4 +8,6 @@ int qemu_default_main(void); extern int (*qemu_main)(void); +void cocoa_enable_runloop_on_main_thread(void); + #endif /* QEMU_MAIN_H */ diff --git a/ui/cocoa.m b/ui/cocoa.m index 4c2dd33532..40f65d7a45 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -2028,6 +2028,7 @@ static void cocoa_clipboard_request(QemuClipboardInfo *info, exit(status); } +static bool run_as_cocoa_app = false; static int cocoa_main(void) { QemuThread thread; @@ -2040,7 +2041,11 @@ static int cocoa_main(void) // Start the main event loop COCOA_DEBUG("Main thread: entering OSX run loop\n"); - [NSApp run]; + if (run_as_cocoa_app) { + [NSApp run]; + } else { + CFRunLoopRun(); + } COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n"); abort(); @@ -2114,13 +2119,19 @@ static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor) }); } +void cocoa_enable_runloop_on_main_thread(void) +{ + qemu_main = cocoa_main; +} + static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); - qemu_main = cocoa_main; + run_as_cocoa_app = true; + cocoa_enable_runloop_on_main_thread(); // Pull this console process up to being a fully-fledged graphical // app with a menubar and Dock icon From patchwork Wed Jul 17 11:03:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phil Dennis-Jordan X-Patchwork-Id: 13735353 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 33413C41513 for ; Wed, 17 Jul 2024 11:06:10 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2TR-0005Rw-7e; Wed, 17 Jul 2024 07:05:29 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sU2Sd-0001Th-EF for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:43 -0400 Received: from 89-104-8-17.customer.bnet.at ([89.104.8.17] helo=intel-mbp.local) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2SZ-00072n-KT for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:39 -0400 Received: by intel-mbp.local (Postfix, from userid 501) id A3BC938F9C8; Wed, 17 Jul 2024 13:04:06 +0200 (CEST) From: Phil Dennis-Jordan To: qemu-devel@nongnu.org, agraf@csgraf.de, pbonzini@redhat.com, phil@philjordan.eu, marcandre.lureau@redhat.com, berrange@redhat.com, thuth@redhat.com, philmd@linaro.org, akihiko.odaki@daynix.com, peter.maydell@linaro.org Subject: [PATCH v2 4/8] hw/display/apple-gfx: Implements texture syncing for non-UMA GPUs Date: Wed, 17 Jul 2024 13:03:22 +0200 Message-Id: <20240717110326.45230-5-phil@philjordan.eu> X-Mailer: git-send-email 2.39.3 (Apple Git-146) In-Reply-To: <20240717110326.45230-1-phil@philjordan.eu> References: <20240717110326.45230-1-phil@philjordan.eu> MIME-Version: 1.0 Received-SPF: none client-ip=89.104.8.17; envelope-from=phil@intel-mbp.local; helo=intel-mbp.local X-Spam_score_int: -6 X-Spam_score: -0.7 X-Spam_bar: / X-Spam_report: (-0.7 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.001, KHOP_HELO_FCRDNS=0.261, NO_DNS_FOR_FROM=0.001, RDNS_DYNAMIC=0.982, SPF_HELO_NONE=0.001, SPF_NONE=0.001, TVD_RCVD_IP=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Renderable Metal textures are handled differently depending on whether the GPU uses a unified memory architecture (no physical distinction between VRAM and system RAM, CPU and GPU share the memory bus) or not. (Traditional discrete GPU with its own VRAM) In the discrete GPU case, textures must be explicitly synchronised to the CPU or the GPU before use after being modified by the other. In this case, we sync after the PV graphics framework has rendered the next frame into the texture using the GPU so that we can read out its contents using the CPU. This fixes the issue where the guest screen stayed black on AMD Radeon GPUs. Signed-off-by: Phil Dennis-Jordan --- hw/display/apple-gfx.h | 1 + hw/display/apple-gfx.m | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h index 9d6d40795e..995ecf7f4a 100644 --- a/hw/display/apple-gfx.h +++ b/hw/display/apple-gfx.h @@ -43,6 +43,7 @@ struct AppleGFXState { /* The following fields should only be accessed from render_queue: */ bool gfx_update_requested; bool new_frame_ready; + bool using_managed_texture_storage; int32_t pending_frames; void *vram; DisplaySurface *surface; diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index b1a8a9f649..3756a9e3ff 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -129,7 +129,12 @@ static void apple_gfx_render_new_frame(AppleGFXState *s) return; } [texture retain]; - + if (s->using_managed_texture_storage) { + /* "Managed" textures exist in both VRAM and RAM and must be synced. */ + id blit = [command_buffer blitCommandEncoder]; + [blit synchronizeResource:texture]; + [blit endEncoding]; + } [command_buffer retain]; [command_buffer addCompletedHandler: ^(id cb) @@ -248,6 +253,9 @@ static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height) texture = [s->mtl newTextureWithDescriptor:textureDescriptor]; } + s->using_managed_texture_storage = + (texture.storageMode == MTLStorageModeManaged); + dispatch_sync(s->render_queue, ^{ id old_texture = nil; From patchwork Wed Jul 17 11:03:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phil Dennis-Jordan X-Patchwork-Id: 13735381 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 75C8CC3DA4B for ; Wed, 17 Jul 2024 11:12:41 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2TT-0005pF-1L; Wed, 17 Jul 2024 07:05:31 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sU2Sj-0001qO-49 for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:47 -0400 Received: from 89-104-8-17.customer.bnet.at ([89.104.8.17] helo=intel-mbp.local) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2Sb-0006yr-DV for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:43 -0400 Received: by intel-mbp.local (Postfix, from userid 501) id AA88138F9D0; Wed, 17 Jul 2024 13:04:06 +0200 (CEST) From: Phil Dennis-Jordan To: qemu-devel@nongnu.org, agraf@csgraf.de, pbonzini@redhat.com, phil@philjordan.eu, marcandre.lureau@redhat.com, berrange@redhat.com, thuth@redhat.com, philmd@linaro.org, akihiko.odaki@daynix.com, peter.maydell@linaro.org Subject: [PATCH v2 5/8] hw/display/apple-gfx: Replaces magic number with queried MMIO length Date: Wed, 17 Jul 2024 13:03:23 +0200 Message-Id: <20240717110326.45230-6-phil@philjordan.eu> X-Mailer: git-send-email 2.39.3 (Apple Git-146) In-Reply-To: <20240717110326.45230-1-phil@philjordan.eu> References: <20240717110326.45230-1-phil@philjordan.eu> MIME-Version: 1.0 Received-SPF: none client-ip=89.104.8.17; envelope-from=phil@intel-mbp.local; helo=intel-mbp.local X-Spam_score_int: -6 X-Spam_score: -0.7 X-Spam_bar: / X-Spam_report: (-0.7 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.001, KHOP_HELO_FCRDNS=0.261, NO_DNS_FOR_FROM=0.001, RDNS_DYNAMIC=0.982, SPF_HELO_NONE=0.001, SPF_NONE=0.001, TVD_RCVD_IP=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Rather than specifying the length of the device's MMIO range as an unnamed literal constant (which is at least documented as a comment in the framework headers), we query the PVG framework's API for the recommended value. This also avoids problems in future, in case the currently documented value for the default changes. Signed-off-by: Phil Dennis-Jordan --- hw/display/apple-gfx.m | 16 +++++++++++++++- hw/display/trace-events | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index 3756a9e3ff..6f374455f9 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -283,12 +283,26 @@ static void create_fb(AppleGFXState *s) s->cursor_show = true; } +static size_t apple_gfx_get_default_mmio_range_size(void) +{ + size_t mmio_range_size; + @autoreleasepool { + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; + mmio_range_size = desc.mmioLength; + [desc release]; + } + return mmio_range_size; +} + void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name) { Error *local_err = NULL; int r; + size_t mmio_range_size = apple_gfx_get_default_mmio_range_size(); - memory_region_init_io(&s->iomem_gfx, obj, &apple_gfx_ops, s, obj_name, 0x4000); + trace_apple_gfx_common_init(obj_name, mmio_range_size); + memory_region_init_io(&s->iomem_gfx, obj, &apple_gfx_ops, s, obj_name, + mmio_range_size); /* TODO: PVG framework supports serialising device state: integrate it! */ if (apple_gfx_mig_blocker == NULL) { diff --git a/hw/display/trace-events b/hw/display/trace-events index e35582d659..1809a358e3 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -208,6 +208,7 @@ apple_gfx_mode_change(uint64_t x, uint64_t y) "x=%"PRId64" y=%"PRId64 apple_gfx_cursor_set(uint32_t bpp, uint64_t width, uint64_t height) "bpp=%d width=%"PRId64" height=0x%"PRId64 apple_gfx_cursor_show(uint32_t show) "show=%d" apple_gfx_cursor_move(void) "" +apple_gfx_common_init(const char *device_name, size_t mmio_size) "device: %s; MMIO size: %zu bytes" # apple-gfx-vmapple.m apple_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 From patchwork Wed Jul 17 11:03:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phil Dennis-Jordan X-Patchwork-Id: 13735355 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 9C16AC3DA5D for ; Wed, 17 Jul 2024 11:06:15 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2TS-0005hu-2v; Wed, 17 Jul 2024 07:05:30 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sU2Sj-0001qM-3g for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:47 -0400 Received: from 89-104-8-17.customer.bnet.at ([89.104.8.17] helo=intel-mbp.local) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2Sa-000734-6H for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:41 -0400 Received: by intel-mbp.local (Postfix, from userid 501) id A7D3038F9CC; Wed, 17 Jul 2024 13:04:06 +0200 (CEST) From: Phil Dennis-Jordan To: qemu-devel@nongnu.org, agraf@csgraf.de, pbonzini@redhat.com, phil@philjordan.eu, marcandre.lureau@redhat.com, berrange@redhat.com, thuth@redhat.com, philmd@linaro.org, akihiko.odaki@daynix.com, peter.maydell@linaro.org Subject: [PATCH v2 6/8] hw/display/apple-gfx: Host GPU picking improvements Date: Wed, 17 Jul 2024 13:03:24 +0200 Message-Id: <20240717110326.45230-7-phil@philjordan.eu> X-Mailer: git-send-email 2.39.3 (Apple Git-146) In-Reply-To: <20240717110326.45230-1-phil@philjordan.eu> References: <20240717110326.45230-1-phil@philjordan.eu> MIME-Version: 1.0 Received-SPF: none client-ip=89.104.8.17; envelope-from=phil@intel-mbp.local; helo=intel-mbp.local X-Spam_score_int: -5 X-Spam_score: -0.6 X-Spam_bar: / X-Spam_report: (-0.6 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.001, KHOP_HELO_FCRDNS=0.261, NO_DNS_FOR_FROM=0.001, RDNS_DYNAMIC=0.982, SPF_HELO_NONE=0.001, TVD_RCVD_IP=0.001, T_SPF_TEMPERROR=0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org During startup of the PV graphics device, we need to specify the host GPU to use for PV acceleration of the guest's graphics operations. On a host system, this is trivial: pick the only one. The MTLCreateSystemDefaultDevice() function will do the right thing in this case. It gets a little more complicated on systems with more than one GPU; first and foremost, this applies to x86-64 MacBook Pros with 15/16" displays. However, with eGPUs, in theory any x86-64 Mac can gain one or more additional GPUs. In these cases, the default is often not ideal - usually, discrete GPUs are selected. In my tests, performance tends to be best with iGPUs, however, and they are usually also best in terms of energy consumption. Ideally, we will want to allow the user to manually select a GPU if they so choose. In this patch, I am interested in picking a sensible default. Instead of the built-in default logic, it is now: 1. Select a GPU with unified memory (iGPU) 2. If (1) fails, select a GPU that is built-in, not an eGPU. 3. If (2) fails, fall back to system default. Signed-off-by: Phil Dennis-Jordan --- hw/display/apple-gfx.m | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index 6f374455f9..018db8bf19 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -500,6 +500,32 @@ static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, return mode_array; } +static id copy_suitable_metal_device(void) +{ + id dev = nil; + NSArray> *devs = MTLCopyAllDevices(); + + /* Prefer a unified memory GPU. Failing that, pick a non-removable GPU. */ + for (size_t i = 0; i < devs.count; ++i) { + if (devs[i].hasUnifiedMemory) { + dev = devs[i]; + break; + } + if (!devs[i].removable) { + dev = devs[i]; + } + } + + if (dev != nil) { + [dev retain]; + } else { + dev = MTLCreateSystemDefaultDevice(); + } + [devs release]; + + return dev; +} + void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc) { PGDisplayDescriptor *disp_desc = nil; @@ -507,7 +533,7 @@ void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc) QTAILQ_INIT(&s->tasks); s->render_queue = dispatch_queue_create("apple-gfx.render", DISPATCH_QUEUE_SERIAL); - s->mtl = MTLCreateSystemDefaultDevice(); + s->mtl = copy_suitable_metal_device(); s->mtl_queue = [s->mtl newCommandQueue]; desc.device = s->mtl; From patchwork Wed Jul 17 11:03:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phil Dennis-Jordan X-Patchwork-Id: 13735405 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 10B17C3DA4B for ; Wed, 17 Jul 2024 11:16:41 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2TD-0003uB-Va; Wed, 17 Jul 2024 07:05:16 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sU2Sj-0001qY-4c for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:47 -0400 Received: from 89-104-8-17.customer.bnet.at ([89.104.8.17] helo=intel-mbp.local) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2SZ-0006zq-VE for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:43 -0400 Received: by intel-mbp.local (Postfix, from userid 501) id A6FE938F9CB; Wed, 17 Jul 2024 13:04:06 +0200 (CEST) From: Phil Dennis-Jordan To: qemu-devel@nongnu.org, agraf@csgraf.de, pbonzini@redhat.com, phil@philjordan.eu, marcandre.lureau@redhat.com, berrange@redhat.com, thuth@redhat.com, philmd@linaro.org, akihiko.odaki@daynix.com, peter.maydell@linaro.org Subject: [PATCH v2 7/8] hw/display/apple-gfx: Adds configurable mode list Date: Wed, 17 Jul 2024 13:03:25 +0200 Message-Id: <20240717110326.45230-8-phil@philjordan.eu> X-Mailer: git-send-email 2.39.3 (Apple Git-146) In-Reply-To: <20240717110326.45230-1-phil@philjordan.eu> References: <20240717110326.45230-1-phil@philjordan.eu> MIME-Version: 1.0 Received-SPF: none client-ip=89.104.8.17; envelope-from=phil@intel-mbp.local; helo=intel-mbp.local X-Spam_score_int: -5 X-Spam_score: -0.6 X-Spam_bar: / X-Spam_report: (-0.6 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.001, KHOP_HELO_FCRDNS=0.261, NO_DNS_FOR_FROM=0.001, RDNS_DYNAMIC=0.982, SPF_NONE=0.001, TVD_RCVD_IP=0.001, T_SPF_HELO_TEMPERROR=0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This change adds a property 'display_modes' on the graphics device which permits specifying a list of display modes. (screen resolution and refresh rate) PCI variant of apple-gfx only for the moment. Signed-off-by: Phil Dennis-Jordan --- hw/display/apple-gfx-pci.m | 43 ++++++++++- hw/display/apple-gfx.h | 17 ++++- hw/display/apple-gfx.m | 151 ++++++++++++++++++++++++++++++++++--- 3 files changed, 198 insertions(+), 13 deletions(-) diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m index b3311e736c..942fba16a2 100644 --- a/hw/display/apple-gfx-pci.m +++ b/hw/display/apple-gfx-pci.m @@ -1,6 +1,7 @@ #include "apple-gfx.h" #include "hw/pci/pci_device.h" #include "hw/pci/msi.h" +#include "hw/qdev-properties.h" #include "qapi/error.h" #include "trace.h" #import @@ -86,6 +87,46 @@ static void apple_gfx_pci_reset(DeviceState *dev) [s->common.pgdev reset]; } +static void apple_gfx_pci_get_display_modes(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + AppleGFXDisplayModeList *mode_list = object_field_prop_ptr(obj, prop); + + apple_gfx_get_display_modes(mode_list, v, name, errp); +} + +static void apple_gfx_pci_set_display_modes(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + AppleGFXDisplayModeList *mode_list = object_field_prop_ptr(obj, prop); + + apple_gfx_set_display_modes(mode_list, v, name, errp); +} + +const PropertyInfo apple_gfx_pci_prop_display_modes = { + .name = "display_modes", + .description = + "Colon-separated list of display modes; " + "x@; the first mode is considered " + "'native'. Example: 3840x2160@60:2560x1440@60:1920x1080@60", + .get = apple_gfx_pci_get_display_modes, + .set = apple_gfx_pci_set_display_modes, +}; + +#define DEFINE_PROP_DISPLAY_MODES(_name, _state, _field) \ + DEFINE_PROP(_name, _state, _field, apple_gfx_pci_prop_display_modes, \ + AppleGFXDisplayModeList) + +static Property apple_gfx_pci_properties[] = { + DEFINE_PROP_DISPLAY_MODES("display-modes", AppleGFXPCIState, + common.display_modes), + DEFINE_PROP_END_OF_LIST(), +}; + static void apple_gfx_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -101,7 +142,7 @@ static void apple_gfx_pci_class_init(ObjectClass *klass, void *data) pci->class_id = PCI_CLASS_DISPLAY_OTHER; pci->realize = apple_gfx_pci_realize; - // TODO: Property for setting mode list + device_class_set_props(dc, apple_gfx_pci_properties); } static TypeInfo apple_gfx_pci_types[] = { diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h index 995ecf7f4a..baad4a9865 100644 --- a/hw/display/apple-gfx.h +++ b/hw/display/apple-gfx.h @@ -5,14 +5,28 @@ #define TYPE_APPLE_GFX_PCI "apple-gfx-pci" #include "qemu/typedefs.h" +#include "qemu/osdep.h" typedef struct AppleGFXState AppleGFXState; +typedef struct AppleGFXDisplayMode { + uint16_t width_px; + uint16_t height_px; + uint16_t refresh_rate_hz; +} AppleGFXDisplayMode; + +typedef struct AppleGFXDisplayModeList { + GArray *modes; +} AppleGFXDisplayModeList; + void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name); +void apple_gfx_get_display_modes(AppleGFXDisplayModeList *mode_list, Visitor *v, + const char *name, Error **errp); +void apple_gfx_set_display_modes(AppleGFXDisplayModeList *mode_list, Visitor *v, + const char *name, Error **errp); #ifdef __OBJC__ -#include "qemu/osdep.h" #include "exec/memory.h" #include "ui/surface.h" #include @@ -38,6 +52,7 @@ struct AppleGFXState { bool new_frame; bool cursor_show; QEMUCursor *cursor; + AppleGFXDisplayModeList display_modes; dispatch_queue_t render_queue; /* The following fields should only be accessed from render_queue: */ diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index 018db8bf19..de6dac9e04 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -16,6 +16,9 @@ #include "trace.h" #include "qemu-main.h" #include "qemu/main-loop.h" +#include "qemu/cutils.h" +#include "qapi/visitor.h" +#include "qapi/error.h" #include "ui/console.h" #include "monitor/monitor.h" #include "qapi/error.h" @@ -23,9 +26,10 @@ #include #import -static const PGDisplayCoord_t apple_gfx_modes[] = { - { .x = 1440, .y = 1080 }, - { .x = 1280, .y = 1024 }, +static const AppleGFXDisplayMode apple_gfx_default_modes[] = { + { 1920, 1080, 60 }, + { 1440, 1080, 60 }, + { 1280, 1024, 60 }, }; typedef struct PGTask_s { // Name matches forward declaration in PG header @@ -278,7 +282,6 @@ static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height) static void create_fb(AppleGFXState *s) { s->con = graphic_console_init(NULL, 0, &apple_gfx_fb_ops, s); - set_mode(s, 1440, 1080); s->cursor_show = true; } @@ -479,20 +482,24 @@ static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, return disp_desc; } -static NSArray* apple_gfx_prepare_display_mode_array(void) +static NSArray* apple_gfx_create_display_mode_array( + const AppleGFXDisplayMode display_modes[], int display_mode_count) { - PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)]; + PGDisplayMode **modes = alloca(sizeof(modes[0]) * display_mode_count); NSArray* mode_array = nil; int i; - for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { + for (i = 0; i < display_mode_count; i++) { + const AppleGFXDisplayMode *mode = &display_modes[i]; + PGDisplayCoord_t mode_size = { mode->width_px, mode->height_px }; modes[i] = - [[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.]; + [[PGDisplayMode alloc] initWithSizeInPixels:mode_size + refreshRateInHz:mode->refresh_rate_hz]; } - mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)]; + mode_array = [NSArray arrayWithObjects:modes count:display_mode_count]; - for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) { + for (i = 0; i < display_mode_count; i++) { [modes[i] release]; modes[i] = nil; } @@ -529,6 +536,8 @@ static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc) { PGDisplayDescriptor *disp_desc = nil; + const AppleGFXDisplayMode *display_modes = apple_gfx_default_modes; + int num_display_modes = ARRAY_SIZE(apple_gfx_default_modes); QTAILQ_INIT(&s->tasks); s->render_queue = dispatch_queue_create("apple-gfx.render", @@ -546,7 +555,127 @@ void apple_gfx_common_realize(AppleGFXState *s, PGDeviceDescriptor *desc) s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc port:0 serialNum:1234]; [disp_desc release]; - s->pgdisp.modeList = apple_gfx_prepare_display_mode_array(); + + if (s->display_modes.modes != NULL && s->display_modes.modes->len > 0) { + display_modes = + &g_array_index(s->display_modes.modes, AppleGFXDisplayMode, 0); + num_display_modes = s->display_modes.modes->len; + } + s->pgdisp.modeList = + apple_gfx_create_display_mode_array(display_modes, num_display_modes); create_fb(s); } + +void apple_gfx_get_display_modes(AppleGFXDisplayModeList *mode_list, Visitor *v, + const char *name, Error **errp) +{ + GArray *modes = mode_list->modes; + /* 3 uint16s (max 5 digits) and 3 separator characters per mode + nul. */ + size_t buffer_size = (5 + 1) * 3 * modes->len + 1; + + char *buffer = alloca(buffer_size); + char *pos = buffer; + + unsigned used = 0; + buffer[0] = '\0'; + for (guint i = 0; i < modes->len; ++i) + { + AppleGFXDisplayMode *mode = + &g_array_index(modes, AppleGFXDisplayMode, i); + int rc = snprintf(pos, buffer_size - used, + "%s%"PRIu16"x%"PRIu16"@%"PRIu16, + i > 0 ? ":" : "", + mode->width_px, mode->height_px, + mode->refresh_rate_hz); + used += rc; + pos += rc; + assert(used < buffer_size); + } + + pos = buffer; + visit_type_str(v, name, &pos, errp); +} + +void apple_gfx_set_display_modes(AppleGFXDisplayModeList *mode_list, Visitor *v, + const char *name, Error **errp) +{ + Error *local_err = NULL; + const char *endptr; + char *str; + int ret; + unsigned int val; + uint32_t num_modes; + GArray *modes; + uint32_t mode_idx; + + visit_type_str(v, name, &str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + // Count colons to estimate modes. No leading/trailing colons so start at 1. + num_modes = 1; + for (size_t i = 0; str[i] != '\0'; ++i) + { + if (str[i] == ':') { + ++num_modes; + } + } + + modes = g_array_sized_new(false, true, sizeof(AppleGFXDisplayMode), num_modes); + + endptr = str; + for (mode_idx = 0; mode_idx < num_modes; ++mode_idx) + { + AppleGFXDisplayMode mode = {}; + if (mode_idx > 0) + { + if (*endptr != ':') { + goto separator_error; + } + ++endptr; + } + + ret = qemu_strtoui(endptr, &endptr, 10, &val); + if (ret || val > UINT16_MAX || val == 0) { + error_setg(errp, "width of '%s' must be a decimal integer number " + "of pixels in the range 1..65535", name); + goto out; + } + mode.width_px = val; + if (*endptr != 'x') { + goto separator_error; + } + + ret = qemu_strtoui(endptr + 1, &endptr, 10, &val); + if (ret || val > UINT16_MAX || val == 0) { + error_setg(errp, "height of '%s' must be a decimal integer number " + "of pixels in the range 1..65535", name); + goto out; + } + mode.height_px = val; + if (*endptr != '@') { + goto separator_error; + } + + ret = qemu_strtoui(endptr + 1, &endptr, 10, &val); + if (ret) { + error_setg(errp, "refresh rate of '%s'" + " must be a non-negative decimal integer (Hertz)", name); + } + mode.refresh_rate_hz = val; + g_array_append_val(modes, mode); + } + + mode_list->modes = modes; + goto out; + +separator_error: + error_setg(errp, "Each display mode takes the format " + "'x@', modes are separated by colons. (:)"); +out: + g_free(str); + return; +} From patchwork Wed Jul 17 11:03:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Phil Dennis-Jordan X-Patchwork-Id: 13735396 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 6A3E3C3DA62 for ; Wed, 17 Jul 2024 11:14:34 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2TO-0005Gj-7y; Wed, 17 Jul 2024 07:05:26 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sU2Sd-0001UX-M1 for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:43 -0400 Received: from 89-104-8-17.customer.bnet.at ([89.104.8.17] helo=intel-mbp.local) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sU2Sa-000735-76 for qemu-devel@nongnu.org; Wed, 17 Jul 2024 07:04:39 -0400 Received: by intel-mbp.local (Postfix, from userid 501) id A8AFE38F9CD; Wed, 17 Jul 2024 13:04:06 +0200 (CEST) From: Phil Dennis-Jordan To: qemu-devel@nongnu.org, agraf@csgraf.de, pbonzini@redhat.com, phil@philjordan.eu, marcandre.lureau@redhat.com, berrange@redhat.com, thuth@redhat.com, philmd@linaro.org, akihiko.odaki@daynix.com, peter.maydell@linaro.org Subject: [PATCH v2 8/8] MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF Date: Wed, 17 Jul 2024 13:03:26 +0200 Message-Id: <20240717110326.45230-9-phil@philjordan.eu> X-Mailer: git-send-email 2.39.3 (Apple Git-146) In-Reply-To: <20240717110326.45230-1-phil@philjordan.eu> References: <20240717110326.45230-1-phil@philjordan.eu> MIME-Version: 1.0 Received-SPF: none client-ip=89.104.8.17; envelope-from=phil@intel-mbp.local; helo=intel-mbp.local X-Spam_score_int: -6 X-Spam_score: -0.7 X-Spam_bar: / X-Spam_report: (-0.7 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.001, KHOP_HELO_FCRDNS=0.261, NO_DNS_FOR_FROM=0.001, RDNS_DYNAMIC=0.982, SPF_HELO_NONE=0.001, SPF_NONE=0.001, TVD_RCVD_IP=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org I'm happy to take responsibility for the macOS PV graphics code. As HVF patches don't seem to get much attention at the moment, I'm also adding myself as designated reviewer for HVF and x86 HVF to try and improve that. I anticipate that the resulting workload should be covered by the funding I'm receiving for improving Qemu in combination with macOS. As of right now this runs out at the end of 2024; I expect the workload on apple-gfx should be relatively minor and manageable in my spare time beyond that. I may have to remove myself from more general HVF duties once the contract runs out if it's more than I can manage. Signed-off-by: Phil Dennis-Jordan --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7d9811458c..b870bd6ad5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -511,6 +511,7 @@ F: target/arm/hvf/ X86 HVF CPUs M: Cameron Esfahani M: Roman Bolshakov +R: Phil Dennis-Jordan W: https://wiki.qemu.org/Features/HVF S: Maintained F: target/i386/hvf/ @@ -518,6 +519,7 @@ F: target/i386/hvf/ HVF M: Cameron Esfahani M: Roman Bolshakov +R: Phil Dennis-Jordan W: https://wiki.qemu.org/Features/HVF S: Maintained F: accel/hvf/ @@ -2624,6 +2626,11 @@ F: hw/display/edid* F: include/hw/display/edid.h F: qemu-edid.c +macOS PV Graphics (apple-gfx) +M: Phil Dennis-Jordan +S: Maintained +F: hw/display/apple-gfx* + PIIX4 South Bridge (i82371AB) M: Hervé Poussineau M: Philippe Mathieu-Daudé