aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS7
-rw-r--r--block/vvfat.c4
-rw-r--r--docs/conf.py2
-rw-r--r--docs/system/device-emulation.rst1
-rw-r--r--docs/system/devices/ivshmem-flat.rst33
-rw-r--r--hw/arm/sbsa-ref.c2
-rw-r--r--hw/arm/virt.c2
-rw-r--r--hw/block/virtio-blk.c43
-rw-r--r--hw/display/Kconfig13
-rw-r--r--hw/display/apple-gfx-mmio.m285
-rw-r--r--hw/display/apple-gfx-pci.m157
-rw-r--r--hw/display/apple-gfx.h74
-rw-r--r--hw/display/apple-gfx.m879
-rw-r--r--hw/display/meson.build7
-rw-r--r--hw/display/qxl.c2
-rw-r--r--hw/display/trace-events30
-rw-r--r--hw/i386/amd_iommu.c11
-rw-r--r--hw/i386/kvm/apic.c2
-rw-r--r--hw/i386/microvm.c2
-rw-r--r--hw/i386/pc.c4
-rw-r--r--hw/i386/vapic.c2
-rw-r--r--hw/i386/xen/xen_apic.c2
-rw-r--r--hw/i386/xen/xen_platform.c2
-rw-r--r--hw/loongarch/virt.c12
-rw-r--r--hw/microblaze/boot.c8
-rw-r--r--hw/microblaze/boot.h4
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c2
-rw-r--r--hw/microblaze/petalogix_s3adsp1800_mmu.c2
-rw-r--r--hw/microblaze/xlnx-zynqmp-pmu.c2
-rw-r--r--hw/mips/loongson3_virt.c2
-rw-r--r--hw/misc/Kconfig5
-rw-r--r--hw/misc/ivshmem-flat.c459
-rw-r--r--hw/misc/ivshmem-pci.c (renamed from hw/misc/ivshmem.c)0
-rw-r--r--hw/misc/meson.build4
-rw-r--r--hw/misc/trace-events16
-rw-r--r--hw/misc/vmcoreinfo.c29
-rw-r--r--hw/net/trace-events4
-rw-r--r--hw/net/xilinx_ethlite.c78
-rw-r--r--hw/nvram/fw_cfg.c1
-rw-r--r--hw/openrisc/openrisc_sim.c2
-rw-r--r--hw/openrisc/virt.c14
-rw-r--r--hw/pci-host/gpex.c43
-rw-r--r--hw/riscv/virt.c12
-rw-r--r--hw/sparc/sun4m_iommu.c2
-rw-r--r--hw/sparc64/sun4u.c2
-rw-r--r--hw/tricore/tricore_testdevice.c2
-rw-r--r--hw/usb/hcd-uhci.c122
-rw-r--r--hw/usb/hcd-xhci-nec.c2
-rw-r--r--hw/usb/hcd-xhci-pci.c6
-rw-r--r--hw/usb/hcd-xhci.c25
-rw-r--r--hw/xen/xen-pvh-common.c2
-rw-r--r--hw/xtensa/virt.c2
-rw-r--r--include/hw/misc/ivshmem-flat.h85
-rw-r--r--include/hw/pci-host/gpex.h7
-rw-r--r--include/hw/usb/uhci-regs.h11
-rw-r--r--include/qemu-main.h14
-rw-r--r--include/qemu/help-texts.h2
-rw-r--r--meson.build4
-rw-r--r--net/vmnet-common.m23
-rw-r--r--system/main.c38
-rw-r--r--tests/functional/meson.build1
-rw-r--r--tests/functional/qemu_test/ports.py56
-rwxr-xr-xtests/functional/test_arm_quanta_gsj.py2
-rwxr-xr-xtests/functional/test_ppc64_hv.py52
-rwxr-xr-xtests/functional/test_rx_gdbsim.py13
-rwxr-xr-x[-rw-r--r--]tests/functional/test_vnc.py (renamed from tests/avocado/vnc.py)55
-rw-r--r--tests/qtest/bios-tables-test.c1
-rw-r--r--tests/qtest/fuzz/fuzz.c1
-rw-r--r--tests/qtest/fw_cfg-test.c6
-rw-r--r--tests/qtest/migration/compression-tests.c54
-rw-r--r--tests/qtest/migration/framework.c8
-rw-r--r--tests/qtest/migration/migration-util.c7
-rw-r--r--tests/qtest/migration/precopy-tests.c5
-rw-r--r--tests/qtest/virtio-iommu-test.c4
-rw-r--r--ui/cocoa.m54
-rw-r--r--ui/gtk.c4
-rw-r--r--ui/sdl2.c4
77 files changed, 2537 insertions, 404 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 38a290e..2101b51 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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>
diff --git a/block/vvfat.c b/block/vvfat.c
index f2eafaa..8ffe8b3 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -426,10 +426,6 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename)
else if(offset<22) offset=14+offset-10;
else offset=28+offset-22;
entry=array_get(&(s->directory),s->directory.next-1-(i/26));
- /* ensure we don't write anything past entry->name */
- if (offset >= sizeof(entry->name)) {
- continue;
- }
if (i >= 2 * length + 2) {
entry->name[offset] = 0xff;
} else if (i % 2 == 0) {
diff --git a/docs/conf.py b/docs/conf.py
index 164a8ee..31bb9a3 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -87,7 +87,7 @@ default_role = 'any'
# General information about the project.
project = u'QEMU'
-copyright = u'2024, The QEMU Project Developers'
+copyright = u'2025, The QEMU Project Developers'
author = u'The QEMU Project Developers'
# The version info for the project you're documenting, acts as replacement for
diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst
index f197774..a1b0d79 100644
--- a/docs/system/device-emulation.rst
+++ b/docs/system/device-emulation.rst
@@ -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
diff --git a/docs/system/devices/ivshmem-flat.rst b/docs/system/devices/ivshmem-flat.rst
new file mode 100644
index 0000000..1f97052
--- /dev/null
+++ b/docs/system/devices/ivshmem-flat.rst
@@ -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).
diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
index 581655d..6183111 100644
--- a/hw/arm/sbsa-ref.c
+++ b/hw/arm/sbsa-ref.c
@@ -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);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index f9b3380..99e0a68 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -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);
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 8806c03..e0acce8 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -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);
}
}
diff --git a/hw/display/Kconfig b/hw/display/Kconfig
index 2250c74..2b53dfd 100644
--- a/hw/display/Kconfig
+++ b/hw/display/Kconfig
@@ -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
diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m
new file mode 100644
index 0000000..b2e0e7a
--- /dev/null
+++ b/hw/display/apple-gfx-mmio.m
@@ -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)
diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m
new file mode 100644
index 0000000..b939bb9
--- /dev/null
+++ b/hw/display/apple-gfx-pci.m
@@ -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)
+
diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h
new file mode 100644
index 0000000..3900cdb
--- /dev/null
+++ b/hw/display/apple-gfx.h
@@ -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
+
diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m
new file mode 100644
index 0000000..aa1455b
--- /dev/null
+++ b/hw/display/apple-gfx.m
@@ -0,0 +1,879 @@
+/*
+ * QEMU Apple ParavirtualizedGraphics.framework device
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/lockable.h"
+#include "qemu/cutils.h"
+#include "qemu/log.h"
+#include "qapi/visitor.h"
+#include "qapi/error.h"
+#include "block/aio-wait.h"
+#include "exec/address-spaces.h"
+#include "system/dma.h"
+#include "migration/blocker.h"
+#include "ui/console.h"
+#include "apple-gfx.h"
+#include "trace.h"
+
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <dispatch/dispatch.h>
+
+#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
+
+static const AppleGFXDisplayMode apple_gfx_default_modes[] = {
+ { 1920, 1080, 60 },
+ { 1440, 1080, 60 },
+ { 1280, 1024, 60 },
+};
+
+static Error *apple_gfx_mig_blocker;
+static uint32_t next_pgdisplay_serial_num = 1;
+
+static dispatch_queue_t get_background_queue(void)
+{
+ return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+}
+
+/* ------ PGTask and task operations: new/destroy/map/unmap ------ */
+
+/*
+ * This implements the type declared in <ParavirtualizedGraphics/PGDevice.h>
+ * which is opaque from the framework's point of view. It is used in callbacks
+ * in the form of its typedef PGTask_t, which also already exists in the
+ * framework headers.
+ *
+ * A "task" in PVG terminology represents a host-virtual contiguous address
+ * range which is reserved in a large chunk on task creation. The mapMemory
+ * callback then requests ranges of guest system memory (identified by their
+ * GPA) to be mapped into subranges of this reserved address space.
+ * This type of operation isn't well-supported by QEMU's memory subsystem,
+ * but it is fortunately trivial to achieve with Darwin's mach_vm_remap() call,
+ * which allows us to refer to the same backing memory via multiple virtual
+ * address ranges. The Mach VM APIs are therefore used throughout for managing
+ * task memory.
+ */
+struct PGTask_s {
+ QTAILQ_ENTRY(PGTask_s) node;
+ AppleGFXState *s;
+ mach_vm_address_t address;
+ uint64_t len;
+ /*
+ * All unique MemoryRegions for which a mapping has been created in in this
+ * task, and on which we have thus called memory_region_ref(). There are
+ * usually very few regions of system RAM in total, so we expect this array
+ * to be very short. Therefore, no need for sorting or fancy search
+ * algorithms, linear search will do.
+ * Protected by AppleGFXState's task_mutex.
+ */
+ GPtrArray *mapped_regions;
+};
+
+static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len)
+{
+ mach_vm_address_t task_mem;
+ PGTask_t *task;
+ kern_return_t r;
+
+ r = mach_vm_allocate(mach_task_self(), &task_mem, len, VM_FLAGS_ANYWHERE);
+ if (r != KERN_SUCCESS) {
+ return NULL;
+ }
+
+ task = g_new0(PGTask_t, 1);
+ task->s = s;
+ task->address = task_mem;
+ task->len = len;
+ task->mapped_regions = g_ptr_array_sized_new(2 /* Usually enough */);
+
+ QEMU_LOCK_GUARD(&s->task_mutex);
+ QTAILQ_INSERT_TAIL(&s->tasks, task, node);
+
+ return task;
+}
+
+static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
+{
+ GPtrArray *regions = task->mapped_regions;
+ MemoryRegion *region;
+ size_t i;
+
+ for (i = 0; i < regions->len; ++i) {
+ region = g_ptr_array_index(regions, i);
+ memory_region_unref(region);
+ }
+ g_ptr_array_unref(regions);
+
+ mach_vm_deallocate(mach_task_self(), task->address, task->len);
+
+ QEMU_LOCK_GUARD(&s->task_mutex);
+ QTAILQ_REMOVE(&s->tasks, task, node);
+ g_free(task);
+}
+
+void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
+ uint64_t length, bool read_only,
+ MemoryRegion **mapping_in_region)
+{
+ MemoryRegion *ram_region;
+ char *host_ptr;
+ hwaddr ram_region_offset = 0;
+ hwaddr ram_region_length = length;
+
+ ram_region = address_space_translate(&address_space_memory,
+ guest_physical,
+ &ram_region_offset,
+ &ram_region_length, !read_only,
+ MEMTXATTRS_UNSPECIFIED);
+
+ if (!ram_region || ram_region_length < length ||
+ !memory_access_is_direct(ram_region, !read_only)) {
+ return NULL;
+ }
+
+ host_ptr = memory_region_get_ram_ptr(ram_region);
+ if (!host_ptr) {
+ return NULL;
+ }
+ host_ptr += ram_region_offset;
+ *mapping_in_region = ram_region;
+ return host_ptr;
+}
+
+static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
+ uint64_t virtual_offset,
+ PGPhysicalMemoryRange_t *ranges,
+ uint32_t range_count, bool read_only)
+{
+ kern_return_t r;
+ void *source_ptr;
+ mach_vm_address_t target;
+ vm_prot_t cur_protection, max_protection;
+ bool success = true;
+ MemoryRegion *region;
+
+ RCU_READ_LOCK_GUARD();
+ QEMU_LOCK_GUARD(&s->task_mutex);
+
+ trace_apple_gfx_map_memory(task, range_count, virtual_offset, read_only);
+ for (int i = 0; i < range_count; i++) {
+ PGPhysicalMemoryRange_t *range = &ranges[i];
+
+ target = task->address + virtual_offset;
+ virtual_offset += range->physicalLength;
+
+ trace_apple_gfx_map_memory_range(i, range->physicalAddress,
+ range->physicalLength);
+
+ region = NULL;
+ source_ptr = apple_gfx_host_ptr_for_gpa_range(range->physicalAddress,
+ range->physicalLength,
+ read_only, &region);
+ if (!source_ptr) {
+ success = false;
+ continue;
+ }
+
+ if (!g_ptr_array_find(task->mapped_regions, region, NULL)) {
+ g_ptr_array_add(task->mapped_regions, region);
+ memory_region_ref(region);
+ }
+
+ cur_protection = 0;
+ 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(), (mach_vm_address_t)source_ptr,
+ false /* shared mapping, no copy */,
+ &cur_protection, &max_protection,
+ VM_INHERIT_COPY);
+ trace_apple_gfx_remap(r, source_ptr, target);
+ g_assert(r == KERN_SUCCESS);
+ }
+
+ return success;
+}
+
+static void apple_gfx_task_unmap_memory(AppleGFXState *s, PGTask_t *task,
+ uint64_t virtual_offset, uint64_t length)
+{
+ kern_return_t r;
+ mach_vm_address_t range_address;
+
+ trace_apple_gfx_unmap_memory(task, virtual_offset, length);
+
+ /*
+ * Replace task memory range with fresh 0 pages, undoing the mapping
+ * from guest RAM.
+ */
+ range_address = task->address + virtual_offset;
+ r = mach_vm_allocate(mach_task_self(), &range_address, length,
+ VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE);
+ g_assert(r == KERN_SUCCESS);
+}
+
+/* ------ Rendering and frame management ------ */
+
+static void apple_gfx_render_frame_completed_bh(void *opaque);
+
+static void apple_gfx_render_new_frame(AppleGFXState *s)
+{
+ bool managed_texture = s->using_managed_texture_storage;
+ uint32_t width = surface_width(s->surface);
+ uint32_t height = surface_height(s->surface);
+ MTLRegion region = MTLRegionMake2D(0, 0, width, height);
+ id<MTLCommandBuffer> command_buffer = [s->mtl_queue commandBuffer];
+ id<MTLTexture> texture = s->texture;
+
+ assert(bql_locked());
+ [texture retain];
+ [command_buffer retain];
+
+ s->rendering_frame_width = width;
+ s->rendering_frame_height = height;
+
+ dispatch_async(get_background_queue(), ^{
+ /*
+ * This is not safe to call from the BQL/BH due to PVG-internal locks
+ * causing deadlocks.
+ */
+ bool r = [s->pgdisp encodeCurrentFrameToCommandBuffer:command_buffer
+ texture:texture
+ region:region];
+ if (!r) {
+ [texture release];
+ [command_buffer release];
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: encodeCurrentFrameToCommandBuffer:texture:region: "
+ "failed\n", __func__);
+ bql_lock();
+ --s->pending_frames;
+ if (s->pending_frames > 0) {
+ apple_gfx_render_new_frame(s);
+ }
+ bql_unlock();
+ return;
+ }
+
+ if (managed_texture) {
+ /* "Managed" textures exist in both VRAM and RAM and must be synced. */
+ id<MTLBlitCommandEncoder> blit = [command_buffer blitCommandEncoder];
+ [blit synchronizeResource:texture];
+ [blit endEncoding];
+ }
+ [texture release];
+ [command_buffer addCompletedHandler:
+ ^(id<MTLCommandBuffer> cb)
+ {
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ apple_gfx_render_frame_completed_bh, s);
+ }];
+ [command_buffer commit];
+ [command_buffer release];
+ });
+}
+
+static void copy_mtl_texture_to_surface_mem(id<MTLTexture> 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_bh(void *opaque)
+{
+ AppleGFXState *s = opaque;
+
+ @autoreleasepool {
+ --s->pending_frames;
+ assert(s->pending_frames >= 0);
+
+ /* Only update display if mode hasn't changed since we started rendering. */
+ if (s->rendering_frame_width == surface_width(s->surface) &&
+ s->rendering_frame_height == surface_height(s->surface)) {
+ copy_mtl_texture_to_surface_mem(s->texture, surface_data(s->surface));
+ 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;
+
+ assert(bql_locked());
+ if (s->new_frame_ready) {
+ dpy_gfx_update_full(s->con);
+ s->new_frame_ready = false;
+ graphic_hw_update_done(s->con);
+ } else if (s->pending_frames > 0) {
+ s->gfx_update_requested = true;
+ } else {
+ graphic_hw_update_done(s->con);
+ }
+}
+
+static const GraphicHwOps apple_gfx_fb_ops = {
+ .gfx_update = apple_gfx_fb_update_display,
+ .gfx_update_async = true,
+};
+
+/* ------ Mouse cursor and display mode setting ------ */
+
+static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height)
+{
+ MTLTextureDescriptor *textureDescriptor;
+
+ if (s->surface &&
+ width == surface_width(s->surface) &&
+ height == surface_height(s->surface)) {
+ return;
+ }
+
+ [s->texture release];
+
+ s->surface = qemu_create_displaysurface(width, height);
+
+ @autoreleasepool {
+ textureDescriptor =
+ [MTLTextureDescriptor
+ texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
+ width:width
+ height:height
+ mipmapped:NO];
+ textureDescriptor.usage = s->pgdisp.minimumTextureUsage;
+ s->texture = [s->mtl newTextureWithDescriptor:textureDescriptor];
+ s->using_managed_texture_storage =
+ (s->texture.storageMode == MTLStorageModeManaged);
+ }
+
+ dpy_gfx_replace_surface(s->con, s->surface);
+}
+
+static void update_cursor(AppleGFXState *s)
+{
+ assert(bql_locked());
+ dpy_mouse_set(s->con, s->pgdisp.cursorPosition.x,
+ s->pgdisp.cursorPosition.y, qatomic_read(&s->cursor_show));
+}
+
+static void update_cursor_bh(void *opaque)
+{
+ AppleGFXState *s = opaque;
+ update_cursor(s);
+}
+
+typedef struct AppleGFXSetCursorGlyphJob {
+ AppleGFXState *s;
+ NSBitmapImageRep *glyph;
+ PGDisplayCoord_t hotspot;
+} AppleGFXSetCursorGlyphJob;
+
+static void set_cursor_glyph(void *opaque)
+{
+ AppleGFXSetCursorGlyphJob *job = opaque;
+ AppleGFXState *s = job->s;
+ NSBitmapImageRep *glyph = job->glyph;
+ 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 = job->hotspot.x;
+ s->cursor->hot_y = job->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);
+ }
+ [glyph release];
+
+ g_free(job);
+}
+
+/* ------ DMA (device reading system memory) ------ */
+
+typedef struct AppleGFXReadMemoryJob {
+ QemuSemaphore sem;
+ hwaddr physical_address;
+ uint64_t length;
+ void *dst;
+ bool success;
+} AppleGFXReadMemoryJob;
+
+static void apple_gfx_do_read_memory(void *opaque)
+{
+ AppleGFXReadMemoryJob *job = opaque;
+ MemTxResult r;
+
+ r = dma_memory_read(&address_space_memory, job->physical_address,
+ job->dst, job->length, MEMTXATTRS_UNSPECIFIED);
+ job->success = (r == MEMTX_OK);
+
+ qemu_sem_post(&job->sem);
+}
+
+static bool apple_gfx_read_memory(AppleGFXState *s, hwaddr physical_address,
+ uint64_t length, void *dst)
+{
+ AppleGFXReadMemoryJob job = {
+ .physical_address = physical_address, .length = length, .dst = dst
+ };
+
+ trace_apple_gfx_read_memory(physical_address, length, dst);
+
+ /* Performing DMA requires BQL, so do it in a BH. */
+ qemu_sem_init(&job.sem, 0);
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ apple_gfx_do_read_memory, &job);
+ qemu_sem_wait(&job.sem);
+ qemu_sem_destroy(&job.sem);
+ return job.success;
+}
+
+/* ------ Memory-mapped device I/O operations ------ */
+
+typedef struct AppleGFXIOJob {
+ AppleGFXState *state;
+ uint64_t offset;
+ uint64_t value;
+ bool completed;
+} AppleGFXIOJob;
+
+static void apple_gfx_do_read(void *opaque)
+{
+ AppleGFXIOJob *job = opaque;
+ job->value = [job->state->pgdev mmioReadAtOffset:job->offset];
+ qatomic_set(&job->completed, true);
+ aio_wait_kick();
+}
+
+static uint64_t apple_gfx_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AppleGFXIOJob job = {
+ .state = opaque,
+ .offset = offset,
+ .completed = false,
+ };
+ dispatch_queue_t queue = get_background_queue();
+
+ dispatch_async_f(queue, &job, apple_gfx_do_read);
+ AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
+
+ trace_apple_gfx_read(offset, job.value);
+ return job.value;
+}
+
+static void apple_gfx_do_write(void *opaque)
+{
+ AppleGFXIOJob *job = opaque;
+ [job->state->pgdev mmioWriteAtOffset:job->offset value:job->value];
+ qatomic_set(&job->completed, true);
+ aio_wait_kick();
+}
+
+static void apple_gfx_write(void *opaque, hwaddr offset, uint64_t val,
+ unsigned size)
+{
+ /*
+ * The methods mmioReadAtOffset: and especially mmioWriteAtOffset: can
+ * trigger synchronous operations on other dispatch queues, which in turn
+ * may call back out on one or more of the callback blocks. For this reason,
+ * and as we are holding the BQL, we invoke the I/O methods on a pool
+ * thread and handle AIO tasks while we wait. Any work in the callbacks
+ * requiring the BQL will in turn schedule BHs which this thread will
+ * process while waiting.
+ */
+ AppleGFXIOJob job = {
+ .state = opaque,
+ .offset = offset,
+ .value = val,
+ .completed = false,
+ };
+ dispatch_queue_t queue = get_background_queue();
+
+ dispatch_async_f(queue, &job, apple_gfx_do_write);
+ AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed));
+
+ trace_apple_gfx_write(offset, val);
+}
+
+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 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;
+}
+
+/* ------ Initialisation and startup ------ */
+
+void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name)
+{
+ size_t mmio_range_size = apple_gfx_get_default_mmio_range_size();
+
+ 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! */
+}
+
+static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s,
+ PGDeviceDescriptor *desc)
+{
+ desc.createTask = ^(uint64_t vmSize, void * _Nullable * _Nonnull baseAddress) {
+ PGTask_t *task = apple_gfx_new_task(s, vmSize);
+ *baseAddress = (void *)task->address;
+ trace_apple_gfx_create_task(vmSize, *baseAddress);
+ return task;
+ };
+
+ desc.destroyTask = ^(PGTask_t * _Nonnull task) {
+ trace_apple_gfx_destroy_task(task, task->mapped_regions->len);
+
+ apple_gfx_destroy_task(s, task);
+ };
+
+ desc.mapMemory = ^bool(PGTask_t * _Nonnull task, uint32_t range_count,
+ uint64_t virtual_offset, bool read_only,
+ PGPhysicalMemoryRange_t * _Nonnull ranges) {
+ return apple_gfx_task_map_memory(s, task, virtual_offset,
+ ranges, range_count, read_only);
+ };
+
+ desc.unmapMemory = ^bool(PGTask_t * _Nonnull task, uint64_t virtual_offset,
+ uint64_t length) {
+ apple_gfx_task_unmap_memory(s, task, virtual_offset, length);
+ return true;
+ };
+
+ desc.readMemory = ^bool(uint64_t physical_address, uint64_t length,
+ void * _Nonnull dst) {
+ return apple_gfx_read_memory(s, physical_address, length, dst);
+ };
+}
+
+static void new_frame_handler_bh(void *opaque)
+{
+ AppleGFXState *s = opaque;
+
+ /* Drop frames if guest gets 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);
+ }
+}
+
+static PGDisplayDescriptor *apple_gfx_prepare_display_descriptor(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();
+ aio_bh_schedule_oneshot(qemu_get_aio_context(), new_frame_handler_bh, s);
+ };
+ disp_desc.modeChangeHandler = ^(PGDisplayCoord_t sizeInPixels,
+ OSType pixelFormat) {
+ trace_apple_gfx_mode_change(sizeInPixels.x, sizeInPixels.y);
+
+ BQL_LOCK_GUARD();
+ set_mode(s, sizeInPixels.x, sizeInPixels.y);
+ };
+ disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
+ PGDisplayCoord_t hotspot) {
+ AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
+ job->s = s;
+ job->glyph = glyph;
+ job->hotspot = hotspot;
+ [glyph retain];
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ set_cursor_glyph, job);
+ };
+ disp_desc.cursorShowHandler = ^(BOOL show) {
+ trace_apple_gfx_cursor_show(show);
+ qatomic_set(&s->cursor_show, show);
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ update_cursor_bh, s);
+ };
+ disp_desc.cursorMoveHandler = ^(void) {
+ trace_apple_gfx_cursor_move();
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ update_cursor_bh, s);
+ };
+
+ return disp_desc;
+}
+
+static NSArray<PGDisplayMode *> *apple_gfx_create_display_mode_array(
+ const AppleGFXDisplayMode display_modes[], uint32_t display_mode_count)
+{
+ PGDisplayMode *mode_obj;
+ NSMutableArray<PGDisplayMode *> *mode_array =
+ [[NSMutableArray alloc] initWithCapacity:display_mode_count];
+
+ for (unsigned i = 0; i < display_mode_count; i++) {
+ const AppleGFXDisplayMode *mode = &display_modes[i];
+ trace_apple_gfx_display_mode(i, mode->width_px, mode->height_px);
+ PGDisplayCoord_t mode_size = { mode->width_px, mode->height_px };
+
+ mode_obj =
+ [[PGDisplayMode alloc] initWithSizeInPixels:mode_size
+ refreshRateInHz:mode->refresh_rate_hz];
+ [mode_array addObject:mode_obj];
+ [mode_obj release];
+ }
+
+ return mode_array;
+}
+
+static id<MTLDevice> copy_suitable_metal_device(void)
+{
+ id<MTLDevice> dev = nil;
+ NSArray<id<MTLDevice>> *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;
+}
+
+bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
+ PGDeviceDescriptor *desc, Error **errp)
+{
+ PGDisplayDescriptor *disp_desc;
+ const AppleGFXDisplayMode *display_modes = apple_gfx_default_modes;
+ uint32_t num_display_modes = ARRAY_SIZE(apple_gfx_default_modes);
+ NSArray<PGDisplayMode *> *mode_array;
+
+ if (apple_gfx_mig_blocker == NULL) {
+ error_setg(&apple_gfx_mig_blocker,
+ "Migration state blocked by apple-gfx display device");
+ if (migrate_add_blocker(&apple_gfx_mig_blocker, errp) < 0) {
+ return false;
+ }
+ }
+
+ qemu_mutex_init(&s->task_mutex);
+ QTAILQ_INIT(&s->tasks);
+ s->mtl = copy_suitable_metal_device();
+ s->mtl_queue = [s->mtl newCommandQueue];
+
+ desc.device = s->mtl;
+
+ apple_gfx_register_task_mapping_handlers(s, desc);
+
+ s->cursor_show = true;
+
+ s->pgdev = PGNewDeviceWithDescriptor(desc);
+
+ disp_desc = apple_gfx_prepare_display_descriptor(s);
+ /*
+ * Although the framework does, this integration currently does not support
+ * multiple virtual displays connected to a single PV graphics device.
+ * It is however possible to create
+ * more than one instance of the device, each with one display. The macOS
+ * guest will ignore these displays if they share the same serial number,
+ * so ensure each instance gets a unique one.
+ */
+ s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc
+ port:0
+ serialNum:next_pgdisplay_serial_num++];
+ [disp_desc release];
+
+ if (s->display_modes != NULL && s->num_display_modes > 0) {
+ trace_apple_gfx_common_realize_modes_property(s->num_display_modes);
+ display_modes = s->display_modes;
+ num_display_modes = s->num_display_modes;
+ }
+ s->pgdisp.modeList = mode_array =
+ apple_gfx_create_display_mode_array(display_modes, num_display_modes);
+ [mode_array release];
+
+ s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s);
+ return true;
+}
+
+/* ------ Display mode list device property ------ */
+
+static void apple_gfx_get_display_mode(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ Property *prop = opaque;
+ AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop);
+ /* 3 uint16s (max 5 digits) + 2 separator characters + nul. */
+ char buffer[5 * 3 + 2 + 1];
+ char *pos = buffer;
+
+ int rc = snprintf(buffer, sizeof(buffer),
+ "%"PRIu16"x%"PRIu16"@%"PRIu16,
+ mode->width_px, mode->height_px,
+ mode->refresh_rate_hz);
+ assert(rc < sizeof(buffer));
+
+ visit_type_str(v, name, &pos, errp);
+}
+
+static void apple_gfx_set_display_mode(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ Property *prop = opaque;
+ AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop);
+ const char *endptr;
+ g_autofree char *str = NULL;
+ int ret;
+ int val;
+
+ if (!visit_type_str(v, name, &str, errp)) {
+ return;
+ }
+
+ endptr = str;
+
+ ret = qemu_strtoi(endptr, &endptr, 10, &val);
+ if (ret || val > UINT16_MAX || val <= 0) {
+ error_setg(errp, "width in '%s' must be a decimal integer number"
+ " of pixels in the range 1..65535", name);
+ return;
+ }
+ mode->width_px = val;
+ if (*endptr != 'x') {
+ goto separator_error;
+ }
+
+ ret = qemu_strtoi(endptr + 1, &endptr, 10, &val);
+ if (ret || val > UINT16_MAX || val <= 0) {
+ error_setg(errp, "height in '%s' must be a decimal integer number"
+ " of pixels in the range 1..65535", name);
+ return;
+ }
+ mode->height_px = val;
+ if (*endptr != '@') {
+ goto separator_error;
+ }
+
+ ret = qemu_strtoi(endptr + 1, &endptr, 10, &val);
+ if (ret || val > UINT16_MAX || val <= 0) {
+ error_setg(errp, "refresh rate in '%s'"
+ " must be a positive decimal integer (Hertz)", name);
+ return;
+ }
+ mode->refresh_rate_hz = val;
+ return;
+
+separator_error:
+ error_setg(errp,
+ "Each display mode takes the format '<width>x<height>@<rate>'");
+}
+
+const PropertyInfo qdev_prop_apple_gfx_display_mode = {
+ .name = "display_mode",
+ .description =
+ "Display mode in pixels and Hertz, as <width>x<height>@<refresh-rate> "
+ "Example: 3840x2160@60",
+ .get = apple_gfx_get_display_mode,
+ .set = apple_gfx_set_display_mode,
+};
diff --git a/hw/display/meson.build b/hw/display/meson.build
index 20a9497..94f4f05 100644
--- a/hw/display/meson.build
+++ b/hw/display/meson.build
@@ -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()
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index f54a15e..2efdc77 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -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, \
diff --git a/hw/display/trace-events b/hw/display/trace-events
index d26d663..52786e6 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -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"
+
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index be522b5..6b13ce8 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -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);
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index 602c769..7575106 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -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)
diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c
index c3d7fe3..a8d354a 100644
--- a/hw/i386/microvm.c
+++ b/hw/i386/microvm.c
@@ -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]);
}
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 53a2f22..7111876 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -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,
diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c
index 0e6d058..14de9b7 100644
--- a/hw/i386/vapic.c
+++ b/hw/i386/vapic.c
@@ -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)
diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c
index 101e16a..a94e900 100644
--- a/hw/i386/xen/xen_apic.c
+++ b/hw/i386/xen/xen_apic.c
@@ -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)
diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c
index 0f68c3f..dd648a2 100644
--- a/hw/i386/xen/xen_platform.c
+++ b/hw/i386/xen/xen_platform.c
@@ -514,7 +514,7 @@ static void platform_mmio_write(void *opaque, hwaddr addr,
static const MemoryRegionOps platform_mmio_handler = {
.read = &platform_mmio_read,
.write = &platform_mmio_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static void platform_mmio_setup(PCIXenPlatformState *d)
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
index 99594a1..60bd4dc 100644
--- a/hw/loongarch/virt.c
+++ b/hw/loongarch/virt.c
@@ -452,7 +452,7 @@ static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms,
{
int pin, dev;
uint32_t irq_map_stride = 0;
- uint32_t full_irq_map[GPEX_NUM_IRQS *GPEX_NUM_IRQS * 10] = {};
+ uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 10] = {};
uint32_t *irq_map = full_irq_map;
const MachineState *ms = MACHINE(lvms);
@@ -465,11 +465,11 @@ static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms,
* to wrap to any number of devices.
*/
- for (dev = 0; dev < GPEX_NUM_IRQS; dev++) {
+ for (dev = 0; dev < PCI_NUM_PINS; dev++) {
int devfn = dev * 0x8;
- for (pin = 0; pin < GPEX_NUM_IRQS; pin++) {
- int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS);
+ for (pin = 0; pin < PCI_NUM_PINS; pin++) {
+ int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
int i = 0;
/* Fill PCI address cells */
@@ -493,7 +493,7 @@ static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms,
qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map,
- GPEX_NUM_IRQS * GPEX_NUM_IRQS *
+ PCI_NUM_PINS * PCI_NUM_PINS *
irq_map_stride * sizeof(uint32_t));
qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask",
0x1800, 0, 0, 0x7);
@@ -805,7 +805,7 @@ static void virt_devices_init(DeviceState *pch_pic,
memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE,
pio_alias);
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
+ for (i = 0; i < PCI_NUM_PINS; i++) {
sysbus_connect_irq(d, i,
qdev_get_gpio_in(pch_pic, 16 + i));
gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i);
diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c
index 966fb2c..3854bc2 100644
--- a/hw/microblaze/boot.c
+++ b/hw/microblaze/boot.c
@@ -114,8 +114,8 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
return addr - 0x30000000LL;
}
-void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
- uint32_t ramsize,
+void microblaze_load_kernel(MicroBlazeCPU *cpu, bool is_little_endian,
+ hwaddr ddr_base, uint32_t ramsize,
const char *initrd_filename,
const char *dtb_filename,
void (*machine_cpu_reset)(MicroBlazeCPU *))
@@ -144,13 +144,13 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
/* Boots a kernel elf binary. */
kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
&entry, NULL, &high, NULL,
- TARGET_BIG_ENDIAN, EM_MICROBLAZE, 0, 0);
+ !is_little_endian, EM_MICROBLAZE, 0, 0);
base32 = entry;
if (base32 == 0xc0000000) {
kernel_size = load_elf(kernel_filename, NULL,
translate_kernel_address, NULL,
&entry, NULL, NULL, NULL,
- TARGET_BIG_ENDIAN, EM_MICROBLAZE, 0, 0);
+ !is_little_endian, EM_MICROBLAZE, 0, 0);
}
/* Always boot into physical ram. */
boot_info.bootstrap_pc = (uint32_t)entry;
diff --git a/hw/microblaze/boot.h b/hw/microblaze/boot.h
index 5a8c2f7..d179a55 100644
--- a/hw/microblaze/boot.h
+++ b/hw/microblaze/boot.h
@@ -2,8 +2,8 @@
#define MICROBLAZE_BOOT_H
-void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
- uint32_t ramsize,
+void microblaze_load_kernel(MicroBlazeCPU *cpu, bool is_little_endian,
+ hwaddr ddr_base, uint32_t ramsize,
const char *initrd_filename,
const char *dtb_filename,
void (*machine_cpu_reset)(MicroBlazeCPU *));
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index deab275..8b44be7 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -204,7 +204,7 @@ petalogix_ml605_init(MachineState *machine)
cpu->cfg.pvr_regs[5] = 0xc56be000;
cpu->cfg.pvr_regs[10] = 0x0e000000; /* virtex 6 */
- microblaze_load_kernel(cpu, MEMORY_BASEADDR, ram_size,
+ microblaze_load_kernel(cpu, true, MEMORY_BASEADDR, ram_size,
machine->initrd_filename,
BINARY_DEVICE_TREE_FILE,
NULL);
diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
index 4a969af..2c0d8c3 100644
--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -129,7 +129,7 @@ petalogix_s3adsp1800_init(MachineState *machine)
create_unimplemented_device("xps_gpio", GPIO_BASEADDR, 0x10000);
- microblaze_load_kernel(cpu, ddr_base, ram_size,
+ microblaze_load_kernel(cpu, !TARGET_BIG_ENDIAN, ddr_base, ram_size,
machine->initrd_filename,
BINARY_DEVICE_TREE_FILE,
NULL);
diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c
index 567aad4..bdbf732 100644
--- a/hw/microblaze/xlnx-zynqmp-pmu.c
+++ b/hw/microblaze/xlnx-zynqmp-pmu.c
@@ -172,7 +172,7 @@ static void xlnx_zynqmp_pmu_init(MachineState *machine)
qdev_realize(DEVICE(pmu), NULL, &error_fatal);
/* Load the kernel */
- microblaze_load_kernel(&pmu->cpu, XLNX_ZYNQMP_PMU_RAM_ADDR,
+ microblaze_load_kernel(&pmu->cpu, true, XLNX_ZYNQMP_PMU_RAM_ADDR,
machine->ram_size,
machine->initrd_filename,
machine->dtb,
diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c
index f12f8c3..f3cc7a8 100644
--- a/hw/mips/loongson3_virt.c
+++ b/hw/mips/loongson3_virt.c
@@ -458,7 +458,7 @@ static inline void loongson3_virt_devices_init(MachineState *machine,
virt_memmap[VIRT_PCIE_PIO].base, s->pio_alias);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, virt_memmap[VIRT_PCIE_PIO].base);
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
+ for (i = 0; i < PCI_NUM_PINS; i++) {
irq = qdev_get_gpio_in(pic, PCIE_IRQ_BASE + i);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ_BASE + i);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1f1baa5..8f9ce2f 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -72,6 +72,11 @@ config IVSHMEM_DEVICE
default y if PCI_DEVICES
depends on PCI && LINUX && IVSHMEM && MSI_NONBROKEN
+config IVSHMEM_FLAT_DEVICE
+ bool
+ default y
+ depends on LINUX && IVSHMEM
+
config ECCMEMCTL
bool
diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c
new file mode 100644
index 0000000..33fc942
--- /dev/null
+++ b/hw/misc/ivshmem-flat.c
@@ -0,0 +1,459 @@
+/*
+ * Inter-VM Shared Memory Flat Device
+ *
+ * SPDX-FileCopyrightText: 2023 Linaro Ltd.
+ * SPDX-FileContributor: Gustavo Romero <gustavo.romero@linaro.org>
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/error-report.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/sysbus.h"
+#include "chardev/char-fe.h"
+#include "exec/address-spaces.h"
+#include "trace.h"
+
+#include "hw/misc/ivshmem-flat.h"
+
+static int64_t ivshmem_flat_recv_msg(IvshmemFTState *s, int *pfd)
+{
+ int64_t msg;
+ int n, ret;
+
+ n = 0;
+ do {
+ ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n,
+ sizeof(msg) - n);
+ if (ret < 0) {
+ if (ret == -EINTR) {
+ continue;
+ }
+ exit(1);
+ }
+ n += ret;
+ } while (n < sizeof(msg));
+
+ if (pfd) {
+ *pfd = qemu_chr_fe_get_msgfd(&s->server_chr);
+ }
+ return le64_to_cpu(msg);
+}
+
+static void ivshmem_flat_irq_handler(void *opaque)
+{
+ VectorInfo *vi = opaque;
+ EventNotifier *e = &vi->event_notifier;
+ uint16_t vector_id;
+ const VectorInfo (*v)[64];
+
+ assert(e->initialized);
+
+ vector_id = vi->id;
+
+ /*
+ * The vector info struct is passed to the handler via the 'opaque' pointer.
+ * This struct pointer allows the retrieval of the vector ID and its
+ * associated event notifier. However, for triggering an interrupt using
+ * qemu_set_irq, it's necessary to also have a pointer to the device state,
+ * i.e., a pointer to the IvshmemFTState struct. Since the vector info
+ * struct is contained within the IvshmemFTState struct, its pointer can be
+ * used to obtain the pointer to IvshmemFTState through simple pointer math.
+ */
+ v = (void *)(vi - vector_id); /* v = &IvshmemPeer->vector[0] */
+ IvshmemPeer *own_peer = container_of(v, IvshmemPeer, vector);
+ IvshmemFTState *s = container_of(own_peer, IvshmemFTState, own);
+
+ /* Clear event */
+ if (!event_notifier_test_and_clear(e)) {
+ return;
+ }
+
+ trace_ivshmem_flat_irq_handler(vector_id);
+
+ /*
+ * Toggle device's output line, which is connected to interrupt controller,
+ * generating an interrupt request to the CPU.
+ */
+ qemu_irq_pulse(s->irq);
+}
+
+static IvshmemPeer *ivshmem_flat_find_peer(IvshmemFTState *s, uint16_t peer_id)
+{
+ IvshmemPeer *peer;
+
+ /* Own ID */
+ if (s->own.id == peer_id) {
+ return &s->own;
+ }
+
+ /* Peer ID */
+ QTAILQ_FOREACH(peer, &s->peer, next) {
+ if (peer->id == peer_id) {
+ return peer;
+ }
+ }
+
+ return NULL;
+}
+
+static IvshmemPeer *ivshmem_flat_add_peer(IvshmemFTState *s, uint16_t peer_id)
+{
+ IvshmemPeer *new_peer;
+
+ new_peer = g_malloc0(sizeof(*new_peer));
+ new_peer->id = peer_id;
+ new_peer->vector_counter = 0;
+
+ QTAILQ_INSERT_TAIL(&s->peer, new_peer, next);
+
+ trace_ivshmem_flat_new_peer(peer_id);
+
+ return new_peer;
+}
+
+static void ivshmem_flat_remove_peer(IvshmemFTState *s, uint16_t peer_id)
+{
+ IvshmemPeer *peer;
+
+ peer = ivshmem_flat_find_peer(s, peer_id);
+ assert(peer);
+
+ QTAILQ_REMOVE(&s->peer, peer, next);
+ for (int n = 0; n < peer->vector_counter; n++) {
+ int efd;
+ efd = event_notifier_get_fd(&(peer->vector[n].event_notifier));
+ close(efd);
+ }
+
+ g_free(peer);
+}
+
+static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer,
+ int vector_fd)
+{
+ if (peer->vector_counter >= IVSHMEM_MAX_VECTOR_NUM) {
+ trace_ivshmem_flat_add_vector_failure(peer->vector_counter,
+ vector_fd, peer->id);
+ close(vector_fd);
+
+ return;
+ }
+
+ trace_ivshmem_flat_add_vector_success(peer->vector_counter,
+ vector_fd, peer->id);
+
+ /*
+ * Set vector ID and its associated eventfd notifier and add them to the
+ * peer.
+ */
+ peer->vector[peer->vector_counter].id = peer->vector_counter;
+ g_unix_set_fd_nonblocking(vector_fd, true, NULL);
+ event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier,
+ vector_fd);
+
+ /*
+ * If it's the device's own ID, register also the handler for the eventfd
+ * so the device can be notified by the other peers.
+ */
+ if (peer == &s->own) {
+ qemu_set_fd_handler(vector_fd, ivshmem_flat_irq_handler, NULL,
+ &peer->vector);
+ }
+
+ peer->vector_counter++;
+}
+
+static void ivshmem_flat_process_msg(IvshmemFTState *s, uint64_t msg, int fd)
+{
+ uint16_t peer_id;
+ IvshmemPeer *peer;
+
+ peer_id = msg & 0xFFFF;
+ peer = ivshmem_flat_find_peer(s, peer_id);
+
+ if (!peer) {
+ peer = ivshmem_flat_add_peer(s, peer_id);
+ }
+
+ if (fd >= 0) {
+ ivshmem_flat_add_vector(s, peer, fd);
+ } else { /* fd == -1, which is received when peers disconnect. */
+ ivshmem_flat_remove_peer(s, peer_id);
+ }
+}
+
+static int ivshmem_flat_can_receive_data(void *opaque)
+{
+ IvshmemFTState *s = opaque;
+
+ assert(s->msg_buffered_bytes < sizeof(s->msg_buf));
+ return sizeof(s->msg_buf) - s->msg_buffered_bytes;
+}
+
+static void ivshmem_flat_read_msg(void *opaque, const uint8_t *buf, int size)
+{
+ IvshmemFTState *s = opaque;
+ int fd;
+ int64_t msg;
+
+ assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf));
+ memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size);
+ s->msg_buffered_bytes += size;
+ if (s->msg_buffered_bytes < sizeof(s->msg_buf)) {
+ return;
+ }
+ msg = le64_to_cpu(s->msg_buf);
+ s->msg_buffered_bytes = 0;
+
+ fd = qemu_chr_fe_get_msgfd(&s->server_chr);
+
+ ivshmem_flat_process_msg(s, msg, fd);
+}
+
+static uint64_t ivshmem_flat_iomem_read(void *opaque,
+ hwaddr offset, unsigned size)
+{
+ IvshmemFTState *s = opaque;
+ uint32_t ret;
+
+ trace_ivshmem_flat_read_mmr(offset);
+
+ switch (offset) {
+ case INTMASK:
+ ret = 0; /* Ignore read since all bits are reserved in rev 1. */
+ break;
+ case INTSTATUS:
+ ret = 0; /* Ignore read since all bits are reserved in rev 1. */
+ break;
+ case IVPOSITION:
+ ret = s->own.id;
+ break;
+ case DOORBELL:
+ trace_ivshmem_flat_read_mmr_doorbell(); /* DOORBELL is write-only */
+ ret = 0;
+ break;
+ default:
+ /* Should never reach out here due to iomem map range being exact */
+ trace_ivshmem_flat_read_write_mmr_invalid(offset);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int ivshmem_flat_interrupt_peer(IvshmemFTState *s,
+ uint16_t peer_id, uint16_t vector_id)
+{
+ IvshmemPeer *peer;
+
+ peer = ivshmem_flat_find_peer(s, peer_id);
+ if (!peer) {
+ trace_ivshmem_flat_interrupt_invalid_peer(peer_id);
+ return 1;
+ }
+
+ event_notifier_set(&(peer->vector[vector_id].event_notifier));
+
+ return 0;
+}
+
+static void ivshmem_flat_iomem_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ IvshmemFTState *s = opaque;
+ uint16_t peer_id = (value >> 16) & 0xFFFF;
+ uint16_t vector_id = value & 0xFFFF;
+
+ trace_ivshmem_flat_write_mmr(offset);
+
+ switch (offset) {
+ case INTMASK:
+ break;
+ case INTSTATUS:
+ break;
+ case IVPOSITION:
+ break;
+ case DOORBELL:
+ trace_ivshmem_flat_interrupt_peer(peer_id, vector_id);
+ ivshmem_flat_interrupt_peer(s, peer_id, vector_id);
+ break;
+ default:
+ /* Should never reach out here due to iomem map range being exact. */
+ trace_ivshmem_flat_read_write_mmr_invalid(offset);
+ break;
+ }
+
+ return;
+}
+
+static const MemoryRegionOps ivshmem_flat_ops = {
+ .read = ivshmem_flat_iomem_read,
+ .write = ivshmem_flat_iomem_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = { /* Read/write aligned at 32 bits. */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void ivshmem_flat_instance_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ IvshmemFTState *s = IVSHMEM_FLAT(obj);
+
+ /*
+ * Init mem region for 4 MMRs (ivshmem_registers),
+ * 32 bits each => 16 bytes (0x10).
+ */
+ memory_region_init_io(&s->iomem, obj, &ivshmem_flat_ops, s,
+ "ivshmem-mmio", 0x10);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ /*
+ * Create one output IRQ that will be connect to the
+ * machine's interrupt controller.
+ */
+ sysbus_init_irq(sbd, &s->irq);
+
+ QTAILQ_INIT(&s->peer);
+}
+
+static bool ivshmem_flat_connect_server(DeviceState *dev, Error **errp)
+{
+ IvshmemFTState *s = IVSHMEM_FLAT(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ int64_t protocol_version, msg;
+ int shmem_fd;
+ uint16_t peer_id;
+ struct stat fdstat;
+
+ /* Check ivshmem server connection. */
+ if (!qemu_chr_fe_backend_connected(&s->server_chr)) {
+ error_setg(errp, "ivshmem server socket not specified or incorret."
+ " Can't create device.");
+ return false;
+ }
+
+ /*
+ * Message sequence from server on new connection:
+ * _____________________________________
+ * |STEP| uint64_t msg | int fd |
+ * -------------------------------------
+ *
+ * 0 PROTOCOL -1 \
+ * 1 OWN PEER ID -1 |-- Header/Greeting
+ * 2 -1 shmem fd /
+ *
+ * 3 PEER IDx Other peer's Vector 0 eventfd
+ * 4 PEER IDx Other peer's Vector 1 eventfd
+ * . .
+ * . .
+ * . .
+ * N PEER IDy Other peer's Vector 0 eventfd
+ * N+1 PEER IDy Other peer's Vector 1 eventfd
+ * . .
+ * . .
+ * . .
+ *
+ * ivshmem_flat_recv_msg() calls return 'msg' and 'fd'.
+ *
+ * See ./docs/specs/ivshmem-spec.txt for details on the protocol.
+ */
+
+ /* Step 0 */
+ protocol_version = ivshmem_flat_recv_msg(s, NULL);
+
+ /* Step 1 */
+ msg = ivshmem_flat_recv_msg(s, NULL);
+ peer_id = 0xFFFF & msg;
+ s->own.id = peer_id;
+ s->own.vector_counter = 0;
+
+ trace_ivshmem_flat_proto_ver_own_id(protocol_version, s->own.id);
+
+ /* Step 2 */
+ msg = ivshmem_flat_recv_msg(s, &shmem_fd);
+ /* Map shmem fd and MMRs into memory regions. */
+ if (msg != -1 || shmem_fd < 0) {
+ error_setg(errp, "Could not receive valid shmem fd."
+ " Can't create device!");
+ return false;
+ }
+
+ if (fstat(shmem_fd, &fdstat) != 0) {
+ error_setg(errp, "Could not determine shmem fd size."
+ " Can't create device!");
+ return false;
+ }
+ trace_ivshmem_flat_shmem_size(shmem_fd, fdstat.st_size);
+
+ /*
+ * Shmem size provided by the ivshmem server must be equal to
+ * device's shmem size.
+ */
+ if (fdstat.st_size != s->shmem_size) {
+ error_setg(errp, "Can't map shmem fd: shmem size different"
+ " from device size!");
+ return false;
+ }
+
+ /*
+ * Beyond step 2 ivshmem_process_msg, called by ivshmem_flat_read_msg
+ * handler -- when data is available on the server socket -- will handle
+ * the additional messages that will be generated by the server as peers
+ * connect or disconnect.
+ */
+ qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_flat_can_receive_data,
+ ivshmem_flat_read_msg, NULL, NULL, s, NULL, true);
+
+ memory_region_init_ram_from_fd(&s->shmem, OBJECT(s),
+ "ivshmem-shmem", s->shmem_size,
+ RAM_SHARED, shmem_fd, 0, NULL);
+ sysbus_init_mmio(sbd, &s->shmem);
+
+ return true;
+}
+
+static void ivshmem_flat_realize(DeviceState *dev, Error **errp)
+{
+ if (!ivshmem_flat_connect_server(dev, errp)) {
+ return;
+ }
+}
+
+static const Property ivshmem_flat_props[] = {
+ DEFINE_PROP_CHR("chardev", IvshmemFTState, server_chr),
+ DEFINE_PROP_UINT32("shmem-size", IvshmemFTState, shmem_size, 4 * MiB),
+};
+
+static void ivshmem_flat_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->hotpluggable = true;
+ dc->realize = ivshmem_flat_realize;
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ device_class_set_props(dc, ivshmem_flat_props);
+
+ /* Reason: Must be wired up in code (sysbus MRs and IRQ) */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo ivshmem_flat_types[] = {
+ {
+ .name = TYPE_IVSHMEM_FLAT,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IvshmemFTState),
+ .instance_init = ivshmem_flat_instance_init,
+ .class_init = ivshmem_flat_class_init,
+ },
+};
+
+DEFINE_TYPES(ivshmem_flat_types)
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem-pci.c
index 900d523..900d523 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem-pci.c
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index d02d96e..55f4935 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -37,7 +37,9 @@ system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c'))
subdir('macio')
-system_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c'))
+# ivshmem devices
+system_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem-pci.c'))
+system_ss.add(when: 'CONFIG_IVSHMEM_FLAT_DEVICE', if_true: files('ivshmem-flat.c'))
system_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true: files('allwinner-sramc.c'))
system_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index b9fbcb0..0f5d2b5 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -368,3 +368,19 @@ aspeed_sli_read(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx
aspeed_sliio_write(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
aspeed_sliio_read(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
+# ivshmem-flat.c
+ivshmem_flat_irq_handler(uint16_t vector_id) "Caught interrupt request: vector %d"
+ivshmem_flat_new_peer(uint16_t peer_id) "New peer ID: %d"
+ivshmem_flat_add_vector_failure(uint16_t vector_id, uint32_t vector_fd, uint16_t peer_id) "Failed to add vector %u (fd = %u) to peer ID %u, maximum number of vectors reached"
+ivshmem_flat_add_vector_success(uint16_t vector_id, uint32_t vector_fd, uint16_t peer_id) "Successful addition of vector %u (fd = %u) to peer ID %u"
+ivshmem_flat_irq_resolved(const char *irq_qompath) "IRQ QOM path '%s' correctly resolved"
+ivshmem_flat_proto_ver_own_id(uint64_t proto_ver, uint16_t peer_id) "Protocol Version = 0x%"PRIx64", Own Peer ID = %u"
+ivshmem_flat_shmem_size(int fd, uint64_t size) "Shmem fd (%d) total size is %"PRIu64" byte(s)"
+ivshmem_flat_shmem_map(uint64_t addr) "Mapping shmem @ 0x%"PRIx64
+ivshmem_flat_mmio_map(uint64_t addr) "Mapping MMRs @ 0x%"PRIx64
+ivshmem_flat_read_mmr(uint64_t addr_offset) "Read access at offset %"PRIu64
+ivshmem_flat_read_mmr_doorbell(void) "DOORBELL register is write-only!"
+ivshmem_flat_read_write_mmr_invalid(uint64_t addr_offset) "No ivshmem register mapped at offset %"PRIu64
+ivshmem_flat_interrupt_invalid_peer(uint16_t peer_id) "Can't interrupt non-existing peer %u"
+ivshmem_flat_write_mmr(uint64_t addr_offset) "Write access at offset %"PRIu64
+ivshmem_flat_interrupt_peer(uint16_t peer_id, uint16_t vector_id) "Interrupting peer ID %u, vector %u..."
diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c
index 0910c64..b1fcc22 100644
--- a/hw/misc/vmcoreinfo.c
+++ b/hw/misc/vmcoreinfo.c
@@ -18,17 +18,17 @@
#include "migration/vmstate.h"
#include "hw/misc/vmcoreinfo.h"
-static void fw_cfg_vmci_write(void *dev, off_t offset, size_t len)
+static void fw_cfg_vmci_write(void *opaque, off_t offset, size_t len)
{
- VMCoreInfoState *s = VMCOREINFO(dev);
+ VMCoreInfoState *s = opaque;
s->has_vmcoreinfo = offset == 0 && len == sizeof(s->vmcoreinfo)
&& s->vmcoreinfo.guest_format != FW_CFG_VMCOREINFO_FORMAT_NONE;
}
-static void vmcoreinfo_reset(void *dev)
+static void vmcoreinfo_reset(void *opaque)
{
- VMCoreInfoState *s = VMCOREINFO(dev);
+ VMCoreInfoState *s = opaque;
s->has_vmcoreinfo = false;
memset(&s->vmcoreinfo, 0, sizeof(s->vmcoreinfo));
@@ -65,7 +65,7 @@ static void vmcoreinfo_realize(DeviceState *dev, Error **errp)
* This device requires to register a global reset because it is
* not plugged to a bus (which, as its QOM parent, would reset it).
*/
- qemu_register_reset(vmcoreinfo_reset, dev);
+ qemu_register_reset(vmcoreinfo_reset, s);
vmcoreinfo_state = s;
}
@@ -93,16 +93,13 @@ static void vmcoreinfo_device_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
-static const TypeInfo vmcoreinfo_device_info = {
- .name = VMCOREINFO_DEVICE,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(VMCoreInfoState),
- .class_init = vmcoreinfo_device_class_init,
+static const TypeInfo vmcoreinfo_types[] = {
+ {
+ .name = VMCOREINFO_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(VMCoreInfoState),
+ .class_init = vmcoreinfo_device_class_init,
+ }
};
-static void vmcoreinfo_register_types(void)
-{
- type_register_static(&vmcoreinfo_device_info);
-}
-
-type_init(vmcoreinfo_register_types)
+DEFINE_TYPES(vmcoreinfo_types)
diff --git a/hw/net/trace-events b/hw/net/trace-events
index 6100ec3..c35bfb2 100644
--- a/hw/net/trace-events
+++ b/hw/net/trace-events
@@ -513,3 +513,7 @@ xen_netdev_connect(int dev, unsigned int tx, unsigned int rx, int port) "vif%u t
xen_netdev_frontend_changed(const char *dev, int state) "vif%s state %d"
xen_netdev_tx(int dev, int ref, int off, int len, unsigned int flags, const char *c, const char *d, const char *m, const char *e) "vif%u ref %u off %u len %u flags 0x%x%s%s%s%s"
xen_netdev_rx(int dev, int idx, int status, int flags) "vif%u idx %d status %d flags 0x%x"
+
+# xilinx_ethlite.c
+ethlite_pkt_lost(uint32_t rx_ctrl) "rx_ctrl:0x%" PRIx32
+ethlite_pkt_size_too_big(uint64_t size) "size:0x%" PRIx64
diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
index f3eb2af..4c0c7fc 100644
--- a/hw/net/xilinx_ethlite.c
+++ b/hw/net/xilinx_ethlite.c
@@ -3,6 +3,9 @@
*
* Copyright (c) 2009 Edgar E. Iglesias.
*
+ * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite
+ * LogiCORE IP XPS Ethernet Lite Media Access Controller
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
@@ -30,9 +33,10 @@
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "net/net.h"
+#include "trace.h"
-#define D(x)
#define R_TX_BUF0 0
+#define BUFSZ_MAX 0x07e4
#define R_TX_LEN0 (0x07f4 / 4)
#define R_TX_GIE0 (0x07f8 / 4)
#define R_TX_CTRL0 (0x07fc / 4)
@@ -53,10 +57,9 @@
#define CTRL_S 0x1
#define TYPE_XILINX_ETHLITE "xlnx.xps-ethernetlite"
-DECLARE_INSTANCE_CHECKER(struct xlx_ethlite, XILINX_ETHLITE,
- TYPE_XILINX_ETHLITE)
+OBJECT_DECLARE_SIMPLE_TYPE(XlnxXpsEthLite, XILINX_ETHLITE)
-struct xlx_ethlite
+struct XlnxXpsEthLite
{
SysBusDevice parent_obj;
@@ -67,13 +70,12 @@ struct xlx_ethlite
uint32_t c_tx_pingpong;
uint32_t c_rx_pingpong;
- unsigned int txbuf;
- unsigned int rxbuf;
+ unsigned int port_index; /* dual port RAM index */
uint32_t regs[R_MAX];
};
-static inline void eth_pulse_irq(struct xlx_ethlite *s)
+static inline void eth_pulse_irq(XlnxXpsEthLite *s)
{
/* Only the first gie reg is active. */
if (s->regs[R_TX_GIE0] & GIE_GIE) {
@@ -84,7 +86,7 @@ static inline void eth_pulse_irq(struct xlx_ethlite *s)
static uint64_t
eth_read(void *opaque, hwaddr addr, unsigned int size)
{
- struct xlx_ethlite *s = opaque;
+ XlnxXpsEthLite *s = opaque;
uint32_t r = 0;
addr >>= 2;
@@ -99,7 +101,6 @@ eth_read(void *opaque, hwaddr addr, unsigned int size)
case R_RX_CTRL1:
case R_RX_CTRL0:
r = s->regs[addr];
- D(qemu_log("%s " HWADDR_FMT_plx "=%x\n", __func__, addr * 4, r));
break;
default:
@@ -113,7 +114,7 @@ static void
eth_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
- struct xlx_ethlite *s = opaque;
+ XlnxXpsEthLite *s = opaque;
unsigned int base = 0;
uint32_t value = val64;
@@ -125,13 +126,10 @@ eth_write(void *opaque, hwaddr addr,
if (addr == R_TX_CTRL1)
base = 0x800 / 4;
- D(qemu_log("%s addr=" HWADDR_FMT_plx " val=%x\n",
- __func__, addr * 4, value));
if ((value & (CTRL_P | CTRL_S)) == CTRL_S) {
qemu_send_packet(qemu_get_queue(s->nic),
(void *) &s->regs[base],
s->regs[base + R_TX_LEN0]);
- D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0]));
if (s->regs[base + R_TX_CTRL0] & CTRL_I)
eth_pulse_irq(s);
} else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) {
@@ -155,8 +153,6 @@ eth_write(void *opaque, hwaddr addr,
case R_TX_LEN0:
case R_TX_LEN1:
case R_TX_GIE0:
- D(qemu_log("%s addr=" HWADDR_FMT_plx " val=%x\n",
- __func__, addr * 4, value));
s->regs[addr] = value;
break;
@@ -178,29 +174,28 @@ static const MemoryRegionOps eth_ops = {
static bool eth_can_rx(NetClientState *nc)
{
- struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
- unsigned int rxbase = s->rxbuf * (0x800 / 4);
+ XlnxXpsEthLite *s = qemu_get_nic_opaque(nc);
+ unsigned int rxbase = s->port_index * (0x800 / 4);
return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S);
}
static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
{
- struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
- unsigned int rxbase = s->rxbuf * (0x800 / 4);
+ XlnxXpsEthLite *s = qemu_get_nic_opaque(nc);
+ unsigned int rxbase = s->port_index * (0x800 / 4);
/* DA filter. */
if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6))
return size;
if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) {
- D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0]));
+ trace_ethlite_pkt_lost(s->regs[R_RX_CTRL0]);
return -1;
}
- D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase));
- if (size > (R_MAX - R_RX_BUF0 - rxbase) * 4) {
- D(qemu_log("ethlite packet is too big, size=%x\n", size));
+ if (size >= BUFSZ_MAX) {
+ trace_ethlite_pkt_size_too_big(size);
return -1;
}
memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size);
@@ -211,15 +206,15 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
}
/* If c_rx_pingpong was set flip buffers. */
- s->rxbuf ^= s->c_rx_pingpong;
+ s->port_index ^= s->c_rx_pingpong;
return size;
}
static void xilinx_ethlite_reset(DeviceState *dev)
{
- struct xlx_ethlite *s = XILINX_ETHLITE(dev);
+ XlnxXpsEthLite *s = XILINX_ETHLITE(dev);
- s->rxbuf = 0;
+ s->port_index = 0;
}
static NetClientInfo net_xilinx_ethlite_info = {
@@ -231,7 +226,7 @@ static NetClientInfo net_xilinx_ethlite_info = {
static void xilinx_ethlite_realize(DeviceState *dev, Error **errp)
{
- struct xlx_ethlite *s = XILINX_ETHLITE(dev);
+ XlnxXpsEthLite *s = XILINX_ETHLITE(dev);
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
@@ -242,7 +237,7 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp)
static void xilinx_ethlite_init(Object *obj)
{
- struct xlx_ethlite *s = XILINX_ETHLITE(obj);
+ XlnxXpsEthLite *s = XILINX_ETHLITE(obj);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
@@ -252,9 +247,9 @@ static void xilinx_ethlite_init(Object *obj)
}
static const Property xilinx_ethlite_properties[] = {
- DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1),
- DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1),
- DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf),
+ DEFINE_PROP_UINT32("tx-ping-pong", XlnxXpsEthLite, c_tx_pingpong, 1),
+ DEFINE_PROP_UINT32("rx-ping-pong", XlnxXpsEthLite, c_rx_pingpong, 1),
+ DEFINE_NIC_PROPERTIES(XlnxXpsEthLite, conf),
};
static void xilinx_ethlite_class_init(ObjectClass *klass, void *data)
@@ -266,17 +261,14 @@ static void xilinx_ethlite_class_init(ObjectClass *klass, void *data)
device_class_set_props(dc, xilinx_ethlite_properties);
}
-static const TypeInfo xilinx_ethlite_info = {
- .name = TYPE_XILINX_ETHLITE,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(struct xlx_ethlite),
- .instance_init = xilinx_ethlite_init,
- .class_init = xilinx_ethlite_class_init,
+static const TypeInfo xilinx_ethlite_types[] = {
+ {
+ .name = TYPE_XILINX_ETHLITE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XlnxXpsEthLite),
+ .instance_init = xilinx_ethlite_init,
+ .class_init = xilinx_ethlite_class_init,
+ },
};
-static void xilinx_ethlite_register_types(void)
-{
- type_register_static(&xilinx_ethlite_info);
-}
-
-type_init(xilinx_ethlite_register_types)
+DEFINE_TYPES(xilinx_ethlite_types)
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
index c553716..a757939 100644
--- a/hw/nvram/fw_cfg.c
+++ b/hw/nvram/fw_cfg.c
@@ -729,7 +729,6 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key,
ptr = s->entries[arch][key].data;
s->entries[arch][key].data = data;
s->entries[arch][key].len = len;
- s->entries[arch][key].callback_opaque = NULL;
s->entries[arch][key].allow_write = false;
return ptr;
diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c
index 87f9cbc..e0da406 100644
--- a/hw/openrisc/openrisc_sim.c
+++ b/hw/openrisc/openrisc_sim.c
@@ -266,7 +266,7 @@ static void openrisc_sim_serial_init(Or1ksimState *state, hwaddr base,
}
serial_mm_init(get_system_memory(), base, 0, serial_irq, 115200,
serial_hd(uart_idx),
- DEVICE_NATIVE_ENDIAN);
+ DEVICE_BIG_ENDIAN);
/* Add device tree node for serial. */
nodename = g_strdup_printf("/serial@%" HWADDR_PRIx, base);
diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c
index 0d83e33..7b60bf8 100644
--- a/hw/openrisc/virt.c
+++ b/hw/openrisc/virt.c
@@ -236,7 +236,7 @@ static void openrisc_virt_serial_init(OR1KVirtState *state, hwaddr base,
qemu_irq serial_irq = get_per_cpu_irq(cpus, num_cpus, irq_pin);
serial_mm_init(get_system_memory(), base, 0, serial_irq, 115200,
- serial_hd(0), DEVICE_NATIVE_ENDIAN);
+ serial_hd(0), DEVICE_BIG_ENDIAN);
/* Add device tree node for serial. */
nodename = g_strdup_printf("/serial@%" HWADDR_PRIx, base);
@@ -318,7 +318,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base,
{
int pin, dev;
uint32_t irq_map_stride = 0;
- uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * 6] = {};
+ uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 6] = {};
uint32_t *irq_map = full_irq_map;
/*
@@ -330,11 +330,11 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base,
* possible slot) seeing the interrupt-map-mask will allow the table
* to wrap to any number of devices.
*/
- for (dev = 0; dev < GPEX_NUM_IRQS; dev++) {
+ for (dev = 0; dev < PCI_NUM_PINS; dev++) {
int devfn = dev << 3;
- for (pin = 0; pin < GPEX_NUM_IRQS; pin++) {
- int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS);
+ for (pin = 0; pin < PCI_NUM_PINS; pin++) {
+ int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
int i = 0;
/* Fill PCI address cells */
@@ -357,7 +357,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base,
}
qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map,
- GPEX_NUM_IRQS * GPEX_NUM_IRQS *
+ PCI_NUM_PINS * PCI_NUM_PINS *
irq_map_stride * sizeof(uint32_t));
qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask",
@@ -409,7 +409,7 @@ static void openrisc_virt_pcie_init(OR1KVirtState *state,
memory_region_add_subregion(get_system_memory(), pio_base, alias);
/* Connect IRQ lines. */
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
+ for (i = 0; i < PCI_NUM_PINS; i++) {
pcie_irq = get_per_cpu_irq(cpus, num_cpus, irq_base + i);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pcie_irq);
diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
index c6aa8e8..9fcedd7 100644
--- a/hw/pci-host/gpex.c
+++ b/hw/pci-host/gpex.c
@@ -32,6 +32,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/irq.h"
+#include "hw/pci/pci_bus.h"
#include "hw/pci-host/gpex.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
@@ -41,20 +42,25 @@
* GPEX host
*/
+struct GPEXIrq {
+ qemu_irq irq;
+ int irq_num;
+};
+
static void gpex_set_irq(void *opaque, int irq_num, int level)
{
GPEXHost *s = opaque;
- qemu_set_irq(s->irq[irq_num], level);
+ qemu_set_irq(s->irq[irq_num].irq, level);
}
int gpex_set_irq_num(GPEXHost *s, int index, int gsi)
{
- if (index >= GPEX_NUM_IRQS) {
+ if (index >= s->num_irqs) {
return -EINVAL;
}
- s->irq_num[index] = gsi;
+ s->irq[index].irq_num = gsi;
return 0;
}
@@ -62,7 +68,7 @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin)
{
PCIINTxRoute route;
GPEXHost *s = opaque;
- int gsi = s->irq_num[pin];
+ int gsi = s->irq[pin].irq_num;
route.irq = gsi;
if (gsi < 0) {
@@ -74,6 +80,13 @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin)
return route;
}
+static int gpex_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin)
+{
+ PCIBus *bus = pci_device_root_bus(pci_dev);
+
+ return (PCI_SLOT(pci_dev->devfn) + pin) % bus->nirq;
+}
+
static void gpex_host_realize(DeviceState *dev, Error **errp)
{
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
@@ -82,6 +95,8 @@ static void gpex_host_realize(DeviceState *dev, Error **errp)
PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
int i;
+ s->irq = g_malloc0_n(s->num_irqs, sizeof(*s->irq));
+
pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX);
sysbus_init_mmio(sbd, &pex->mmio);
@@ -128,19 +143,27 @@ static void gpex_host_realize(DeviceState *dev, Error **errp)
sysbus_init_mmio(sbd, &s->io_ioport);
}
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
- sysbus_init_irq(sbd, &s->irq[i]);
- s->irq_num[i] = -1;
+ for (i = 0; i < s->num_irqs; i++) {
+ sysbus_init_irq(sbd, &s->irq[i].irq);
+ s->irq[i].irq_num = -1;
}
pci->bus = pci_register_root_bus(dev, "pcie.0", gpex_set_irq,
- pci_swizzle_map_irq_fn, s, &s->io_mmio,
- &s->io_ioport, 0, 4, TYPE_PCIE_BUS);
+ gpex_swizzle_map_irq_fn,
+ s, &s->io_mmio, &s->io_ioport, 0,
+ s->num_irqs, TYPE_PCIE_BUS);
pci_bus_set_route_irq_fn(pci->bus, gpex_route_intx_pin_to_irq);
qdev_realize(DEVICE(&s->gpex_root), BUS(pci->bus), &error_fatal);
}
+static void gpex_host_unrealize(DeviceState *dev)
+{
+ GPEXHost *s = GPEX_HOST(dev);
+
+ g_free(s->irq);
+}
+
static const char *gpex_host_root_bus_path(PCIHostState *host_bridge,
PCIBus *rootbus)
{
@@ -166,6 +189,7 @@ static const Property gpex_host_properties[] = {
gpex_cfg.mmio64.base, 0),
DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MMIO_SIZE, GPEXHost,
gpex_cfg.mmio64.size, 0),
+ DEFINE_PROP_UINT8("num-irqs", GPEXHost, num_irqs, PCI_NUM_PINS),
};
static void gpex_host_class_init(ObjectClass *klass, void *data)
@@ -175,6 +199,7 @@ static void gpex_host_class_init(ObjectClass *klass, void *data)
hc->root_bus_path = gpex_host_root_bus_path;
dc->realize = gpex_host_realize;
+ dc->unrealize = gpex_host_unrealize;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->fw_name = "pci";
device_class_set_props(dc, gpex_host_properties);
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index c792ab9..2bc5a9d 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -179,7 +179,7 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename,
{
int pin, dev;
uint32_t irq_map_stride = 0;
- uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS *
+ uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS *
FDT_MAX_INT_MAP_WIDTH] = {};
uint32_t *irq_map = full_irq_map;
@@ -191,11 +191,11 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename,
* possible slot) seeing the interrupt-map-mask will allow the table
* to wrap to any number of devices.
*/
- for (dev = 0; dev < GPEX_NUM_IRQS; dev++) {
+ for (dev = 0; dev < PCI_NUM_PINS; dev++) {
int devfn = dev * 0x8;
- for (pin = 0; pin < GPEX_NUM_IRQS; pin++) {
- int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS);
+ for (pin = 0; pin < PCI_NUM_PINS; pin++) {
+ int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
int i = 0;
/* Fill PCI address cells */
@@ -221,7 +221,7 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename,
}
qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map,
- GPEX_NUM_IRQS * GPEX_NUM_IRQS *
+ PCI_NUM_PINS * PCI_NUM_PINS *
irq_map_stride * sizeof(uint32_t));
qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask",
@@ -1246,7 +1246,7 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base);
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
+ for (i = 0; i < PCI_NUM_PINS; i++) {
irq = qdev_get_gpio_in(irqchip, PCIE_IRQ + i);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c
index 8c1fc82..5a4c1f5 100644
--- a/hw/sparc/sun4m_iommu.c
+++ b/hw/sparc/sun4m_iommu.c
@@ -238,7 +238,7 @@ static void iommu_mem_write(void *opaque, hwaddr addr,
static const MemoryRegionOps iommu_mem_ops = {
.read = iommu_mem_read,
.write = iommu_mem_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 5778709..0980b44 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -254,7 +254,7 @@ static void power_mem_write(void *opaque, hwaddr addr,
static const MemoryRegionOps power_mem_ops = {
.read = power_mem_read,
.write = power_mem_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
diff --git a/hw/tricore/tricore_testdevice.c b/hw/tricore/tricore_testdevice.c
index e60866d..d2da74e 100644
--- a/hw/tricore/tricore_testdevice.c
+++ b/hw/tricore/tricore_testdevice.c
@@ -47,7 +47,7 @@ static const MemoryRegionOps tricore_testdevice_ops = {
.min_access_size = 4,
.max_access_size = 4,
},
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static void tricore_testdevice_init(Object *obj)
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 0559b3a..8528d49 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -67,7 +67,7 @@ struct UHCIPCIDeviceClass {
UHCIInfo info;
};
-/*
+/*
* Pending async transaction.
* 'packet' must be the first field because completion
* handler does "(UHCIAsync *) pkt" cast.
@@ -220,8 +220,9 @@ static void uhci_async_cancel(UHCIAsync *async)
uhci_async_unlink(async);
trace_usb_uhci_packet_cancel(async->queue->token, async->td_addr,
async->done);
- if (!async->done)
+ if (!async->done) {
usb_cancel_packet(&async->packet);
+ }
uhci_async_free(async);
}
@@ -322,7 +323,7 @@ static void uhci_reset(DeviceState *dev)
s->fl_base_addr = 0;
s->sof_timing = 64;
- for(i = 0; i < UHCI_PORTS; i++) {
+ for (i = 0; i < UHCI_PORTS; i++) {
port = &s->ports[i];
port->ctrl = 0x0080;
if (port->port.dev && port->port.dev->attached) {
@@ -387,8 +388,8 @@ static void uhci_port_write(void *opaque, hwaddr addr,
trace_usb_uhci_mmio_writew(addr, val);
- switch(addr) {
- case 0x00:
+ switch (addr) {
+ case UHCI_USBCMD:
if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
/* start frame processing */
trace_usb_uhci_schedule_start();
@@ -404,7 +405,7 @@ static void uhci_port_write(void *opaque, hwaddr addr,
int i;
/* send reset on the USB bus */
- for(i = 0; i < UHCI_PORTS; i++) {
+ for (i = 0; i < UHCI_PORTS; i++) {
port = &s->ports[i];
usb_device_reset(port->port.dev);
}
@@ -423,34 +424,38 @@ static void uhci_port_write(void *opaque, hwaddr addr,
}
}
break;
- case 0x02:
+ case UHCI_USBSTS:
s->status &= ~val;
- /* XXX: the chip spec is not coherent, so we add a hidden
- register to distinguish between IOC and SPD */
- if (val & UHCI_STS_USBINT)
+ /*
+ * XXX: the chip spec is not coherent, so we add a hidden
+ * register to distinguish between IOC and SPD
+ */
+ if (val & UHCI_STS_USBINT) {
s->status2 = 0;
+ }
uhci_update_irq(s);
break;
- case 0x04:
+ case UHCI_USBINTR:
s->intr = val;
uhci_update_irq(s);
break;
- case 0x06:
- if (s->status & UHCI_STS_HCHALTED)
+ case UHCI_USBFRNUM:
+ if (s->status & UHCI_STS_HCHALTED) {
s->frnum = val & 0x7ff;
+ }
break;
- case 0x08:
+ case UHCI_USBFLBASEADD:
s->fl_base_addr &= 0xffff0000;
s->fl_base_addr |= val & ~0xfff;
break;
- case 0x0a:
+ case UHCI_USBFLBASEADD + 2:
s->fl_base_addr &= 0x0000ffff;
s->fl_base_addr |= (val << 16);
break;
- case 0x0c:
+ case UHCI_USBSOF:
s->sof_timing = val & 0xff;
break;
- case 0x10 ... 0x1f:
+ case UHCI_USBPORTSC1 ... UHCI_USBPORTSC4:
{
UHCIPort *port;
USBDevice *dev;
@@ -464,8 +469,8 @@ static void uhci_port_write(void *opaque, hwaddr addr,
dev = port->port.dev;
if (dev && dev->attached) {
/* port reset */
- if ( (val & UHCI_PORT_RESET) &&
- !(port->ctrl & UHCI_PORT_RESET) ) {
+ if ((val & UHCI_PORT_RESET) &&
+ !(port->ctrl & UHCI_PORT_RESET)) {
usb_device_reset(dev);
}
}
@@ -487,29 +492,29 @@ static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size)
UHCIState *s = opaque;
uint32_t val;
- switch(addr) {
- case 0x00:
+ switch (addr) {
+ case UHCI_USBCMD:
val = s->cmd;
break;
- case 0x02:
+ case UHCI_USBSTS:
val = s->status;
break;
- case 0x04:
+ case UHCI_USBINTR:
val = s->intr;
break;
- case 0x06:
+ case UHCI_USBFRNUM:
val = s->frnum;
break;
- case 0x08:
+ case UHCI_USBFLBASEADD:
val = s->fl_base_addr & 0xffff;
break;
- case 0x0a:
+ case UHCI_USBFLBASEADD + 2:
val = (s->fl_base_addr >> 16) & 0xffff;
break;
- case 0x0c:
+ case UHCI_USBSOF:
val = s->sof_timing;
break;
- case 0x10 ... 0x1f:
+ case UHCI_USBPORTSC1 ... UHCI_USBPORTSC4:
{
UHCIPort *port;
int n;
@@ -533,12 +538,13 @@ static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size)
}
/* signal resume if controller suspended */
-static void uhci_resume (void *opaque)
+static void uhci_resume(void *opaque)
{
UHCIState *s = (UHCIState *)opaque;
- if (!s)
+ if (!s) {
return;
+ }
if (s->cmd & UHCI_CMD_EGSM) {
s->cmd |= UHCI_CMD_FGR;
@@ -674,7 +680,8 @@ static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr,
return ret;
}
-static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
+static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async,
+ uint32_t *int_mask)
{
int len = 0, max_len;
uint8_t pid;
@@ -682,8 +689,9 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff;
- if (td->ctrl & TD_CTRL_IOS)
+ if (td->ctrl & TD_CTRL_IOS) {
td->ctrl &= ~TD_CTRL_ACTIVE;
+ }
if (async->packet.status != USB_RET_SUCCESS) {
return uhci_handle_td_error(s, td, async->td_addr,
@@ -693,12 +701,15 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
len = async->packet.actual_length;
td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
- /* The NAK bit may have been set by a previous frame, so clear it
- here. The docs are somewhat unclear, but win2k relies on this
- behavior. */
+ /*
+ * The NAK bit may have been set by a previous frame, so clear it
+ * here. The docs are somewhat unclear, but win2k relies on this
+ * behavior.
+ */
td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK);
- if (td->ctrl & TD_CTRL_IOC)
+ if (td->ctrl & TD_CTRL_IOC) {
*int_mask |= 0x01;
+ }
if (pid == USB_TOKEN_IN) {
pci_dma_write(&s->dev, td->buffer, async->buf, len);
@@ -780,9 +791,11 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
if (async) {
if (queuing) {
- /* we are busy filling the queue, we are not prepared
- to consume completed packages then, just leave them
- in async state */
+ /*
+ * we are busy filling the queue, we are not prepared
+ * to consume completed packages then, just leave them
+ * in async state
+ */
return TD_RESULT_ASYNC_CONT;
}
if (!async->done) {
@@ -832,7 +845,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
}
usb_packet_addbuf(&async->packet, async->buf, max_len);
- switch(pid) {
+ switch (pid) {
case USB_TOKEN_OUT:
case USB_TOKEN_SETUP:
pci_dma_read(&s->dev, td->buffer, async->buf, max_len);
@@ -911,12 +924,15 @@ static void qhdb_reset(QhDb *db)
static int qhdb_insert(QhDb *db, uint32_t addr)
{
int i;
- for (i = 0; i < db->count; i++)
- if (db->addr[i] == addr)
+ for (i = 0; i < db->count; i++) {
+ if (db->addr[i] == addr) {
return 1;
+ }
+ }
- if (db->count >= UHCI_MAX_QUEUES)
+ if (db->count >= UHCI_MAX_QUEUES) {
return 1;
+ }
db->addr[db->count++] = addr;
return 0;
@@ -970,8 +986,10 @@ static void uhci_process_frame(UHCIState *s)
for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) {
if (!s->completions_only && s->frame_bytes >= s->frame_bandwidth) {
- /* We've reached the usb 1.1 bandwidth, which is
- 1280 bytes/frame, stop processing */
+ /*
+ * We've reached the usb 1.1 bandwidth, which is
+ * 1280 bytes/frame, stop processing
+ */
trace_usb_uhci_frame_stop_bandwidth();
break;
}
@@ -1120,8 +1138,10 @@ static void uhci_frame_timer(void *opaque)
uhci_async_validate_begin(s);
uhci_process_frame(s);
uhci_async_validate_end(s);
- /* The spec says frnum is the frame currently being processed, and
- * the guest must look at frnum - 1 on interrupt, so inc frnum now */
+ /*
+ * The spec says frnum is the frame currently being processed, and
+ * the guest must look at frnum - 1 on interrupt, so inc frnum now
+ */
s->frnum = (s->frnum + 1) & 0x7ff;
s->expire_time += frame_t;
}
@@ -1174,7 +1194,7 @@ void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
if (s->masterbus) {
USBPort *ports[UHCI_PORTS];
- for(i = 0; i < UHCI_PORTS; i++) {
+ for (i = 0; i < UHCI_PORTS; i++) {
ports[i] = &s->ports[i].port;
}
usb_register_companion(s->masterbus, ports, UHCI_PORTS,
@@ -1200,8 +1220,10 @@ void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
memory_region_init_io(&s->io_bar, OBJECT(s), &uhci_ioport_ops, s,
"uhci", 0x20);
- /* Use region 4 for consistency with real hardware. BSD guests seem
- to rely on this. */
+ /*
+ * Use region 4 for consistency with real hardware. BSD guests seem
+ * to rely on this.
+ */
pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
}
diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c
index b1df95b..1df518b 100644
--- a/hw/usb/hcd-xhci-nec.c
+++ b/hw/usb/hcd-xhci-nec.c
@@ -37,8 +37,6 @@ struct XHCINecState {
};
static const Property nec_xhci_properties[] = {
- DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO),
- DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO),
DEFINE_PROP_UINT32("intrs", XHCINecState, intrs, XHCI_MAXINTRS),
DEFINE_PROP_UINT32("slots", XHCINecState, slots, XHCI_MAXSLOTS),
};
diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c
index e110840..a069b42 100644
--- a/hw/usb/hcd-xhci-pci.c
+++ b/hw/usb/hcd-xhci-pci.c
@@ -197,6 +197,11 @@ static void xhci_instance_init(Object *obj)
qdev_alias_all_properties(DEVICE(&s->xhci), obj);
}
+static const Property xhci_pci_properties[] = {
+ DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO),
+ DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO),
+};
+
static void xhci_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
@@ -208,6 +213,7 @@ static void xhci_class_init(ObjectClass *klass, void *data)
k->realize = usb_xhci_pci_realize;
k->exit = usb_xhci_pci_exit;
k->class_id = PCI_CLASS_SERIAL_USB;
+ device_class_set_props(dc, xhci_pci_properties);
}
static const TypeInfo xhci_pci_info = {
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 3719c0f..7dc0994 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -2810,9 +2810,15 @@ static uint64_t xhci_port_read(void *ptr, hwaddr reg, unsigned size)
case 0x08: /* PORTLI */
ret = 0;
break;
- case 0x0c: /* reserved */
+ case 0x0c: /* PORTHLPMC */
+ ret = 0;
+ qemu_log_mask(LOG_UNIMP, "%s: read from port register PORTHLPMC",
+ __func__);
+ break;
default:
- trace_usb_xhci_unimplemented("port read", reg);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: read from port offset 0x%" HWADDR_PRIx,
+ __func__, reg);
ret = 0;
}
@@ -2881,9 +2887,22 @@ static void xhci_port_write(void *ptr, hwaddr reg,
}
break;
case 0x04: /* PORTPMSC */
+ case 0x0c: /* PORTHLPMC */
+ qemu_log_mask(LOG_UNIMP,
+ "%s: write 0x%" PRIx64
+ " (%u bytes) to port register at offset 0x%" HWADDR_PRIx,
+ __func__, val, size, reg);
+ break;
case 0x08: /* PORTLI */
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Write to read-only PORTLI register",
+ __func__);
+ break;
default:
- trace_usb_xhci_unimplemented("port write", reg);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: write 0x%" PRIx64 " (%u bytes) to unknown port "
+ "register at offset 0x%" HWADDR_PRIx,
+ __func__, val, size, reg);
+ break;
}
}
diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c
index a10c44c..9c21fa8 100644
--- a/hw/xen/xen-pvh-common.c
+++ b/hw/xen/xen-pvh-common.c
@@ -169,7 +169,7 @@ static inline void xenpvh_gpex_init(XenPVHMachineState *s,
*/
assert(xpc->set_pci_intx_irq);
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
+ for (i = 0; i < PCI_NUM_PINS; i++) {
qemu_irq irq = qemu_allocate_irq(xpc->set_pci_intx_irq, s, i);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c
index 98622ae..b08404f 100644
--- a/hw/xtensa/virt.c
+++ b/hw/xtensa/virt.c
@@ -93,7 +93,7 @@ static void create_pcie(MachineState *ms, CPUXtensaState *env, int irq_base,
/* Connect IRQ lines. */
extints = xtensa_get_extints(env);
- for (i = 0; i < GPEX_NUM_IRQS; i++) {
+ for (i = 0; i < PCI_NUM_PINS; i++) {
void *q = extints[irq_base + i];
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, q);
diff --git a/include/hw/misc/ivshmem-flat.h b/include/hw/misc/ivshmem-flat.h
new file mode 100644
index 0000000..97ca0dd
--- /dev/null
+++ b/include/hw/misc/ivshmem-flat.h
@@ -0,0 +1,85 @@
+/*
+ * Inter-VM Shared Memory Flat Device
+ *
+ * SPDX-FileCopyrightText: 2023 Linaro Ltd.
+ * SPDX-FileContributor: Gustavo Romero <gustavo.romero@linaro.org>
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef IVSHMEM_FLAT_H
+#define IVSHMEM_FLAT_H
+
+#include "qemu/queue.h"
+#include "qemu/event_notifier.h"
+#include "chardev/char-fe.h"
+#include "exec/memory.h"
+#include "qom/object.h"
+#include "hw/sysbus.h"
+
+#define IVSHMEM_MAX_VECTOR_NUM 64
+
+/*
+ * QEMU interface:
+ * + QOM property "chardev" is the character device id of the ivshmem server
+ * socket
+ * + QOM property "shmem-size" sets the size of the RAM region shared between
+ * the device and the ivshmem server
+ * + sysbus MMIO region 0: device I/O mapped registers
+ * + sysbus MMIO region 1: shared memory with ivshmem server
+ * + sysbus IRQ 0: single output interrupt
+ */
+
+#define TYPE_IVSHMEM_FLAT "ivshmem-flat"
+typedef struct IvshmemFTState IvshmemFTState;
+
+DECLARE_INSTANCE_CHECKER(IvshmemFTState, IVSHMEM_FLAT, TYPE_IVSHMEM_FLAT)
+
+/* Ivshmem registers. See ./docs/specs/ivshmem-spec.txt for details. */
+enum ivshmem_registers {
+ INTMASK = 0,
+ INTSTATUS = 4,
+ IVPOSITION = 8,
+ DOORBELL = 12,
+};
+
+typedef struct VectorInfo {
+ EventNotifier event_notifier;
+ uint16_t id;
+} VectorInfo;
+
+typedef struct IvshmemPeer {
+ QTAILQ_ENTRY(IvshmemPeer) next;
+ VectorInfo vector[IVSHMEM_MAX_VECTOR_NUM];
+ int vector_counter;
+ uint16_t id;
+} IvshmemPeer;
+
+struct IvshmemFTState {
+ SysBusDevice parent_obj;
+
+ uint64_t msg_buf;
+ int msg_buffered_bytes;
+
+ QTAILQ_HEAD(, IvshmemPeer) peer;
+ IvshmemPeer own;
+
+ CharBackend server_chr;
+
+ /* IRQ */
+ qemu_irq irq;
+
+ /* I/O registers */
+ MemoryRegion iomem;
+ uint32_t intmask;
+ uint32_t intstatus;
+ uint32_t ivposition;
+ uint32_t doorbell;
+
+ /* Shared memory */
+ MemoryRegion shmem;
+ int shmem_fd;
+ uint32_t shmem_size;
+};
+
+#endif /* IVSHMEM_FLAT_H */
diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
index dce8835..8447153 100644
--- a/include/hw/pci-host/gpex.h
+++ b/include/hw/pci-host/gpex.h
@@ -32,8 +32,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(GPEXHost, GPEX_HOST)
#define TYPE_GPEX_ROOT_DEVICE "gpex-root"
OBJECT_DECLARE_SIMPLE_TYPE(GPEXRootState, GPEX_ROOT_DEVICE)
-#define GPEX_NUM_IRQS 4
-
struct GPEXRootState {
/*< private >*/
PCIDevice parent_obj;
@@ -49,6 +47,7 @@ struct GPEXConfig {
PCIBus *bus;
};
+typedef struct GPEXIrq GPEXIrq;
struct GPEXHost {
/*< private >*/
PCIExpressHost parent_obj;
@@ -60,8 +59,8 @@ struct GPEXHost {
MemoryRegion io_mmio;
MemoryRegion io_ioport_window;
MemoryRegion io_mmio_window;
- qemu_irq irq[GPEX_NUM_IRQS];
- int irq_num[GPEX_NUM_IRQS];
+ GPEXIrq *irq;
+ uint8_t num_irqs;
bool allow_unmapped_accesses;
diff --git a/include/hw/usb/uhci-regs.h b/include/hw/usb/uhci-regs.h
index fd45d29..5b81714 100644
--- a/include/hw/usb/uhci-regs.h
+++ b/include/hw/usb/uhci-regs.h
@@ -1,6 +1,17 @@
#ifndef HW_USB_UHCI_REGS_H
#define HW_USB_UHCI_REGS_H
+#define UHCI_USBCMD 0
+#define UHCI_USBSTS 2
+#define UHCI_USBINTR 4
+#define UHCI_USBFRNUM 6
+#define UHCI_USBFLBASEADD 8
+#define UHCI_USBSOF 0x0c
+#define UHCI_USBPORTSC1 0x10
+#define UHCI_USBPORTSC2 0x12
+#define UHCI_USBPORTSC3 0x14
+#define UHCI_USBPORTSC4 0x16
+
#define UHCI_CMD_FGR (1 << 4)
#define UHCI_CMD_EGSM (1 << 3)
#define UHCI_CMD_GRESET (1 << 2)
diff --git a/include/qemu-main.h b/include/qemu-main.h
index 940960a..2ee83be 100644
--- a/include/qemu-main.h
+++ b/include/qemu-main.h
@@ -5,7 +5,19 @@
#ifndef QEMU_MAIN_H
#define QEMU_MAIN_H
-int qemu_default_main(void);
+/*
+ * The function to run on the main (initial) thread of the process.
+ * NULL means QEMU's main event loop.
+ * When non-NULL, QEMU's main event loop will run on a purposely created
+ * thread, after which the provided function pointer will be invoked on
+ * the initial thread.
+ * This is useful on platforms which treat the main thread as special
+ * (macOS/Darwin) and/or require all UI API calls to occur from the main
+ * thread. Those platforms can initialise it to a specific function,
+ * while UI implementations may reset it to NULL during their init if they
+ * will handle system and UI events on the main thread via QEMU's own main
+ * event loop.
+ */
extern int (*qemu_main)(void);
#endif /* QEMU_MAIN_H */
diff --git a/include/qemu/help-texts.h b/include/qemu/help-texts.h
index 353ab2a..bc8fab9 100644
--- a/include/qemu/help-texts.h
+++ b/include/qemu/help-texts.h
@@ -2,7 +2,7 @@
#define QEMU_HELP_TEXTS_H
/* Copyright string for -version arguments, About dialogs, etc */
-#define QEMU_COPYRIGHT "Copyright (c) 2003-2024 " \
+#define QEMU_COPYRIGHT "Copyright (c) 2003-2025 " \
"Fabrice Bellard and the QEMU Project developers"
/* Bug reporting information for --help arguments, About dialogs, etc */
diff --git a/meson.build b/meson.build
index f4109cd..e62251c 100644
--- a/meson.build
+++ b/meson.build
@@ -817,6 +817,8 @@ socket = []
version_res = []
coref = []
iokit = []
+pvg = not_found
+metal = []
emulator_link_args = []
midl = not_found
widl = not_found
@@ -838,6 +840,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'),
diff --git a/net/vmnet-common.m b/net/vmnet-common.m
index dba5b5b..54d900b 100644
--- a/net/vmnet-common.m
+++ b/net/vmnet-common.m
@@ -18,6 +18,7 @@
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "system/runstate.h"
+#include "net/eth.h"
#include <vmnet/vmnet.h>
#include <dispatch/dispatch.h>
@@ -147,10 +148,26 @@ static int vmnet_read_packets(VmnetState *s)
*/
static void vmnet_write_packets_to_qemu(VmnetState *s)
{
+ uint8_t *pkt;
+ size_t pktsz;
+ uint8_t min_pkt[ETH_ZLEN];
+ size_t min_pktsz;
+ ssize_t size;
+
while (s->packets_send_current_pos < s->packets_send_end_pos) {
- ssize_t size = qemu_send_packet_async(&s->nc,
- s->iov_buf[s->packets_send_current_pos].iov_base,
- s->packets_buf[s->packets_send_current_pos].vm_pkt_size,
+ pkt = s->iov_buf[s->packets_send_current_pos].iov_base;
+ pktsz = s->packets_buf[s->packets_send_current_pos].vm_pkt_size;
+
+ if (net_peer_needs_padding(&s->nc)) {
+ min_pktsz = sizeof(min_pkt);
+
+ if (eth_pad_short_frame(min_pkt, &min_pktsz, pkt, pktsz)) {
+ pkt = min_pkt;
+ pktsz = min_pktsz;
+ }
+ }
+
+ size = qemu_send_packet_async(&s->nc, pkt, pktsz,
vmnet_send_completed);
if (size == 0) {
diff --git a/system/main.c b/system/main.c
index 4923520..ecb12fd 100644
--- a/system/main.c
+++ b/system/main.c
@@ -24,26 +24,56 @@
#include "qemu/osdep.h"
#include "qemu-main.h"
+#include "qemu/main-loop.h"
#include "system/system.h"
#ifdef CONFIG_SDL
+/*
+ * SDL insists on wrapping the main() function with its own implementation on
+ * some platforms; it does so via a macro that renames our main function, so
+ * <SDL.h> must be #included here even with no SDL code called from this file.
+ */
#include <SDL.h>
#endif
-int qemu_default_main(void)
+#ifdef CONFIG_DARWIN
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+static void *qemu_default_main(void *opaque)
{
int status;
+ bql_lock();
status = qemu_main_loop();
qemu_cleanup(status);
+ bql_unlock();
- return status;
+ exit(status);
}
-int (*qemu_main)(void) = qemu_default_main;
+int (*qemu_main)(void);
+
+#ifdef CONFIG_DARWIN
+static int os_darwin_cfrunloop_main(void)
+{
+ CFRunLoopRun();
+ g_assert_not_reached();
+}
+int (*qemu_main)(void) = os_darwin_cfrunloop_main;
+#endif
int main(int argc, char **argv)
{
qemu_init(argc, argv);
- return qemu_main();
+ bql_unlock();
+ if (qemu_main) {
+ QemuThread main_loop_thread;
+ qemu_thread_create(&main_loop_thread, "qemu_main",
+ qemu_default_main, NULL, QEMU_THREAD_DETACHED);
+ return qemu_main();
+ } else {
+ qemu_default_main(NULL);
+ g_assert_not_reached();
+ }
}
diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index 24f7f8f..a5087fc 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -224,6 +224,7 @@ tests_x86_64_system_quick = [
'pc_cpu_hotplug_props',
'virtio_version',
'x86_cpu_model_versions',
+ 'vnc',
]
tests_x86_64_system_thorough = [
diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py
new file mode 100644
index 0000000..cc39939
--- /dev/null
+++ b/tests/functional/qemu_test/ports.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+#
+# Simple functional tests for VNC functionality
+#
+# Copyright 2018, 2024 Red Hat, Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import fcntl
+import os
+import socket
+import sys
+import tempfile
+
+from .config import BUILD_DIR
+from typing import List
+
+class Ports():
+
+ PORTS_ADDR = '127.0.0.1'
+ PORTS_RANGE_SIZE = 1024
+ PORTS_START = 49152 + ((os.getpid() * PORTS_RANGE_SIZE) % 16384)
+ PORTS_END = PORTS_START + PORTS_RANGE_SIZE
+
+ def __enter__(self):
+ lock_file = os.path.join(BUILD_DIR, "tests", "functional", "port_lock")
+ self.lock_fh = os.open(lock_file, os.O_CREAT)
+ fcntl.flock(self.lock_fh, fcntl.LOCK_EX)
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ fcntl.flock(self.lock_fh, fcntl.LOCK_UN)
+ os.close(self.lock_fh)
+
+ def check_bind(self, port: int) -> bool:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ try:
+ sock.bind((self.PORTS_ADDR, port))
+ except OSError:
+ return False
+
+ return True
+
+ def find_free_ports(self, count: int) -> List[int]:
+ result = []
+ for port in range(self.PORTS_START, self.PORTS_END):
+ if self.check_bind(port):
+ result.append(port)
+ if len(result) >= count:
+ break
+ assert len(result) == count
+ return result
+
+ def find_free_port(self) -> int:
+ return self.find_free_ports(1)[0]
diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/test_arm_quanta_gsj.py
index 7aa5209..7b82e21 100755
--- a/tests/functional/test_arm_quanta_gsj.py
+++ b/tests/functional/test_arm_quanta_gsj.py
@@ -35,7 +35,7 @@ class EmcraftSf2Machine(LinuxKernelTest):
@skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout')
def test_arm_quanta_gsj(self):
self.set_machine('quanta-gsj')
- image_path = self.uncompress(ASSET_IMAGE, 'obmc.mtd', format='gz')
+ image_path = self.uncompress(self.ASSET_IMAGE, format='gz')
self.vm.set_console()
drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0'
diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py
index 037dfdf..62f996a 100755
--- a/tests/functional/test_ppc64_hv.py
+++ b/tests/functional/test_ppc64_hv.py
@@ -12,6 +12,7 @@
from qemu_test import QemuSystemTest, Asset
from qemu_test import wait_for_console_pattern, exec_command
from qemu_test import skipIfMissingCommands, skipBigDataTest
+from qemu_test import exec_command_and_wait_for_pattern
import os
import time
import subprocess
@@ -34,9 +35,9 @@ class HypervisorTest(QemuSystemTest):
good_message = 'VFS: Cannot open root device'
ASSET_ISO = Asset(
- ('https://dl-cdn.alpinelinux.org/alpine/v3.18/'
- 'releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso'),
- 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff')
+ ('https://dl-cdn.alpinelinux.org/alpine/v3.21/'
+ 'releases/ppc64le/alpine-standard-3.21.0-ppc64le.iso'),
+ '7651ab4e3027604535c0b36e86c901b4695bf8fe97b908f5b48590f6baae8f30')
def extract_from_iso(self, iso, path):
"""
@@ -73,31 +74,29 @@ class HypervisorTest(QemuSystemTest):
"id=drive0,read-only=true")
self.vm.launch()
- wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18')
- exec_command(self, 'root')
+ ps1='localhost:~#'
wait_for_console_pattern(self, 'localhost login:')
- wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.')
+ exec_command_and_wait_for_pattern(self, 'root', ps1)
# If the time is wrong, SSL certificates can fail.
- exec_command(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"'))
- exec_command(self, 'setup-alpine -qe')
- wait_for_console_pattern(self, 'Updating repository indexes... done.')
+ exec_command_and_wait_for_pattern(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"'), ps1)
+ ps1='alpine:~#'
+ exec_command_and_wait_for_pattern(self, 'setup-alpine -qe', ps1)
+ exec_command_and_wait_for_pattern(self, 'setup-apkrepos -c1', ps1)
+ exec_command_and_wait_for_pattern(self, 'apk update', ps1)
+ # Could upgrade here but it usually should not be necessary
+ # exec_command_and_wait_for_pattern(self, 'apk upgrade --available', ps1)
def do_stop_alpine(self):
- exec_command(self, 'poweroff')
+ exec_command(self, 'echo "TEST ME"')
wait_for_console_pattern(self, 'alpine:~#')
+ exec_command(self, 'poweroff')
+ wait_for_console_pattern(self, 'reboot: Power down')
self.vm.wait()
def do_setup_kvm(self):
- exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/main > /etc/apk/repositories')
- wait_for_console_pattern(self, 'alpine:~#')
- exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /etc/apk/repositories')
- wait_for_console_pattern(self, 'alpine:~#')
- exec_command(self, 'apk update')
- wait_for_console_pattern(self, 'alpine:~#')
- exec_command(self, 'apk add qemu-system-ppc64')
- wait_for_console_pattern(self, 'alpine:~#')
- exec_command(self, 'modprobe kvm-hv')
- wait_for_console_pattern(self, 'alpine:~#')
+ ps1='alpine:~#'
+ exec_command_and_wait_for_pattern(self, 'apk add qemu-system-ppc64', ps1)
+ exec_command_and_wait_for_pattern(self, 'modprobe kvm-hv', ps1)
# This uses the host's block device as the source file for guest block
# device for install media. This is a bit hacky but allows reuse of the
@@ -115,16 +114,13 @@ class HypervisorTest(QemuSystemTest):
'-initrd /media/nvme0n1/boot/initramfs-lts '
'-kernel /media/nvme0n1/boot/vmlinuz-lts '
'-append \'usbcore.nousb ' + append + '\'')
- # Alpine 3.18 kernel seems to crash in XHCI USB driver.
- wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18')
- exec_command(self, 'root')
+ # Alpine 3.21 kernel seems to crash in XHCI USB driver.
+ ps1='localhost:~#'
wait_for_console_pattern(self, 'localhost login:')
- wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.')
- exec_command(self, 'poweroff >& /dev/null')
- wait_for_console_pattern(self, 'localhost:~#')
+ exec_command_and_wait_for_pattern(self, 'root', ps1)
+ exec_command(self, 'poweroff')
wait_for_console_pattern(self, 'reboot: Power down')
- time.sleep(1)
- exec_command(self, '')
+ # Now wait for the host's prompt to come back
wait_for_console_pattern(self, 'alpine:~#')
def test_hv_pseries(self):
diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/test_rx_gdbsim.py
index 20623aa..4924579 100755
--- a/tests/functional/test_rx_gdbsim.py
+++ b/tests/functional/test_rx_gdbsim.py
@@ -21,13 +21,16 @@ class RxGdbSimMachine(QemuSystemTest):
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
ASSET_UBOOT = Asset(
- 'https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz',
- '7146567d669e91dbac166384b29aeba1715beb844c8551e904b86831bfd9d046')
+ ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/'
+ 'u-boot.bin'),
+ 'dd7dd4220cccf7aeb32227b26233bf39600db05c3f8e26005bcc2bf6c927207d')
ASSET_DTB = Asset(
- 'https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb',
+ ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/'
+ 'rx-gdbsim.dtb'),
'aa278d9c1907a4501741d7ee57e7f65c02dd1b3e0323b33c6d4247f1b32cf29a')
ASSET_KERNEL = Asset(
- 'http://acc.dl.osdn.jp/users/23/23845/zImage',
+ ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/'
+ 'zImage'),
'baa43205e74a7220ed8482188c5e9ce497226712abb7f4e7e4f825ce19ff9656')
def test_uboot(self):
@@ -36,7 +39,7 @@ class RxGdbSimMachine(QemuSystemTest):
"""
self.set_machine('gdbsim-r5f562n8')
- uboot_path = self.uncompress(self.ASSET_UBOOT)
+ uboot_path = self.ASSET_UBOOT.fetch()
self.vm.set_console()
self.vm.add_args('-bios', uboot_path,
diff --git a/tests/avocado/vnc.py b/tests/functional/test_vnc.py
index 862c899..1916be0 100644..100755
--- a/tests/avocado/vnc.py
+++ b/tests/functional/test_vnc.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+#
# Simple functional tests for VNC functionality
#
# Copyright (c) 2018 Red Hat, Inc.
@@ -11,23 +13,10 @@
import socket
from typing import List
-from avocado_qemu import QemuSystemTest
-
+from qemu_test import QemuSystemTest
+from qemu_test.ports import Ports
VNC_ADDR = '127.0.0.1'
-VNC_PORT_START = 32768
-VNC_PORT_END = VNC_PORT_START + 1024
-
-
-def check_bind(port: int) -> bool:
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
- try:
- sock.bind((VNC_ADDR, port))
- except OSError:
- return False
-
- return True
-
def check_connect(port: int) -> bool:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
@@ -38,27 +27,7 @@ def check_connect(port: int) -> bool:
return True
-
-def find_free_ports(count: int) -> List[int]:
- result = []
- for port in range(VNC_PORT_START, VNC_PORT_END):
- if check_bind(port):
- result.append(port)
- if len(result) >= count:
- break
- assert len(result) == count
- return result
-
-
class Vnc(QemuSystemTest):
- """
- :avocado: tags=vnc,quick
- :avocado: tags=machine:none
- """
- def test_no_vnc(self):
- self.vm.add_args('-nodefaults', '-S')
- self.vm.launch()
- self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled'])
def test_no_vnc_change_password(self):
self.vm.add_args('-nodefaults', '-S')
@@ -73,7 +42,7 @@ class Vnc(QemuSystemTest):
'Could not set password')
def test_change_password_requires_a_password(self):
- self.vm.add_args('-nodefaults', '-S', '-vnc', ':0')
+ self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999')
self.vm.launch()
self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled'])
set_password_response = self.vm.qmp('change-vnc-password',
@@ -85,14 +54,13 @@ class Vnc(QemuSystemTest):
'Could not set password')
def test_change_password(self):
- self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password=on')
+ self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999,password=on')
self.vm.launch()
self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled'])
self.vm.cmd('change-vnc-password',
password='new_password')
- def test_change_listen(self):
- a, b, c = find_free_ports(3)
+ def do_test_change_listen(self, a, b, c):
self.assertFalse(check_connect(a))
self.assertFalse(check_connect(b))
self.assertFalse(check_connect(c))
@@ -113,3 +81,12 @@ class Vnc(QemuSystemTest):
self.assertFalse(check_connect(a))
self.assertTrue(check_connect(b))
self.assertTrue(check_connect(c))
+
+ def test_change_listen(self):
+ with Ports() as ports:
+ a, b, c = ports.find_free_ports(3)
+ self.do_test_change_listen(a, b, c)
+
+
+if __name__ == '__main__':
+ QemuSystemTest.main()
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index 6035ec2..0a333ec 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -292,6 +292,7 @@ static void dump_aml_files(test_data *data, bool rebuild)
g_free(aml_file);
}
+ free_test_data(&exp_data);
}
static bool create_tmp_asl(AcpiSdtTable *sdt)
diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c
index 8274000..ca248a5 100644
--- a/tests/qtest/fuzz/fuzz.c
+++ b/tests/qtest/fuzz/fuzz.c
@@ -41,6 +41,7 @@ static FuzzTargetList *fuzz_target_list;
static FuzzTarget *fuzz_target;
static QTestState *fuzz_qts;
+int (*qemu_main)(void);
void flush_events(QTestState *s)
diff --git a/tests/qtest/fw_cfg-test.c b/tests/qtest/fw_cfg-test.c
index 5dc807b..e48b34a 100644
--- a/tests/qtest/fw_cfg-test.c
+++ b/tests/qtest/fw_cfg-test.c
@@ -243,12 +243,6 @@ int main(int argc, char **argv)
qtest_add_func("fw_cfg/ram_size", test_fw_cfg_ram_size);
qtest_add_func("fw_cfg/nographic", test_fw_cfg_nographic);
qtest_add_func("fw_cfg/nb_cpus", test_fw_cfg_nb_cpus);
-#if 0
- qtest_add_func("fw_cfg/machine_id", test_fw_cfg_machine_id);
- qtest_add_func("fw_cfg/kernel", test_fw_cfg_kernel);
- qtest_add_func("fw_cfg/initrd", test_fw_cfg_initrd);
- qtest_add_func("fw_cfg/boot_device", test_fw_cfg_boot_device);
-#endif
qtest_add_func("fw_cfg/max_cpus", test_fw_cfg_max_cpus);
qtest_add_func("fw_cfg/numa", test_fw_cfg_numa);
qtest_add_func("fw_cfg/boot_menu", test_fw_cfg_boot_menu);
diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c
index 6de87bc..d78f1f1 100644
--- a/tests/qtest/migration/compression-tests.c
+++ b/tests/qtest/migration/compression-tests.c
@@ -88,59 +88,6 @@ migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from,
return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk");
}
-static void *
-migrate_hook_start_xbzrle(QTestState *from,
- QTestState *to)
-{
- migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432);
-
- migrate_set_capability(from, "xbzrle", true);
- migrate_set_capability(to, "xbzrle", true);
-
- return NULL;
-}
-
-static void test_precopy_unix_xbzrle(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = uri,
- .start_hook = migrate_hook_start_xbzrle,
- .iterations = 2,
- /*
- * XBZRLE needs pages to be modified when doing the 2nd+ round
- * iteration to have real data pushed to the stream.
- */
- .live = true,
- };
-
- test_precopy_common(&args);
-}
-
-static void *
-migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from,
- QTestState *to)
-{
- /*
- * Overloading this test to also check that set_parameter does not error.
- * This is also done in the tests for the other compression methods.
- */
- migrate_set_parameter_int(from, "multifd-zlib-level", 2);
- migrate_set_parameter_int(to, "multifd-zlib-level", 2);
-
- return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib");
-}
-
-static void test_multifd_tcp_zlib(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib,
- };
- test_precopy_common(&args);
-}
-
static void test_multifd_tcp_uadk(void)
{
MigrateCommon args = {
@@ -151,7 +98,6 @@ static void test_multifd_tcp_uadk(void)
}
#endif /* CONFIG_UADK */
-
static void *
migrate_hook_start_xbzrle(QTestState *from,
QTestState *to)
diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
index a902936..47ce078 100644
--- a/tests/qtest/migration/framework.c
+++ b/tests/qtest/migration/framework.c
@@ -197,8 +197,6 @@ static void cleanup(const char *filename)
int migrate_start(QTestState **from, QTestState **to, const char *uri,
MigrateStart *args)
{
- g_autofree gchar *arch_source = NULL;
- g_autofree gchar *arch_target = NULL;
/* options for source and target */
g_autofree gchar *arch_opts = NULL;
g_autofree gchar *cmd_source = NULL;
@@ -307,12 +305,11 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
"-name source,debug-threads=on "
"-m %s "
"-serial file:%s/src_serial "
- "%s %s %s %s %s",
+ "%s %s %s %s",
kvm_opts ? kvm_opts : "",
machine, machine_opts,
memory_size, tmpfs,
arch_opts ? arch_opts : "",
- arch_source ? arch_source : "",
shmem_opts ? shmem_opts : "",
args->opts_source ? args->opts_source : "",
ignore_stderr);
@@ -329,12 +326,11 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri,
"-m %s "
"-serial file:%s/dest_serial "
"-incoming %s "
- "%s %s %s %s %s",
+ "%s %s %s %s",
kvm_opts ? kvm_opts : "",
machine, machine_opts,
memory_size, tmpfs, uri,
arch_opts ? arch_opts : "",
- arch_target ? arch_target : "",
shmem_opts ? shmem_opts : "",
args->opts_target ? args->opts_target : "",
ignore_stderr);
diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c
index 525bf1e..526bed7 100644
--- a/tests/qtest/migration/migration-util.c
+++ b/tests/qtest/migration/migration-util.c
@@ -22,6 +22,11 @@
#include "migration/bootfile.h"
#include "migration/migration-util.h"
+#if defined(__linux__)
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#endif
+
/* for uffd_version_check() */
#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
#include <sys/eventfd.h>
@@ -31,7 +36,6 @@
/* For dirty ring test; so far only x86_64 is supported */
#if defined(__linux__) && defined(HOST_X86_64)
#include "linux/kvm.h"
-#include <sys/ioctl.h>
#endif
@@ -284,6 +288,7 @@ bool probe_o_direct_support(const char *tmpfs)
buf = qemu_try_memalign(len, len);
g_assert(buf);
+ memset(buf, 0, len);
ret = pwrite(fd, buf, len, offset);
unlink(filename);
diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c
index b709d90..23599b2 100644
--- a/tests/qtest/migration/precopy-tests.c
+++ b/tests/qtest/migration/precopy-tests.c
@@ -877,6 +877,11 @@ static void test_dirty_limit(void)
migrate_cancel(from);
wait_for_migration_status(from, "cancelled", NULL);
+ /* destination always fails after cancel */
+ migration_event_wait(to, "failed");
+ qtest_set_expected_status(to, EXIT_FAILURE);
+ qtest_quit(to);
+
/* Check if dirty limit throttle switched off, set timeout 1ms */
do {
throttle_us_per_full =
diff --git a/tests/qtest/virtio-iommu-test.c b/tests/qtest/virtio-iommu-test.c
index afb2259..98ffa27 100644
--- a/tests/qtest/virtio-iommu-test.c
+++ b/tests/qtest/virtio-iommu-test.c
@@ -105,7 +105,7 @@ static int send_map(QTestState *qts, QVirtioIOMMU *v_iommu,
QVirtQueue *vq = v_iommu->vq;
uint64_t ro_addr, wr_addr;
uint32_t free_head;
- struct virtio_iommu_req_map req;
+ struct virtio_iommu_req_map req = {};
size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
size_t wr_size = sizeof(struct virtio_iommu_req_tail);
struct virtio_iommu_req_tail buffer;
@@ -147,7 +147,7 @@ static int send_unmap(QTestState *qts, QVirtioIOMMU *v_iommu,
QVirtQueue *vq = v_iommu->vq;
uint64_t ro_addr, wr_addr;
uint32_t free_head;
- struct virtio_iommu_req_unmap req;
+ struct virtio_iommu_req_unmap req = {};
size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
size_t wr_size = sizeof(struct virtio_iommu_req_tail);
struct virtio_iommu_req_tail buffer;
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 3a88535..23b7a73 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -73,6 +73,8 @@ typedef struct {
int height;
} QEMUScreen;
+@class QemuCocoaPasteboardTypeOwner;
+
static void cocoa_update(DisplayChangeListener *dcl,
int x, int y, int w, int h);
@@ -107,6 +109,7 @@ static bool allow_events;
static NSInteger cbchangecount = -1;
static QemuClipboardInfo *cbinfo;
static QemuEvent cbevent;
+static QemuCocoaPasteboardTypeOwner *cbowner;
// Utility functions to run specified code block with the BQL held
typedef void (^CodeBlock)(void);
@@ -1326,8 +1329,10 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
{
COCOA_DEBUG("QemuCocoaAppController: dealloc\n");
- if (cocoaView)
- [cocoaView release];
+ [cocoaView release];
+ [cbowner release];
+ cbowner = nil;
+
[super dealloc];
}
@@ -1943,8 +1948,6 @@ static Notifier mouse_mode_change_notifier = {
@end
-static QemuCocoaPasteboardTypeOwner *cbowner;
-
static void cocoa_clipboard_notify(Notifier *notifier, void *data);
static void cocoa_clipboard_request(QemuClipboardInfo *info,
QemuClipboardType type);
@@ -2007,43 +2010,8 @@ static void cocoa_clipboard_request(QemuClipboardInfo *info,
}
}
-/*
- * The startup process for the OSX/Cocoa UI is complicated, because
- * OSX insists that the UI runs on the initial main thread, and so we
- * need to start a second thread which runs the qemu_default_main():
- * in main():
- * in cocoa_display_init():
- * assign cocoa_main to qemu_main
- * create application, menus, etc
- * in cocoa_main():
- * create qemu-main thread
- * enter OSX run loop
- */
-
-static void *call_qemu_main(void *opaque)
-{
- int status;
-
- COCOA_DEBUG("Second thread: calling qemu_default_main()\n");
- bql_lock();
- status = qemu_default_main();
- bql_unlock();
- COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n");
- [cbowner release];
- exit(status);
-}
-
static int cocoa_main(void)
{
- QemuThread thread;
-
- COCOA_DEBUG("Entered %s()\n", __func__);
-
- bql_unlock();
- qemu_thread_create(&thread, "qemu_main", call_qemu_main,
- NULL, QEMU_THREAD_DETACHED);
-
- // Start the main event loop
COCOA_DEBUG("Main thread: entering OSX run loop\n");
[NSApp run];
COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n");
@@ -2125,8 +2093,6 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
- qemu_main = cocoa_main;
-
// Pull this console process up to being a fully-fledged graphical
// app with a menubar and Dock icon
ProcessSerialNumber psn = { 0, kCurrentProcess };
@@ -2190,6 +2156,12 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
qemu_clipboard_peer_register(&cbpeer);
[pool release];
+
+ /*
+ * The Cocoa UI will run the NSApplication runloop on the main thread
+ * rather than the default Core Foundation one.
+ */
+ qemu_main = cocoa_main;
}
static QemuDisplay qemu_display_cocoa = {
diff --git a/ui/gtk.c b/ui/gtk.c
index 0d38c07..c023743 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -38,6 +38,7 @@
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
+#include "qemu-main.h"
#include "ui/console.h"
#include "ui/gtk.h"
@@ -2485,6 +2486,9 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
#ifdef CONFIG_GTK_CLIPBOARD
gd_clipboard_init(s);
#endif /* CONFIG_GTK_CLIPBOARD */
+
+ /* GTK's event polling must happen on the main thread. */
+ qemu_main = NULL;
}
static void early_gtk_display_init(DisplayOptions *opts)
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 1fb72f6..445eb1d 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -34,6 +34,7 @@
#include "system/system.h"
#include "ui/win32-kbd-hook.h"
#include "qemu/log.h"
+#include "qemu-main.h"
static int sdl2_num_outputs;
static struct sdl2_console *sdl2_console;
@@ -965,6 +966,9 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
}
atexit(sdl_cleanup);
+
+ /* SDL's event polling (in dpy_refresh) must happen on the main thread. */
+ qemu_main = NULL;
}
static QemuDisplay qemu_display_sdl2 = {