Merge tag 'hw-misc-20241231' of https://github.com/philmd/qemu into staging

Misc HW patches queue

- Allow more than 4 legacy IRQs on Generic PCI Express Bridge (Alexander)
- Add MMIO-based Inter-VM shared memory device 'ivshmem-flat' (Gustavo)
- Use UHCI register definitions (Guenter)
- Propagate CPU endianness to microblaze_load_kernel (Philippe)
- Mark x86/TriCore devices as little-endian, OpenRISC/SPARC as big (Philippe)
- Don't set callback_opaque NULL in fw_cfg_modify_bytes_read (Shameer)
- Simplify non-KVM checks on AMD IOMMU XTSup feature (Philippe)
- Trivial cleanups on xilinx_ethlite, vmcoreinfo, qxl (Philippe, Hyman)
- Move USB-HCD-XHCI msi/msix properties from NEC to superclass (Phil)
- Redesign of main thread event handling due to macOS Cocoa (Phil)
- Introduce ParavirtualizedGraphics.Framework support 'apple-gfx' (Phil)
- Pad short Ethernet frames on macOS vmnet (William)

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmd0Ul0ACgkQ4+MsLN6t
# wN7sCA/9HFWahKYW+6Y+gHfLPvJzkIqC5mwfQAUY7GsrNVFdIpUjK9ln9xUEqCQz
# DkVxoZQcP++d8cnnl17wXHsRcavyDDadGU5/161eNC7fbKbLRAslObz/dtExxDn2
# sctx9HMcbLl1UMFPqi/Pbt8NEZr0iOLzDDl+nRuOK8QRFnd2zGm1lF1oHeyja3t1
# flnQKI9YD0U/+0RVNR2FOpUam2Fu1EuQEPp0jMwkmcoyoNLwCXrP9XyRybVZnzgM
# cFm9fYbVlwjsVia+Bsk3CmHX5Gna/1bS3CL8Y9gUScYYwYU5VDAA8Fvv4gPsa4+u
# WSyttL2qCFdgF75S5FoAvEQzYFBcw25eFf8jJhbEn4I6MuQew8lww5OZEyvE8rag
# 2hg3nc4W0x76mLunqrNm+h+Z3vqd/amFcd9YNZjpzxQK//TwvOAQTWi31VtWa4OF
# F1qdv78tQKkRY7noq8WkcL/io6D7iE/BMx/XIOF8uPf8BLIBMvPDnDABjaB/yLkS
# Q/e+/monxkhknDY6K9xkVei7rn6c0LkuLzKxVzEzVKPVzM8N0JAl/1KaNVO8fxjJ
# kLvfGP/RdYOZqG4dNi8W3PhV/+UZz1FS3L1MpI4NXQ59br57BbVQP9ARGO6WpPWn
# O9zIJOAqdzcWU0aULIsvQA3nC1iJnFHEovq0bl8qBbY51k26Lg0=
# =AL3L
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 31 Dec 2024 15:21:49 EST
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* tag 'hw-misc-20241231' of https://github.com/philmd/qemu: (29 commits)
  hw/display/qxl: Do not use C99 // comments
  net/vmnet: Pad short Ethernet frames
  MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF
  hw/display/apple-gfx: Adds configurable mode list
  hw/display/apple-gfx: Adds PCI implementation
  hw/display/apple-gfx: Introduce ParavirtualizedGraphics.Framework support
  ui & main loop: Redesign of system-specific main thread event handling
  hw/usb/hcd-xhci: Unimplemented/guest error logging for port MMIO
  hw/usb/hcd-xhci-pci: Move msi/msix properties from NEC to superclass
  hw/block/virtio-blk: Replaces request free function with g_free
  hw/i386/amd_iommu: Simplify non-KVM checks on XTSup feature
  hw/misc/vmcoreinfo: Rename opaque pointer as 'opaque'
  hw/misc/vmcoreinfo: Declare QOM type using DEFINE_TYPES macro
  fw_cfg: Don't set callback_opaque NULL in fw_cfg_modify_bytes_read()
  hw/net/xilinx_ethlite: Rename rxbuf -> port_index
  hw/net/xilinx_ethlite: Correct maximum RX buffer size
  hw/net/xilinx_ethlite: Update QOM style
  hw/net/xilinx_ethlite: Remove unuseful debug logs
  hw/net/xilinx_ethlite: Convert some debug logs to trace events
  hw/sparc: Mark devices as big-endian
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi
2025-01-01 15:17:08 -05:00
61 changed files with 2413 additions and 256 deletions
+7
View File
@@ -509,6 +509,7 @@ F: target/arm/hvf/
X86 HVF CPUs
M: Cameron Esfahani <dirty@apple.com>
M: Roman Bolshakov <rbolshakov@ddn.com>
R: Phil Dennis-Jordan <phil@philjordan.eu>
W: https://wiki.qemu.org/Features/HVF
S: Maintained
F: target/i386/hvf/
@@ -516,6 +517,7 @@ F: target/i386/hvf/
HVF
M: Cameron Esfahani <dirty@apple.com>
M: Roman Bolshakov <rbolshakov@ddn.com>
R: Phil Dennis-Jordan <phil@philjordan.eu>
W: https://wiki.qemu.org/Features/HVF
S: Maintained
F: accel/hvf/
@@ -2631,6 +2633,11 @@ F: hw/display/edid*
F: include/hw/display/edid.h
F: qemu-edid.c
macOS PV Graphics (apple-gfx)
M: Phil Dennis-Jordan <phil@philjordan.eu>
S: Maintained
F: hw/display/apple-gfx*
PIIX4 South Bridge (i82371AB)
M: Hervé Poussineau <hpoussin@reactos.org>
M: Philippe Mathieu-Daudé <philmd@linaro.org>
+1
View File
@@ -86,6 +86,7 @@ Emulated Devices
devices/ccid.rst
devices/cxl.rst
devices/ivshmem.rst
devices/ivshmem-flat.rst
devices/keyboard.rst
devices/net.rst
devices/nvme.rst
+33
View File
@@ -0,0 +1,33 @@
Inter-VM Shared Memory Flat Device
----------------------------------
The ivshmem-flat device is meant to be used on machines that lack a PCI bus,
making them unsuitable for the use of the traditional ivshmem device modeled as
a PCI device. Machines like those with a Cortex-M MCU are good candidates to use
the ivshmem-flat device. Also, since the flat version maps the control and
status registers directly to the memory, it requires a quite tiny "device
driver" to interact with other VMs, which is useful in some RTOSes, like
Zephyr, which usually run on constrained resource targets.
Similar to the ivshmem device, the ivshmem-flat device supports both peer
notification via HW interrupts and Inter-VM shared memory. This allows the
device to be used together with the traditional ivshmem, enabling communication
between, for instance, an aarch64 VM (using the traditional ivshmem device and
running Linux), and an arm VM (using the ivshmem-flat device and running Zephyr
instead).
The ivshmem-flat device does not support the use of a ``memdev`` option (see
ivshmem.rst for more details). It relies on the ivshmem server to create and
distribute the proper shared memory file descriptor and the eventfd(s) to notify
(interrupt) the peers. Therefore, to use this device, it is always necessary to
have an ivshmem server up and running for proper device creation.
Although the ivshmem-flat supports both peer notification (interrupts) and
shared memory, the interrupt mechanism is optional. If no input IRQ is
specified for the device it is disabled, preventing the VM from notifying or
being notified by other VMs (a warning will be displayed to the user to inform
the IRQ mechanism is disabled). The shared memory region is always present.
The MMRs (INTRMASK, INTRSTATUS, IVPOSITION, and DOORBELL registers) offsets at
the MMR region, and their functions, follow the ivshmem spec, so they work
exactly as in the ivshmem PCI device (see ./specs/ivshmem-spec.txt).
+1 -1
View File
@@ -673,7 +673,7 @@ static void create_pcie(SBSAMachineState *sms)
/* Map IO port space */
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio);
for (i = 0; i < GPEX_NUM_IRQS; i++) {
for (i = 0; i < PCI_NUM_PINS; i++) {
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
qdev_get_gpio_in(sms->gic, irq + i));
gpex_set_irq_num(GPEX_HOST(dev), i, irq + i);
+1 -1
View File
@@ -1547,7 +1547,7 @@ static void create_pcie(VirtMachineState *vms)
/* Map IO port space */
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio);
for (i = 0; i < GPEX_NUM_IRQS; i++) {
for (i = 0; i < PCI_NUM_PINS; i++) {
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
qdev_get_gpio_in(vms->gic, irq + i));
gpex_set_irq_num(GPEX_HOST(dev), i, irq + i);
+19 -24
View File
@@ -50,11 +50,6 @@ static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq,
req->mr_next = NULL;
}
static void virtio_blk_free_request(VirtIOBlockReq *req)
{
g_free(req);
}
static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
{
VirtIOBlock *s = req->dev;
@@ -93,7 +88,7 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
if (acct_failed) {
block_acct_failed(blk_get_stats(s->blk), &req->acct);
}
virtio_blk_free_request(req);
g_free(req);
}
blk_error_action(s->blk, action, is_read, error);
@@ -136,7 +131,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret)
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
block_acct_done(blk_get_stats(s->blk), &req->acct);
virtio_blk_free_request(req);
g_free(req);
}
}
@@ -151,7 +146,7 @@ static void virtio_blk_flush_complete(void *opaque, int ret)
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
block_acct_done(blk_get_stats(s->blk), &req->acct);
virtio_blk_free_request(req);
g_free(req);
}
static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret)
@@ -169,7 +164,7 @@ static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret)
if (is_write_zeroes) {
block_acct_done(blk_get_stats(s->blk), &req->acct);
}
virtio_blk_free_request(req);
g_free(req);
}
static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq)
@@ -214,7 +209,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
fail:
virtio_blk_req_complete(req, status);
virtio_blk_free_request(req);
g_free(req);
}
static inline void submit_requests(VirtIOBlock *s, MultiReqBuffer *mrb,
@@ -612,7 +607,7 @@ static void virtio_blk_zone_report_complete(void *opaque, int ret)
out:
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
g_free(req);
g_free(data->zone_report_data.zones);
g_free(data);
}
@@ -661,7 +656,7 @@ static void virtio_blk_handle_zone_report(VirtIOBlockReq *req,
return;
out:
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
g_free(req);
}
static void virtio_blk_zone_mgmt_complete(void *opaque, int ret)
@@ -677,7 +672,7 @@ static void virtio_blk_zone_mgmt_complete(void *opaque, int ret)
}
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
g_free(req);
}
static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op)
@@ -719,7 +714,7 @@ static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op)
return 0;
out:
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
g_free(req);
return err_status;
}
@@ -750,7 +745,7 @@ static void virtio_blk_zone_append_complete(void *opaque, int ret)
out:
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
g_free(req);
g_free(data);
}
@@ -788,7 +783,7 @@ static int virtio_blk_handle_zone_append(VirtIOBlockReq *req,
out:
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
g_free(req);
return err_status;
}
@@ -855,7 +850,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
block_acct_invalid(blk_get_stats(s->blk),
is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
virtio_blk_free_request(req);
g_free(req);
return 0;
}
@@ -911,7 +906,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
VIRTIO_BLK_ID_BYTES));
iov_from_buf(in_iov, in_num, 0, serial, size);
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
virtio_blk_free_request(req);
g_free(req);
break;
}
case VIRTIO_BLK_T_ZONE_APPEND & ~VIRTIO_BLK_T_OUT:
@@ -943,7 +938,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
if (unlikely(!(type & VIRTIO_BLK_T_OUT) ||
out_len > sizeof(dwz_hdr))) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
virtio_blk_free_request(req);
g_free(req);
return 0;
}
@@ -960,14 +955,14 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
is_write_zeroes);
if (err_status != VIRTIO_BLK_S_OK) {
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
g_free(req);
}
break;
}
default:
virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
virtio_blk_free_request(req);
g_free(req);
}
return 0;
}
@@ -988,7 +983,7 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
while ((req = virtio_blk_get_request(s, vq))) {
if (virtio_blk_handle_request(req, &mrb)) {
virtqueue_detach_element(req->vq, &req->elem, 0);
virtio_blk_free_request(req);
g_free(req);
break;
}
}
@@ -1038,7 +1033,7 @@ static void virtio_blk_dma_restart_bh(void *opaque)
while (req) {
next = req->next;
virtqueue_detach_element(req->vq, &req->elem, 0);
virtio_blk_free_request(req);
g_free(req);
req = next;
}
break;
@@ -1121,7 +1116,7 @@ static void virtio_blk_reset(VirtIODevice *vdev)
/* No other threads can access req->vq here */
virtqueue_detach_element(req->vq, &req->elem, 0);
virtio_blk_free_request(req);
g_free(req);
}
}
+13
View File
@@ -140,3 +140,16 @@ config XLNX_DISPLAYPORT
config DM163
bool
config MAC_PVG
bool
default y
config MAC_PVG_MMIO
bool
depends on MAC_PVG && AARCH64
config MAC_PVG_PCI
bool
depends on MAC_PVG && PCI
default y if PCI_DEVICES
+285
View File
@@ -0,0 +1,285 @@
/*
* QEMU Apple ParavirtualizedGraphics.framework device, MMIO (arm64) variant
*
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* ParavirtualizedGraphics.framework is a set of libraries that macOS provides
* which implements 3d graphics passthrough to the host as well as a
* proprietary guest communication channel to drive it. This device model
* implements support to drive that library from within QEMU as an MMIO-based
* system device for macOS on arm64 VMs.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "block/aio-wait.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "apple-gfx.h"
#include "trace.h"
#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXMMIOState, APPLE_GFX_MMIO)
/*
* ParavirtualizedGraphics.Framework only ships header files for the PCI
* 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 *, void *, void *, void *, void *, void *);
typedef bool(^IOSFCMapMemory)(uint64_t phys, uint64_t len, bool ro, void **va,
void *, void *);
@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
struct AppleGFXMapSurfaceMemoryJob;
struct AppleGFXMMIOState {
SysBusDevice parent_obj;
AppleGFXState common;
qemu_irq irq_gfx;
qemu_irq irq_iosfc;
MemoryRegion iomem_iosfc;
PGIOSurfaceHostDevice *pgiosfc;
};
typedef struct AppleGFXMMIOJob {
AppleGFXMMIOState *state;
uint64_t offset;
uint64_t value;
bool completed;
} AppleGFXMMIOJob;
static void iosfc_do_read(void *opaque)
{
AppleGFXMMIOJob *job = opaque;
job->value = [job->state->pgiosfc mmioReadAtOffset:job->offset];
qatomic_set(&job->completed, true);
aio_wait_kick();
}
static uint64_t iosfc_read(void *opaque, hwaddr offset, unsigned size)
{
AppleGFXMMIOJob job = {
.state = opaque,
.offset = offset,
.completed = false,
};
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async_f(queue, &job, iosfc_do_read);
AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
trace_apple_gfx_mmio_iosfc_read(offset, job.value);
return job.value;
}
static void iosfc_do_write(void *opaque)
{
AppleGFXMMIOJob *job = opaque;
[job->state->pgiosfc mmioWriteAtOffset:job->offset value:job->value];
qatomic_set(&job->completed, true);
aio_wait_kick();
}
static void iosfc_write(void *opaque, hwaddr offset, uint64_t val,
unsigned size)
{
AppleGFXMMIOJob job = {
.state = opaque,
.offset = offset,
.value = val,
.completed = false,
};
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async_f(queue, &job, iosfc_do_write);
AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
trace_apple_gfx_mmio_iosfc_write(offset, val);
}
static const MemoryRegionOps apple_iosfc_ops = {
.read = iosfc_read,
.write = 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 void raise_irq_bh(void *opaque)
{
qemu_irq *irq = opaque;
qemu_irq_pulse(*irq);
}
static void *apple_gfx_mmio_map_surface_memory(uint64_t guest_physical_address,
uint64_t length, bool read_only)
{
void *mem;
MemoryRegion *region = NULL;
RCU_READ_LOCK_GUARD();
mem = apple_gfx_host_ptr_for_gpa_range(guest_physical_address,
length, read_only, &region);
if (mem) {
memory_region_ref(region);
}
return mem;
}
static bool apple_gfx_mmio_unmap_surface_memory(void *ptr)
{
MemoryRegion *region;
ram_addr_t offset = 0;
RCU_READ_LOCK_GUARD();
region = memory_region_from_host(ptr, &offset);
if (!region) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: memory at %p to be unmapped not found.\n",
__func__, ptr);
return false;
}
trace_apple_gfx_iosfc_unmap_memory_region(ptr, region);
memory_region_unref(region);
return true;
}
static PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device(
AppleGFXMMIOState *s)
{
PGIOSurfaceHostDeviceDescriptor *iosfc_desc =
[PGIOSurfaceHostDeviceDescriptor new];
PGIOSurfaceHostDevice *iosfc_host_dev;
iosfc_desc.mapMemory =
^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
*va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
return *va != NULL;
};
iosfc_desc.unmapMemory =
^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
return apple_gfx_mmio_unmap_surface_memory(va);
};
iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) {
trace_apple_gfx_iosfc_raise_irq(vector);
aio_bh_schedule_oneshot(qemu_get_aio_context(),
raise_irq_bh, &s->irq_iosfc);
return true;
};
iosfc_host_dev =
[[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc];
[iosfc_desc release];
return iosfc_host_dev;
}
static void apple_gfx_mmio_realize(DeviceState *dev, Error **errp)
{
@autoreleasepool {
AppleGFXMMIOState *s = APPLE_GFX_MMIO(dev);
PGDeviceDescriptor *desc = [PGDeviceDescriptor new];
desc.raiseInterrupt = ^(uint32_t vector) {
trace_apple_gfx_raise_irq(vector);
aio_bh_schedule_oneshot(qemu_get_aio_context(),
raise_irq_bh, &s->irq_gfx);
};
desc.usingIOSurfaceMapper = true;
s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) {
[s->pgiosfc release];
s->pgiosfc = nil;
}
[desc release];
desc = nil;
}
}
static void apple_gfx_mmio_init(Object *obj)
{
AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj);
apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_MMIO);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->common.iomem_gfx);
memory_region_init_io(&s->iomem_iosfc, obj, &apple_iosfc_ops, s,
TYPE_APPLE_GFX_MMIO, 0x10000);
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_mmio_reset(Object *obj, ResetType type)
{
AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj);
[s->common.pgdev reset];
}
static const Property apple_gfx_mmio_properties[] = {
DEFINE_PROP_ARRAY("display-modes", AppleGFXMMIOState,
common.num_display_modes, common.display_modes,
qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode),
};
static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->phases.hold = apple_gfx_mmio_reset;
dc->hotpluggable = false;
dc->realize = apple_gfx_mmio_realize;
device_class_set_props(dc, apple_gfx_mmio_properties);
}
static const TypeInfo apple_gfx_mmio_types[] = {
{
.name = TYPE_APPLE_GFX_MMIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AppleGFXMMIOState),
.class_init = apple_gfx_mmio_class_init,
.instance_init = apple_gfx_mmio_init,
}
};
DEFINE_TYPES(apple_gfx_mmio_types)
+157
View File
@@ -0,0 +1,157 @@
/*
* QEMU Apple ParavirtualizedGraphics.framework device, PCI variant
*
* Copyright © 2023-2024 Phil Dennis-Jordan
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* ParavirtualizedGraphics.framework is a set of libraries that macOS provides
* which implements 3d graphics passthrough to the host as well as a
* proprietary guest communication channel to drive it. This device model
* implements support to drive that library from within QEMU as a PCI device
* aimed primarily at x86-64 macOS VMs.
*/
#include "qemu/osdep.h"
#include "hw/pci/pci_device.h"
#include "hw/pci/msi.h"
#include "apple-gfx.h"
#include "trace.h"
#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXPCIState, APPLE_GFX_PCI)
struct AppleGFXPCIState {
PCIDevice parent_obj;
AppleGFXState common;
};
static const char *apple_gfx_pci_option_rom_path = NULL;
static void apple_gfx_init_option_rom_path(void)
{
NSURL *option_rom_url = PGCopyOptionROMURL();
const char *option_rom_path = option_rom_url.fileSystemRepresentation;
apple_gfx_pci_option_rom_path = g_strdup(option_rom_path);
[option_rom_url release];
}
static void apple_gfx_pci_init(Object *obj)
{
AppleGFXPCIState *s = APPLE_GFX_PCI(obj);
if (!apple_gfx_pci_option_rom_path) {
/*
* The following is done on device not class init to avoid running
* ObjC code before fork() in -daemonize mode.
*/
PCIDeviceClass *pci = PCI_DEVICE_CLASS(object_get_class(obj));
apple_gfx_init_option_rom_path();
pci->romfile = apple_gfx_pci_option_rom_path;
}
apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_PCI);
}
typedef struct AppleGFXPCIInterruptJob {
PCIDevice *device;
uint32_t vector;
} AppleGFXPCIInterruptJob;
static void apple_gfx_pci_raise_interrupt(void *opaque)
{
AppleGFXPCIInterruptJob *job = opaque;
if (msi_enabled(job->device)) {
msi_notify(job->device, job->vector);
}
g_free(job);
}
static void apple_gfx_pci_interrupt(PCIDevice *dev, uint32_t vector)
{
AppleGFXPCIInterruptJob *job;
trace_apple_gfx_raise_irq(vector);
job = g_malloc0(sizeof(*job));
job->device = dev;
job->vector = vector;
aio_bh_schedule_oneshot(qemu_get_aio_context(),
apple_gfx_pci_raise_interrupt, job);
}
static void apple_gfx_pci_realize(PCIDevice *dev, Error **errp)
{
AppleGFXPCIState *s = APPLE_GFX_PCI(dev);
int ret;
pci_register_bar(dev, PG_PCI_BAR_MMIO,
PCI_BASE_ADDRESS_SPACE_MEMORY, &s->common.iomem_gfx);
ret = msi_init(dev, 0x0 /* config offset; 0 = find space */,
PG_PCI_MAX_MSI_VECTORS, true /* msi64bit */,
false /* msi_per_vector_mask */, errp);
if (ret != 0) {
return;
}
@autoreleasepool {
PGDeviceDescriptor *desc = [PGDeviceDescriptor new];
desc.raiseInterrupt = ^(uint32_t vector) {
apple_gfx_pci_interrupt(dev, vector);
};
apple_gfx_common_realize(&s->common, DEVICE(dev), desc, errp);
[desc release];
desc = nil;
}
}
static void apple_gfx_pci_reset(Object *obj, ResetType type)
{
AppleGFXPCIState *s = APPLE_GFX_PCI(obj);
[s->common.pgdev reset];
}
static const Property apple_gfx_pci_properties[] = {
DEFINE_PROP_ARRAY("display-modes", AppleGFXPCIState,
common.num_display_modes, common.display_modes,
qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode),
};
static void apple_gfx_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *pci = PCI_DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->phases.hold = apple_gfx_pci_reset;
dc->desc = "macOS Paravirtualized Graphics PCI Display Controller";
dc->hotpluggable = false;
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
pci->vendor_id = PG_PCI_VENDOR_ID;
pci->device_id = PG_PCI_DEVICE_ID;
pci->class_id = PCI_CLASS_DISPLAY_OTHER;
pci->realize = apple_gfx_pci_realize;
device_class_set_props(dc, apple_gfx_pci_properties);
}
static const TypeInfo apple_gfx_pci_types[] = {
{
.name = TYPE_APPLE_GFX_PCI,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(AppleGFXPCIState),
.class_init = apple_gfx_pci_class_init,
.instance_init = apple_gfx_pci_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_PCIE_DEVICE },
{ },
},
}
};
DEFINE_TYPES(apple_gfx_pci_types)
+74
View File
@@ -0,0 +1,74 @@
/*
* Data structures and functions shared between variants of the macOS
* ParavirtualizedGraphics.framework based apple-gfx display adapter.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef QEMU_APPLE_GFX_H
#define QEMU_APPLE_GFX_H
#include "qemu/queue.h"
#include "exec/memory.h"
#include "hw/qdev-properties.h"
#include "ui/surface.h"
#define TYPE_APPLE_GFX_MMIO "apple-gfx-mmio"
#define TYPE_APPLE_GFX_PCI "apple-gfx-pci"
@class PGDeviceDescriptor;
@protocol PGDevice;
@protocol PGDisplay;
@protocol MTLDevice;
@protocol MTLTexture;
@protocol MTLCommandQueue;
typedef QTAILQ_HEAD(, PGTask_s) PGTaskList;
typedef struct AppleGFXDisplayMode {
uint16_t width_px;
uint16_t height_px;
uint16_t refresh_rate_hz;
} AppleGFXDisplayMode;
typedef struct AppleGFXState {
/* Initialised on init/realize() */
MemoryRegion iomem_gfx;
id<PGDevice> pgdev;
id<PGDisplay> pgdisp;
QemuConsole *con;
id<MTLDevice> mtl;
id<MTLCommandQueue> mtl_queue;
AppleGFXDisplayMode *display_modes;
uint32_t num_display_modes;
/* List `tasks` is protected by task_mutex */
QemuMutex task_mutex;
PGTaskList tasks;
/* Mutable state (BQL protected) */
QEMUCursor *cursor;
DisplaySurface *surface;
id<MTLTexture> texture;
int8_t pending_frames; /* # guest frames in the rendering pipeline */
bool gfx_update_requested; /* QEMU display system wants a new frame */
bool new_frame_ready; /* Guest has rendered a frame, ready to be used */
bool using_managed_texture_storage;
uint32_t rendering_frame_width;
uint32_t rendering_frame_height;
/* Mutable state (atomic) */
bool cursor_show;
} AppleGFXState;
void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name);
bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
PGDeviceDescriptor *desc, Error **errp);
void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
uint64_t length, bool read_only,
MemoryRegion **mapping_in_region);
extern const PropertyInfo qdev_prop_apple_gfx_display_mode;
#endif
File diff suppressed because it is too large Load Diff
+7
View File
@@ -61,6 +61,13 @@ 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])
if host_os == 'darwin'
system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal])
system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx-pci.m'), pvg, metal])
if cpu == 'aarch64'
system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.m'), pvg, metal])
endif
endif
if config_all_devices.has_key('CONFIG_VIRTIO_GPU')
virtio_gpu_ss = ss.source_set()
+1 -1
View File
@@ -50,7 +50,7 @@
#undef ALIGN
#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9"
#define PIXEL_SIZE 0.2936875 /* 1280x1024 is 14.8" x 11.9" */
#define QXL_MODE(_x, _y, _b, _o) \
{ .x_res = _x, \
+30
View File
@@ -194,3 +194,33 @@ 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, unsigned int num_mapped_regions) "task=%p, task->mapped_regions->len=%u"
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, void *source_ptr, uint64_t target) "retval=%"PRId64" source=%p 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_common_init(const char *device_name, size_t mmio_size) "device: %s; MMIO size: %zu bytes"
apple_gfx_common_realize_modes_property(uint32_t num_modes) "using %u modes supplied by 'display-modes' device property"
apple_gfx_display_mode(uint32_t mode_idx, uint16_t width_px, uint16_t height_px) "mode %2"PRIu32": %4"PRIu16"x%4"PRIu16
# apple-gfx-mmio.m
apple_gfx_mmio_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64
apple_gfx_mmio_iosfc_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64
apple_gfx_iosfc_map_memory(uint64_t phys, uint64_t len, uint32_t ro, void *va, void *e, void *f, void* va_result) "phys=0x%"PRIx64" len=0x%"PRIx64" ro=%d va=%p e=%p f=%p -> *va=%p"
apple_gfx_iosfc_map_memory_new_region(size_t i, void *region, uint64_t start, uint64_t end) "index=%zu, region=%p, 0x%"PRIx64"-0x%"PRIx64
apple_gfx_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_gfx_iosfc_unmap_memory_region(void* mem, void *region) "unmapping @ %p from memory region %p"
apple_gfx_iosfc_raise_irq(uint32_t vector) "vector=0x%x"
+2 -9
View File
@@ -1652,17 +1652,10 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion_overlap(&s->mr_sys, AMDVI_INT_ADDR_FIRST,
&s->mr_ir, 1);
/* AMD IOMMU with x2APIC mode requires xtsup=on */
if (x86ms->apic_id_limit > 255 && !s->xtsup) {
error_report("AMD IOMMU with x2APIC confguration requires xtsup=on");
if (kvm_enabled() && x86ms->apic_id_limit > 255 && !s->xtsup) {
error_report("AMD IOMMU with x2APIC configuration requires xtsup=on");
exit(EXIT_FAILURE);
}
if (s->xtsup) {
if (kvm_irqchip_is_split() && !kvm_enable_x2apic()) {
error_report("AMD IOMMU xtsup=on requires support on the KVM side");
exit(EXIT_FAILURE);
}
}
pci_setup_iommu(bus, &amdvi_iommu_ops, s);
amdvi_init(s);
+1 -1
View File
@@ -214,7 +214,7 @@ static void kvm_apic_mem_write(void *opaque, hwaddr addr,
static const MemoryRegionOps kvm_apic_io_ops = {
.read = kvm_apic_mem_read,
.write = kvm_apic_mem_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void kvm_apic_reset(APICCommonState *s)
+1 -1
View File
@@ -139,7 +139,7 @@ static void create_gpex(MicrovmMachineState *mms)
mms->gpex.mmio64.base, mmio64_alias);
}
for (i = 0; i < GPEX_NUM_IRQS; i++) {
for (i = 0; i < PCI_NUM_PINS; i++) {
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
x86ms->gsi[mms->gpex.irq + i]);
}
+2 -2
View File
@@ -1068,7 +1068,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
static const MemoryRegionOps ioport80_io_ops = {
.write = ioport80_write,
.read = ioport80_read,
.endianness = DEVICE_NATIVE_ENDIAN,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
@@ -1078,7 +1078,7 @@ static const MemoryRegionOps ioport80_io_ops = {
static const MemoryRegionOps ioportF0_io_ops = {
.write = ioportF0_write,
.read = ioportF0_read,
.endianness = DEVICE_NATIVE_ENDIAN,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
+1 -1
View File
@@ -718,7 +718,7 @@ static uint64_t vapic_read(void *opaque, hwaddr addr, unsigned size)
static const MemoryRegionOps vapic_ops = {
.write = vapic_write,
.read = vapic_read,
.endianness = DEVICE_NATIVE_ENDIAN,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void vapic_realize(DeviceState *dev, Error **errp)
+1 -1
View File
@@ -36,7 +36,7 @@ static void xen_apic_mem_write(void *opaque, hwaddr addr,
static const MemoryRegionOps xen_apic_io_ops = {
.read = xen_apic_mem_read,
.write = xen_apic_mem_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void xen_apic_realize(DeviceState *dev, Error **errp)

Some files were not shown because too many files have changed in this diff Show More