:p
atchew
Login
Hopefully last target-arm pull before the softfreeze. Certainly the last large one. -- PMM The following changes since commit 314ff2e07ddc6163554077d68aed5d76a50b8e3d: Merge tag 'pull-request-2026-03-05' of https://gitlab.com/thuth/qemu into staging (2026-03-05 16:58:20 +0000) are available in the Git repository at: https://gitlab.com/pm215/qemu.git tags/pull-target-arm-20260306 for you to fetch changes up to 9c3ae30d411a94ddd4a0e09d26ba095748b75be3: hvf: enable nested virtualisation support (2026-03-06 13:33:58 +0000) ---------------------------------------------------------------- target-arm queue: * Remove deprecated 'highbank' and 'midway' machines * hw/arm: Add missing dependencies for STM32F405 SoC * hw/arm/smmuv3-accel: Read and propagate host vIOMMU events * Minor MAINTAINERS updates * target/arm: Improve logging of migration errors due to system register mismatches between source and destination * hw/arm/aspeed_gpio: Don't leak string in aspeed_gpio_init() * tests/qtest/iommu-smmuv3-test: Free QPCIDevice * chardev: Fix various sanitizer detected leaks * tests/qtest/test-x86-cpuid-compat: Free allocated memory * tests/qtest/qos-test: Plug a couple of leaks * hw/arm/smmuv3: Fix various minor bugs * hvf/arm: expose FEAT_SME2 to guest if available * hvf/arm: Add hvf vGIC interrupt controller support * hvf: enable nested virtualisation support ---------------------------------------------------------------- CLEMENT MATHIEU--DRIF (1): MAINTAINERS: Update Clement Mathieu--Drif's email address Chisheng Chen (1): hw/arm: Add missing dependencies for STM32F405 SoC Eric Auger (7): vmstate: Introduce VMSTATE_VARRAY_INT32_ALLOC target/arm/machine: Use VMSTATE_VARRAY_INT32_ALLOC for cpreg arrays target/arm/kvm: Export kvm_print_register_name() target/arm/kvm: Tweak print_register_name() for arm64 system register target/arm/machine: Trace cpreg names which do not match on migration target/arm/machine: Trace all register mismatches target/arm/machine: Fix detection of unknown incoming cpregs Fabiano Rosas (5): chardev: Fix QIOChannel refcount chardev: Don't attempt to unregister yank function more than once chardev: Consolidate yank registration tests/qtest/test-x86-cpuid-compat: Free allocated memory tests/qtest/qos-test: Plug a couple of leaks Magnus Kulke (1): MAINTAINERS: fix magnuskulke email-address Manos Pitsidianakis (2): hvf/arm: handle FEAT_SME2 migration hvf/arm: expose FEAT_SME2 to guest if available Mohamed Mediouni (15): hw/arm: virt: remove hvf_arm.h include hvf: hvf-all: stop including hvf_arm.h hw/intc: Add hvf vGIC interrupt controller support hw/intc: arm_gicv3_hvf: save/restore Apple GIC state accel, hw/arm, include/system/hvf: infrastructure changes for HVF vGIC target/arm: hvf: instantiate GIC early hw/arm, target/arm: nested virtualisation on HVF hvf: only call hvf_sync_vtimer() when running without the platform vGIC hvf: gate ARM_FEATURE_PMU register emulation behind not being at EL2 hvf: arm: allow exposing minimal PMU when running with nested virt on target/arm: hvf: add asserts for code paths not leveraged when using the vGIC hvf: sync registers used at EL2 target/arm: hvf: pass through CNTHCTL_EL2 and MDCCINT_EL1 hvf: arm: disable SME when nested virt is active hvf: enable nested virtualisation support Nicolin Chen (2): backends/iommufd: Introduce iommufd_backend_alloc_veventq hw/arm/smmuv3-accel: Allocate vEVENTQ for accelerated SMMUv3 devices Paul Durrant (1): MAINTAINERS: remove myself as a Xen maintainer Peter Maydell (6): system/qtest: Support comments in input commands hw/net/smc91c111: Don't allow negative-length packets scripts: Move lsan_suppressions.txt out of oss-fuzz subdir scripts/lsan_suppressions.txt: Add more leaks hw/arm/aspeed_gpio: Don't leak string in aspeed_gpio_init() tests/qtest/iommu-smmuv3-test: Free QPCIDevice Shameer Kolothum (3): hw/arm/smmuv3-accel: Add viommu free helper hw/arm/smmuv3: Introduce a helper function for event propagation hw/arm/smmuv3-accel: Read and propagate host vIOMMU events Tao Tang (3): hw/arm/smmuv3-common: Fix incorrect reserved mask for SMMU CR0 register hw/arm/smmuv3: Correct SMMUEN field name in CR0 hw/arm/smmuv3: Fix CFGI_CD handling when stage-1 is unsupported Thomas Huth (2): hw/arm: Remove the deprecated "highbank" and "midway" machines hw/net: Remove the xgmac device .gitlab-ci.d/buildtest.yml | 2 +- MAINTAINERS | 16 +- accel/hvf/hvf-all.c | 52 +- accel/stubs/hvf-stub.c | 2 + backends/iommufd.c | 31 ++ backends/trace-events | 1 + chardev/char-io.c | 5 + chardev/char-socket.c | 34 +- configs/devices/arm-softmmu/default.mak | 1 - docs/about/deprecated.rst | 7 - docs/about/removed-features.rst | 7 + docs/system/arm/highbank.rst | 19 - docs/system/target-arm.rst | 1 - hw/arm/Kconfig | 21 +- hw/arm/highbank.c | 404 ---------------- hw/arm/meson.build | 1 - hw/arm/smmuv3-accel.c | 146 +++++- hw/arm/smmuv3-accel.h | 6 + hw/arm/smmuv3-internal.h | 6 +- hw/arm/smmuv3.c | 35 +- hw/arm/trace-events | 2 +- hw/arm/virt.c | 38 +- hw/gpio/aspeed_gpio.c | 2 +- hw/intc/arm_gicv3_common.c | 4 + hw/intc/arm_gicv3_hvf.c | 822 ++++++++++++++++++++++++++++++++ hw/intc/arm_gicv3_hvf_stub.c | 25 + hw/intc/meson.build | 2 + hw/net/Kconfig | 3 - hw/net/meson.build | 1 - hw/net/smc91c111.c | 16 +- hw/net/xgmac.c | 443 ----------------- include/hw/arm/smmuv3-common.h | 4 +- include/hw/intc/arm_gicv3_common.h | 4 + include/migration/vmstate.h | 10 + include/system/hvf.h | 8 + include/system/iommufd.h | 14 + scripts/coverity-scan/COMPONENTS.md | 2 +- scripts/lsan_suppressions.txt | 26 + scripts/oss-fuzz/lsan_suppressions.txt | 5 - system/qtest.c | 5 +- system/vl.c | 2 + target/arm/helper.c | 5 - target/arm/hvf/hvf.c | 466 +++++++++++++++++- target/arm/hvf/hvf_sme_stubs.h | 172 +++++++ target/arm/hvf/sysreg.c.inc | 52 ++ target/arm/hvf_arm.h | 46 ++ target/arm/kvm-stub.c | 5 + target/arm/kvm.c | 9 +- target/arm/kvm_arm.h | 9 + target/arm/machine.c | 117 ++++- target/arm/trace-events | 3 + target/arm/whpx/whpx-all.c | 7 - tests/docker/test-fuzz | 2 +- tests/qtest/iommu-smmuv3-test.c | 1 + tests/qtest/qos-test.c | 36 +- tests/qtest/test-x86-cpuid-compat.c | 4 + 56 files changed, 2128 insertions(+), 1041 deletions(-) delete mode 100644 docs/system/arm/highbank.rst delete mode 100644 hw/arm/highbank.c create mode 100644 hw/intc/arm_gicv3_hvf.c create mode 100644 hw/intc/arm_gicv3_hvf_stub.c delete mode 100644 hw/net/xgmac.c create mode 100644 scripts/lsan_suppressions.txt delete mode 100644 scripts/oss-fuzz/lsan_suppressions.txt create mode 100644 target/arm/hvf/hvf_sme_stubs.h
From: Thomas Huth <thuth@redhat.com> These machines have been marked as deprecated two releases ago, and so far nobody complained that they are still useful, so it's time to remove these now. Signed-off-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260226090704.27699-1-thuth@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 9 - configs/devices/arm-softmmu/default.mak | 1 - docs/about/deprecated.rst | 7 - docs/about/removed-features.rst | 7 + docs/system/arm/highbank.rst | 19 -- docs/system/target-arm.rst | 1 - hw/arm/Kconfig | 16 - hw/arm/highbank.c | 404 ------------------------ hw/arm/meson.build | 1 - 9 files changed, 7 insertions(+), 458 deletions(-) delete mode 100644 docs/system/arm/highbank.rst delete mode 100644 hw/arm/highbank.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: include/hw/*/exynos* F: docs/system/arm/exynos.rst F: tests/functional/arm/test_smdkc210.py -Calxeda Highbank -M: Rob Herring <robh@kernel.org> -M: Peter Maydell <peter.maydell@linaro.org> -L: qemu-arm@nongnu.org -S: Odd Fixes -F: hw/arm/highbank.c -F: hw/net/xgmac.c -F: docs/system/arm/highbank.rst - Canon DIGIC M: Antony Pavlov <antonynpavlov@gmail.com> M: Peter Maydell <peter.maydell@linaro.org> diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak index XXXXXXX..XXXXXXX 100644 --- a/configs/devices/arm-softmmu/default.mak +++ b/configs/devices/arm-softmmu/default.mak @@ -XXX,XX +XXX,XX @@ # keep out of the build. # CONFIG_CUBIEBOARD=n # CONFIG_EXYNOS4=n -# CONFIG_HIGHBANK=n # CONFIG_INTEGRATOR=n # CONFIG_FSL_IMX31=n # CONFIG_MUSICPAL=n diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -XXX,XX +XXX,XX @@ they want to use and avoids confusion. Existing users of the ``spike`` machine must ensure that they're setting the ``spike`` machine in the command line (``-M spike``). -Arm ``highbank`` and ``midway`` machines (since 10.1) -''''''''''''''''''''''''''''''''''''''''''''''''''''' - -There are no known users left for these machines (if you still use it, -please write a mail to the qemu-devel mailing list). If you just want to -boot a Cortex-A15 or Cortex-A9 Linux, use the ``virt`` machine instead. - Backend options --------------- diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -XXX,XX +XXX,XX @@ and serves as the initial engineering sample rather than a production version. A newer revision, A1, is now supported, and the ``ast2700a1-evb`` should replace the older A0 version. +Arm ``highbank`` and ``midway`` machines (removed in 11.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +There were no known users left for these machines. If you just want to +boot a Cortex-A15 or Cortex-A9 Linux, use the ``virt`` machine instead. + + linux-user mode CPUs -------------------- diff --git a/docs/system/arm/highbank.rst b/docs/system/arm/highbank.rst deleted file mode 100644 index XXXXXXX..XXXXXXX --- a/docs/system/arm/highbank.rst +++ /dev/null @@ -XXX,XX +XXX,XX @@ -Calxeda Highbank and Midway (``highbank``, ``midway``) -====================================================== - -``highbank`` is a model of the Calxeda Highbank (ECX-1000) system, -which has four Cortex-A9 cores. - -``midway`` is a model of the Calxeda Midway (ECX-2000) system, -which has four Cortex-A15 cores. - -Emulated devices: - -- L2x0 cache controller -- SP804 dual timer -- PL011 UART -- PL061 GPIOs -- PL031 RTC -- PL022 synchronous serial port controller -- AHCI -- XGMAC ethernet controllers diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -XXX,XX +XXX,XX @@ Board-specific documentation arm/bananapi_m2u.rst arm/b-l475e-iot01a.rst arm/sabrelite - arm/highbank arm/digic arm/cubieboard arm/emcraft-sf2 diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config EXYNOS4 select USB_EHCI_SYSBUS select OR_IRQ -config HIGHBANK - bool - default y - depends on TCG && ARM - select A9MPCORE - select A15MPCORE - select AHCI_SYSBUS - select ARM_TIMER # sp804 - select ARM_V7M - select PL011 # UART - select PL022 # SPI - select PL031 # RTC - select PL061 # GPIO - select PL310 # cache controller - select XGMAC # ethernet - config INTEGRATOR bool default y diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c deleted file mode 100644 index XXXXXXX..XXXXXXX --- a/hw/arm/highbank.c +++ /dev/null @@ -XXX,XX +XXX,XX @@ -/* - * Calxeda Highbank SoC emulation - * - * Copyright (c) 2010-2012 Calxeda - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2 or later, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include "qemu/osdep.h" -#include "qemu/datadir.h" -#include "qapi/error.h" -#include "hw/core/sysbus.h" -#include "migration/vmstate.h" -#include "hw/arm/boot.h" -#include "hw/arm/machines-qom.h" -#include "hw/core/loader.h" -#include "net/net.h" -#include "system/runstate.h" -#include "system/system.h" -#include "hw/core/boards.h" -#include "qemu/error-report.h" -#include "hw/char/pl011.h" -#include "hw/ide/ahci-sysbus.h" -#include "hw/cpu/a9mpcore.h" -#include "hw/cpu/a15mpcore.h" -#include "qemu/log.h" -#include "qom/object.h" -#include "cpu.h" -#include "target/arm/cpu-qom.h" - -#define SMP_BOOT_ADDR 0x100 -#define SMP_BOOT_REG 0x40 -#define MPCORE_PERIPHBASE 0xfff10000 - -#define MVBAR_ADDR 0x200 -#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t)) - -#define GIC_EXT_IRQS 128 /* EnergyCore ECX-1000 & ECX-2000 */ - -/* Board init. */ - -#define NUM_REGS 0x200 -static void hb_regs_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - uint32_t *regs = opaque; - - if (offset == 0xf00) { - if (value == 1 || value == 2) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - } else if (value == 3) { - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - } - } - - if (offset / 4 >= NUM_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "highbank: bad write offset 0x%" HWADDR_PRIx "\n", offset); - return; - } - regs[offset / 4] = value; -} - -static uint64_t hb_regs_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint32_t value; - uint32_t *regs = opaque; - - if (offset / 4 >= NUM_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "highbank: bad read offset 0x%" HWADDR_PRIx "\n", offset); - return 0; - } - value = regs[offset / 4]; - - if ((offset == 0x100) || (offset == 0x108) || (offset == 0x10C)) { - value |= 0x30000000; - } - - return value; -} - -static const MemoryRegionOps hb_mem_ops = { - .read = hb_regs_read, - .write = hb_regs_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -#define TYPE_HIGHBANK_REGISTERS "highbank-regs" -OBJECT_DECLARE_SIMPLE_TYPE(HighbankRegsState, HIGHBANK_REGISTERS) - -struct HighbankRegsState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t regs[NUM_REGS]; -}; - -static const VMStateDescription vmstate_highbank_regs = { - .name = "highbank-regs", - .version_id = 0, - .minimum_version_id = 0, - .fields = (const VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, HighbankRegsState, NUM_REGS), - VMSTATE_END_OF_LIST(), - }, -}; - -static void highbank_regs_reset(DeviceState *dev) -{ - HighbankRegsState *s = HIGHBANK_REGISTERS(dev); - - s->regs[0x40] = 0x05F20121; - s->regs[0x41] = 0x2; - s->regs[0x42] = 0x05F30121; - s->regs[0x43] = 0x05F40121; -} - -static void highbank_regs_init(Object *obj) -{ - HighbankRegsState *s = HIGHBANK_REGISTERS(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - - memory_region_init_io(&s->iomem, obj, &hb_mem_ops, s->regs, - "highbank_regs", 0x1000); - sysbus_init_mmio(dev, &s->iomem); -} - -static void highbank_regs_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "Calxeda Highbank registers"; - dc->vmsd = &vmstate_highbank_regs; - device_class_set_legacy_reset(dc, highbank_regs_reset); -} - -static const TypeInfo highbank_regs_info = { - .name = TYPE_HIGHBANK_REGISTERS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(HighbankRegsState), - .instance_init = highbank_regs_init, - .class_init = highbank_regs_class_init, -}; - -static void highbank_regs_register_types(void) -{ - type_register_static(&highbank_regs_info); -} - -type_init(highbank_regs_register_types) - -static struct arm_boot_info highbank_binfo; - -enum cxmachines { - CALXEDA_HIGHBANK, - CALXEDA_MIDWAY, -}; - -/* ram_size must be set to match the upper bound of memory in the - * device tree (linux/arch/arm/boot/dts/highbank.dts), which is - * normally 0xff900000 or -m 4089. When running this board on a - * 32-bit host, set the reg value of memory to 0xf7ff00000 in the - * device tree and pass -m 2047 to QEMU. - */ -static void calxeda_init(MachineState *machine, enum cxmachines machine_id) -{ - DeviceState *dev = NULL; - SysBusDevice *busdev; - qemu_irq pic[GIC_EXT_IRQS]; - int n; - unsigned int smp_cpus = machine->smp.cpus; - qemu_irq cpu_irq[4]; - qemu_irq cpu_fiq[4]; - qemu_irq cpu_virq[4]; - qemu_irq cpu_vfiq[4]; - MemoryRegion *sysram; - MemoryRegion *sysmem; - char *sysboot_filename; - - switch (machine_id) { - case CALXEDA_HIGHBANK: - machine->cpu_type = ARM_CPU_TYPE_NAME("cortex-a9"); - break; - case CALXEDA_MIDWAY: - machine->cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); - break; - default: - g_assert_not_reached(); - } - - for (n = 0; n < smp_cpus; n++) { - Object *cpuobj; - ARMCPU *cpu; - - cpuobj = object_new(machine->cpu_type); - cpu = ARM_CPU(cpuobj); - - object_property_add_child(OBJECT(machine), "cpu[*]", cpuobj); - object_property_set_int(cpuobj, "psci-conduit", QEMU_PSCI_CONDUIT_SMC, - &error_abort); - - if (object_property_find(cpuobj, "reset-cbar")) { - object_property_set_int(cpuobj, "reset-cbar", MPCORE_PERIPHBASE, - &error_abort); - } - qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); - cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ); - cpu_fiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ); - cpu_virq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VIRQ); - cpu_vfiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VFIQ); - } - - sysmem = get_system_memory(); - /* SDRAM at address zero. */ - memory_region_add_subregion(sysmem, 0, machine->ram); - - sysram = g_new(MemoryRegion, 1); - memory_region_init_ram(sysram, NULL, "highbank.sysram", 0x8000, - &error_fatal); - memory_region_add_subregion(sysmem, 0xfff88000, sysram); - if (machine->firmware != NULL) { - sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware); - if (sysboot_filename != NULL) { - if (load_image_targphys(sysboot_filename, 0xfff88000, 0x8000, - NULL) < 0) { - error_report("Unable to load %s", machine->firmware); - exit(1); - } - g_free(sysboot_filename); - } else { - error_report("Unable to find %s", machine->firmware); - exit(1); - } - } - - switch (machine_id) { - case CALXEDA_HIGHBANK: - dev = qdev_new("l2x0"); - busdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_mmio_map(busdev, 0, 0xfff12000); - - dev = qdev_new(TYPE_A9MPCORE_PRIV); - break; - case CALXEDA_MIDWAY: - dev = qdev_new(TYPE_A15MPCORE_PRIV); - break; - } - qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); - qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); - busdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); - for (n = 0; n < smp_cpus; n++) { - sysbus_connect_irq(busdev, n, cpu_irq[n]); - sysbus_connect_irq(busdev, n + smp_cpus, cpu_fiq[n]); - sysbus_connect_irq(busdev, n + 2 * smp_cpus, cpu_virq[n]); - sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]); - } - - for (n = 0; n < GIC_EXT_IRQS; n++) { - pic[n] = qdev_get_gpio_in(dev, n); - } - - dev = qdev_new("sp804"); - qdev_prop_set_uint32(dev, "freq0", 150000000); - qdev_prop_set_uint32(dev, "freq1", 150000000); - busdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_mmio_map(busdev, 0, 0xfff34000); - sysbus_connect_irq(busdev, 0, pic[18]); - pl011_create(0xfff36000, pic[20], serial_hd(0)); - - dev = qdev_new(TYPE_HIGHBANK_REGISTERS); - busdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_mmio_map(busdev, 0, 0xfff3c000); - - sysbus_create_simple("pl061", 0xfff30000, pic[14]); - sysbus_create_simple("pl061", 0xfff31000, pic[15]); - sysbus_create_simple("pl061", 0xfff32000, pic[16]); - sysbus_create_simple("pl061", 0xfff33000, pic[17]); - sysbus_create_simple("pl031", 0xfff35000, pic[19]); - sysbus_create_simple("pl022", 0xfff39000, pic[23]); - - sysbus_create_simple(TYPE_SYSBUS_AHCI, 0xffe08000, pic[83]); - - dev = qemu_create_nic_device("xgmac", true, NULL); - if (dev) { - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff50000); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[77]); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[78]); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[79]); - } - - dev = qemu_create_nic_device("xgmac", true, NULL); - if (dev) { - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff51000); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[80]); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[81]); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[82]); - } - - /* TODO create and connect IDE devices for ide_drive_get() */ - - highbank_binfo.ram_size = machine->ram_size; - /* highbank requires a dtb in order to boot, and the dtb will override - * the board ID. The following value is ignored, so set it to -1 to be - * clear that the value is meaningless. - */ - highbank_binfo.board_id = -1; - highbank_binfo.loader_start = 0; - highbank_binfo.board_setup_addr = BOARD_SETUP_ADDR; - highbank_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; - - arm_load_kernel(ARM_CPU(first_cpu), machine, &highbank_binfo); -} - -static void highbank_init(MachineState *machine) -{ - calxeda_init(machine, CALXEDA_HIGHBANK); -} - -static void midway_init(MachineState *machine) -{ - calxeda_init(machine, CALXEDA_MIDWAY); -} - -static void highbank_class_init(ObjectClass *oc, const void *data) -{ - static const char * const valid_cpu_types[] = { - ARM_CPU_TYPE_NAME("cortex-a9"), - NULL - }; - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Calxeda Highbank (ECX-1000)"; - mc->init = highbank_init; - mc->valid_cpu_types = valid_cpu_types; - mc->block_default_type = IF_IDE; - mc->units_per_default_bus = 1; - mc->max_cpus = 4; - mc->ignore_memory_transaction_failures = true; - mc->default_ram_id = "highbank.dram"; - mc->deprecation_reason = "no known users left for this machine"; -} - -static const TypeInfo highbank_type = { - .name = MACHINE_TYPE_NAME("highbank"), - .parent = TYPE_MACHINE, - .class_init = highbank_class_init, - .interfaces = arm_machine_interfaces, -}; - -static void midway_class_init(ObjectClass *oc, const void *data) -{ - static const char * const valid_cpu_types[] = { - ARM_CPU_TYPE_NAME("cortex-a15"), - NULL - }; - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Calxeda Midway (ECX-2000)"; - mc->init = midway_init; - mc->valid_cpu_types = valid_cpu_types; - mc->block_default_type = IF_IDE; - mc->units_per_default_bus = 1; - mc->max_cpus = 4; - mc->ignore_memory_transaction_failures = true; - mc->default_ram_id = "highbank.dram"; - mc->deprecation_reason = "no known users left for this machine"; -} - -static const TypeInfo midway_type = { - .name = MACHINE_TYPE_NAME("midway"), - .parent = TYPE_MACHINE, - .class_init = midway_class_init, - .interfaces = arm_machine_interfaces, -}; - -static void calxeda_machines_init(void) -{ - type_register_static(&highbank_type); - type_register_static(&midway_type); -} - -type_init(calxeda_machines_init) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -XXX,XX +XXX,XX @@ arm_common_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c')) arm_common_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) arm_common_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) arm_common_ss.add(when: 'CONFIG_EMCRAFT_SF2', if_true: files('msf2-som.c')) -arm_common_ss.add(when: 'CONFIG_HIGHBANK', if_true: files('highbank.c')) arm_common_ss.add(when: 'CONFIG_INTEGRATOR', if_true: files('integratorcp.c')) arm_common_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c')) arm_common_ss.add(when: 'CONFIG_MPS3R', if_true: files('mps3r.c')) -- 2.43.0
From: Thomas Huth <thuth@redhat.com> The xgmac device was only used by the highbank machine that just has been removed. Being a sysbus device that cannot be instantiated by the user, this is dead code now and thus can be removed, too. Signed-off-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/net/Kconfig | 3 - hw/net/meson.build | 1 - hw/net/xgmac.c | 443 ---------------------------- scripts/coverity-scan/COMPONENTS.md | 2 +- 4 files changed, 1 insertion(+), 448 deletions(-) delete mode 100644 hw/net/xgmac.c diff --git a/hw/net/Kconfig b/hw/net/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -XXX,XX +XXX,XX @@ config NE2000_ISA config OPENCORES_ETH bool -config XGMAC - bool - config ALLWINNER_EMAC bool diff --git a/hw/net/meson.build b/hw/net/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_LAN9118', if_true: files('lan9118.c')) system_ss.add(when: 'CONFIG_LAN9118_PHY', if_true: files('lan9118_phy.c')) system_ss.add(when: 'CONFIG_NE2000_ISA', if_true: files('ne2000-isa.c')) system_ss.add(when: 'CONFIG_OPENCORES_ETH', if_true: files('opencores_eth.c')) -system_ss.add(when: 'CONFIG_XGMAC', if_true: files('xgmac.c')) system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axienet.c')) system_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')) system_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c')) diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c deleted file mode 100644 index XXXXXXX..XXXXXXX --- a/hw/net/xgmac.c +++ /dev/null @@ -XXX,XX +XXX,XX @@ -/* - * QEMU model of XGMAC Ethernet. - * - * derived from the Xilinx AXI-Ethernet by Edgar E. Iglesias. - * - * Copyright (c) 2011 Calxeda, Inc. - * - * 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 - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/core/irq.h" -#include "hw/core/qdev-properties.h" -#include "hw/core/sysbus.h" -#include "exec/cpu-common.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "net/net.h" -#include "qom/object.h" - -#ifdef DEBUG_XGMAC -#define DEBUGF_BRK(message, args...) do { \ - fprintf(stderr, (message), ## args); \ - } while (0) -#else -#define DEBUGF_BRK(message, args...) do { } while (0) -#endif - -#define XGMAC_CONTROL 0x00000000 /* MAC Configuration */ -#define XGMAC_FRAME_FILTER 0x00000001 /* MAC Frame Filter */ -#define XGMAC_FLOW_CTRL 0x00000006 /* MAC Flow Control */ -#define XGMAC_VLAN_TAG 0x00000007 /* VLAN Tags */ -#define XGMAC_VERSION 0x00000008 /* Version */ -/* VLAN tag for insertion or replacement into tx frames */ -#define XGMAC_VLAN_INCL 0x00000009 -#define XGMAC_LPI_CTRL 0x0000000a /* LPI Control and Status */ -#define XGMAC_LPI_TIMER 0x0000000b /* LPI Timers Control */ -#define XGMAC_TX_PACE 0x0000000c /* Transmit Pace and Stretch */ -#define XGMAC_VLAN_HASH 0x0000000d /* VLAN Hash Table */ -#define XGMAC_DEBUG 0x0000000e /* Debug */ -#define XGMAC_INT_STATUS 0x0000000f /* Interrupt and Control */ -/* HASH table registers */ -#define XGMAC_HASH(n) ((0x00000300/4) + (n)) -#define XGMAC_NUM_HASH 16 -/* Operation Mode */ -#define XGMAC_OPMODE (0x00000400/4) -/* Remote Wake-Up Frame Filter */ -#define XGMAC_REMOTE_WAKE (0x00000700/4) -/* PMT Control and Status */ -#define XGMAC_PMT (0x00000704/4) - -#define XGMAC_ADDR_HIGH(reg) (0x00000010+((reg) * 2)) -#define XGMAC_ADDR_LOW(reg) (0x00000011+((reg) * 2)) - -#define DMA_BUS_MODE 0x000003c0 /* Bus Mode */ -#define DMA_XMT_POLL_DEMAND 0x000003c1 /* Transmit Poll Demand */ -#define DMA_RCV_POLL_DEMAND 0x000003c2 /* Received Poll Demand */ -#define DMA_RCV_BASE_ADDR 0x000003c3 /* Receive List Base */ -#define DMA_TX_BASE_ADDR 0x000003c4 /* Transmit List Base */ -#define DMA_STATUS 0x000003c5 /* Status Register */ -#define DMA_CONTROL 0x000003c6 /* Ctrl (Operational Mode) */ -#define DMA_INTR_ENA 0x000003c7 /* Interrupt Enable */ -#define DMA_MISSED_FRAME_CTR 0x000003c8 /* Missed Frame Counter */ -/* Receive Interrupt Watchdog Timer */ -#define DMA_RI_WATCHDOG_TIMER 0x000003c9 -#define DMA_AXI_BUS 0x000003ca /* AXI Bus Mode */ -#define DMA_AXI_STATUS 0x000003cb /* AXI Status */ -#define DMA_CUR_TX_DESC_ADDR 0x000003d2 /* Current Host Tx Descriptor */ -#define DMA_CUR_RX_DESC_ADDR 0x000003d3 /* Current Host Rx Descriptor */ -#define DMA_CUR_TX_BUF_ADDR 0x000003d4 /* Current Host Tx Buffer */ -#define DMA_CUR_RX_BUF_ADDR 0x000003d5 /* Current Host Rx Buffer */ -#define DMA_HW_FEATURE 0x000003d6 /* Enabled Hardware Features */ - -/* DMA Status register defines */ -#define DMA_STATUS_GMI 0x08000000 /* MMC interrupt */ -#define DMA_STATUS_GLI 0x04000000 /* GMAC Line interface int */ -#define DMA_STATUS_EB_MASK 0x00380000 /* Error Bits Mask */ -#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */ -#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */ -#define DMA_STATUS_TS_MASK 0x00700000 /* Transmit Process State */ -#define DMA_STATUS_TS_SHIFT 20 -#define DMA_STATUS_RS_MASK 0x000e0000 /* Receive Process State */ -#define DMA_STATUS_RS_SHIFT 17 -#define DMA_STATUS_NIS 0x00010000 /* Normal Interrupt Summary */ -#define DMA_STATUS_AIS 0x00008000 /* Abnormal Interrupt Summary */ -#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */ -#define DMA_STATUS_FBI 0x00002000 /* Fatal Bus Error Interrupt */ -#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */ -#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */ -#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */ -#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */ -#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */ -#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */ -#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */ -#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */ -#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavailable */ -#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */ -#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ - -/* DMA Control register defines */ -#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */ -#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */ -#define DMA_CONTROL_DFF 0x01000000 /* Disable flush of rx frames */ - -struct desc { - uint32_t ctl_stat; - uint16_t buffer1_size; - uint16_t buffer2_size; - uint32_t buffer1_addr; - uint32_t buffer2_addr; - uint32_t ext_stat; - uint32_t res[3]; -}; - -#define R_MAX 0x400 - -typedef struct RxTxStats { - uint64_t rx_bytes; - uint64_t tx_bytes; - - uint64_t rx; - uint64_t rx_bcast; - uint64_t rx_mcast; -} RxTxStats; - -#define TYPE_XGMAC "xgmac" -OBJECT_DECLARE_SIMPLE_TYPE(XgmacState, XGMAC) - -struct XgmacState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq sbd_irq; - qemu_irq pmt_irq; - qemu_irq mci_irq; - NICState *nic; - NICConf conf; - - struct RxTxStats stats; - uint32_t regs[R_MAX]; -}; - -static const VMStateDescription vmstate_rxtx_stats = { - .name = "xgmac_stats", - .version_id = 1, - .minimum_version_id = 1, - .fields = (const VMStateField[]) { - VMSTATE_UINT64(rx_bytes, RxTxStats), - VMSTATE_UINT64(tx_bytes, RxTxStats), - VMSTATE_UINT64(rx, RxTxStats), - VMSTATE_UINT64(rx_bcast, RxTxStats), - VMSTATE_UINT64(rx_mcast, RxTxStats), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xgmac = { - .name = "xgmac", - .version_id = 1, - .minimum_version_id = 1, - .fields = (const VMStateField[]) { - VMSTATE_STRUCT(stats, XgmacState, 0, vmstate_rxtx_stats, RxTxStats), - VMSTATE_UINT32_ARRAY(regs, XgmacState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void xgmac_read_desc(XgmacState *s, struct desc *d, int rx) -{ - uint32_t addr = rx ? s->regs[DMA_CUR_RX_DESC_ADDR] : - s->regs[DMA_CUR_TX_DESC_ADDR]; - cpu_physical_memory_read(addr, d, sizeof(*d)); -} - -static void xgmac_write_desc(XgmacState *s, struct desc *d, int rx) -{ - int reg = rx ? DMA_CUR_RX_DESC_ADDR : DMA_CUR_TX_DESC_ADDR; - uint32_t addr = s->regs[reg]; - - if (!rx && (d->ctl_stat & 0x00200000)) { - s->regs[reg] = s->regs[DMA_TX_BASE_ADDR]; - } else if (rx && (d->buffer1_size & 0x8000)) { - s->regs[reg] = s->regs[DMA_RCV_BASE_ADDR]; - } else { - s->regs[reg] += sizeof(*d); - } - cpu_physical_memory_write(addr, d, sizeof(*d)); -} - -static void xgmac_enet_send(XgmacState *s) -{ - struct desc bd; - int frame_size; - int len; - QEMU_UNINITIALIZED uint8_t frame[8192]; - uint8_t *ptr; - - ptr = frame; - frame_size = 0; - while (1) { - xgmac_read_desc(s, &bd, 0); - if ((bd.ctl_stat & 0x80000000) == 0) { - /* Run out of descriptors to transmit. */ - break; - } - len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff); - - /* - * FIXME: these cases of malformed tx descriptors (bad sizes) - * should probably be reported back to the guest somehow - * rather than simply silently stopping processing, but we - * don't know what the hardware does in this situation. - * This will only happen for buggy guests anyway. - */ - if ((bd.buffer1_size & 0xfff) > 2048) { - DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- " - "xgmac buffer 1 len on send > 2048 (0x%x)\n", - __func__, bd.buffer1_size & 0xfff); - break; - } - if ((bd.buffer2_size & 0xfff) != 0) { - DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- " - "xgmac buffer 2 len on send != 0 (0x%x)\n", - __func__, bd.buffer2_size & 0xfff); - break; - } - if (frame_size + len >= sizeof(frame)) { - DEBUGF_BRK("qemu:%s: buffer overflow %d read into %zu " - "buffer\n" , __func__, frame_size + len, sizeof(frame)); - DEBUGF_BRK("qemu:%s: buffer1.size=%d; buffer2.size=%d\n", - __func__, bd.buffer1_size, bd.buffer2_size); - break; - } - - cpu_physical_memory_read(bd.buffer1_addr, ptr, len); - ptr += len; - frame_size += len; - if (bd.ctl_stat & 0x20000000) { - /* Last buffer in frame. */ - qemu_send_packet(qemu_get_queue(s->nic), frame, len); - ptr = frame; - frame_size = 0; - s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS; - } - bd.ctl_stat &= ~0x80000000; - /* Write back the modified descriptor. */ - xgmac_write_desc(s, &bd, 0); - } -} - -static void enet_update_irq(XgmacState *s) -{ - int stat = s->regs[DMA_STATUS] & s->regs[DMA_INTR_ENA]; - qemu_set_irq(s->sbd_irq, !!stat); -} - -static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size) -{ - XgmacState *s = opaque; - uint64_t r = 0; - addr >>= 2; - - switch (addr) { - case XGMAC_VERSION: - r = 0x1012; - break; - default: - if (addr < ARRAY_SIZE(s->regs)) { - r = s->regs[addr]; - } - break; - } - return r; -} - -static void enet_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - XgmacState *s = opaque; - - addr >>= 2; - switch (addr) { - case DMA_BUS_MODE: - s->regs[DMA_BUS_MODE] = value & ~0x1; - break; - case DMA_XMT_POLL_DEMAND: - xgmac_enet_send(s); - break; - case DMA_STATUS: - s->regs[DMA_STATUS] = s->regs[DMA_STATUS] & ~value; - break; - case DMA_RCV_BASE_ADDR: - s->regs[DMA_RCV_BASE_ADDR] = s->regs[DMA_CUR_RX_DESC_ADDR] = value; - break; - case DMA_TX_BASE_ADDR: - s->regs[DMA_TX_BASE_ADDR] = s->regs[DMA_CUR_TX_DESC_ADDR] = value; - break; - default: - if (addr < ARRAY_SIZE(s->regs)) { - s->regs[addr] = value; - } - break; - } - enet_update_irq(s); -} - -static const MemoryRegionOps enet_mem_ops = { - .read = enet_read, - .write = enet_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static int eth_can_rx(XgmacState *s) -{ - /* RX enabled? */ - return s->regs[DMA_CONTROL] & DMA_CONTROL_SR; -} - -static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) -{ - XgmacState *s = qemu_get_nic_opaque(nc); - static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, - 0xff, 0xff, 0xff}; - int unicast, broadcast, multicast; - struct desc bd; - ssize_t ret; - - if (!eth_can_rx(s)) { - return -1; - } - unicast = ~buf[0] & 0x1; - broadcast = memcmp(buf, sa_bcast, 6) == 0; - multicast = !unicast && !broadcast; - if (size < 12) { - s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS; - ret = -1; - goto out; - } - - xgmac_read_desc(s, &bd, 1); - if ((bd.ctl_stat & 0x80000000) == 0) { - s->regs[DMA_STATUS] |= DMA_STATUS_RU | DMA_STATUS_AIS; - ret = size; - goto out; - } - - cpu_physical_memory_write(bd.buffer1_addr, buf, size); - - /* Add in the 4 bytes for crc (the real hw returns length incl crc) */ - size += 4; - bd.ctl_stat = (size << 16) | 0x300; - xgmac_write_desc(s, &bd, 1); - - s->stats.rx_bytes += size; - s->stats.rx++; - if (multicast) { - s->stats.rx_mcast++; - } else if (broadcast) { - s->stats.rx_bcast++; - } - - s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS; - ret = size; - -out: - enet_update_irq(s); - return ret; -} - -static NetClientInfo net_xgmac_enet_info = { - .type = NET_CLIENT_DRIVER_NIC, - .size = sizeof(NICState), - .receive = eth_rx, -}; - -static void xgmac_enet_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - XgmacState *s = XGMAC(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &enet_mem_ops, s, - "xgmac", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->sbd_irq); - sysbus_init_irq(sbd, &s->pmt_irq); - sysbus_init_irq(sbd, &s->mci_irq); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, - &dev->mem_reentrancy_guard, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) | - s->conf.macaddr.a[4]; - s->regs[XGMAC_ADDR_LOW(0)] = (s->conf.macaddr.a[3] << 24) | - (s->conf.macaddr.a[2] << 16) | - (s->conf.macaddr.a[1] << 8) | - s->conf.macaddr.a[0]; -} - -static const Property xgmac_properties[] = { - DEFINE_NIC_PROPERTIES(XgmacState, conf), -}; - -static void xgmac_enet_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = xgmac_enet_realize; - dc->vmsd = &vmstate_xgmac; - device_class_set_props(dc, xgmac_properties); -} - -static const TypeInfo xgmac_enet_info = { - .name = TYPE_XGMAC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XgmacState), - .class_init = xgmac_enet_class_init, -}; - -static void xgmac_enet_register_types(void) -{ - type_register_static(&xgmac_enet_info); -} - -type_init(xgmac_enet_register_types) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index XXXXXXX..XXXXXXX 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -XXX,XX +XXX,XX @@ alpha ~ .*/qemu((/include)?/hw/alpha/.*|/target/alpha/.*) arm - ~ .*/qemu((/include)?/hw/arm/.*|(/include)?/hw/.*/(arm|allwinner-a10|bcm28|digic|exynos|imx|omap|stellaris|pxa2xx|versatile|zynq|cadence).*|/hw/net/xgmac.c|/hw/ssi/xilinx_spips.c|/target/arm/.*) + ~ .*/qemu((/include)?/hw/arm/.*|(/include)?/hw/.*/(arm|allwinner-a10|bcm28|digic|exynos|imx|omap|stellaris|pxa2xx|versatile|zynq|cadence).*|/hw/ssi/xilinx_spips.c|/target/arm/.*) avr ~ .*/qemu((/include)?/hw/avr/.*|/target/avr/.*) -- 2.43.0
From: Chisheng Chen <johnny1001s000602@gmail.com> The STM32F405 SoC relies on STM32F2xx peripherals (ADC, SPI, TIMER, USART) and the unimplemented device (UNIMP). However, they are not selected in Kconfig. This added these dependencies. Signed-off-by: Chisheng Chen <johnny1001s000602@gmail.com> Message-id: 20260228070622.2195836-1-johnny1001s000602@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config STM32F405_SOC select ARM_V7M select OR_IRQ select STM32_RCC + select STM32F2XX_ADC + select STM32F2XX_SPI + select STM32F2XX_TIMER + select STM32F2XX_USART select STM32F4XX_SYSCFG select STM32F4XX_EXTI + select UNIMP config B_L475E_IOT01A bool -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> Add a new helper for IOMMU_VEVENTQ_ALLOC ioctl to allocate a virtual event queue (vEVENTQ) for a vIOMMU object. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260226084456.112142-2-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- backends/iommufd.c | 31 +++++++++++++++++++++++++++++++ backends/trace-events | 1 + include/system/iommufd.h | 14 ++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index XXXXXXX..XXXXXXX 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_alloc_vdev(IOMMUFDBackend *be, uint32_t dev_id, return true; } +bool iommufd_backend_alloc_veventq(IOMMUFDBackend *be, uint32_t viommu_id, + uint32_t type, uint32_t depth, + uint32_t *out_veventq_id, + uint32_t *out_veventq_fd, Error **errp) +{ + int ret; + struct iommu_veventq_alloc alloc_veventq = { + .size = sizeof(alloc_veventq), + .flags = 0, + .type = type, + .veventq_depth = depth, + .viommu_id = viommu_id, + }; + + ret = ioctl(be->fd, IOMMU_VEVENTQ_ALLOC, &alloc_veventq); + + trace_iommufd_viommu_alloc_eventq(be->fd, viommu_id, type, + alloc_veventq.out_veventq_id, + alloc_veventq.out_veventq_fd, ret); + if (ret) { + error_setg_errno(errp, errno, "IOMMU_VEVENTQ_ALLOC failed"); + return false; + } + + g_assert(out_veventq_id); + g_assert(out_veventq_fd); + *out_veventq_id = alloc_veventq.out_veventq_id; + *out_veventq_fd = alloc_veventq.out_veventq_fd; + return true; +} + bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev, uint32_t hwpt_id, Error **errp) { diff --git a/backends/trace-events b/backends/trace-events index XXXXXXX..XXXXXXX 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -XXX,XX +XXX,XX @@ iommufd_backend_get_dirty_bitmap(int iommufd, uint32_t hwpt_id, uint64_t iova, u iommufd_backend_invalidate_cache(int iommufd, uint32_t id, uint32_t data_type, uint32_t entry_len, uint32_t entry_num, uint32_t done_num, uint64_t data_ptr, int ret) " iommufd=%d id=%u data_type=%u entry_len=%u entry_num=%u done_num=%u data_ptr=0x%"PRIx64" (%d)" iommufd_backend_alloc_viommu(int iommufd, uint32_t dev_id, uint32_t type, uint32_t hwpt_id, uint32_t viommu_id, int ret) " iommufd=%d type=%u dev_id=%u hwpt_id=%u viommu_id=%u (%d)" iommufd_backend_alloc_vdev(int iommufd, uint32_t dev_id, uint32_t viommu_id, uint64_t virt_id, uint32_t vdev_id, int ret) " iommufd=%d dev_id=%u viommu_id=%u virt_id=0x%"PRIx64" vdev_id=%u (%d)" +iommufd_viommu_alloc_eventq(int iommufd, uint32_t viommu_id, uint32_t type, uint32_t veventq_id, uint32_t veventq_fd, int ret) " iommufd=%d viommu_id=%u type=%u veventq_id=%u veventq_fd=%u (%d)" # igvm-cfg.c igvm_reset_enter(int type) "type=%u" diff --git a/include/system/iommufd.h b/include/system/iommufd.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -XXX,XX +XXX,XX @@ typedef struct IOMMUFDVdev { uint32_t virt_id; /* virtual device ID */ } IOMMUFDVdev; +/* Virtual event queue interface for a vIOMMU */ +typedef struct IOMMUFDVeventq { + IOMMUFDViommu *viommu; + uint32_t veventq_id; + uint32_t veventq_fd; + uint32_t last_event_seq; /* Sequence number of last processed event */ + bool event_start; /* True after first valid event; cleared on overflow */ +} IOMMUFDVeventq; + bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp); void iommufd_backend_disconnect(IOMMUFDBackend *be); @@ -XXX,XX +XXX,XX @@ bool iommufd_backend_alloc_vdev(IOMMUFDBackend *be, uint32_t dev_id, uint32_t viommu_id, uint64_t virt_id, uint32_t *out_vdev_id, Error **errp); +bool iommufd_backend_alloc_veventq(IOMMUFDBackend *be, uint32_t viommu_id, + uint32_t type, uint32_t depth, + uint32_t *out_veventq_id, + uint32_t *out_veventq_fd, Error **errp); + bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be, uint32_t hwpt_id, bool start, Error **errp); bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, uint32_t hwpt_id, -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Move viommu teardown into a helper function and use it from the last device removal path. This groups related cleanup logic in one place and improves readability. It also makes it easier to extend the teardown in future, for example when freeing related objects such as vEVENTQ. No functional change. Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260226084456.112142-3-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_issue_inv_cmd(SMMUv3State *bs, void *cmd, SMMUDevice *sdev, sizeof(Cmd), &entry_num, cmd, errp); } +static void smmuv3_accel_free_viommu(SMMUv3AccelState *accel) +{ + IOMMUFDViommu *viommu = accel->viommu; + + if (!viommu) { + return; + } + iommufd_backend_free_id(viommu->iommufd, accel->bypass_hwpt_id); + iommufd_backend_free_id(viommu->iommufd, accel->abort_hwpt_id); + iommufd_backend_free_id(viommu->iommufd, accel->viommu->viommu_id); + g_free(viommu); + accel->viommu = NULL; +} + static bool smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, Error **errp) @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_unset_iommu_device(PCIBus *bus, void *opaque, trace_smmuv3_accel_unset_iommu_device(devfn, idev->devid); if (QLIST_EMPTY(&accel->device_list)) { - iommufd_backend_free_id(accel->viommu->iommufd, accel->bypass_hwpt_id); - iommufd_backend_free_id(accel->viommu->iommufd, accel->abort_hwpt_id); - iommufd_backend_free_id(accel->viommu->iommufd, - accel->viommu->viommu_id); - g_free(accel->viommu); - accel->viommu = NULL; + smmuv3_accel_free_viommu(accel); } } -- 2.43.0
From: Nicolin Chen <nicolinc@nvidia.com> When the guest enables the Event Queue and a vIOMMU is present, allocate a vEVENTQ object so that host-side events related to the vIOMMU can be received and propagated back to the guest. Allocate a vEVENTQ only when both of the following conditions are met: 1) The guest SMMUv3 driver has set EVENTQEN = 1 in SMMU_CR0. 2) A vIOMMU exists (created when the first VFIO device is attached). These two conditions may occur in any order. In the cold-plug case, the vIOMMU already exists before the guest driver probes. When the guest sets EVENTQEN = 1 during driver probe, the vEVENTQ is allocated at that point. With hot-plug, the VFIO device may be attached either before or after the guest sets EVENTQEN. If the vIOMMU is created first, allocation is deferred until EVENTQEN = 1. If EVENTQEN is already set, allocation happens when the vIOMMU is created. In all cases, allocation is triggered when the second required condition becomes true. Errors from command queue consumption and vEVENTQ allocation are reported independently as the two operations are unrelated. Event read and propagation will be added in a later patch. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260226084456.112142-4-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 61 +++++++++++++++++++++++++++++++++++++++++-- hw/arm/smmuv3-accel.h | 6 +++++ hw/arm/smmuv3.c | 6 +++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_issue_inv_cmd(SMMUv3State *bs, void *cmd, SMMUDevice *sdev, sizeof(Cmd), &entry_num, cmd, errp); } +static void smmuv3_accel_free_veventq(SMMUv3AccelState *accel) +{ + IOMMUFDVeventq *veventq = accel->veventq; + + if (!veventq) { + return; + } + close(veventq->veventq_fd); + iommufd_backend_free_id(accel->viommu->iommufd, veventq->veventq_id); + g_free(veventq); + accel->veventq = NULL; +} + static void smmuv3_accel_free_viommu(SMMUv3AccelState *accel) { IOMMUFDViommu *viommu = accel->viommu; @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_free_viommu(SMMUv3AccelState *accel) if (!viommu) { return; } + smmuv3_accel_free_veventq(accel); iommufd_backend_free_id(viommu->iommufd, accel->bypass_hwpt_id); iommufd_backend_free_id(viommu->iommufd, accel->abort_hwpt_id); iommufd_backend_free_id(viommu->iommufd, accel->viommu->viommu_id); @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_free_viommu(SMMUv3AccelState *accel) accel->viommu = NULL; } +bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp) +{ + SMMUv3AccelState *accel = s->s_accel; + IOMMUFDVeventq *veventq; + uint32_t veventq_id; + uint32_t veventq_fd; + + if (!accel || !accel->viommu) { + return true; + } + + if (accel->veventq) { + return true; + } + + if (!smmuv3_eventq_enabled(s)) { + return true; + } + + if (!iommufd_backend_alloc_veventq(accel->viommu->iommufd, + accel->viommu->viommu_id, + IOMMU_VEVENTQ_TYPE_ARM_SMMUV3, + 1 << s->eventq.log2size, &veventq_id, + &veventq_fd, errp)) { + return false; + } + + veventq = g_new0(IOMMUFDVeventq, 1); + veventq->veventq_id = veventq_id; + veventq->veventq_fd = veventq_fd; + veventq->viommu = accel->viommu; + accel->veventq = veventq; + return true; +} + static bool smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, Error **errp) @@ -XXX,XX +XXX,XX @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, viommu->viommu_id = viommu_id; viommu->s2_hwpt_id = s2_hwpt_id; viommu->iommufd = idev->iommufd; + accel->viommu = viommu; /* * Pre-allocate HWPTs for S1 bypass and abort cases. These will be attached @@ -XXX,XX +XXX,XX @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, goto free_abort_hwpt; } + /* Allocate a vEVENTQ if guest has enabled event queue */ + if (!smmuv3_accel_alloc_veventq(s, errp)) { + goto free_bypass_hwpt; + } + /* Attach a HWPT based on SMMUv3 GBPA.ABORT value */ hwpt_id = smmuv3_accel_gbpa_hwpt(s, accel); if (!host_iommu_device_iommufd_attach_hwpt(idev, hwpt_id, errp)) { - goto free_bypass_hwpt; + goto free_veventq; } - accel->viommu = viommu; return true; +free_veventq: + smmuv3_accel_free_veventq(accel); free_bypass_hwpt: iommufd_backend_free_id(idev->iommufd, accel->bypass_hwpt_id); free_abort_hwpt: @@ -XXX,XX +XXX,XX @@ free_abort_hwpt: free_viommu: iommufd_backend_free_id(idev->iommufd, viommu->viommu_id); g_free(viommu); + accel->viommu = NULL; return false; } diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.h +++ b/hw/arm/smmuv3-accel.h @@ -XXX,XX +XXX,XX @@ */ typedef struct SMMUv3AccelState { IOMMUFDViommu *viommu; + IOMMUFDVeventq *veventq; uint32_t bypass_hwpt_id; uint32_t abort_hwpt_id; QLIST_HEAD(, SMMUv3AccelDevice) device_list; @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_attach_gbpa_hwpt(SMMUv3State *s, Error **errp); bool smmuv3_accel_issue_inv_cmd(SMMUv3State *s, void *cmd, SMMUDevice *sdev, Error **errp); void smmuv3_accel_idr_override(SMMUv3State *s); +bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp); void smmuv3_accel_reset(SMMUv3State *s); #else static inline void smmuv3_accel_init(SMMUv3State *s) @@ -XXX,XX +XXX,XX @@ smmuv3_accel_issue_inv_cmd(SMMUv3State *s, void *cmd, SMMUDevice *sdev, static inline void smmuv3_accel_idr_override(SMMUv3State *s) { } +static inline bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp) +{ + return true; +} static inline void smmuv3_accel_reset(SMMUv3State *s) { } diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset, s->cr0ack = data & ~SMMU_CR0_RESERVED; /* in case the command queue has been enabled */ smmuv3_cmdq_consume(s, &local_err); + if (local_err) { + error_report_err(local_err); + local_err = NULL; + } + /* Allocate vEVENTQ if EVENTQ is enabled and a vIOMMU is available */ + smmuv3_accel_alloc_veventq(s, &local_err); break; case A_CR1: s->cr[1] = data; -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Factor out the code that propagates event records to the guest into a helper function. The accelerated SMMUv3 path can use this to propagate host events in a subsequent patch. Take the mutex inside the helper before accessing the Event Queue. Today event propagation occurs only in the core SMMUv3 path and is effectively serialized. A subsequent patch will also invoke this helper from the accelerated event read path, which may run concurrently. Therefore serialization is required here. No functional change intended. Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260226084456.112142-5-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-internal.h | 4 ++++ hw/arm/smmuv3.c | 20 ++++++++++++++------ hw/arm/trace-events | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -XXX,XX +XXX,XX @@ typedef struct SMMUEventInfo { (x)->word[6] = (uint32_t)(addr & 0xffffffff); \ } while (0) +#define EVT_GET_TYPE(x) extract32((x)->word[0], 0, 8) +#define EVT_GET_SID(x) ((x)->word[1]) + void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event); +void smmuv3_propagate_event(SMMUv3State *s, Evt *evt); int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event); static inline int oas2bits(int oas_field) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static MemTxResult smmuv3_write_eventq(SMMUv3State *s, Evt *evt) return MEMTX_OK; } +void smmuv3_propagate_event(SMMUv3State *s, Evt *evt) +{ + MemTxResult r; + + trace_smmuv3_propagate_event(smmu_event_string(EVT_GET_TYPE(evt)), + EVT_GET_SID(evt)); + QEMU_LOCK_GUARD(&s->mutex); + r = smmuv3_write_eventq(s, evt); + if (r != MEMTX_OK) { + smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_EVENTQ_ABT_ERR_MASK); + } +} + void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info) { Evt evt = {}; - MemTxResult r; if (!smmuv3_eventq_enabled(s)) { return; @@ -XXX,XX +XXX,XX @@ void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info) g_assert_not_reached(); } - trace_smmuv3_record_event(smmu_event_string(info->type), info->sid); - r = smmuv3_write_eventq(s, &evt); - if (r != MEMTX_OK) { - smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_EVENTQ_ABT_ERR_MASK); - } + smmuv3_propagate_event(s, &evt); info->recorded = true; } diff --git a/hw/arm/trace-events b/hw/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -XXX,XX +XXX,XX @@ smmuv3_cmdq_opcode(const char *opcode) "<--- %s" smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d " smmuv3_cmdq_consume_error(const char *cmd_name, uint8_t cmd_error) "Error on %s command execution: %d" smmuv3_write_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" -smmuv3_record_event(const char *type, uint32_t sid) "%s sid=0x%x" +smmuv3_propagate_event(const char *type, uint32_t sid) "%s sid=0x%x" smmuv3_find_ste(uint16_t sid, uint32_t features, uint16_t sid_split) "sid=0x%x features:0x%x, sid_split:0x%x" smmuv3_find_ste_2lvl(uint64_t strtab_base, uint64_t l1ptr, int l1_ste_offset, uint64_t l2ptr, int l2_ste_offset, int max_l2_ste) "strtab_base:0x%"PRIx64" l1ptr:0x%"PRIx64" l1_off:0x%x, l2ptr:0x%"PRIx64" l2_off:0x%x max_l2_ste:%d" smmuv3_get_ste(uint64_t addr) "STE addr: 0x%"PRIx64 -- 2.43.0
From: Shameer Kolothum <skolothumtho@nvidia.com> Install an event handler on the vEVENTQ fd to read and propagate host generated vIOMMU events to the guest. The handler runs in QEMU's main loop, using a non-blocking fd registered via qemu_set_fd_handler(). Tested-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260226084456.112142-6-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-accel.c | 64 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_issue_inv_cmd(SMMUv3State *bs, void *cmd, SMMUDevice *sdev, sizeof(Cmd), &entry_num, cmd, errp); } +static void smmuv3_accel_event_read(void *opaque) +{ + SMMUv3State *s = opaque; + IOMMUFDVeventq *veventq = s->s_accel->veventq; + struct { + struct iommufd_vevent_header hdr; + struct iommu_vevent_arm_smmuv3 vevent; + } buf; + enum iommu_veventq_type type = IOMMU_VEVENTQ_TYPE_ARM_SMMUV3; + uint32_t id = veventq->veventq_id; + uint32_t last_seq = veventq->last_event_seq; + ssize_t bytes; + + bytes = read(veventq->veventq_fd, &buf, sizeof(buf)); + if (bytes <= 0) { + if (errno == EAGAIN || errno == EINTR) { + return; + } + error_report_once("vEVENTQ(type %u id %u): read failed (%m)", type, id); + return; + } + + if (bytes == sizeof(buf.hdr) && + (buf.hdr.flags & IOMMU_VEVENTQ_FLAG_LOST_EVENTS)) { + error_report_once("vEVENTQ(type %u id %u): overflowed", type, id); + veventq->event_start = false; + return; + } + if (bytes < sizeof(buf)) { + error_report_once("vEVENTQ(type %u id %u): short read(%zd/%zd bytes)", + type, id, bytes, sizeof(buf)); + return; + } + + /* Check sequence in hdr for lost events if any */ + if (veventq->event_start && (buf.hdr.sequence - last_seq != 1)) { + error_report_once("vEVENTQ(type %u id %u): lost %u event(s)", + type, id, buf.hdr.sequence - last_seq - 1); + } + veventq->last_event_seq = buf.hdr.sequence; + veventq->event_start = true; + smmuv3_propagate_event(s, (Evt *)&buf.vevent); +} + static void smmuv3_accel_free_veventq(SMMUv3AccelState *accel) { IOMMUFDVeventq *veventq = accel->veventq; @@ -XXX,XX +XXX,XX @@ static void smmuv3_accel_free_veventq(SMMUv3AccelState *accel) if (!veventq) { return; } + qemu_set_fd_handler(veventq->veventq_fd, NULL, NULL, NULL); close(veventq->veventq_fd); iommufd_backend_free_id(accel->viommu->iommufd, veventq->veventq_id); g_free(veventq); @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp) IOMMUFDVeventq *veventq; uint32_t veventq_id; uint32_t veventq_fd; + int flags; if (!accel || !accel->viommu) { return true; @@ -XXX,XX +XXX,XX @@ bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp) return false; } + flags = fcntl(veventq_fd, F_GETFL); + if (flags < 0) { + error_setg_errno(errp, errno, "Failed to get flags for vEVENTQ fd"); + goto free_veventq; + } + if (fcntl(veventq_fd, F_SETFL, flags | O_NONBLOCK) < 0) { + error_setg_errno(errp, errno, "Failed to set O_NONBLOCK on vEVENTQ fd"); + goto free_veventq; + } + veventq = g_new0(IOMMUFDVeventq, 1); veventq->veventq_id = veventq_id; veventq->veventq_fd = veventq_fd; veventq->viommu = accel->viommu; accel->veventq = veventq; + + /* Set up event handler for veventq fd */ + qemu_set_fd_handler(veventq_fd, smmuv3_accel_event_read, NULL, s); return true; + +free_veventq: + close(veventq_fd); + iommufd_backend_free_id(accel->viommu->iommufd, veventq_id); + return false; } static bool -- 2.43.0
From: Paul Durrant <pdurrant@amazon.com> I am no longer actively involved in the Xen Project. Signed-off-by: Paul Durrant <pdurrant@amazon.com> Message-id: 20260224112215.83355-1-paul@xen.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ Guest CPU Cores (Xen) X86 Xen CPUs M: Stefano Stabellini <sstabellini@kernel.org> M: Anthony PERARD <anthony@xenproject.org> -M: Paul Durrant <paul@xen.org> M: Edgar E. Iglesias <edgar.iglesias@gmail.com> L: xen-devel@lists.xenproject.org S: Supported -- 2.43.0
From: CLEMENT MATHIEU--DRIF <clement.mathieu--drif@bull.com> Switch to bull.com email address following a company split. The previous eviden.com address will remain active for a few months. Signed-off-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com> Message-id: 20260302143403.1778326-1-clement.mathieu--drif@bull.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ VT-d Emulation M: Michael S. Tsirkin <mst@redhat.com> R: Jason Wang <jasowang@redhat.com> R: Yi Liu <yi.l.liu@intel.com> -R: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com> +R: Clément Mathieu--Drif <clement.mathieu--drif@bull.com> S: Supported F: hw/i386/intel_iommu.c F: hw/i386/intel_iommu_internal.h -- 2.43.0
Allow the qtest input to include comment lines, which start with '#'. This allows writing an input file for qtest which includes commentary, like this: # set up TCR in bank 0 write 0x1001000e 2 0 # TCR TXEN write 0x10010000 2 1 which can make hand-writing or annotating reproduce cases a bit more convenient. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260226175700.1319767-1-peter.maydell@linaro.org --- system/qtest.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/system/qtest.c b/system/qtest.c index XXXXXXX..XXXXXXX 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -XXX,XX +XXX,XX @@ static void *qtest_server_send_opaque; * * Extra ASCII space characters in command inputs are permitted and ignored. * Lines containing only spaces are permitted and ignored. + * Lines that start with a '#' character (comments) are permitted and ignored. * * Valid requests * ^^^^^^^^^^^^^^ @@ -XXX,XX +XXX,XX @@ static void qtest_process_command(CharFrontend *chr, gchar **words) fprintf(qtest_log_fp, "\n"); } - if (!command) { - /* Input line was blank: ignore it */ + if (!command || command[0] == '#') { + /* Input line was blank or a comment: ignore it */ return; } -- 2.43.0
The smc91c111 data frame format in memory (figure 8-1 in the datasheet) includes a "byte count" field which is intended to be the total size of the data frame, including not just the packet data but also the leading and trailing information like the status word and the byte count field itself. It is therefore possible for the guest to set this to a value so small that the leading and trailing fields won't fit and the packet has effectively a negative area. We weren't checking for this, with the result that when we subtract 6 from the length to get the length of the packet proper we end up with a negative length, which is then inconsistently handled in the qemu_send_packet() code such that we can try to transmit a very large amount of data and read off the end of the device's data array. Treat excessively small length values the same way we do excessively large values. As with the oversized case, the datasheet does not describe what happens for this software error case, and there is no relevant tx error condition for this, so we just log and drop the packet. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3304 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260226175549.1319476-1-peter.maydell@linaro.org --- hw/net/smc91c111.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index XXXXXXX..XXXXXXX 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -XXX,XX +XXX,XX @@ * LAN91C111 datasheet). */ #define MAX_PACKET_SIZE 2048 +/* + * Size of the non-data fields in a data frame: status word, + * byte count, control byte, and last data byte; this defines + * the smallest value the byte count in the frame can validly be. + */ +#define MIN_PACKET_SIZE 6 #define TYPE_SMC91C111 "smc91c111" OBJECT_DECLARE_SIMPLE_TYPE(smc91c111_state, SMC91C111) @@ -XXX,XX +XXX,XX @@ static void smc91c111_do_tx(smc91c111_state *s) *(p++) = 0x40; len = *(p++); len |= ((int)*(p++)) << 8; - if (len > MAX_PACKET_SIZE) { + if (len < MIN_PACKET_SIZE || len > MAX_PACKET_SIZE) { /* * Datasheet doesn't say what to do here, and there is no * relevant tx error condition listed. Log, and drop the packet. @@ -XXX,XX +XXX,XX @@ static void smc91c111_do_tx(smc91c111_state *s) smc91c111_complete_tx_packet(s, packetnum); continue; } - len -= 6; + /* + * Convert from size of the data frame to number of bytes of + * actual packet data. Whether the "last data byte" field is + * included in the packet depends on the ODD bit in the control + * byte at the end of the frame. + */ + len -= MIN_PACKET_SIZE; control = p[len + 1]; if (control & 0x20) len++; -- 2.43.0
From: Magnus Kulke <magnuskulke@linux.microsoft.com> Consolidating email aliases. Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: include/system/whpx-common.h F: include/system/whpx-internal.h MSHV -M: Magnus Kulke <magnus.kulke@linux.microsoft.com> +M: Magnus Kulke <magnuskulke@linux.microsoft.com> R: Wei Liu <wei.liu@kernel.org> S: Supported F: accel/mshv/ @@ -XXX,XX +XXX,XX @@ F: accel/nitro/ F: include/system/nitro-accel.h X86 MSHV CPUs -M: Magnus Kulke <magnus.kulke@linux.microsoft.com> +M: Magnus Kulke <magnuskulke@linux.microsoft.com> R: Wei Liu <wei.liu@kernel.org> S: Supported F: target/i386/mshv/ -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> Already existing VMSTATE_VARRAY_INT32 requires an array to be pre-allocated, however there are cases when the size is not known in advance and there is no real need to enforce it. Introduce VMSTATE_VARRAY_INT32_ALLOC as we currently have for UINT32 and UINT16. The first user of this variant will be the target/arm/machine.c cpreg indexes/values arrays. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Xu <peterx@redhat.com> Message-id: 20260304101625.1962633-2-eric.auger@redhat.com Suggested-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- include/migration/vmstate.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index XXXXXXX..XXXXXXX 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -XXX,XX +XXX,XX @@ extern const VMStateInfo vmstate_info_qlist; .offset = vmstate_offset_pointer(_state, _field, _type), \ } +#define VMSTATE_VARRAY_INT32_ALLOC(_field, _state, _field_num, _version, _info, _type) {\ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .num_offset = vmstate_offset_value(_state, _field_num, int32_t), \ + .info = &(_info), \ + .size = sizeof(_type), \ + .flags = VMS_VARRAY_INT32 | VMS_POINTER | VMS_ALLOC, \ + .offset = vmstate_offset_pointer(_state, _field, _type), \ +} + #define VMSTATE_VARRAY_UINT32_ALLOC(_field, _state, _field_num, _version, _info, _type) {\ .name = (stringify(_field)), \ .version_id = (_version), \ -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> This removes the need for explicitly allocating cpreg_vmstate arrays. On post save we simply point to cpreg arrays and set the length accordingly. Remove VMSTATE_VARRAY_INT32 for cpreg_vmstate_array_len as now the array is dynamically allocated. Also add a trace point on post_load to trace potential mismatch between the number of incoming cpregs versus current ones. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260304101625.1962633-3-eric.auger@redhat.com Suggested-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 5 ----- target/arm/kvm.c | 5 ----- target/arm/machine.c | 45 +++++++++++++++++++++++++++----------- target/arm/trace-events | 3 +++ target/arm/whpx/whpx-all.c | 7 ------ 5 files changed, 35 insertions(+), 30 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ void arm_init_cpreg_list(ARMCPU *cpu) if (arraylen) { cpu->cpreg_indexes = g_new(uint64_t, arraylen); cpu->cpreg_values = g_new(uint64_t, arraylen); - cpu->cpreg_vmstate_indexes = g_new(uint64_t, arraylen); - cpu->cpreg_vmstate_values = g_new(uint64_t, arraylen); } else { cpu->cpreg_indexes = NULL; cpu->cpreg_values = NULL; - cpu->cpreg_vmstate_indexes = NULL; - cpu->cpreg_vmstate_values = NULL; } - cpu->cpreg_vmstate_array_len = arraylen; cpu->cpreg_array_len = 0; g_hash_table_foreach(cpu->cp_regs, add_cpreg_to_list, cpu); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -XXX,XX +XXX,XX @@ static int kvm_arm_init_cpreg_list(ARMCPU *cpu) cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen); cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen); - cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes, - arraylen); - cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values, - arraylen); cpu->cpreg_array_len = arraylen; - cpu->cpreg_vmstate_array_len = arraylen; for (i = 0, arraylen = 0; i < rlp->n; i++) { uint64_t regidx = rlp->reg[i]; diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "cpu.h" +#include "trace.h" #include "qemu/error-report.h" #include "system/kvm.h" #include "system/tcg.h" @@ -XXX,XX +XXX,XX @@ static int cpu_pre_save(void *opaque) } } + /* + * On outbound migration, send the data in our cpreg_{values,indexes} + * arrays. The migration code will not allocate anything, but just + * reads the data pointed to by the VMSTATE_VARRAY_INT32_ALLOC() fields. + */ + cpu->cpreg_vmstate_indexes = cpu->cpreg_indexes; + cpu->cpreg_vmstate_values = cpu->cpreg_values; cpu->cpreg_vmstate_array_len = cpu->cpreg_array_len; - memcpy(cpu->cpreg_vmstate_indexes, cpu->cpreg_indexes, - cpu->cpreg_array_len * sizeof(uint64_t)); - memcpy(cpu->cpreg_vmstate_values, cpu->cpreg_values, - cpu->cpreg_array_len * sizeof(uint64_t)); return 0; } @@ -XXX,XX +XXX,XX @@ static int cpu_post_save(void *opaque) pmu_op_finish(&cpu->env); } + cpu->cpreg_vmstate_indexes = NULL; + cpu->cpreg_vmstate_values = NULL; + return 0; } @@ -XXX,XX +XXX,XX @@ static int cpu_pre_load(void *opaque) pmu_op_start(env); } + g_assert(!cpu->cpreg_vmstate_indexes); + g_assert(!cpu->cpreg_vmstate_values); + return 0; } @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) CPUARMState *env = &cpu->env; int i, v; + trace_cpu_post_load(cpu->cpreg_vmstate_array_len, + cpu->cpreg_array_len); + /* * Handle migration compatibility from old QEMU which didn't * send the irq-line-state subsection. A QEMU without it did not @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) } } + g_free(cpu->cpreg_vmstate_indexes); + g_free(cpu->cpreg_vmstate_values); + cpu->cpreg_vmstate_indexes = NULL; + cpu->cpreg_vmstate_values = NULL; + /* * Misaligned thumb pc is architecturally impossible. Fail the * incoming migration. For TCG it would trigger the assert in @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT32_ARRAY(env.fiq_regs, ARMCPU, 5), VMSTATE_UINT64_ARRAY(env.elr_el, ARMCPU, 4), VMSTATE_UINT64_ARRAY(env.sp_el, ARMCPU, 4), - /* The length-check must come before the arrays to avoid - * incoming data possibly overflowing the array. + /* + * The length must come before the arrays so we can + * allocate the arrays before their data arrives */ - VMSTATE_INT32_POSITIVE_LE(cpreg_vmstate_array_len, ARMCPU), - VMSTATE_VARRAY_INT32(cpreg_vmstate_indexes, ARMCPU, - cpreg_vmstate_array_len, - 0, vmstate_info_uint64, uint64_t), - VMSTATE_VARRAY_INT32(cpreg_vmstate_values, ARMCPU, - cpreg_vmstate_array_len, - 0, vmstate_info_uint64, uint64_t), + VMSTATE_INT32(cpreg_vmstate_array_len, ARMCPU), + VMSTATE_VARRAY_INT32_ALLOC(cpreg_vmstate_indexes, ARMCPU, + cpreg_vmstate_array_len, + 0, vmstate_info_uint64, uint64_t), + VMSTATE_VARRAY_INT32_ALLOC(cpreg_vmstate_values, ARMCPU, + cpreg_vmstate_array_len, + 0, vmstate_info_uint64, uint64_t), VMSTATE_UINT64(env.exclusive_addr, ARMCPU), VMSTATE_UINT64(env.exclusive_val, ARMCPU), VMSTATE_UINT64(env.exclusive_high, ARMCPU), diff --git a/target/arm/trace-events b/target/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -XXX,XX +XXX,XX @@ arm_powerctl_reset_cpu(uint64_t mp_aff) "cpu %" PRIu64 # tcg/psci.c and hvf/hvf.c arm_psci_call(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t cpuid) "PSCI Call x0=0x%016"PRIx64" x1=0x%016"PRIx64" x2=0x%016"PRIx64" x3=0x%016"PRIx64" cpuid=0x%x" + +# machine.c +cpu_post_load(uint32_t cpreg_vmstate_array_len, uint32_t cpreg_array_len) "cpreg_vmstate_array_len=%d cpreg_array_len=%d" diff --git a/target/arm/whpx/whpx-all.c b/target/arm/whpx/whpx-all.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/whpx/whpx-all.c +++ b/target/arm/whpx/whpx-all.c @@ -XXX,XX +XXX,XX @@ int whpx_init_vcpu(CPUState *cpu) sregs_match_len); arm_cpu->cpreg_values = g_renew(uint64_t, arm_cpu->cpreg_values, sregs_match_len); - arm_cpu->cpreg_vmstate_indexes = g_renew(uint64_t, - arm_cpu->cpreg_vmstate_indexes, - sregs_match_len); - arm_cpu->cpreg_vmstate_values = g_renew(uint64_t, - arm_cpu->cpreg_vmstate_values, - sregs_match_len); memset(arm_cpu->cpreg_values, 0, sregs_match_len * sizeof(uint64_t)); @@ -XXX,XX +XXX,XX @@ int whpx_init_vcpu(CPUState *cpu) } } arm_cpu->cpreg_array_len = sregs_cnt; - arm_cpu->cpreg_vmstate_array_len = sregs_cnt; assert(write_cpustate_to_list(arm_cpu, false)); -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> We want to use kvm_print_register_name() in machine.c so let's export the helper and implement a stub when kvm is not enabled. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260304101625.1962633-4-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/kvm-stub.c | 5 +++++ target/arm/kvm.c | 2 +- target/arm/kvm_arm.h | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/kvm-stub.c +++ b/target/arm/kvm-stub.c @@ -XXX,XX +XXX,XX @@ void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3) { g_assert_not_reached(); } + +char *kvm_print_register_name(uint64_t regidx) +{ + g_assert_not_reached(); +} diff --git a/target/arm/kvm.c b/target/arm/kvm.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -XXX,XX +XXX,XX @@ static gchar *kvm_print_sve_register_name(uint64_t regidx) } } -static gchar *kvm_print_register_name(uint64_t regidx) +char *kvm_print_register_name(uint64_t regidx) { switch ((regidx & KVM_REG_ARM_COPROC_MASK)) { case KVM_REG_ARM_CORE: diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -XXX,XX +XXX,XX @@ void arm_cpu_kvm_set_irq(void *arm_cpu, int irq, int level); void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3); +/* + * kvm_print_register_name: + * @regidx: register KVM index + * + * Returns a human-readable string representing this register + * The caller must free the string with g_free(). + */ +char *kvm_print_register_name(uint64_t regidx); + #endif -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> As opposed to other register types, arm64 system register decoding is not introduced by any 'register' mention which can lead to unfriendly user-facing traces. Let's add "system register" Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260304101625.1962633-5-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -XXX,XX +XXX,XX @@ char *kvm_print_register_name(uint64_t regidx) case KVM_REG_ARM_DEMUX: return g_strdup_printf("demuxed reg %"PRIx64, regidx); case KVM_REG_ARM64_SYSREG: - return g_strdup_printf("op0:%d op1:%d crn:%d crm:%d op2:%d", + return g_strdup_printf("system register op0:%d op1:%d crn:%d crm:%d op2:%d", CP_REG_ARM64_SYSREG_OP(regidx, OP0), CP_REG_ARM64_SYSREG_OP(regidx, OP1), CP_REG_ARM64_SYSREG_OP(regidx, CRN), -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> Whenever there is a mismatch between cpreg indexes in the incoming stream and cpregs exposed by the destination output the name of the register. We use a print_register_name() wrapper helper. At the moment we are only able to do a nice decoding of the index for KVM regs. Without this patch, the error would be: qemu-system-aarch64: load of migration failed: Operation not permitted: error while loading state for instance 0x0 of device 'cpu': post load hook failed for: cpu, version_id: 22, minimum_version: 22, ret: -1 which is not helpful for the end user to understand the actual issue. This patch adds the actual information about the probme: qemu-system-aarch64: cpu_post_load: system register op0:3 op1:0 crn:2 crm:0 op2:3 in the incoming stream but unknown on the destination, fail migration Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260304101625.1962633-6-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/machine.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "cpu.h" +#include "cpregs.h" #include "trace.h" #include "qemu/error-report.h" #include "system/kvm.h" @@ -XXX,XX +XXX,XX @@ static int cpu_pre_load(void *opaque) return 0; } +static gchar *print_register_name(uint64_t kvm_regidx) +{ + if (kvm_enabled()) { + return kvm_print_register_name(kvm_regidx); + } else { + return g_strdup_printf("system register 0x%x", kvm_to_cpreg_id(kvm_regidx)); + } +} + static int cpu_post_load(void *opaque, int version_id) { ARMCPU *cpu = opaque; @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) for (i = 0, v = 0; i < cpu->cpreg_array_len && v < cpu->cpreg_vmstate_array_len; i++) { if (cpu->cpreg_vmstate_indexes[v] > cpu->cpreg_indexes[i]) { - /* register in our list but not incoming : skip it */ + g_autofree gchar *name = print_register_name(cpu->cpreg_indexes[i]); + + warn_report("%s: %s " + "expected by the destination but not in the incoming stream: " + "skip it", __func__, name); continue; } if (cpu->cpreg_vmstate_indexes[v] < cpu->cpreg_indexes[i]) { - /* register in their list but not ours: fail migration */ + g_autofree gchar *name = print_register_name(cpu->cpreg_vmstate_indexes[v]); + + error_report("%s: %s in the incoming stream but unknown on the destination: " + "fail migration", __func__, name); return -1; } /* matching register, copy the value over */ -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> At the moment, cpu_post_load() exits with error on the first catch of unexpected register in the incoming stream. Let the code go further and trace all the issues before exiting. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260304101625.1962633-7-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/machine.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) { ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; + bool fail = false; int i, v; trace_cpu_post_load(cpu->cpreg_vmstate_array_len, @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) */ for (i = 0, v = 0; i < cpu->cpreg_array_len - && v < cpu->cpreg_vmstate_array_len; i++) { + && v < cpu->cpreg_vmstate_array_len;) { if (cpu->cpreg_vmstate_indexes[v] > cpu->cpreg_indexes[i]) { g_autofree gchar *name = print_register_name(cpu->cpreg_indexes[i]); warn_report("%s: %s " "expected by the destination but not in the incoming stream: " "skip it", __func__, name); + i++; continue; } if (cpu->cpreg_vmstate_indexes[v] < cpu->cpreg_indexes[i]) { @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) error_report("%s: %s in the incoming stream but unknown on the destination: " "fail migration", __func__, name); - return -1; + v++; + fail = true; + continue; } /* matching register, copy the value over */ cpu->cpreg_values[i] = cpu->cpreg_vmstate_values[v]; + i++; v++; } + if (fail) { + return -1; + } if (kvm_enabled()) { if (!kvm_arm_cpu_post_load(cpu)) { -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> Currently the check of cpreg index matches fail to detect a situation where the length of both arrays is same but - destination has an extra register not found in the incoming stream (idx1) - source has an extra register not found in the destination (idx2) where idx1 < = idx2 Normally this should fail but it does not. Fix the logic to scan all indexes. Fixes: 721fae12536 ("target-arm: Convert TCG to using (index,value) list for cp migration") Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260304101625.1962633-8-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/machine.c | 61 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ static gchar *print_register_name(uint64_t kvm_regidx) } } +/* + * Handle the situation where @kvmidx is on destination but not + * in the incoming stream. This never fails the migration. + */ +static void handle_cpreg_missing_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx) +{ + g_autofree gchar *name = print_register_name(kvmidx); + + warn_report("%s: %s " + "expected by the destination but not in the incoming stream: " + "skip it", __func__, name); +} + +/* + * Handle the situation where @kvmidx is in the incoming stream + * but not on destination. This currently fails the migration but + * we plan to accomodate some exceptions, hence the boolean returned value. + */ +static bool handle_cpreg_only_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx) +{ + g_autofree gchar *name = print_register_name(kvmidx); + bool fail = true; + + error_report("%s: %s in the incoming stream but unknown on the " + "destination: fail migration", __func__, name); + + return fail; +} + static int cpu_post_load(void *opaque, int version_id) { ARMCPU *cpu = opaque; @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) for (i = 0, v = 0; i < cpu->cpreg_array_len && v < cpu->cpreg_vmstate_array_len;) { if (cpu->cpreg_vmstate_indexes[v] > cpu->cpreg_indexes[i]) { - g_autofree gchar *name = print_register_name(cpu->cpreg_indexes[i]); - - warn_report("%s: %s " - "expected by the destination but not in the incoming stream: " - "skip it", __func__, name); - i++; + handle_cpreg_missing_in_incoming_stream(cpu, cpu->cpreg_indexes[i++]); continue; } if (cpu->cpreg_vmstate_indexes[v] < cpu->cpreg_indexes[i]) { - g_autofree gchar *name = print_register_name(cpu->cpreg_vmstate_indexes[v]); - - error_report("%s: %s in the incoming stream but unknown on the destination: " - "fail migration", __func__, name); - v++; - fail = true; + fail = handle_cpreg_only_in_incoming_stream(cpu, + cpu->cpreg_vmstate_indexes[v++]); continue; } /* matching register, copy the value over */ @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) i++; v++; } + + /* + * if we have reached the end of the incoming array but there are + * still regs in cpreg, continue parsing the regs which are missing + * in the input stream + */ + for ( ; i < cpu->cpreg_array_len; i++) { + handle_cpreg_missing_in_incoming_stream(cpu, cpu->cpreg_indexes[i]); + } + /* + * if we have reached the end of the cpreg array but there are + * still regs in the input stream, continue parsing the vmstate array + */ + for ( ; v < cpu->cpreg_vmstate_array_len; v++) { + fail = handle_cpreg_only_in_incoming_stream(cpu, + cpu->cpreg_vmstate_indexes[v]); + } if (fail) { return -1; } -- 2.43.0
The oss-fuzz code uses an lsan_suppressions file to suppress certain leak-sanitizer cases that are known issues or not our code's bug. This is useful more widely than just for the fuzzer harness: if you want to build QEMU with the leak sanitizer enabled and run 'make check' then you will want to suppress some bogus leak reports. Move the file up a directory. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Yodel Eldar <yodel.eldar@yodel.dev> Message-id: 20260302092225.4088227-2-peter.maydell@linaro.org --- .gitlab-ci.d/buildtest.yml | 2 +- scripts/{oss-fuzz => }/lsan_suppressions.txt | 0 tests/docker/test-fuzz | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename scripts/{oss-fuzz => }/lsan_suppressions.txt (100%) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index XXXXXXX..XXXXXXX 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -XXX,XX +XXX,XX @@ build-oss-fuzz: IMAGE: fedora script: - mkdir build-oss-fuzz - - export LSAN_OPTIONS=suppressions=scripts/oss-fuzz/lsan_suppressions.txt + - export LSAN_OPTIONS=suppressions=scripts/lsan_suppressions.txt - CC="clang" CXX="clang++" CFLAGS="-fsanitize=address" ./scripts/oss-fuzz/build.sh - export ASAN_OPTIONS="fast_unwind_on_malloc=0" diff --git a/scripts/oss-fuzz/lsan_suppressions.txt b/scripts/lsan_suppressions.txt similarity index 100% rename from scripts/oss-fuzz/lsan_suppressions.txt rename to scripts/lsan_suppressions.txt diff --git a/tests/docker/test-fuzz b/tests/docker/test-fuzz index XXXXXXX..XXXXXXX 100755 --- a/tests/docker/test-fuzz +++ b/tests/docker/test-fuzz @@ -XXX,XX +XXX,XX @@ cd "$BUILD_DIR" cp -a $QEMU_SRC . cd src mkdir build-oss-fuzz -export LSAN_OPTIONS=suppressions=scripts/oss-fuzz/lsan_suppressions.txt +export LSAN_OPTIONS=suppressions=scripts/lsan_suppressions.txt env CC="clang" CXX="clang++" CFLAGS="-fsanitize=address" ./scripts/oss-fuzz/build.sh export ASAN_OPTIONS="fast_unwind_on_malloc=0" for fuzzer in $(find ./build-oss-fuzz/DEST_DIR/ -executable -type f | grep -v slirp); do -- 2.43.0
Running "make check" with the clang leak sanitizer reveals some leak reports which are either not our problem or else not a leak which is worth our time to fix. Add some suppressions for these. While we're touching the file, add the usual SPDX header and a comment explaining how to use it. Reviewed-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260302092225.4088227-3-peter.maydell@linaro.org --- scripts/lsan_suppressions.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/scripts/lsan_suppressions.txt b/scripts/lsan_suppressions.txt index XXXXXXX..XXXXXXX 100644 --- a/scripts/lsan_suppressions.txt +++ b/scripts/lsan_suppressions.txt @@ -XXX,XX +XXX,XX @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# This is a set of suppressions for LeakSanitizer; you can use it by setting +# LSAN_OPTIONS="suppressions=/path/to/scripts/lsan_suppressions.txt" +# when running a QEMU built with the leak-sanitizer. + # The tcmalloc on Fedora37 confuses things leak:/lib64/libtcmalloc_minimal.so.4 # libxkbcommon also leaks in qemu-keymap leak:/lib64/libxkbcommon.so.0 + +# g_set_user_dirs() deliberately leaks the previous cached g_get_user_* +# values. This is documented in upstream glib's valgrind-format +# suppression file: +# https://github.com/GNOME/glib/blob/main/tools/glib.supp +# This avoids false positive leak reports for the qga-ssh-test. +leak:g_set_user_dirs + +# qemu_irq_intercept_in is only used by the qtest harness, and +# its API inherently involves a leak. +# While we could keep track of the old IRQ data structure +# in order to free it, it doesn't seem very important to fix +# since it is only used by the qtest test harness. +# Just ignore the leak, at least for the moment. +leak:qemu_irq_intercept_in -- 2.43.0
We allocate the string for the GPIO property name, but never free it. Use g_autofree to avoid this. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260302092225.4088227-4-peter.maydell@linaro.org --- hw/gpio/aspeed_gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index XXXXXXX..XXXXXXX 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -XXX,XX +XXX,XX @@ static void aspeed_gpio_init(Object *obj) } for (int i = 0; i < agc->nr_gpio_sets; i++) { - char *name = g_strdup_printf("gpio-set[%d]", i); + g_autofree char *name = g_strdup_printf("gpio-set[%d]", i); object_property_add(obj, name, "uint32", aspeed_gpio_get_set, aspeed_gpio_set_set, NULL, NULL); } -- 2.43.0
The QPCIDevice we get via qpci_device_foreach() is allocated memory, and we need to g_free() it after use. This fixes asan leaks like this: Direct leak of 64 byte(s) in 1 object(s) allocated from: #0 0x622a5f16913d in calloc (/home/pm215/qemu/build/arm-clang/tests/qtest/iommu-smmuv3-test+0x1d413d) (BuildId: bc598be1f4ad6d1a9a600c55aeef36108bdb6a04) #1 0x73ee41c0f771 in g_malloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x63771) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3) #2 0x622a5f1d4cec in qpci_device_find /home/pm215/qemu/build/arm-clang/../../tests/qtest/libqos/pci.c:82:11 #3 0x622a5f1d4cec in qpci_device_foreach /home/pm215/qemu/build/arm-clang/../../tests/qtest/libqos/pci.c:34:19 #4 0x622a5f23cc73 in setup_qtest_pci_device /home/pm215/qemu/build/arm-clang/../../tests/qtest/iommu-smmuv3-test.c:45:5 #5 0x622a5f23cc73 in run_smmuv3_translation /home/pm215/qemu/build/arm-clang/../../tests/qtest/iommu-smmuv3-test.c:74:11 Reviewed-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260302092225.4088227-5-peter.maydell@linaro.org --- tests/qtest/iommu-smmuv3-test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/iommu-smmuv3-test.c b/tests/qtest/iommu-smmuv3-test.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/iommu-smmuv3-test.c +++ b/tests/qtest/iommu-smmuv3-test.c @@ -XXX,XX +XXX,XX @@ static void run_smmuv3_translation(const QSMMUTestConfig *cfg) g_test_message("### SMMUv3 translation mode=%d sec_sid=%d ###", cfg->trans_mode, cfg->sec_sid); qsmmu_run_translation_case(qts, dev, bar, VIRT_SMMU_BASE, cfg); + g_free(dev); qtest_quit(qts); } -- 2.43.0
From: Fabiano Rosas <farosas@suse.de> The IOWatchPoll holds a reference to the iochannel while the "child" source (iwp->src) is removed from the context and freed. Freeing the source leads to the iochannel being also freed at qio_channel_fd_source_finalize(). Later, io_watch_poll_prepare() tries to create another source with the same iochannel and hits an use after free: ==8241==ERROR: AddressSanitizer: heap-use-after-free on address 0x514000000040 READ of size 8 at 0x514000000040 thread T2 #0 0x561c2d272fcd in object_get_class ../qom/object.c:1043:17 #1 0x561c2d338f84 in QIO_CHANNEL_GET_CLASS include/io/channel.h:29:1 #2 0x561c2d33b26f in qio_channel_create_watch ../io/channel.c:388:30 #3 0x561c2d2f0993 in io_watch_poll_prepare ../chardev/char-io.c:65:20 ... 0x514000000040 is located 0 bytes inside of 392-byte region [0x514000000040,0x5140000001c8) freed by thread T2 here: #0 0x561c2d2319a5 in free #1 0x7fb2c0926638 in g_free #2 0x561c2d276507 in object_finalize ../qom/object.c:734:9 #3 0x561c2d271d0d in object_unref ../qom/object.c:1231:9 #4 0x561c2d32ef1d in qio_channel_fd_source_finalize ../io/channel-watch.c:95:5 #5 0x7fb2c091d124 in g_source_unref_internal ../glib/gmain.c:2298 #6 0x561c2d2f0b6c in io_watch_poll_prepare ../chardev/char-io.c:71:9 ... previously allocated by thread T3 (connect) here: #0 0x561c2d231c69 in malloc #1 0x7fb2c0926518 in g_malloc #2 0x561c2d27246e in object_new_with_type ../qom/object.c:767:15 #3 0x561c2d272530 in object_new ../qom/object.c:789:12 #4 0x561c2d320193 in qio_channel_socket_new ../io/channel-socket.c:64:31 #5 0x561c2d308013 in tcp_chr_connect_client_async ../chardev/char-socket.c:1181:12 #6 0x561c2d3002e7 in qmp_chardev_open_socket_client ../chardev/char-socket.c:1281:9 ... Fix the issue by incrementing the iochannel reference count when the IOWatchPoll takes a reference and decrementing when it is finalized. Signed-off-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260302092225.4088227-6-peter.maydell@linaro.org [PMM: rebased] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- chardev/char-io.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chardev/char-io.c b/chardev/char-io.c index XXXXXXX..XXXXXXX 100644 --- a/chardev/char-io.c +++ b/chardev/char-io.c @@ -XXX,XX +XXX,XX @@ static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback, static void io_watch_poll_finalize(GSource *source) { IOWatchPoll *iwp = io_watch_poll_from_source(source); + + object_unref(OBJECT(iwp->ioc)); + if (iwp->src) { g_source_destroy(iwp->src); g_source_unref(iwp->src); @@ -XXX,XX +XXX,XX @@ GSource *io_add_watch_poll(Chardev *chr, iwp->fd_can_read = fd_can_read; iwp->opaque = user_data; iwp->ioc = ioc; + object_ref(OBJECT(iwp->ioc)); + iwp->fd_read = (GSourceFunc) fd_read; iwp->src = NULL; iwp->context = context; -- 2.43.0
From: Fabiano Rosas <farosas@suse.de> tcp_chr_free_connection() can be called multiple times in succession, in which case the yank function will get as argument a NULL s->sioc that has been cleared by the previous tcp_chr_free_connection() call. This leads to an abort() at yank_unregister_function(). #0 __GI_raise (sig=6) at ../sysdeps/unix/sysv/linux/raise.c:51 #1 __GI_abort () at abort.c:79 #2 qtest_check_status (s=0x513000005600) at ../tests/qtest/libqtest.c:209 #3 qtest_wait_qemu (s=0x513000005600) at ../tests/qtest/libqtest.c:273 #4 qtest_kill_qemu (s=0x513000005600) at ../tests/qtest/libqtest.c:285 #5 kill_qemu_hook_func (s=0x513000005600) at ../tests/qtest/libqtest.c:294 #6 g_hook_list_invoke (hook_list=0x55ea9cc750c0 <abrt_hooks>, may_recurse=0) at ../glib/ghook.c:534 #7 sigabrt_handler (signo=6) at ../tests/qtest/libqtest.c:299 #8 <signal handler called> #9 __GI_raise (sig=6) at ../sysdeps/unix/sysv/linux/raise.c:51 #10 __GI_abort () at abort.c:79 #11 yank_unregister_function (instance=0x7fb26f2ea9a0, func=0x55ea9bcc0a10 <char_socket_yank_iochannel>, opaque=0x0) at ../util/yank.c:151 #12 tcp_chr_free_connection (chr=0x51300000ffc0) at ../chardev/char-socket.c:385 #13 tcp_chr_disconnect_locked (chr=0x51300000ffc0) at ../chardev/char-socket.c:477 #14 tcp_chr_disconnect (chr=0x51300000ffc0) at ../chardev/char-socket.c:495 #15 tcp_chr_hup (channel=0x514000000040, cond=G_IO_HUP, opaque=0x51300000ffc0) at ../chardev/char-socket.c:536 #16 qio_channel_fd_source_dispatch (source=0x50c0000b5fc0, callback=0x55ea9bcd6770 <tcp_chr_hup>, user_data=0x51300000ffc0) at ../io/channel-watch.c:84 #17 g_main_dispatch (context=0x50f000000040) at ../glib/gmain.c:3381 #18 g_main_context_dispatch (context=context@entry=0x50f000000040) at ../glib/gmain.c:4099 #19 g_main_context_iterate (context=0x50f000000040, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:4175 #20 g_main_loop_run (loop=0x502000055690) at ../glib/gmain.c:4373 Commit ebae6477dc ("chardev: check if the chardev is registered for yanking") seems to have encountered a similar issue, but checking s->registered_yank is not a complete solution because that flag pertains to the yank instance, not to each individual function. Skip the yank_unregister_function() in case s->sioc is already NULL, which indicates the last yank function was already removed. Signed-off-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260302092225.4088227-7-peter.maydell@linaro.org [PMM: rebased] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- chardev/char-socket.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index XXXXXXX..XXXXXXX 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -XXX,XX +XXX,XX @@ static void tcp_chr_free_connection(Chardev *chr) tcp_chr_set_msgfds(chr, NULL, 0); remove_fd_in_watch(chr); - if (s->registered_yank && + + if (s->registered_yank && s->sioc && (s->state == TCP_CHARDEV_STATE_CONNECTING || s->state == TCP_CHARDEV_STATE_CONNECTED)) { yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), -- 2.43.0
From: Fabiano Rosas <farosas@suse.de> There's currently five places where the yank function is being registered and they all come right before tcp_chr_new_client(). Fold them into it. Signed-off-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260302092225.4088227-8-peter.maydell@linaro.org [PMM: rebased] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- chardev/char-socket.c | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index XXXXXXX..XXXXXXX 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -XXX,XX +XXX,XX @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc) s->sioc = sioc; object_ref(OBJECT(sioc)); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + char_socket_yank_iochannel, + QIO_CHANNEL(sioc)); + } + if (s->do_nodelay) { qio_channel_set_delay(s->ioc, false); } @@ -XXX,XX +XXX,XX @@ static int tcp_chr_add_client(Chardev *chr, int fd) } tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, sioc); - if (s->registered_yank) { - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - char_socket_yank_iochannel, - QIO_CHANNEL(sioc)); - } ret = tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); return ret; @@ -XXX,XX +XXX,XX @@ static void tcp_chr_accept(QIONetListener *listener, tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, cioc); - if (s->registered_yank) { - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - char_socket_yank_iochannel, - QIO_CHANNEL(cioc)); - } tcp_chr_new_client(chr, cioc); } @@ -XXX,XX +XXX,XX @@ static int tcp_chr_connect_client_sync(Chardev *chr, Error **errp) object_unref(OBJECT(sioc)); return -1; } - if (s->registered_yank) { - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - char_socket_yank_iochannel, - QIO_CHANNEL(sioc)); - } tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); return 0; @@ -XXX,XX +XXX,XX @@ static void tcp_chr_accept_server_sync(Chardev *chr) tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); sioc = qio_net_listener_wait_client(s->listener); tcp_chr_set_client_ioc_name(chr, sioc); - if (s->registered_yank) { - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - char_socket_yank_iochannel, - QIO_CHANNEL(sioc)); - } tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); } @@ -XXX,XX +XXX,XX @@ static void tcp_chr_connect_client_async(Chardev *chr) tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); sioc = qio_channel_socket_new(); tcp_chr_set_client_ioc_name(chr, sioc); - if (s->registered_yank) { - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - char_socket_yank_iochannel, - QIO_CHANNEL(sioc)); - } /* * Normally code would use the qio_channel_socket_connect_async * method which uses a QIOTask + qio_task_set_error internally -- 2.43.0
From: Fabiano Rosas <farosas@suse.de> Free the test arguments after test execution. Signed-off-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260302092225.4088227-9-peter.maydell@linaro.org --- tests/qtest/test-x86-cpuid-compat.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -XXX,XX +XXX,XX @@ static void test_cpuid_prop(const void *data) qobject_unref(value); g_free(path); + g_free((void *)args->cmdline); + g_free((void *)data); } static void add_cpuid_test(const char *name, const char *cpu, @@ -XXX,XX +XXX,XX @@ static void test_feature_flag(const void *data) qobject_unref(present); qobject_unref(filtered); g_free(path); + g_free((void *)args->cmdline); + g_free((void *)data); } /* -- 2.43.0
From: Fabiano Rosas <farosas@suse.de> The walk_path() function of qos-test.c, which walks the graph and adds tests to the test suite uses GLib's g_test_add_data_func_full() function: g_test_add_data_func_full (const char *testpath, gpointer test_data, GTestDataFunc test_func, GDestroyNotify data_free_func) Despite GLib's documentation stating that @data_free_func is a destructor for @test_data, this is not the case. The destructor is supposed to be paired with a constructor, which GLib only accepts via g_test_create_case(). Providing externally allocated data plus a destructor function only works if the test is guaranteed to execute, otherwise the test_data is never deallocated. Due to how subprocessess are implemented in qos-test, each test gets added twice and an extra test gets added per subprocess. In a regular run, the extra subprocess will not be executed and in a single test run (-p), none of the other tests will be executed (+1 per subprocess), leaking 'path_vec' and 'subprocess_path'. Fix this by storing all the path vectors in a list and freeing them all at the end of the program (including subprocess invocations) and moving the allocation of 'subprocess_path' into run_one_subprocess(). While here add some documentation explaining why the graph needs to be walked twice and tests re-added. Signed-off-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260302092225.4088227-10-peter.maydell@linaro.org [PMM: rebased; rewrote the comment in main() a bit to account for the if (g_test_subprocess()) block it was previously inside no longer being present. ] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- tests/qtest/qos-test.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -XXX,XX +XXX,XX @@ #include "libqos/qos_external.h" static char *old_path; +static GSList *path_vecs; /** @@ -XXX,XX +XXX,XX @@ static void run_one_test(const void *arg) static void subprocess_run_one_test(const void *arg) { - const gchar *path = arg; - g_test_trap_subprocess(path, 180 * G_USEC_PER_SEC, + char **path_vec = (char **) arg; + gchar *path = g_strjoinv("/", path_vec + 1); + gchar *subprocess_path = g_strdup_printf("/%s/subprocess", path); + + g_test_trap_subprocess(subprocess_path, 180 * G_USEC_PER_SEC, G_TEST_SUBPROCESS_INHERIT_STDOUT | G_TEST_SUBPROCESS_INHERIT_STDERR); g_test_trap_assert_passed(); + g_free(path); + g_free(subprocess_path); } static void destroy_pathv(void *arg) @@ -XXX,XX +XXX,XX @@ static void walk_path(QOSGraphNode *orig_path, int len) GString *cmd_line = g_string_new(""); GString *cmd_line2 = g_string_new(""); + path_vecs = g_slist_append(path_vecs, path_vec); path = qos_graph_get_node(node_name); /* root */ node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */ @@ -XXX,XX +XXX,XX @@ static void walk_path(QOSGraphNode *orig_path, int len) path_vec[0] = g_string_free(cmd_line, false); if (path->u.test.subprocess) { - gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess", - qtest_get_arch(), path_str); - qtest_add_data_func_full(path_str, subprocess_path, - subprocess_run_one_test, g_free); - g_test_add_data_func_full(subprocess_path, path_vec, - run_one_test, destroy_pathv); + gchar *subprocess_path = g_strdup_printf("%s/%s", path_str, + "subprocess"); + + qtest_add_data_func(path_str, path_vec, subprocess_run_one_test); + qtest_add_data_func(subprocess_path, path_vec, run_one_test); + + g_free(subprocess_path); } else { - qtest_add_data_func_full(path_str, path_vec, - run_one_test, destroy_pathv); + qtest_add_data_func(path_str, path_vec, run_one_test); } g_free(path_str); @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char** envp) module_call_init(MODULE_INIT_LIBQOS); qos_set_machines_devices_available(); + /* + * Even if this invocation was done to run a single test in a + * subprocess (i.e. g_test_subprocess() is true), gtester doesn't + * expose the test name, so w still need to execute the whole + * thing as normal, including walking the QOS graph to add all + * the tests, in order for g_test_run() to find the one /subprocess + * test that it is going to execute. + */ qos_graph_foreach_test_path(walk_path); if (g_test_verbose()) { qos_dump_graph(); @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char** envp) qtest_end(); qos_graph_destroy(); g_free(old_path); + g_slist_free_full(path_vecs, (GDestroyNotify)destroy_pathv); return 0; } -- 2.43.0
From: Tao Tang <tangtao1634@phytium.com.cn> The current definition of the SMMU_CR0_RESERVED mask is incorrect. It mistakenly treats bit 10 (DPT_WALK_EN) as a reserved bit while treating bit 9 (RES0) as an implemented bit. According to the SMMU architecture specification, the layout for CR0 is: | 31:11| RES0 | | 10 | DPT_WALK_EN | | 9 | RES0 | | 8:6 | VMW | | 5 | RES0 | | 4 | ATSCHK | | 3 | CMDQEN | | 2 | EVENTQEN | | 1 | PRIQEN | | 0 | SMMUEN | Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Mostafa Saleh <smostafa@google.com> Message-id: 20260304142344.3341444-2-tangtao1634@phytium.com.cn Fixes: fae4be38b35 ("hw/arm/smmuv3: Implement MMIO write operations") Link: https://lists.gnu.org/archive/html/qemu-arm/2025-06/msg00088.html Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- include/hw/arm/smmuv3-common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/arm/smmuv3-common.h b/include/hw/arm/smmuv3-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/smmuv3-common.h +++ b/include/hw/arm/smmuv3-common.h @@ -XXX,XX +XXX,XX @@ REG32(CR0, 0x20) FIELD(CR0, EVENTQEN, 2, 1) FIELD(CR0, CMDQEN, 3, 1) -#define SMMU_CR0_RESERVED 0xFFFFFC20 +#define SMMU_CR0_RESERVED 0xFFFFFA20 REG32(CR0ACK, 0x24) REG32(CR1, 0x28) -- 2.43.0
From: Tao Tang <tangtao1634@phytium.com.cn> The FIELD macro for the SMMU enable bit in the CR0 register was incorrectly named SMMU_ENABLE. The ARM SMMUv3 Architecture Specification (both older IHI 0070.E.a and newer IHI 0070.G.b) consistently refers to the SMMU enable bit as SMMUEN. This change makes our implementation consistent with the manual. Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Mostafa Saleh <smostafa@google.com> Message-id: 20260304142344.3341444-3-tangtao1634@phytium.com.cn Fixes: 10a83cb9887 ("hw/arm/smmuv3: Skeleton") Link: https://lists.nongnu.org/archive/html/qemu-arm/2025-09/msg01270.html Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-internal.h | 2 +- include/hw/arm/smmuv3-common.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -XXX,XX +XXX,XX @@ typedef enum SMMUTranslationClass { static inline int smmu_enabled(SMMUv3State *s) { - return FIELD_EX32(s->cr[0], CR0, SMMU_ENABLE); + return FIELD_EX32(s->cr[0], CR0, SMMUEN); } /* Command Queue Entry */ diff --git a/include/hw/arm/smmuv3-common.h b/include/hw/arm/smmuv3-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/smmuv3-common.h +++ b/include/hw/arm/smmuv3-common.h @@ -XXX,XX +XXX,XX @@ REG32(IDR5, 0x14) REG32(IIDR, 0x18) REG32(AIDR, 0x1c) REG32(CR0, 0x20) - FIELD(CR0, SMMU_ENABLE, 0, 1) + FIELD(CR0, SMMUEN, 0, 1) FIELD(CR0, EVENTQEN, 2, 1) FIELD(CR0, CMDQEN, 3, 1) -- 2.43.0
From: Tao Tang <tangtao1634@phytium.com.cn> Add a STAGE1_SUPPORTED check in the CMD_CFGI_CD and CMD_CFGI_CD_ALL path and return CERROR_ILL when stage-1 translation is not implemented, matching the architecture requirement (IHI 0070G.b, page 176). Fixes: 32cfd7f39e08 ("hw/arm/smmuv3: Cache/invalidate config data") Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260304142344.3341444-4-tangtao1634@phytium.com.cn Links: https://lore.kernel.org/qemu-devel/20260221101733.2995020-1-tangtao1634@phytium.com.cn/ Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -XXX,XX +XXX,XX @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp) break; } + /* + * This command raises CERROR_ILL when stage 1 is not implemented + * according to (IHI 0070G.b) Page 176. + */ + if (!STAGE1_SUPPORTED(s)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + trace_smmuv3_cmdq_cfgi_cd(sid); smmuv3_flush_config(sdev); if (!smmuv3_accel_issue_inv_cmd(s, &cmd, sdev, errp)) { -- 2.43.0
From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> SME2 support adds the following state for HVF guests: - Vector registers Z0, ... , Z31 (introduced by FEAT_SVE but HVF does not support it) - Predicate registers P0, .., P15 (also FEAT_SVE) - ZA register - ZT0 register - PSTATE.{SM,ZA} bits (SVCR pseudo-register) - SMPRI_EL1 which handles the PE's priority in the SMCU - TPIDR2_EL0 the thread local ID register for SME Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr> Message-id: 20260306-sme2-hvf-v7-1-e72eeda41ed3@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 298 ++++++++++++++++++++++++++++++++- target/arm/hvf/hvf_sme_stubs.h | 172 +++++++++++++++++++ target/arm/hvf/sysreg.c.inc | 8 + target/arm/hvf_arm.h | 41 +++++ target/arm/machine.c | 2 +- 5 files changed, 518 insertions(+), 3 deletions(-) create mode 100644 target/arm/hvf/hvf_sme_stubs.h diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ static const struct hvf_reg_match hvf_fpreg_match[] = { { HV_SIMD_FP_REG_Q31, offsetof(CPUARMState, vfp.zregs[31]) }, }; +static const struct hvf_reg_match hvf_sme2_zreg_match[] = { + { HV_SME_Z_REG_0, offsetof(CPUARMState, vfp.zregs[0]) }, + { HV_SME_Z_REG_1, offsetof(CPUARMState, vfp.zregs[1]) }, + { HV_SME_Z_REG_2, offsetof(CPUARMState, vfp.zregs[2]) }, + { HV_SME_Z_REG_3, offsetof(CPUARMState, vfp.zregs[3]) }, + { HV_SME_Z_REG_4, offsetof(CPUARMState, vfp.zregs[4]) }, + { HV_SME_Z_REG_5, offsetof(CPUARMState, vfp.zregs[5]) }, + { HV_SME_Z_REG_6, offsetof(CPUARMState, vfp.zregs[6]) }, + { HV_SME_Z_REG_7, offsetof(CPUARMState, vfp.zregs[7]) }, + { HV_SME_Z_REG_8, offsetof(CPUARMState, vfp.zregs[8]) }, + { HV_SME_Z_REG_9, offsetof(CPUARMState, vfp.zregs[9]) }, + { HV_SME_Z_REG_10, offsetof(CPUARMState, vfp.zregs[10]) }, + { HV_SME_Z_REG_11, offsetof(CPUARMState, vfp.zregs[11]) }, + { HV_SME_Z_REG_12, offsetof(CPUARMState, vfp.zregs[12]) }, + { HV_SME_Z_REG_13, offsetof(CPUARMState, vfp.zregs[13]) }, + { HV_SME_Z_REG_14, offsetof(CPUARMState, vfp.zregs[14]) }, + { HV_SME_Z_REG_15, offsetof(CPUARMState, vfp.zregs[15]) }, + { HV_SME_Z_REG_16, offsetof(CPUARMState, vfp.zregs[16]) }, + { HV_SME_Z_REG_17, offsetof(CPUARMState, vfp.zregs[17]) }, + { HV_SME_Z_REG_18, offsetof(CPUARMState, vfp.zregs[18]) }, + { HV_SME_Z_REG_19, offsetof(CPUARMState, vfp.zregs[19]) }, + { HV_SME_Z_REG_20, offsetof(CPUARMState, vfp.zregs[20]) }, + { HV_SME_Z_REG_21, offsetof(CPUARMState, vfp.zregs[21]) }, + { HV_SME_Z_REG_22, offsetof(CPUARMState, vfp.zregs[22]) }, + { HV_SME_Z_REG_23, offsetof(CPUARMState, vfp.zregs[23]) }, + { HV_SME_Z_REG_24, offsetof(CPUARMState, vfp.zregs[24]) }, + { HV_SME_Z_REG_25, offsetof(CPUARMState, vfp.zregs[25]) }, + { HV_SME_Z_REG_26, offsetof(CPUARMState, vfp.zregs[26]) }, + { HV_SME_Z_REG_27, offsetof(CPUARMState, vfp.zregs[27]) }, + { HV_SME_Z_REG_28, offsetof(CPUARMState, vfp.zregs[28]) }, + { HV_SME_Z_REG_29, offsetof(CPUARMState, vfp.zregs[29]) }, + { HV_SME_Z_REG_30, offsetof(CPUARMState, vfp.zregs[30]) }, + { HV_SME_Z_REG_31, offsetof(CPUARMState, vfp.zregs[31]) }, +}; + +static const struct hvf_reg_match hvf_sme2_preg_match[] = { + { HV_SME_P_REG_0, offsetof(CPUARMState, vfp.pregs[0]) }, + { HV_SME_P_REG_1, offsetof(CPUARMState, vfp.pregs[1]) }, + { HV_SME_P_REG_2, offsetof(CPUARMState, vfp.pregs[2]) }, + { HV_SME_P_REG_3, offsetof(CPUARMState, vfp.pregs[3]) }, + { HV_SME_P_REG_4, offsetof(CPUARMState, vfp.pregs[4]) }, + { HV_SME_P_REG_5, offsetof(CPUARMState, vfp.pregs[5]) }, + { HV_SME_P_REG_6, offsetof(CPUARMState, vfp.pregs[6]) }, + { HV_SME_P_REG_7, offsetof(CPUARMState, vfp.pregs[7]) }, + { HV_SME_P_REG_8, offsetof(CPUARMState, vfp.pregs[8]) }, + { HV_SME_P_REG_9, offsetof(CPUARMState, vfp.pregs[9]) }, + { HV_SME_P_REG_10, offsetof(CPUARMState, vfp.pregs[10]) }, + { HV_SME_P_REG_11, offsetof(CPUARMState, vfp.pregs[11]) }, + { HV_SME_P_REG_12, offsetof(CPUARMState, vfp.pregs[12]) }, + { HV_SME_P_REG_13, offsetof(CPUARMState, vfp.pregs[13]) }, + { HV_SME_P_REG_14, offsetof(CPUARMState, vfp.pregs[14]) }, + { HV_SME_P_REG_15, offsetof(CPUARMState, vfp.pregs[15]) }, +}; + /* * QEMU uses KVM system register ids in the migration format. * Conveniently, HVF uses the same encoding of the op* and cr* parameters @@ -XXX,XX +XXX,XX @@ static const struct hvf_reg_match hvf_fpreg_match[] = { #define HVF_TO_KVMID(HVF) \ (CP_REG_ARM64 | CP_REG_SIZE_U64 | CP_REG_ARM64_SYSREG | (HVF)) -/* Verify this at compile-time. */ +/* + * Verify this at compile-time. + * + * SME2 registers are guarded by a runtime availability attribute instead of a + * compile-time def, so verify those at runtime in hvf_arch_init_vcpu() below. + */ #define DEF_SYSREG(HVF_ID, ...) \ QEMU_BUILD_BUG_ON(HVF_ID != KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__))); +#define DEF_SYSREG_15_02(...) #include "sysreg.c.inc" #undef DEF_SYSREG +#undef DEF_SYSREG_15_02 #define DEF_SYSREG(HVF_ID, op0, op1, crn, crm, op2) HVF_ID, +#define DEF_SYSREG_15_02(...) static const hv_sys_reg_t hvf_sreg_list[] = { #include "sysreg.c.inc" }; #undef DEF_SYSREG +#undef DEF_SYSREG_15_02 + +#define DEF_SYSREG(...) +#define DEF_SYSREG_15_02(HVF_ID, op0, op1, crn, crm, op2) HVF_ID, + +API_AVAILABLE(macos(15.2)) +static const hv_sys_reg_t hvf_sreg_list_sme2[] = { +#include "sysreg.c.inc" +}; + +#undef DEF_SYSREG +#undef DEF_SYSREG_15_02 + +/* + * For FEAT_SME2 migration, we need to store PSTATE.{SM,ZA} bits which are + * accessible with the SVCR pseudo-register. However, in the HVF API this is + * not exposed as a system-register (i.e. HVF_SYS_REG_SVCR) but a custom + * struct, hv_vcpu_sme_state_t. So we need to define our own KVMID in order to + * store it in cpreg_values and make it migrateable. + */ +#define SVCR KVMID_AA64_SYS_REG64(3, 3, 4, 2, 2) + +API_AVAILABLE(macos(15.2)) +static void hvf_arch_put_sme(CPUState *cpu) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + const size_t svl_bytes = hvf_arm_sme2_get_svl(); + const size_t z_size = svl_bytes; + const size_t preg_size = DIV_ROUND_UP(z_size, 8); + const size_t za_size = svl_bytes * svl_bytes; + hv_vcpu_sme_state_t sme_state = { 0 }; + hv_return_t ret; + uint64_t svcr; + int n; + + /* + * Set PSTATE.{SM,ZA} bits + */ + svcr = arm_cpu->cpreg_values[arm_cpu->cpreg_array_len - 1]; + env->svcr = svcr; + + /* + * Construct SVCR (PSTATE.{SM,ZA}) state to pass to HVF: + */ + sme_state.streaming_sve_mode_enabled = FIELD_EX64(env->svcr, SVCR, SM) > 0; + sme_state.za_storage_enabled = FIELD_EX64(env->svcr, SVCR, ZA) > 0; + ret = hv_vcpu_set_sme_state(cpu->accel->fd, &sme_state); + assert_hvf_ok(ret); + + /* + * We only care about Z/P registers if we're in streaming SVE mode, i.e. + * PSTATE.SM is set, because only then can instructions that access them be + * used. We don't care about the register values otherwise. This is because + * when the processing unit exits/enters this mode, it zeroes out those + * registers. + */ + if (sme_state.streaming_sve_mode_enabled) { + for (n = 0; n < ARRAY_SIZE(hvf_sme2_zreg_match); ++n) { + ret = hv_vcpu_set_sme_z_reg(cpu->accel->fd, + hvf_sme2_zreg_match[n].reg, + (uint8_t *)&env->vfp.zregs[n].d[0], + z_size); + assert_hvf_ok(ret); + } + + for (n = 0; n < ARRAY_SIZE(hvf_sme2_preg_match); ++n) { + ret = hv_vcpu_set_sme_p_reg(cpu->accel->fd, + hvf_sme2_preg_match[n].reg, + (uint8_t *)&env->vfp.pregs[n].p[0], + preg_size); + assert_hvf_ok(ret); + } + } + + /* + * If PSTATE.ZA bit is set then ZA and ZT0 are valid, otherwise they are + * zeroed out. + */ + if (sme_state.za_storage_enabled) { + hv_sme_zt0_uchar64_t tmp = { 0 }; + + memcpy(&tmp, &env->za_state.zt0, 64); + ret = hv_vcpu_set_sme_zt0_reg(cpu->accel->fd, &tmp); + assert_hvf_ok(ret); + + ret = hv_vcpu_set_sme_za_reg(cpu->accel->fd, + (uint8_t *)&env->za_state.za, + za_size); + assert_hvf_ok(ret); + } + + return; +} + +API_AVAILABLE(macos(15.2)) +static void hvf_arch_get_sme(CPUState *cpu) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + const size_t svl_bytes = hvf_arm_sme2_get_svl(); + const size_t z_size = svl_bytes; + const size_t preg_size = DIV_ROUND_UP(z_size, 8); + const size_t za_size = svl_bytes * svl_bytes; + hv_vcpu_sme_state_t sme_state = { 0 }; + hv_return_t ret; + uint64_t svcr; + int n; + + /* + * Get SVCR (PSTATE.{SM,ZA}) state from HVF: + */ + ret = hv_vcpu_get_sme_state(cpu->accel->fd, &sme_state); + assert_hvf_ok(ret); + + /* + * Set SVCR first because changing it will zero out Z/P regs + */ + svcr = + (sme_state.za_storage_enabled ? R_SVCR_ZA_MASK : 0) + | (sme_state.streaming_sve_mode_enabled ? R_SVCR_SM_MASK : 0); + + aarch64_set_svcr(env, svcr, R_SVCR_ZA_MASK | R_SVCR_SM_MASK); + arm_cpu->cpreg_values[arm_cpu->cpreg_array_len - 1] = svcr; + + /* + * We only care about Z/P registers if we're in streaming SVE mode, i.e. + * PSTATE.SM is set, because only then can instructions that access them be + * used. We don't care about the register values otherwise. This is because + * when the processing unit exits/enters this mode, it zeroes out those + * registers. + */ + if (sme_state.streaming_sve_mode_enabled) { + for (n = 0; n < ARRAY_SIZE(hvf_sme2_zreg_match); ++n) { + ret = hv_vcpu_get_sme_z_reg(cpu->accel->fd, + hvf_sme2_zreg_match[n].reg, + (uint8_t *)&env->vfp.zregs[n].d[0], + z_size); + assert_hvf_ok(ret); + } + + for (n = 0; n < ARRAY_SIZE(hvf_sme2_preg_match); ++n) { + ret = hv_vcpu_get_sme_p_reg(cpu->accel->fd, + hvf_sme2_preg_match[n].reg, + (uint8_t *)&env->vfp.pregs[n].p[0], + preg_size); + assert_hvf_ok(ret); + } + } + + /* + * If PSTATE.ZA bit is set then ZA and ZT0 are valid, otherwise they are + * zeroed out. + */ + if (sme_state.za_storage_enabled) { + hv_sme_zt0_uchar64_t tmp = { 0 }; + + /* Get ZT0 in a tmp vector, and then copy it to env.za_state.zt0 */ + ret = hv_vcpu_get_sme_zt0_reg(cpu->accel->fd, &tmp); + assert_hvf_ok(ret); + + memcpy(&env->za_state.zt0, &tmp, 64); + ret = hv_vcpu_get_sme_za_reg(cpu->accel->fd, + (uint8_t *)&env->za_state.za, + za_size); + assert_hvf_ok(ret); + + } + + return; +} static uint32_t hvf_reg2cp_reg(uint32_t reg) { @@ -XXX,XX +XXX,XX @@ int hvf_arch_get_registers(CPUState *cpu) uint64_t kvm_id = arm_cpu->cpreg_indexes[i]; int hvf_id = KVMID_TO_HVF(kvm_id); + if (kvm_id == HVF_TO_KVMID(SVCR)) { + continue; + } + if (cpu->accel->guest_debug_enabled) { /* Handle debug registers */ switch (hvf_id) { @@ -XXX,XX +XXX,XX @@ int hvf_arch_get_registers(CPUState *cpu) arm_cpu->cpreg_values[i] = val; } + if (cpu_isar_feature(aa64_sme, arm_cpu)) { + if (__builtin_available(macOS 15.2, *)) { + hvf_arch_get_sme(cpu); + } else { + g_assert_not_reached(); + } + } assert(write_list_to_cpustate(arm_cpu)); aarch64_restore_sp(env, arm_current_el(env)); @@ -XXX,XX +XXX,XX @@ int hvf_arch_put_registers(CPUState *cpu) hv_simd_fp_uchar16_t fpval; int i, n; + /* + * Set SVCR first because changing it will zero out Z/P (including NEON) + * regs + */ + if (cpu_isar_feature(aa64_sme, arm_cpu)) { + if (__builtin_available(macOS 15.2, *)) { + hvf_arch_put_sme(cpu); + } else { + g_assert_not_reached(); + } + } + for (i = 0; i < ARRAY_SIZE(hvf_reg_match); i++) { val = *(uint64_t *)((void *)env + hvf_reg_match[i].offset); ret = hv_vcpu_set_reg(cpu->accel->fd, hvf_reg_match[i].reg, val); @@ -XXX,XX +XXX,XX @@ int hvf_arch_put_registers(CPUState *cpu) uint64_t kvm_id = arm_cpu->cpreg_indexes[i]; int hvf_id = KVMID_TO_HVF(kvm_id); + if (kvm_id == HVF_TO_KVMID(SVCR)) { + continue; + } + if (cpu->accel->guest_debug_enabled) { /* Handle debug registers */ switch (hvf_id) { @@ -XXX,XX +XXX,XX @@ int hvf_arch_init_vcpu(CPUState *cpu) hv_return_t ret; int i; + if (__builtin_available(macOS 15.2, *)) { + if (hvf_arm_sme2_supported()) { + sregs_match_len += ARRAY_SIZE(hvf_sreg_list_sme2) + 1; + } + +#define DEF_SYSREG_15_02(HVF_ID, ...) \ + g_assert(HVF_ID == KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__))); +#define DEF_SYSREG(...) + +#include "sysreg.c.inc" + +#undef DEF_SYSREG +#undef DEF_SYSREG_15_02 + } env->aarch64 = true; /* system count frequency sanity check */ @@ -XXX,XX +XXX,XX @@ int hvf_arch_init_vcpu(CPUState *cpu) memset(arm_cpu->cpreg_values, 0, sregs_match_len * sizeof(uint64_t)); /* Populate cp list for all known sysregs */ - for (i = 0; i < sregs_match_len; i++) { + for (i = 0; i < ARRAY_SIZE(hvf_sreg_list); i++) { hv_sys_reg_t hvf_id = hvf_sreg_list[i]; uint64_t kvm_id = HVF_TO_KVMID(hvf_id); uint32_t key = kvm_to_cpreg_id(kvm_id); @@ -XXX,XX +XXX,XX @@ int hvf_arch_init_vcpu(CPUState *cpu) arm_cpu->cpreg_indexes[sregs_cnt++] = kvm_id; } } + if (__builtin_available(macOS 15.2, *)) { + if (hvf_arm_sme2_supported()) { + for (i = 0; i < ARRAY_SIZE(hvf_sreg_list_sme2); i++) { + hv_sys_reg_t hvf_id = hvf_sreg_list_sme2[i]; + uint64_t kvm_id = HVF_TO_KVMID(hvf_id); + uint32_t key = kvm_to_cpreg_id(kvm_id); + const ARMCPRegInfo *ri = get_arm_cp_reginfo(arm_cpu->cp_regs, key); + + if (ri) { + assert(!(ri->type & ARM_CP_NO_RAW)); + arm_cpu->cpreg_indexes[sregs_cnt++] = kvm_id; + } + } + /* + * Add SVCR last. It is elsewhere assumed its index is after + * hvf_sreg_list and hvf_sreg_list_sme2. + */ + arm_cpu->cpreg_indexes[sregs_cnt++] = HVF_TO_KVMID(SVCR); + } + } arm_cpu->cpreg_array_len = sregs_cnt; arm_cpu->cpreg_vmstate_array_len = sregs_cnt; diff --git a/target/arm/hvf/hvf_sme_stubs.h b/target/arm/hvf/hvf_sme_stubs.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/target/arm/hvf/hvf_sme_stubs.h @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +typedef int32_t hv_return_t; +typedef uint64_t hv_vcpu_t; + +static inline bool hvf_arm_sme2_supported(void) +{ + return false; +} + +static inline uint32_t hvf_arm_sme2_get_svl(void) +{ + g_assert_not_reached(); +} + +typedef enum hv_sme_p_reg_t { + HV_SME_P_REG_0, + HV_SME_P_REG_1, + HV_SME_P_REG_2, + HV_SME_P_REG_3, + HV_SME_P_REG_4, + HV_SME_P_REG_5, + HV_SME_P_REG_6, + HV_SME_P_REG_7, + HV_SME_P_REG_8, + HV_SME_P_REG_9, + HV_SME_P_REG_10, + HV_SME_P_REG_11, + HV_SME_P_REG_12, + HV_SME_P_REG_13, + HV_SME_P_REG_14, + HV_SME_P_REG_15, +} hv_sme_p_reg_t; + +/* + * The system version of this type declares it with + * __attribute__((ext_vector_type(64))) + * However, that is clang specific and not supported by GCC. + * Since these headers are only here for the case where the system + * headers do not provide these types (including both older macos + * and non-macos hosts), we don't need to make the type match + * exactly, so we declare it as a uint8_t array. + */ +typedef uint8_t hv_sme_zt0_uchar64_t[64]; + +typedef enum hv_sme_z_reg_t { + HV_SME_Z_REG_0, + HV_SME_Z_REG_1, + HV_SME_Z_REG_2, + HV_SME_Z_REG_3, + HV_SME_Z_REG_4, + HV_SME_Z_REG_5, + HV_SME_Z_REG_6, + HV_SME_Z_REG_7, + HV_SME_Z_REG_8, + HV_SME_Z_REG_9, + HV_SME_Z_REG_10, + HV_SME_Z_REG_11, + HV_SME_Z_REG_12, + HV_SME_Z_REG_13, + HV_SME_Z_REG_14, + HV_SME_Z_REG_15, + HV_SME_Z_REG_16, + HV_SME_Z_REG_17, + HV_SME_Z_REG_18, + HV_SME_Z_REG_19, + HV_SME_Z_REG_20, + HV_SME_Z_REG_21, + HV_SME_Z_REG_22, + HV_SME_Z_REG_23, + HV_SME_Z_REG_24, + HV_SME_Z_REG_25, + HV_SME_Z_REG_26, + HV_SME_Z_REG_27, + HV_SME_Z_REG_28, + HV_SME_Z_REG_29, + HV_SME_Z_REG_30, + HV_SME_Z_REG_31, +} hv_sme_z_reg_t; + +enum { + HV_SYS_REG_SMCR_EL1, + HV_SYS_REG_SMPRI_EL1, + HV_SYS_REG_TPIDR2_EL0, + HV_SYS_REG_ID_AA64ZFR0_EL1, + HV_SYS_REG_ID_AA64SMFR0_EL1, +}; + +enum { + HV_FEATURE_REG_ID_AA64SMFR0_EL1, + HV_FEATURE_REG_ID_AA64ZFR0_EL1, +}; + +typedef struct { + bool streaming_sve_mode_enabled; + bool za_storage_enabled; +} hv_vcpu_sme_state_t; + +static inline hv_return_t hv_sme_config_get_max_svl_bytes(size_t *value) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_get_sme_state(hv_vcpu_t vcpu, + hv_vcpu_sme_state_t *sme_state) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_set_sme_state(hv_vcpu_t vcpu, + const hv_vcpu_sme_state_t *sme_state) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_get_sme_z_reg(hv_vcpu_t vcpu, + hv_sme_z_reg_t reg, + uint8_t *value, + size_t length) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_set_sme_z_reg(hv_vcpu_t vcpu, + hv_sme_z_reg_t reg, + const uint8_t *value, + size_t length) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_get_sme_p_reg(hv_vcpu_t vcpu, + hv_sme_p_reg_t reg, + uint8_t *value, + size_t length) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_set_sme_p_reg(hv_vcpu_t vcpu, + hv_sme_p_reg_t reg, + const uint8_t *value, + size_t length) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_get_sme_za_reg(hv_vcpu_t vcpu, + uint8_t *value, + size_t length) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_set_sme_za_reg(hv_vcpu_t vcpu, + const uint8_t *value, + size_t length) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_get_sme_zt0_reg(hv_vcpu_t vcpu, + hv_sme_zt0_uchar64_t *value) +{ + g_assert_not_reached(); +} + +static inline hv_return_t hv_vcpu_set_sme_zt0_reg(hv_vcpu_t vcpu, + const hv_sme_zt0_uchar64_t *value) +{ + g_assert_not_reached(); +} diff --git a/target/arm/hvf/sysreg.c.inc b/target/arm/hvf/sysreg.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/sysreg.c.inc +++ b/target/arm/hvf/sysreg.c.inc @@ -XXX,XX +XXX,XX @@ DEF_SYSREG(HV_SYS_REG_TPIDRRO_EL0, 3, 3, 13, 0, 3) DEF_SYSREG(HV_SYS_REG_CNTV_CTL_EL0, 3, 3, 14, 3, 1) DEF_SYSREG(HV_SYS_REG_CNTV_CVAL_EL0, 3, 3, 14, 3, 2) DEF_SYSREG(HV_SYS_REG_SP_EL1, 3, 4, 4, 1, 0) + +DEF_SYSREG_15_02(HV_SYS_REG_SMCR_EL1, 3, 0, 1, 2, 6) +DEF_SYSREG_15_02(HV_SYS_REG_SMPRI_EL1, 3, 0, 1, 2, 4) +DEF_SYSREG_15_02(HV_SYS_REG_TPIDR2_EL0, 3, 3, 13, 0, 5) +DEF_SYSREG_15_02(HV_SYS_REG_ID_AA64ZFR0_EL1, 3, 0, 0, 4, 4) +DEF_SYSREG_15_02(HV_SYS_REG_ID_AA64SMFR0_EL1, 3, 0, 0, 4, 5) +DEF_SYSREG_15_02(HV_SYS_REG_SMPRI_EL1, 3, 0, 1, 2, 4) +DEF_SYSREG_15_02(HV_SYS_REG_SMCR_EL1, 3, 0, 1, 2, 6) diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf_arm.h +++ b/target/arm/hvf_arm.h @@ -XXX,XX +XXX,XX @@ void hvf_arm_init_debug(void); void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu); +/* + * We need access to types from macOS SDK >=15.2, so expose stubs if the + * headers are not available until we raise our minimum macOS version. + */ +#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED + #if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 150200) + #include "system/hvf_int.h" + + static inline bool hvf_arm_sme2_supported(void) + { + if (__builtin_available(macOS 15.2, *)) { + size_t svl_bytes; + hv_return_t result = hv_sme_config_get_max_svl_bytes(&svl_bytes); + if (result == HV_UNSUPPORTED) { + return false; + } + assert_hvf_ok(result); + return svl_bytes > 0; + } else { + return false; + } + } + + static inline uint32_t hvf_arm_sme2_get_svl(void) + { + if (__builtin_available(macOS 15.2, *)) { + size_t svl_bytes; + hv_return_t result = hv_sme_config_get_max_svl_bytes(&svl_bytes); + assert_hvf_ok(result); + return svl_bytes; + } else { + abort(); + } + } + #else /* (__MAC_OS_X_VERSION_MAX_ALLOWED >= 150200) */ + #include "hvf/hvf_sme_stubs.h" + #endif /* (__MAC_OS_X_VERSION_MAX_ALLOWED >= 150200) */ +#else /* ifdef __MAC_OS_X_VERSION_MAX_ALLOWED */ + #include "hvf/hvf_sme_stubs.h" +#endif /* ifdef __MAC_OS_X_VERSION_MAX_ALLOWED */ + #endif diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ static bool sve_needed(void *opaque) { ARMCPU *cpu = opaque; - return cpu_isar_feature(aa64_sve, cpu); + return cpu_isar_feature(aa64_sve, cpu) || cpu_isar_feature(aa64_sme, cpu); } /* The first two words of each Zreg is stored in VFP state. */ -- 2.43.0
From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Starting from M4 cores and MacOS 15.2 SDK, HVF can virtualise FEAT_SME2. Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr> Message-id: 20260306-sme2-hvf-v7-2-e72eeda41ed3@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ typedef struct ARMHostCPUFeatures { uint64_t features; uint64_t midr; uint32_t reset_sctlr; + uint32_t sme_vq_supported; const char *dtb_compatible; } ARMHostCPUFeatures; @@ -XXX,XX +XXX,XX @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) r |= hv_vcpu_config_get_feature_reg(config, regs[i].reg, &host_isar.idregs[regs[i].index]); } + + if (__builtin_available(macOS 15.2, *)) { + static const struct sme_isar_regs { + hv_feature_reg_t reg; + ARMIDRegisterIdx index; + } sme_regs[] = { + { HV_FEATURE_REG_ID_AA64SMFR0_EL1, ID_AA64SMFR0_EL1_IDX }, + { HV_FEATURE_REG_ID_AA64ZFR0_EL1, ID_AA64ZFR0_EL1_IDX }, + }; + + if (hvf_arm_sme2_supported()) { + for (i = 0; i < ARRAY_SIZE(sme_regs); i++) { + r |= hv_vcpu_config_get_feature_reg(config, sme_regs[i].reg, + &host_isar.idregs[sme_regs[i].index]); + } + } + } + os_release(config); /* @@ -XXX,XX +XXX,XX @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) clamp_id_aa64mmfr0_parange_to_ipa_size(&host_isar); - /* - * Disable SME, which is not properly handled by QEMU hvf yet. - * To allow this through we would need to: - * - make sure that the SME state is correctly handled in the - * get_registers/put_registers functions - * - get the SME-specific CPU properties to work with accelerators - * other than TCG - * - fix any assumptions we made that SME implies SVE (since - * on the M4 there is SME but not SVE) - */ - SET_IDREG(&host_isar, ID_AA64PFR1, - GET_IDREG(&host_isar, ID_AA64PFR1) & ~R_ID_AA64PFR1_SME_MASK); - ahcf->isar = host_isar; /* @@ -XXX,XX +XXX,XX @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) */ ahcf->reset_sctlr |= 0x00800000; + ahcf->sme_vq_supported = hvf_arm_sme2_supported() ? hvf_arm_sme2_get_svl() : 0; + /* Make sure we don't advertise AArch32 support for EL0/EL1 */ if ((GET_IDREG(&host_isar, ID_AA64PFR0) & 0xff) != 0x11) { return false; @@ -XXX,XX +XXX,XX @@ void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu) cpu->env.features = arm_host_cpu_features.features; cpu->midr = arm_host_cpu_features.midr; cpu->reset_sctlr = arm_host_cpu_features.reset_sctlr; + cpu->sme_vq.supported = arm_host_cpu_features.sme_vq_supported; } void hvf_arch_vcpu_destroy(CPUState *cpu) @@ -XXX,XX +XXX,XX @@ int hvf_arch_init_vcpu(CPUState *cpu) arm_cpu->isar.idregs[ID_AA64MMFR0_EL1_IDX]); assert_hvf_ok(ret); + aarch64_add_sme_properties(OBJECT(cpu)); return 0; } -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> We don't need anything in this header, so drop the include. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> [PMM: updated commit message] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260306130107.35359-4-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/virt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -XXX,XX +XXX,XX @@ #include "hw/intc/arm_gicv3_its_common.h" #include "hw/core/irq.h" #include "kvm_arm.h" -#include "hvf_arm.h" #include "whpx_arm.h" #include "hw/firmware/smbios.h" #include "qapi/visitor.h" -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> We don't need this target-specific header in this target-agnostic source file. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260306130107.35359-5-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- accel/hvf/hvf-all.c | 1 - 1 file changed, 1 deletion(-) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index XXXXXXX..XXXXXXX 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -XXX,XX +XXX,XX @@ #include "system/hvf_int.h" #include "hw/core/cpu.h" #include "hw/core/boards.h" -#include "target/arm/hvf_arm.h" #include "trace.h" bool hvf_allowed; -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> This opens up the door to nested virtualisation support. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-6-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/intc/arm_gicv3_hvf.c | 735 +++++++++++++++++++++++++++++ hw/intc/meson.build | 1 + include/hw/intc/arm_gicv3_common.h | 1 + 3 files changed, 737 insertions(+) create mode 100644 hw/intc/arm_gicv3_hvf.c diff --git a/hw/intc/arm_gicv3_hvf.c b/hw/intc/arm_gicv3_hvf.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/intc/arm_gicv3_hvf.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * ARM Generic Interrupt Controller using HVF platform support + * + * Copyright (c) 2025 Mohamed Mediouni + * Based on vGICv3 KVM code by Pavel Fedin + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/intc/arm_gicv3_common.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "system/runstate.h" +#include "system/hvf.h" +#include "system/hvf_int.h" +#include "hvf_arm.h" +#include "gicv3_internal.h" +#include "vgic_common.h" +#include "qom/object.h" +#include "target/arm/cpregs.h" +#include <Hypervisor/Hypervisor.h> + +struct HVFARMGICv3Class { + ARMGICv3CommonClass parent_class; + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + +typedef struct HVFARMGICv3Class HVFARMGICv3Class; + +/* This is reusing the GICv3State typedef from ARM_GICV3_ITS_COMMON */ +DECLARE_OBJ_CHECKERS(GICv3State, HVFARMGICv3Class, + HVF_GICV3, TYPE_HVF_GICV3); + +/* + * Loop through each distributor IRQ related register; since bits + * corresponding to SPIs and PPIs are RAZ/WI when affinity routing + * is enabled, we skip those. + */ +#define for_each_dist_irq_reg(_irq, _max, _field_width) \ + for (_irq = GIC_INTERNAL; _irq < _max; _irq += (32 / _field_width)) + +/* + * Wrap calls to the vGIC APIs to assert_hvf_ok() + * as a macro to keep the code clean. + */ +#define hv_gic_get_distributor_reg(offset, reg) \ + assert_hvf_ok(hv_gic_get_distributor_reg(offset, reg)) + +#define hv_gic_set_distributor_reg(offset, reg) \ + assert_hvf_ok(hv_gic_set_distributor_reg(offset, reg)) + +#define hv_gic_get_redistributor_reg(vcpu, reg, value) \ + assert_hvf_ok(hv_gic_get_redistributor_reg(vcpu, reg, value)) + +#define hv_gic_set_redistributor_reg(vcpu, reg, value) \ + assert_hvf_ok(hv_gic_set_redistributor_reg(vcpu, reg, value)) + +#define hv_gic_get_icc_reg(vcpu, reg, value) \ + assert_hvf_ok(hv_gic_get_icc_reg(vcpu, reg, value)) + +#define hv_gic_set_icc_reg(vcpu, reg, value) \ + assert_hvf_ok(hv_gic_set_icc_reg(vcpu, reg, value)) + +#define hv_gic_get_ich_reg(vcpu, reg, value) \ + assert_hvf_ok(hv_gic_get_ich_reg(vcpu, reg, value)) + +#define hv_gic_set_ich_reg(vcpu, reg, value) \ + assert_hvf_ok(hv_gic_set_ich_reg(vcpu, reg, value)) + +static void hvf_dist_get_priority(GICv3State *s, hv_gic_distributor_reg_t offset + , uint8_t *bmp) +{ + uint64_t reg; + uint32_t *field; + int irq; + field = (uint32_t *)(bmp); + + for_each_dist_irq_reg(irq, s->num_irq, 8) { + hv_gic_get_distributor_reg(offset, ®); + *field = reg; + offset += 4; + field++; + } +} + +static void hvf_dist_put_priority(GICv3State *s, hv_gic_distributor_reg_t offset + , uint8_t *bmp) +{ + uint32_t reg, *field; + int irq; + field = (uint32_t *)(bmp); + + for_each_dist_irq_reg(irq, s->num_irq, 8) { + reg = *field; + hv_gic_set_distributor_reg(offset, reg); + offset += 4; + field++; + } +} + +static void hvf_dist_get_edge_trigger(GICv3State *s, hv_gic_distributor_reg_t offset, + uint32_t *bmp) +{ + uint64_t reg; + int irq; + + for_each_dist_irq_reg(irq, s->num_irq, 2) { + hv_gic_get_distributor_reg(offset, ®); + reg = half_unshuffle32(reg >> 1); + if (irq % 32 != 0) { + reg = (reg << 16); + } + *gic_bmp_ptr32(bmp, irq) |= reg; + offset += 4; + } +} + +static void hvf_dist_put_edge_trigger(GICv3State *s, hv_gic_distributor_reg_t offset, + uint32_t *bmp) +{ + uint32_t reg; + int irq; + + for_each_dist_irq_reg(irq, s->num_irq, 2) { + reg = *gic_bmp_ptr32(bmp, irq); + if (irq % 32 != 0) { + reg = (reg & 0xffff0000) >> 16; + } else { + reg = reg & 0xffff; + } + reg = half_shuffle32(reg) << 1; + hv_gic_set_distributor_reg(offset, reg); + offset += 4; + } +} + +/* Read a bitmap register group from the kernel VGIC. */ +static void hvf_dist_getbmp(GICv3State *s, hv_gic_distributor_reg_t offset, uint32_t *bmp) +{ + uint64_t reg; + int irq; + + for_each_dist_irq_reg(irq, s->num_irq, 1) { + + hv_gic_get_distributor_reg(offset, ®); + *gic_bmp_ptr32(bmp, irq) = reg; + offset += 4; + } +} + +static void hvf_dist_putbmp(GICv3State *s, hv_gic_distributor_reg_t offset, + hv_gic_distributor_reg_t clroffset, uint32_t *bmp) +{ + uint32_t reg; + int irq; + + for_each_dist_irq_reg(irq, s->num_irq, 1) { + /* + * If this bitmap is a set/clear register pair, first write to the + * clear-reg to clear all bits before using the set-reg to write + * the 1 bits. + */ + if (clroffset != 0) { + reg = 0; + hv_gic_set_distributor_reg(clroffset, reg); + clroffset += 4; + } + reg = *gic_bmp_ptr32(bmp, irq); + hv_gic_set_distributor_reg(offset, reg); + offset += 4; + } +} + +static void hvf_gicv3_check(GICv3State *s) +{ + uint64_t reg; + uint32_t num_irq; + + /* Sanity checking s->num_irq */ + hv_gic_get_distributor_reg(HV_GIC_DISTRIBUTOR_REG_GICD_TYPER, ®); + num_irq = ((reg & 0x1f) + 1) * 32; + + if (num_irq < s->num_irq) { + error_report("Model requests %u IRQs, but HVF supports max %u", + s->num_irq, num_irq); + abort(); + } +} + +static void hvf_gicv3_put_cpu_el2(CPUState *cpu_state, run_on_cpu_data arg) +{ + int num_pri_bits; + + /* Redistributor state */ + GICv3CPUState *c = arg.host_ptr; + hv_vcpu_t vcpu = c->cpu->accel->fd; + + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_VMCR_EL2, c->ich_vmcr_el2); + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_HCR_EL2, c->ich_hcr_el2); + + for (int i = 0; i < GICV3_LR_MAX; i++) { + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_LR0_EL2, c->ich_lr_el2[i]); + } + + num_pri_bits = c->vpribits; + + switch (num_pri_bits) { + case 7: + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_AP0R0_EL2 + 3, + c->ich_apr[GICV3_G0][3]); + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_AP0R0_EL2 + 2, + c->ich_apr[GICV3_G0][2]); + /* fall through */ + case 6: + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_AP0R0_EL2 + 1, + c->ich_apr[GICV3_G0][1]); + /* fall through */ + default: + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_AP0R0_EL2, + c->ich_apr[GICV3_G0][0]); + } + + switch (num_pri_bits) { + case 7: + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_AP1R0_EL2 + 3, + c->ich_apr[GICV3_G1NS][3]); + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_AP1R0_EL2 + 2, + c->ich_apr[GICV3_G1NS][2]); + /* fall through */ + case 6: + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_AP1R0_EL2 + 1, + c->ich_apr[GICV3_G1NS][1]); + /* fall through */ + default: + hv_gic_set_ich_reg(vcpu, HV_GIC_ICH_REG_AP1R0_EL2, + c->ich_apr[GICV3_G1NS][0]); + } +} + +static void hvf_gicv3_put_cpu(CPUState *cpu_state, run_on_cpu_data arg) +{ + uint32_t reg; + uint64_t reg64; + int i, num_pri_bits; + + /* Redistributor state */ + GICv3CPUState *c = arg.host_ptr; + hv_vcpu_t vcpu = c->cpu->accel->fd; + + reg = c->gicr_waker; + hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_IGROUPR0, reg); + + reg = c->gicr_igroupr0; + hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_IGROUPR0, reg); + + reg = ~0; + hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ICENABLER0, reg); + reg = c->gicr_ienabler0; + hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ISENABLER0, reg); + + /* Restore config before pending so we treat level/edge correctly */ + reg = half_shuffle32(c->edge_trigger >> 16) << 1; + hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ICFGR1, reg); + + reg = ~0; + hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ICPENDR0, reg); + reg = c->gicr_ipendr0; + hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ISPENDR0, reg); + + reg = ~0; + hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ICACTIVER0, reg); + reg = c->gicr_iactiver0; + hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ISACTIVER0, reg); + + for (i = 0; i < GIC_INTERNAL; i += 4) { + reg = c->gicr_ipriorityr[i] | + (c->gicr_ipriorityr[i + 1] << 8) | + (c->gicr_ipriorityr[i + 2] << 16) | + (c->gicr_ipriorityr[i + 3] << 24); + hv_gic_set_redistributor_reg(vcpu, + HV_GIC_REDISTRIBUTOR_REG_GICR_IPRIORITYR0 + i, reg); + } + + /* CPU interface state */ + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_SRE_EL1, c->icc_sre_el1); + + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_CTLR_EL1, + c->icc_ctlr_el1[GICV3_NS]); + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_IGRPEN0_EL1, + c->icc_igrpen[GICV3_G0]); + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_IGRPEN1_EL1, + c->icc_igrpen[GICV3_G1NS]); + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_PMR_EL1, c->icc_pmr_el1); + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_BPR0_EL1, c->icc_bpr[GICV3_G0]); + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_BPR1_EL1, c->icc_bpr[GICV3_G1NS]); + + num_pri_bits = ((c->icc_ctlr_el1[GICV3_NS] & + ICC_CTLR_EL1_PRIBITS_MASK) >> + ICC_CTLR_EL1_PRIBITS_SHIFT) + 1; + + switch (num_pri_bits) { + case 7: + reg64 = c->icc_apr[GICV3_G0][3]; + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 3, reg64); + reg64 = c->icc_apr[GICV3_G0][2]; + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 2, reg64); + /* fall through */ + case 6: + reg64 = c->icc_apr[GICV3_G0][1]; + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 1, reg64); + /* fall through */ + default: + reg64 = c->icc_apr[GICV3_G0][0]; + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1, reg64); + } + + switch (num_pri_bits) { + case 7: + reg64 = c->icc_apr[GICV3_G1NS][3]; + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 3, reg64); + reg64 = c->icc_apr[GICV3_G1NS][2]; + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 2, reg64); + /* fall through */ + case 6: + reg64 = c->icc_apr[GICV3_G1NS][1]; + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 1, reg64); + /* fall through */ + default: + reg64 = c->icc_apr[GICV3_G1NS][0]; + hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1, reg64); + } + + /* Registers beyond this point are with nested virt only */ + if (c->gic->maint_irq) { + hvf_gicv3_put_cpu_el2(cpu_state, arg); + } +} + +static void hvf_gicv3_put(GICv3State *s) +{ + uint32_t reg; + int ncpu, i; + + hvf_gicv3_check(s); + + reg = s->gicd_ctlr; + hv_gic_set_distributor_reg(HV_GIC_DISTRIBUTOR_REG_GICD_CTLR, reg); + + /* per-CPU state */ + + for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { + run_on_cpu_data data; + data.host_ptr = &s->cpu[ncpu]; + run_on_cpu(s->cpu[ncpu].cpu, hvf_gicv3_put_cpu, data); + } + + /* s->enable bitmap -> GICD_ISENABLERn */ + hvf_dist_putbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISENABLER0 + , HV_GIC_DISTRIBUTOR_REG_GICD_ICENABLER0, s->enabled); + + /* s->group bitmap -> GICD_IGROUPRn */ + hvf_dist_putbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_IGROUPR0 + , 0, s->group); + + /* Restore targets before pending to ensure the pending state is set on + * the appropriate CPU interfaces in the kernel + */ + + /* s->gicd_irouter[irq] -> GICD_IROUTERn */ + for (i = GIC_INTERNAL; i < s->num_irq; i++) { + uint32_t offset = HV_GIC_DISTRIBUTOR_REG_GICD_IROUTER32 + (8 * i) + - (8 * GIC_INTERNAL); + hv_gic_set_distributor_reg(offset, s->gicd_irouter[i]); + } + + /* + * s->trigger bitmap -> GICD_ICFGRn + * (restore configuration registers before pending IRQs so we treat + * level/edge correctly) + */ + hvf_dist_put_edge_trigger(s, HV_GIC_DISTRIBUTOR_REG_GICD_ICFGR0, s->edge_trigger); + + /* s->pending bitmap -> GICD_ISPENDRn */ + hvf_dist_putbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISPENDR0, + HV_GIC_DISTRIBUTOR_REG_GICD_ICPENDR0, s->pending); + + /* s->active bitmap -> GICD_ISACTIVERn */ + hvf_dist_putbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISACTIVER0, + HV_GIC_DISTRIBUTOR_REG_GICD_ICACTIVER0, s->active); + + /* s->gicd_ipriority[] -> GICD_IPRIORITYRn */ + hvf_dist_put_priority(s, HV_GIC_DISTRIBUTOR_REG_GICD_IPRIORITYR0, s->gicd_ipriority); +} + +static void hvf_gicv3_get_cpu_el2(CPUState *cpu_state, run_on_cpu_data arg) +{ + int num_pri_bits; + + /* Redistributor state */ + GICv3CPUState *c = arg.host_ptr; + hv_vcpu_t vcpu = c->cpu->accel->fd; + + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_VMCR_EL2, &c->ich_vmcr_el2); + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_HCR_EL2, &c->ich_hcr_el2); + + for (int i = 0; i < GICV3_LR_MAX; i++) { + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_LR0_EL2, &c->ich_lr_el2[i]); + } + + num_pri_bits = c->vpribits; + + switch (num_pri_bits) { + case 7: + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_AP0R0_EL2 + 3, + &c->ich_apr[GICV3_G0][3]); + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_AP0R0_EL2 + 2, + &c->ich_apr[GICV3_G0][2]); + /* fall through */ + case 6: + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_AP0R0_EL2 + 1, + &c->ich_apr[GICV3_G0][1]); + /* fall through */ + default: + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_AP0R0_EL2, + &c->ich_apr[GICV3_G0][0]); + } + + switch (num_pri_bits) { + case 7: + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_AP1R0_EL2 + 3, + &c->ich_apr[GICV3_G1NS][3]); + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_AP1R0_EL2 + 2, + &c->ich_apr[GICV3_G1NS][2]); + /* fall through */ + case 6: + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_AP1R0_EL2 + 1, + &c->ich_apr[GICV3_G1NS][1]); + /* fall through */ + default: + hv_gic_get_ich_reg(vcpu, HV_GIC_ICH_REG_AP1R0_EL2, + &c->ich_apr[GICV3_G1NS][0]); + } +} + +static void hvf_gicv3_get_cpu(CPUState *cpu_state, run_on_cpu_data arg) +{ + uint64_t reg; + int i, num_pri_bits; + + /* Redistributor state */ + GICv3CPUState *c = arg.host_ptr; + hv_vcpu_t vcpu = c->cpu->accel->fd; + + hv_gic_get_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_IGROUPR0, + ®); + c->gicr_igroupr0 = reg; + hv_gic_get_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ISENABLER0, + ®); + c->gicr_ienabler0 = reg; + hv_gic_get_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ICFGR1, + ®); + c->edge_trigger = half_unshuffle32(reg >> 1) << 16; + hv_gic_get_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ISPENDR0, + ®); + c->gicr_ipendr0 = reg; + hv_gic_get_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ISACTIVER0, + ®); + c->gicr_iactiver0 = reg; + + for (i = 0; i < GIC_INTERNAL; i += 4) { + hv_gic_get_redistributor_reg( + vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_IPRIORITYR0 + i, ®); + c->gicr_ipriorityr[i] = extract32(reg, 0, 8); + c->gicr_ipriorityr[i + 1] = extract32(reg, 8, 8); + c->gicr_ipriorityr[i + 2] = extract32(reg, 16, 8); + c->gicr_ipriorityr[i + 3] = extract32(reg, 24, 8); + } + + /* CPU interface */ + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_SRE_EL1, &c->icc_sre_el1); + + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_CTLR_EL1, + &c->icc_ctlr_el1[GICV3_NS]); + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_IGRPEN0_EL1, + &c->icc_igrpen[GICV3_G0]); + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_IGRPEN1_EL1, + &c->icc_igrpen[GICV3_G1NS]); + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_PMR_EL1, &c->icc_pmr_el1); + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_BPR0_EL1, &c->icc_bpr[GICV3_G0]); + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_BPR1_EL1, &c->icc_bpr[GICV3_G1NS]); + num_pri_bits = ((c->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_PRIBITS_MASK) >> + ICC_CTLR_EL1_PRIBITS_SHIFT) + + 1; + + switch (num_pri_bits) { + case 7: + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 3, + &c->icc_apr[GICV3_G0][3]); + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 2, + &c->icc_apr[GICV3_G0][2]); + /* fall through */ + case 6: + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 1, + &c->icc_apr[GICV3_G0][1]); + /* fall through */ + default: + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1, + &c->icc_apr[GICV3_G0][0]); + } + + switch (num_pri_bits) { + case 7: + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 3, + &c->icc_apr[GICV3_G1NS][3]); + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 2, + &c->icc_apr[GICV3_G1NS][2]); + /* fall through */ + case 6: + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 1, + &c->icc_apr[GICV3_G1NS][1]); + /* fall through */ + default: + hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1, + &c->icc_apr[GICV3_G1NS][0]); + } + + /* Registers beyond this point are with nested virt only */ + if (c->gic->maint_irq) { + hvf_gicv3_get_cpu_el2(cpu_state, arg); + } +} + +static void hvf_gicv3_get(GICv3State *s) +{ + uint64_t reg; + int ncpu, i; + + hvf_gicv3_check(s); + + hv_gic_get_distributor_reg(HV_GIC_DISTRIBUTOR_REG_GICD_CTLR, ®); + s->gicd_ctlr = reg; + + /* Redistributor state (one per CPU) */ + + for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { + run_on_cpu_data data; + data.host_ptr = &s->cpu[ncpu]; + run_on_cpu(s->cpu[ncpu].cpu, hvf_gicv3_get_cpu, data); + } + + /* GICD_IGROUPRn -> s->group bitmap */ + hvf_dist_getbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_IGROUPR0, s->group); + + /* GICD_ISENABLERn -> s->enabled bitmap */ + hvf_dist_getbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISENABLER0, s->enabled); + + /* GICD_ISPENDRn -> s->pending bitmap */ + hvf_dist_getbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISPENDR0, s->pending); + + /* GICD_ISACTIVERn -> s->active bitmap */ + hvf_dist_getbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISACTIVER0, s->active); + + /* GICD_ICFGRn -> s->trigger bitmap */ + hvf_dist_get_edge_trigger(s, HV_GIC_DISTRIBUTOR_REG_GICD_ICFGR0 + , s->edge_trigger); + + /* GICD_IPRIORITYRn -> s->gicd_ipriority[] */ + hvf_dist_get_priority(s, HV_GIC_DISTRIBUTOR_REG_GICD_IPRIORITYR0 + , s->gicd_ipriority); + + /* GICD_IROUTERn -> s->gicd_irouter[irq] */ + for (i = GIC_INTERNAL; i < s->num_irq; i++) { + uint32_t offset = HV_GIC_DISTRIBUTOR_REG_GICD_IROUTER32 + + (8 * i) - (8 * GIC_INTERNAL); + hv_gic_get_distributor_reg(offset, &s->gicd_irouter[i]); + } +} + +static void hvf_gicv3_set_irq(void *opaque, int irq, int level) +{ + GICv3State *s = opaque; + if (irq > s->num_irq) { + return; + } + hv_gic_set_spi(GIC_INTERNAL + irq, !!level); +} + +static void hvf_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *c; + + c = env->gicv3state; + c->icc_pmr_el1 = 0; + /* + * Architecturally the reset value of the ICC_BPR registers + * is UNKNOWN. We set them all to 0 here; when the kernel + * uses these values to program the ICH_VMCR_EL2 fields that + * determine the guest-visible ICC_BPR register values, the + * hardware's "writing a value less than the minimum sets + * the field to the minimum value" behaviour will result in + * them effectively resetting to the correct minimum value + * for the host GIC. + */ + c->icc_bpr[GICV3_G0] = 0; + c->icc_bpr[GICV3_G1] = 0; + c->icc_bpr[GICV3_G1NS] = 0; + + c->icc_sre_el1 = 0x7; + memset(c->icc_apr, 0, sizeof(c->icc_apr)); + memset(c->icc_igrpen, 0, sizeof(c->icc_igrpen)); +} + +static void hvf_gicv3_reset_hold(Object *obj, ResetType type) +{ + GICv3State *s = ARM_GICV3_COMMON(obj); + HVFARMGICv3Class *kgc = HVF_GICV3_GET_CLASS(s); + + if (kgc->parent_phases.hold) { + kgc->parent_phases.hold(obj, type); + } + + hvf_gicv3_put(s); +} + + +/* + * CPU interface registers of GIC needs to be reset on CPU reset. + * For the calling arm_gicv3_icc_reset() on CPU reset, we register + * below ARMCPRegInfo. As we reset the whole cpu interface under single + * register reset, we define only one register of CPU interface instead + * of defining all the registers. + */ +static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { + { .name = "ICC_CTLR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 4, + /* + * If ARM_CP_NOP is used, resetfn is not called, + * So ARM_CP_NO_RAW is appropriate type. + */ + .type = ARM_CP_NO_RAW, + .access = PL1_RW, + .readfn = arm_cp_read_zero, + .writefn = arm_cp_write_ignore, + /* + * We hang the whole cpu interface reset routine off here + * rather than parcelling it out into one little function + * per register + */ + .resetfn = hvf_gicv3_icc_reset, + }, +}; + +static void hvf_gicv3_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + GICv3State *s = HVF_GICV3(dev); + HVFARMGICv3Class *kgc = HVF_GICV3_GET_CLASS(s); + int i; + + kgc->parent_realize(dev, errp); + if (*errp) { + return; + } + + if (s->revision != 3) { + error_setg(errp, "unsupported GIC revision %d for platform GIC", + s->revision); + } + + if (s->security_extn) { + error_setg(errp, "the platform vGICv3 does not implement the " + "security extensions"); + return; + } + + if (s->nmi_support) { + error_setg(errp, "NMI is not supported with the platform GIC"); + return; + } + + if (s->nb_redist_regions > 1) { + error_setg(errp, "Multiple VGICv3 redistributor regions are not " + "supported by HVF"); + error_append_hint(errp, "A maximum of %d VCPUs can be used", + s->redist_region_count[0]); + return; + } + + gicv3_init_irqs_and_mmio(s, hvf_gicv3_set_irq, NULL); + + for (i = 0; i < s->num_cpu; i++) { + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); + + define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); + } + + if (s->maint_irq && s->maint_irq != HV_GIC_INT_MAINTENANCE) { + error_setg(errp, "vGIC maintenance IRQ mismatch with the hardcoded one in HVF."); + return; + } +} + +static void hvf_gicv3_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); + HVFARMGICv3Class *kgc = HVF_GICV3_CLASS(klass); + + agcc->pre_save = hvf_gicv3_get; + agcc->post_load = hvf_gicv3_put; + + device_class_set_parent_realize(dc, hvf_gicv3_realize, + &kgc->parent_realize); + resettable_class_set_parent_phases(rc, NULL, hvf_gicv3_reset_hold, NULL, + &kgc->parent_phases); +} + +static const TypeInfo hvf_arm_gicv3_info = { + .name = TYPE_HVF_GICV3, + .parent = TYPE_ARM_GICV3_COMMON, + .instance_size = sizeof(GICv3State), + .class_init = hvf_gicv3_class_init, + .class_size = sizeof(HVFARMGICv3Class), +}; + +static void hvf_gicv3_register_types(void) +{ + type_register_static(&hvf_arm_gicv3_info); +} + +type_init(hvf_gicv3_register_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -XXX,XX +XXX,XX @@ arm_common_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common arm_common_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c')) specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c')) specific_ss.add(when: ['CONFIG_WHPX', 'TARGET_AARCH64'], if_true: files('arm_gicv3_whpx.c')) +specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_hvf.c')) specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c')) diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -XXX,XX +XXX,XX @@ DECLARE_OBJ_CHECKERS(GICv3State, ARMGICv3CommonClass, /* Types for GICv3 kernel-irqchip */ #define TYPE_WHPX_GICV3 "whpx-arm-gicv3" +#define TYPE_HVF_GICV3 "hvf-arm-gicv3" struct ARMGICv3CommonClass { /*< private >*/ -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> On HVF, some of the GIC state is in an opaque Apple-provided structure. Save/restore that state to be able to save/restore VMs that use the hardware GIC. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-7-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/intc/arm_gicv3_common.c | 1 + hw/intc/arm_gicv3_hvf.c | 95 ++++++++++++++++++++++++++++-- hw/intc/arm_gicv3_hvf_stub.c | 25 ++++++++ hw/intc/meson.build | 1 + include/hw/intc/arm_gicv3_common.h | 3 + 5 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 hw/intc/arm_gicv3_hvf_stub.c diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_gicv3 = { .subsections = (const VMStateDescription * const []) { &vmstate_gicv3_gicd_no_migration_shift_bug, &vmstate_gicv3_gicd_nmi, + &vmstate_gicv3_hvf, NULL } }; diff --git a/hw/intc/arm_gicv3_hvf.c b/hw/intc/arm_gicv3_hvf.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_hvf.c +++ b/hw/intc/arm_gicv3_hvf.c @@ -XXX,XX +XXX,XX @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "system/runstate.h" +#include "migration/vmstate.h" #include "system/hvf.h" #include "system/hvf_int.h" #include "hvf_arm.h" @@ -XXX,XX +XXX,XX @@ struct HVFARMGICv3Class { typedef struct HVFARMGICv3Class HVFARMGICv3Class; -/* This is reusing the GICv3State typedef from ARM_GICV3_ITS_COMMON */ -DECLARE_OBJ_CHECKERS(GICv3State, HVFARMGICv3Class, +typedef struct HVFGICv3State { + GICv3State gicv3_state; + uint32_t size; + void *state; +} HVFGICv3State; + +DECLARE_OBJ_CHECKERS(HVFGICv3State, HVFARMGICv3Class, HVF_GICV3, TYPE_HVF_GICV3); /* @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { static void hvf_gicv3_realize(DeviceState *dev, Error **errp) { ERRP_GUARD(); - GICv3State *s = HVF_GICV3(dev); + GICv3State *s = (GICv3State *)HVF_GICV3(dev); HVFARMGICv3Class *kgc = HVF_GICV3_GET_CLASS(s); int i; @@ -XXX,XX +XXX,XX @@ static void hvf_gicv3_realize(DeviceState *dev, Error **errp) } } +/* + * HVF doesn't have a way to save the RDIST pending tables + * to guest memory, only to an opaque data structure. + */ +static bool gicv3_is_hvf(void *opaque) +{ + return hvf_enabled() && hvf_irqchip_in_kernel(); +} + +static int hvf_gic_opaque_state_save(void *opaque) +{ + HVFGICv3State *gic = opaque; + hv_gic_state_t gic_state; + hv_return_t err; + size_t size; + + gic_state = hv_gic_state_create(); + if (gic_state == NULL) { + error_report("hvf: vgic: failed to create hv_gic_state_create."); + return 1; + } + err = hv_gic_state_get_size(gic_state, &size); + gic->size = size; + if (err != HV_SUCCESS) { + error_report("hvf: vgic: failed to get GIC state size."); + os_release(gic_state); + return 1; + } + gic->state = g_malloc(gic->size); + err = hv_gic_state_get_data(gic_state, gic->state); + if (err != HV_SUCCESS) { + error_report("hvf: vgic: failed to get GIC state."); + os_release(gic_state); + return 1; + } + + os_release(gic_state); + return 0; +} + +static int hvf_gic_opaque_state_free(void *opaque) +{ + HVFGICv3State *gic = opaque; + free(gic->state); + return 0; +} + +static int hvf_gic_opaque_state_restore(void *opaque, int version_id) +{ + HVFGICv3State *gic = opaque; + hv_return_t err; + if (!gic->size) { + return 0; + } + err = hv_gic_set_state(gic->state, gic->size); + if (err != HV_SUCCESS) { + error_report("hvf: vgic: failed to restore GIC state."); + return 1; + } + return 0; +} + +const VMStateDescription vmstate_gicv3_hvf = { + .name = "arm_gicv3/hvf_gic_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = gicv3_is_hvf, + .pre_save = hvf_gic_opaque_state_save, + .post_save = hvf_gic_opaque_state_free, + .post_load = hvf_gic_opaque_state_restore, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(size, HVFGICv3State), + VMSTATE_VBUFFER_ALLOC_UINT32(state, + HVFGICv3State, 0, 0, + size), + VMSTATE_END_OF_LIST() + }, +}; + static void hvf_gicv3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -XXX,XX +XXX,XX @@ static void hvf_gicv3_class_init(ObjectClass *klass, const void *data) static const TypeInfo hvf_arm_gicv3_info = { .name = TYPE_HVF_GICV3, .parent = TYPE_ARM_GICV3_COMMON, - .instance_size = sizeof(GICv3State), + .instance_size = sizeof(HVFGICv3State), .class_init = hvf_gicv3_class_init, .class_size = sizeof(HVFARMGICv3Class), }; diff --git a/hw/intc/arm_gicv3_hvf_stub.c b/hw/intc/arm_gicv3_hvf_stub.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/intc/arm_gicv3_hvf_stub.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * ARM Generic Interrupt Controller using HVF platform support stub + * + * Copyright (c) 2026 Mohamed Mediouni + * + */ +#include "qemu/osdep.h" +#include "hw/intc/arm_gicv3_common.h" +#include "migration/vmstate.h" +#include "qemu/typedefs.h" + +static bool needed_never(void *opaque) +{ + return false; +} + +const VMStateDescription vmstate_gicv3_hvf = { + .name = "arm_gicv3/hvf_gic_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = needed_never, + .version_id = 1, + .minimum_version_id = 1, +}; diff --git a/hw/intc/meson.build b/hw/intc/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -XXX,XX +XXX,XX @@ arm_common_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c')) specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c')) specific_ss.add(when: ['CONFIG_WHPX', 'TARGET_AARCH64'], if_true: files('arm_gicv3_whpx.c')) specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_hvf.c')) +specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_false: files('arm_gicv3_hvf_stub.c')) specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c')) diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -XXX,XX +XXX,XX @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, */ const char *gicv3_class_name(void); +/* HVF vGIC-specific state: stubbed out on a build with HVF disabled */ +extern const VMStateDescription vmstate_gicv3_hvf; + #endif -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> Misc changes needed for HVF vGIC enablement. Note: x86_64 macOS exposes interrupt controller virtualisation since macOS 12. Keeping an #ifdef here in case we end up supporting that... However, given that x86_64 macOS is on its way out, it'll probably (?) not be supported in Qemu. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-8-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- accel/hvf/hvf-all.c | 50 ++++++++++++++++++++++++++++++++++++++ accel/stubs/hvf-stub.c | 1 + hw/arm/virt.c | 23 +++++++++++++++--- hw/intc/arm_gicv3_common.c | 3 +++ include/system/hvf.h | 3 +++ system/vl.c | 2 ++ 6 files changed, 78 insertions(+), 4 deletions(-) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index XXXXXXX..XXXXXXX 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-common.h" #include "accel/accel-ops.h" #include "exec/cpu-common.h" #include "system/address-spaces.h" @@ -XXX,XX +XXX,XX @@ #include "trace.h" bool hvf_allowed; +bool hvf_kernel_irqchip; const char *hvf_return_string(hv_return_t ret) { @@ -XXX,XX +XXX,XX @@ static int hvf_gdbstub_sstep_flags(AccelState *as) return SSTEP_ENABLE | SSTEP_NOIRQ; } +static void hvf_set_kernel_irqchip(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + OnOffSplit mode; + if (!visit_type_OnOffSplit(v, name, &mode, errp)) { + return; + } + + switch (mode) { + case ON_OFF_SPLIT_ON: +#ifdef HOST_X86_64 + /* macOS 12 onwards exposes an HVF virtual APIC. */ + error_setg(errp, "HVF: kernel irqchip is not currently implemented for x86."); + break; +#else + hvf_kernel_irqchip = true; + break; +#endif + + case ON_OFF_SPLIT_OFF: + hvf_kernel_irqchip = false; + break; + + case ON_OFF_SPLIT_SPLIT: + error_setg(errp, "HVF: split irqchip is not supported on HVF."); + break; + + default: + /* + * The value was checked in visit_type_OnOffSplit() above. If + * we get here, then something is wrong in QEMU. + */ + abort(); + } +} + static void hvf_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); @@ -XXX,XX +XXX,XX @@ static void hvf_accel_class_init(ObjectClass *oc, const void *data) ac->init_machine = hvf_accel_init; ac->allowed = &hvf_allowed; ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags; +#ifdef HOST_X86_64 + hvf_kernel_irqchip = false; +#else + hvf_kernel_irqchip = true; +#endif + object_class_property_add(oc, "kernel-irqchip", "on|off|split", + NULL, hvf_set_kernel_irqchip, + NULL, NULL); + object_class_property_set_description(oc, "kernel-irqchip", + "Configure HVF irqchip"); } static const TypeInfo hvf_accel_type = { diff --git a/accel/stubs/hvf-stub.c b/accel/stubs/hvf-stub.c index XXXXXXX..XXXXXXX 100644 --- a/accel/stubs/hvf-stub.c +++ b/accel/stubs/hvf-stub.c @@ -XXX,XX +XXX,XX @@ #include "system/hvf.h" bool hvf_allowed; +bool hvf_kernel_irqchip; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -XXX,XX +XXX,XX @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) * interrupts; there are always 32 of the former (mandated by GIC spec). */ qdev_prop_set_uint32(vms->gic, "num-irq", NUM_IRQS + 32); - if (!kvm_irqchip_in_kernel()) { + if (!kvm_irqchip_in_kernel() && !hvf_irqchip_in_kernel()) { qdev_prop_set_bit(vms->gic, "has-security-extensions", vms->secure); } @@ -XXX,XX +XXX,XX @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) qdev_prop_set_array(vms->gic, "redist-region-count", redist_region_count); - if (!kvm_irqchip_in_kernel()) { + if (!kvm_irqchip_in_kernel() && + !(hvf_enabled() && hvf_irqchip_in_kernel())) { if (vms->tcg_its) { object_property_set_link(OBJECT(vms->gic), "sysmem", OBJECT(mem), &error_fatal); @@ -XXX,XX +XXX,XX @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) ARCH_GIC_MAINT_IRQ); } } else { - if (!kvm_irqchip_in_kernel()) { + if (!kvm_irqchip_in_kernel() && !hvf_irqchip_in_kernel()) { qdev_prop_set_bit(vms->gic, "has-virtualization-extensions", vms->virt); } @@ -XXX,XX +XXX,XX @@ static void finalize_gic_version(VirtMachineState *vms) accel_name = "KVM with kernel-irqchip=off"; } else if (whpx_enabled()) { gics_supported |= VIRT_GIC_VERSION_3_MASK; - } else if (tcg_enabled() || hvf_enabled() || qtest_enabled()) { + } else if (hvf_enabled()) { + if (!hvf_irqchip_in_kernel()) { + gics_supported |= VIRT_GIC_VERSION_2_MASK; + } + /* Hypervisor.framework doesn't expose EL2<->1 transition notifiers */ + if (!(!hvf_irqchip_in_kernel() && vms->virt)) { + gics_supported |= VIRT_GIC_VERSION_3_MASK; + } + } else if (tcg_enabled() || qtest_enabled()) { gics_supported |= VIRT_GIC_VERSION_2_MASK; if (module_object_class_by_name("arm-gicv3")) { gics_supported |= VIRT_GIC_VERSION_3_MASK; @@ -XXX,XX +XXX,XX @@ static void finalize_msi_controller(VirtMachineState *vms) vms->msi_controller = VIRT_MSI_CTRL_GICV2M; } else if (whpx_enabled()) { vms->msi_controller = VIRT_MSI_CTRL_GICV2M; + } else if (hvf_enabled() && hvf_irqchip_in_kernel()) { + vms->msi_controller = VIRT_MSI_CTRL_GICV2M; } else { vms->msi_controller = VIRT_MSI_CTRL_ITS; } @@ -XXX,XX +XXX,XX @@ static void finalize_msi_controller(VirtMachineState *vms) error_report("ITS not supported on WHPX."); exit(1); } + if (hvf_enabled() && hvf_irqchip_in_kernel()) { + error_report("ITS not supported on HVF when using the hardware vGIC."); + exit(1); + } } assert(vms->msi_controller != VIRT_MSI_CTRL_AUTO); diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -XXX,XX +XXX,XX @@ #include "hw/arm/linux-boot-if.h" #include "system/kvm.h" #include "system/whpx.h" +#include "system/hvf.h" static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs) @@ -XXX,XX +XXX,XX @@ const char *gicv3_class_name(void) return "kvm-arm-gicv3"; } else if (whpx_enabled()) { return TYPE_WHPX_GICV3; + } else if (hvf_enabled() && hvf_irqchip_in_kernel()) { + return TYPE_HVF_GICV3; } else { if (kvm_enabled()) { error_report("Userspace GICv3 is not supported with KVM"); diff --git a/include/system/hvf.h b/include/system/hvf.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/hvf.h +++ b/include/system/hvf.h @@ -XXX,XX +XXX,XX @@ #ifdef CONFIG_HVF_IS_POSSIBLE extern bool hvf_allowed; #define hvf_enabled() (hvf_allowed) +extern bool hvf_kernel_irqchip; +#define hvf_irqchip_in_kernel() (hvf_kernel_irqchip) #else /* !CONFIG_HVF_IS_POSSIBLE */ #define hvf_enabled() 0 +#define hvf_irqchip_in_kernel() 0 #endif /* !CONFIG_HVF_IS_POSSIBLE */ #define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf") diff --git a/system/vl.c b/system/vl.c index XXXXXXX..XXXXXXX 100644 --- a/system/vl.c +++ b/system/vl.c @@ -XXX,XX +XXX,XX @@ static void qemu_apply_legacy_machine_options(QDict *qdict) false); object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), "kernel-irqchip", value, false); + object_register_sugar_prop(ACCEL_CLASS_NAME("hvf"), "kernel-irqchip", value, + false); qdict_del(qdict, "kernel-irqchip"); } -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> While figuring out a better spot for it, put it in hv_arch_vm_create(). After hv_vcpu_create is documented as too late, and deferring vCPU initialization isn't enough either. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-9-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range) chosen_ipa_bit_size = pa_range; ret = hv_vm_create(config); + if (hvf_irqchip_in_kernel()) { + /* + * Instantiate GIC. + * This must be done prior to the creation of any vCPU + * but past hv_vm_create() + */ + hv_gic_config_t cfg = hv_gic_config_create(); + hv_gic_config_set_distributor_base(cfg, 0x08000000); + hv_gic_config_set_redistributor_base(cfg, 0x080A0000); + hv_return_t err = hv_gic_create(cfg); + if (err != HV_SUCCESS) { + error_report("error creating platform VGIC"); + goto cleanup; + } + os_release(cfg); + } cleanup: os_release(config); -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-10-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- accel/hvf/hvf-all.c | 1 + accel/stubs/hvf-stub.c | 1 + hw/arm/virt.c | 5 +++++ include/system/hvf.h | 5 +++++ target/arm/hvf/hvf.c | 36 ++++++++++++++++++++++++++++++++++-- 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index XXXXXXX..XXXXXXX 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -XXX,XX +XXX,XX @@ bool hvf_allowed; bool hvf_kernel_irqchip; +bool hvf_nested_virt; const char *hvf_return_string(hv_return_t ret) { diff --git a/accel/stubs/hvf-stub.c b/accel/stubs/hvf-stub.c index XXXXXXX..XXXXXXX 100644 --- a/accel/stubs/hvf-stub.c +++ b/accel/stubs/hvf-stub.c @@ -XXX,XX +XXX,XX @@ bool hvf_allowed; bool hvf_kernel_irqchip; +bool hvf_nested_virt; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -XXX,XX +XXX,XX @@ static void virt_set_virt(Object *obj, bool value, Error **errp) VirtMachineState *vms = VIRT_MACHINE(obj); vms->virt = value; + /* + * At this point, HVF is not initialised yet. + * However, it needs to know if nested virt is enabled at init time. + */ + hvf_nested_virt_enable(value); } static bool virt_get_highmem(Object *obj, Error **errp) diff --git a/include/system/hvf.h b/include/system/hvf.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/hvf.h +++ b/include/system/hvf.h @@ -XXX,XX +XXX,XX @@ extern bool hvf_allowed; #define hvf_enabled() (hvf_allowed) extern bool hvf_kernel_irqchip; #define hvf_irqchip_in_kernel() (hvf_kernel_irqchip) +extern bool hvf_nested_virt; +#define hvf_nested_virt_enabled() (hvf_nested_virt) +#define hvf_nested_virt_enable(enable) hvf_nested_virt = enable #else /* !CONFIG_HVF_IS_POSSIBLE */ #define hvf_enabled() 0 #define hvf_irqchip_in_kernel() 0 +#define hvf_nested_virt_enabled() 0 +#define hvf_nested_virt_enable(enable) 0 #endif /* !CONFIG_HVF_IS_POSSIBLE */ #define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf") diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ #include "system/memory.h" #include "hw/core/boards.h" #include "hw/core/irq.h" +#include "hw/arm/virt.h" #include "qemu/main-loop.h" #include "system/cpus.h" #include "arm-powerctl.h" @@ -XXX,XX +XXX,XX @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) (1ULL << ARM_FEATURE_PMU) | (1ULL << ARM_FEATURE_GENERIC_TIMER); + if (hvf_nested_virt_enabled()) { + ahcf->features |= 1ULL << ARM_FEATURE_EL2; + } + for (i = 0; i < ARRAY_SIZE(regs); i++) { r |= hv_vcpu_config_get_feature_reg(config, regs[i].reg, &host_isar.idregs[regs[i].index]); @@ -XXX,XX +XXX,XX @@ void hvf_arch_vcpu_destroy(CPUState *cpu) assert_hvf_ok(ret); } +static bool hvf_arm_el2_supported(void) +{ + bool is_nested_virt_supported; + hv_return_t ret = hv_vm_config_get_el2_supported(&is_nested_virt_supported); + assert_hvf_ok(ret); + return is_nested_virt_supported; +} + + hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range) { hv_return_t ret; @@ -XXX,XX +XXX,XX @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range) } chosen_ipa_bit_size = pa_range; + if (hvf_nested_virt_enabled()) { + if (!hvf_arm_el2_supported()) { + error_report("Nested virtualization not supported on this system."); + goto cleanup; + } + ret = hv_vm_config_set_el2_enabled(config, true); + if (ret != HV_SUCCESS) { + error_report("Failed to enable nested virtualization."); + goto cleanup; + } + } + ret = hv_vm_create(config); if (hvf_irqchip_in_kernel()) { /* @@ -XXX,XX +XXX,XX @@ static void hvf_psci_cpu_off(ARMCPU *arm_cpu) assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS); } +static int hvf_psci_get_target_el(void) +{ + if (hvf_nested_virt_enabled()) { + return 2; + } + return 1; +} /* * Handle a PSCI call. * @@ -XXX,XX +XXX,XX @@ static bool hvf_handle_psci_call(CPUState *cpu, int *excp_ret) CPUState *target_cpu_state; ARMCPU *target_cpu; target_ulong entry; - int target_el = 1; int32_t ret = 0; trace_arm_psci_call(param[0], param[1], param[2], param[3], @@ -XXX,XX +XXX,XX @@ static bool hvf_handle_psci_call(CPUState *cpu, int *excp_ret) entry = param[2]; context_id = param[3]; ret = arm_set_cpu_on(mpidr, entry, context_id, - target_el, target_aarch64); + hvf_psci_get_target_el(), target_aarch64); break; case QEMU_PSCI_0_1_FN_CPU_OFF: case QEMU_PSCI_0_2_FN_CPU_OFF: -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> When running with the Apple vGIC, the EL1 vtimer is handled by the platform. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Mads Ynddal <mads@ynddal.dk> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-11-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ static int hvf_handle_vmexit(CPUState *cpu, hv_vcpu_exit_t *exit) switch (exit->reason) { case HV_EXIT_REASON_EXCEPTION: - hvf_sync_vtimer(cpu); + if (!hvf_irqchip_in_kernel()) { + hvf_sync_vtimer(cpu); + } ret = hvf_handle_exception(cpu, &exit->exception); break; case HV_EXIT_REASON_VTIMER_ACTIVATED: -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> From Apple documentation: > When EL2 is disabled, PMU register accesses trigger "Trapped MSR, MRS, or > System Instruction" exceptions. When this happens, hv_vcpu_run() returns, and the > hv_vcpu_exit_t object contains the information about this exception. > When EL2 is enabled, the handling of PMU register accesses is determined by the PMUVer > field of ID_AA64DFR0_EL1 register. > If the PMUVer field value is zero or is invalid, PMU register accesses generate "Undefined" > exceptions, which are sent to the guest. > If the PMUVer field value is non-zero and valid, PMU register accesses are emulated by the framework. > The ID_AA64DFR0_EL1 register can be modified via hv_vcpu_set_sys_reg API. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-12-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) ARMCPU *arm_cpu = ARM_CPU(cpu); CPUARMState *env = &arm_cpu->env; - if (arm_feature(env, ARM_FEATURE_PMU)) { + if (!hvf_nested_virt_enabled() && arm_feature(env, ARM_FEATURE_PMU)) { switch (reg) { case SYSREG_PMCR_EL0: *val = env->cp15.c9_pmcr; @@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) SYSREG_OP2(reg), val); - if (arm_feature(env, ARM_FEATURE_PMU)) { + if (!hvf_nested_virt_enabled() && arm_feature(env, ARM_FEATURE_PMU)) { switch (reg) { case SYSREG_PMCCNTR_EL0: pmu_op_start(env); -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> When running with nested virt on, a minimum PMU is exposed by Hypervisor.framework if a valid PMUVer register value is set. That PMU isn't exposed otherwise. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-13-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) clamp_id_aa64mmfr0_parange_to_ipa_size(&host_isar); + if (hvf_nested_virt_enabled()) { + FIELD_DP64_IDREG(&host_isar, ID_AA64DFR0, PMUVER, 0x1); + } + ahcf->isar = host_isar; /* -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> When using the vGIC, timers are directly handled by the platform. No vmexits ought to happen in that case. Abort if reaching those code paths. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Mads Ynddal <mads@ynddal.dk> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-14-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) case SYSREG_ICC_SGI1R_EL1: case SYSREG_ICC_SRE_EL1: case SYSREG_ICC_CTLR_EL1: + assert(!hvf_irqchip_in_kernel()); /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ if (hvf_sysreg_read_cp(cpu, "GICv3", reg, val)) { return 0; @@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_ICC_SGI0R_EL1: case SYSREG_ICC_SGI1R_EL1: case SYSREG_ICC_SRE_EL1: + assert(!hvf_irqchip_in_kernel()); /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ if (hvf_sysreg_write_cp(cpu, "GICv3", reg, val)) { return 0; @@ -XXX,XX +XXX,XX @@ static int hvf_handle_vmexit(CPUState *cpu, hv_vcpu_exit_t *exit) ret = hvf_handle_exception(cpu, &exit->exception); break; case HV_EXIT_REASON_VTIMER_ACTIVATED: + assert(!hvf_irqchip_in_kernel()); qemu_set_irq(arm_cpu->gt_timer_outputs[GTIMER_VIRT], 1); cpu->accel->vtimer_masked = true; break; -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> When starting up the VM at EL2, more sysregs are available. Sync the state of those. In addition, sync the state of the EL1 physical timer when the vGIC is used, even if running at EL1. However, no OS running at EL1 is expected to use those registers. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Message-id: 20260306130107.35359-15-mohamed@unpredictable.fr Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 55 ++++++++++++++++++++++++++++++++----- target/arm/hvf/sysreg.c.inc | 44 +++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ static const struct hvf_reg_match hvf_sme2_preg_match[] = { QEMU_BUILD_BUG_ON(HVF_ID != KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__))); #define DEF_SYSREG_15_02(...) +#define DEF_SYSREG_EL2(HVF_ID, ...) \ + QEMU_BUILD_BUG_ON(HVF_ID != KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__))); + +#define DEF_SYSREG_VGIC(HVF_ID, ...) \ + QEMU_BUILD_BUG_ON(HVF_ID != KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__))); + +#define DEF_SYSREG_VGIC_EL2(HVF_ID, ...) \ + QEMU_BUILD_BUG_ON(HVF_ID != KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__))); + #include "sysreg.c.inc" #undef DEF_SYSREG #undef DEF_SYSREG_15_02 +#undef DEF_SYSREG_EL2 +#undef DEF_SYSREG_VGIC +#undef DEF_SYSREG_VGIC_EL2 -#define DEF_SYSREG(HVF_ID, op0, op1, crn, crm, op2) HVF_ID, +#define DEF_SYSREG(HVF_ID, op0, op1, crn, crm, op2) {HVF_ID}, #define DEF_SYSREG_15_02(...) +#define DEF_SYSREG_EL2(HVF_ID, op0, op1, crn, crm, op2) {HVF_ID, .el2 = true}, +#define DEF_SYSREG_VGIC(HVF_ID, op0, op1, crn, crm, op2) {HVF_ID, .vgic = true}, +#define DEF_SYSREG_VGIC_EL2(HVF_ID, op0, op1, crn, crm, op2) {HVF_ID, true, true}, -static const hv_sys_reg_t hvf_sreg_list[] = { +struct hvf_sreg { + hv_sys_reg_t sreg; + bool vgic; + bool el2; +}; + +static struct hvf_sreg hvf_sreg_list[] = { #include "sysreg.c.inc" }; #undef DEF_SYSREG #undef DEF_SYSREG_15_02 +#undef DEF_SYSREG_EL2 +#undef DEF_SYSREG_VGIC +#undef DEF_SYSREG_VGIC_EL2 #define DEF_SYSREG(...) -#define DEF_SYSREG_15_02(HVF_ID, op0, op1, crn, crm, op2) HVF_ID, +#define DEF_SYSREG_15_02(HVF_ID, op0, op1, crn, crm, op2) {HVF_ID}, +#define DEF_SYSREG_EL2(...) +#define DEF_SYSREG_VGIC(...) +#define DEF_SYSREG_VGIC_EL2(...) API_AVAILABLE(macos(15.2)) -static const hv_sys_reg_t hvf_sreg_list_sme2[] = { +static struct hvf_sreg hvf_sreg_list_sme2[] = { #include "sysreg.c.inc" }; #undef DEF_SYSREG #undef DEF_SYSREG_15_02 +#undef DEF_SYSREG_EL2 +#undef DEF_SYSREG_VGIC +#undef DEF_SYSREG_VGIC_EL2 /* * For FEAT_SME2 migration, we need to store PSTATE.{SM,ZA} bits which are @@ -XXX,XX +XXX,XX @@ int hvf_arch_init_vcpu(CPUState *cpu) #define DEF_SYSREG_15_02(HVF_ID, ...) \ g_assert(HVF_ID == KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__))); #define DEF_SYSREG(...) +#define DEF_SYSREG_EL2(...) +#define DEF_SYSREG_VGIC(...) +#define DEF_SYSREG_VGIC_EL2(...) #include "sysreg.c.inc" @@ -XXX,XX +XXX,XX @@ int hvf_arch_init_vcpu(CPUState *cpu) memset(arm_cpu->cpreg_values, 0, sregs_match_len * sizeof(uint64_t)); /* Populate cp list for all known sysregs */ - for (i = 0; i < ARRAY_SIZE(hvf_sreg_list); i++) { - hv_sys_reg_t hvf_id = hvf_sreg_list[i]; + for (i = 0; i < sregs_match_len; i++) { + hv_sys_reg_t hvf_id = hvf_sreg_list[i].sreg; uint64_t kvm_id = HVF_TO_KVMID(hvf_id); uint32_t key = kvm_to_cpreg_id(kvm_id); const ARMCPRegInfo *ri = get_arm_cp_reginfo(arm_cpu->cp_regs, key); + if (hvf_sreg_list[i].vgic && !hvf_irqchip_in_kernel()) { + continue; + } + + if (hvf_sreg_list[i].el2 && !hvf_nested_virt_enabled()) { + continue; + } + if (ri) { assert(!(ri->type & ARM_CP_NO_RAW)); arm_cpu->cpreg_indexes[sregs_cnt++] = kvm_id; @@ -XXX,XX +XXX,XX @@ int hvf_arch_init_vcpu(CPUState *cpu) if (__builtin_available(macOS 15.2, *)) { if (hvf_arm_sme2_supported()) { for (i = 0; i < ARRAY_SIZE(hvf_sreg_list_sme2); i++) { - hv_sys_reg_t hvf_id = hvf_sreg_list_sme2[i]; + hv_sys_reg_t hvf_id = hvf_sreg_list_sme2[i].sreg; uint64_t kvm_id = HVF_TO_KVMID(hvf_id); uint32_t key = kvm_to_cpreg_id(kvm_id); const ARMCPRegInfo *ri = get_arm_cp_reginfo(arm_cpu->cp_regs, key); diff --git a/target/arm/hvf/sysreg.c.inc b/target/arm/hvf/sysreg.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/sysreg.c.inc +++ b/target/arm/hvf/sysreg.c.inc @@ -XXX,XX +XXX,XX @@ DEF_SYSREG_15_02(HV_SYS_REG_ID_AA64ZFR0_EL1, 3, 0, 0, 4, 4) DEF_SYSREG_15_02(HV_SYS_REG_ID_AA64SMFR0_EL1, 3, 0, 0, 4, 5) DEF_SYSREG_15_02(HV_SYS_REG_SMPRI_EL1, 3, 0, 1, 2, 4) DEF_SYSREG_15_02(HV_SYS_REG_SMCR_EL1, 3, 0, 1, 2, 6) +/* + * Block these because of the same issue as virtual counters in + * that caused the revert in 28b0ed32b32c7e5094cf2f1ec9c0645c65fad2aa + * + * DEF_SYSREG_VGIC(HV_SYS_REG_CNTP_CTL_EL0, 3, 3, 14, 2, 1) + * DEF_SYSREG_VGIC(HV_SYS_REG_CNTP_CVAL_EL0, 3, 3, 14, 2, 2) + */ +#ifdef SYNC_NO_RAW_REGS +DEF_SYSREG_VGIC(HV_SYS_REG_CNTP_TVAL_EL0, 3, 3, 14, 2, 0) +#endif + +/* + * Also block these because of the same issue as virtual counters in + * that caused the revert in 28b0ed32b32c7e5094cf2f1ec9c0645c65fad2aa + * + * DEF_SYSREG_VGIC_EL2(HV_SYS_REG_CNTHP_CVAL_EL2, 3, 4, 14, 2, 2) + * DEF_SYSREG_VGIC_EL2(HV_SYS_REG_CNTHP_CTL_EL2, 3, 4, 14, 2, 1) + */ +DEF_SYSREG_VGIC_EL2(HV_SYS_REG_CNTHCTL_EL2, 3, 4, 14, 1, 0) +#ifdef SYNC_NO_RAW_REGS +DEF_SYSREG_VGIC_EL2(HV_SYS_REG_CNTHP_TVAL_EL2, 3, 4, 14, 2, 0) +#endif +DEF_SYSREG_VGIC_EL2(HV_SYS_REG_CNTVOFF_EL2, 3, 4, 14, 0, 3) + +DEF_SYSREG_EL2(HV_SYS_REG_CPTR_EL2, 3, 4, 1, 1, 2) +DEF_SYSREG_EL2(HV_SYS_REG_ELR_EL2, 3, 4, 4, 0, 1) +DEF_SYSREG_EL2(HV_SYS_REG_ESR_EL2, 3, 4, 5, 2, 0) +DEF_SYSREG_EL2(HV_SYS_REG_FAR_EL2, 3, 4, 6, 0, 0) +DEF_SYSREG_EL2(HV_SYS_REG_HCR_EL2, 3, 4, 1, 1, 0) +DEF_SYSREG_EL2(HV_SYS_REG_HPFAR_EL2, 3, 4, 6, 0, 4) +DEF_SYSREG_EL2(HV_SYS_REG_MAIR_EL2, 3, 4, 10, 2, 0) +DEF_SYSREG_EL2(HV_SYS_REG_MDCR_EL2, 3, 4, 1, 1, 1) +DEF_SYSREG_EL2(HV_SYS_REG_SCTLR_EL2, 3, 4, 1, 0, 0) +DEF_SYSREG_EL2(HV_SYS_REG_SPSR_EL2, 3, 4, 4, 0, 0) +DEF_SYSREG_EL2(HV_SYS_REG_SP_EL2, 3, 6, 4, 1, 0) +DEF_SYSREG_EL2(HV_SYS_REG_TCR_EL2, 3, 4, 2, 0, 2) +DEF_SYSREG_EL2(HV_SYS_REG_TPIDR_EL2, 3, 4, 13, 0, 2) +DEF_SYSREG_EL2(HV_SYS_REG_TTBR0_EL2, 3, 4, 2, 0, 0) +DEF_SYSREG_EL2(HV_SYS_REG_TTBR1_EL2, 3, 4, 2, 0, 1) +DEF_SYSREG_EL2(HV_SYS_REG_VBAR_EL2, 3, 4, 12, 0, 0) +DEF_SYSREG_EL2(HV_SYS_REG_VMPIDR_EL2, 3, 4, 0, 0, 5) +DEF_SYSREG_EL2(HV_SYS_REG_VPIDR_EL2, 3, 4, 0, 0, 0) +DEF_SYSREG_EL2(HV_SYS_REG_VTCR_EL2, 3, 4, 2, 1, 2) +DEF_SYSREG_EL2(HV_SYS_REG_VTTBR_EL2, 3, 4, 2, 1, 0) -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> HVF traps accesses to CNTHCTL_EL2. For nested guests, HVF traps accesses to MDCCINT_EL1. Pass through those accesses to the Hypervisor.framework library. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-16-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ void hvf_arm_init_debug(void) #define SYSREG_DBGWVR15_EL1 SYSREG(2, 0, 0, 15, 6) #define SYSREG_DBGWCR15_EL1 SYSREG(2, 0, 0, 15, 7) +/* EL2 registers */ +#define SYSREG_CNTHCTL_EL2 SYSREG(3, 4, 14, 1, 0) +#define SYSREG_MDCCINT_EL1 SYSREG(2, 0, 0, 2, 0) + #define WFX_IS_WFE (1 << 0) #define TMR_CTL_ENABLE (1 << 0) @@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) case SYSREG_OSDLR_EL1: /* Dummy register */ return 0; + case SYSREG_CNTHCTL_EL2: + if (__builtin_available(macOS 15.0, *)) { + assert_hvf_ok(hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTHCTL_EL2, val)); + } + return 0; + case SYSREG_MDCCINT_EL1: + assert_hvf_ok(hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_MDCCINT_EL1, val)); + return 0; case SYSREG_ICC_AP0R0_EL1: case SYSREG_ICC_AP0R1_EL1: case SYSREG_ICC_AP0R2_EL1: @@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_OSDLR_EL1: /* Dummy register */ return 0; + case SYSREG_CNTHCTL_EL2: + if (__builtin_available(macOS 15.0, *)) { + assert_hvf_ok(hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTHCTL_EL2, val)); + } + return 0; + case SYSREG_MDCCINT_EL1: + assert_hvf_ok(hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_MDCCINT_EL1, val)); + return 0; case SYSREG_LORC_EL1: /* Dummy register */ return 0; -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> Currently, Apple doesn't support the nested virtualisation + SME combination. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Message-id: 20260306130107.35359-17-mohamed@unpredictable.fr [PMM: use FIELD_DP64_IDREG()] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 2 ++ target/arm/hvf_arm.h | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) if (hvf_nested_virt_enabled()) { FIELD_DP64_IDREG(&host_isar, ID_AA64DFR0, PMUVER, 0x1); + /* SME is not implemented with nested virt on the Apple side */ + FIELD_DP64_IDREG(&host_isar, ID_AA64PFR1, SME, 0); } ahcf->isar = host_isar; diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf_arm.h +++ b/target/arm/hvf_arm.h @@ -XXX,XX +XXX,XX @@ #ifndef QEMU_HVF_ARM_H #define QEMU_HVF_ARM_H +#include "system/hvf.h" #include "target/arm/cpu-qom.h" /** @@ -XXX,XX +XXX,XX @@ void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu); if (__builtin_available(macOS 15.2, *)) { size_t svl_bytes; hv_return_t result = hv_sme_config_get_max_svl_bytes(&svl_bytes); + /* Nested virt not supported together with SME right now. */ + if (hvf_nested_virt_enabled()) { + return false; + } if (result == HV_UNSUPPORTED) { return false; } -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260306130107.35359-18-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/virt.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -XXX,XX +XXX,XX @@ static void machvirt_init(MachineState *machine) exit(1); } - if (vms->virt && !kvm_enabled() && !tcg_enabled() && !qtest_enabled()) { + if (vms->virt && !kvm_enabled() && !tcg_enabled() + && !hvf_enabled() && !qtest_enabled()) { error_report("mach-virt: %s does not support providing " "Virtualization extensions to the guest CPU", current_accel_name()); exit(1); } + if (vms->virt && hvf_enabled() && !hvf_irqchip_in_kernel()) { + error_report("mach-virt: For HVF and Virtualization extensions " + "only kernel-irqchip=on is currently supported"); + exit(1); + } + if (vms->mte && hvf_enabled()) { error_report("mach-virt: %s does not support providing " "MTE to the guest CPU", -- 2.43.0
v3->v4: Windows headers define an INT type which clashed with an enum value name in arm_gicv3_its.c... The following changes since commit eae587e8e3694b1aceab23239493fb4c7e1a80f5: Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-09-13' into staging (2021-09-13 11:00:30 +0100) are available in the Git repository at: https://git.linaro.org/people/pmaydell/qemu-arm.git tags/pull-target-arm-20210913-3 for you to fetch changes up to 28e987a7e7edaa3ca7feeac65edca26145df8814: hw/arm/mps2.c: Mark internal-only I2C buses as 'full' (2021-09-13 21:01:08 +0100) ---------------------------------------------------------------- target-arm queue: * mark MPS2/MPS3 board-internal i2c buses as 'full' so that command line user-created devices are not plugged into them * Take an exception if PSTATE.IL is set * Support an emulated ITS in the virt board * Add support for kudo-bmc board * Probe for KVM_CAP_ARM_VM_IPA_SIZE when creating scratch VM * cadence_uart: Fix clock handling issues that prevented u-boot from running ---------------------------------------------------------------- Bin Meng (6): hw/misc: zynq_slcr: Correctly compute output clocks in the reset exit phase hw/char: cadence_uart: Disable transmit when input clock is disabled hw/char: cadence_uart: Move clock/reset check to uart_can_receive() hw/char: cadence_uart: Convert to memop_with_attrs() ops hw/char: cadence_uart: Ignore access when unclocked or in reset for uart_{read, write}() hw/char: cadence_uart: Log a guest error when device is unclocked or in reset Chris Rauer (1): hw/arm: Add support for kudo-bmc board. Marc Zyngier (1): hw/arm/virt: KVM: Probe for KVM_CAP_ARM_VM_IPA_SIZE when creating scratch VM Peter Maydell (5): target/arm: Take an exception if PSTATE.IL is set qdev: Support marking individual buses as 'full' hw/arm/mps2-tz.c: Add extra data parameter to MakeDevFn hw/arm/mps2-tz.c: Mark internal-only I2C buses as 'full' hw/arm/mps2.c: Mark internal-only I2C buses as 'full' Richard Henderson (1): target/arm: Merge disas_a64_insn into aarch64_tr_translate_insn Shashi Mallela (9): hw/intc: GICv3 ITS initial framework hw/intc: GICv3 ITS register definitions added hw/intc: GICv3 ITS command queue framework hw/intc: GICv3 ITS Command processing hw/intc: GICv3 ITS Feature enablement hw/intc: GICv3 redistributor ITS processing tests/data/acpi/virt: Add IORT files for ITS hw/arm/virt: add ITS support in virt GIC tests/data/acpi/virt: Update IORT files for ITS docs/system/arm/nuvoton.rst | 1 + hw/intc/gicv3_internal.h | 188 ++++- include/hw/arm/virt.h | 2 + include/hw/intc/arm_gicv3_common.h | 13 + include/hw/intc/arm_gicv3_its_common.h | 32 +- include/hw/qdev-core.h | 24 + target/arm/cpu.h | 1 + target/arm/kvm_arm.h | 4 +- target/arm/syndrome.h | 5 + target/arm/translate.h | 2 + hw/arm/mps2-tz.c | 92 ++- hw/arm/mps2.c | 12 +- hw/arm/npcm7xx_boards.c | 34 + hw/arm/virt.c | 29 +- hw/char/cadence_uart.c | 61 +- hw/intc/arm_gicv3.c | 14 + hw/intc/arm_gicv3_common.c | 13 + hw/intc/arm_gicv3_cpuif.c | 7 +- hw/intc/arm_gicv3_dist.c | 5 +- hw/intc/arm_gicv3_its.c | 1322 ++++++++++++++++++++++++++++++++ hw/intc/arm_gicv3_its_common.c | 7 +- hw/intc/arm_gicv3_its_kvm.c | 2 +- hw/intc/arm_gicv3_redist.c | 153 +++- hw/misc/zynq_slcr.c | 31 +- softmmu/qdev-monitor.c | 7 +- target/arm/helper-a64.c | 1 + target/arm/helper.c | 8 + target/arm/kvm.c | 7 +- target/arm/translate-a64.c | 255 +++--- target/arm/translate.c | 21 + hw/intc/meson.build | 1 + tests/data/acpi/virt/IORT | Bin 0 -> 124 bytes tests/data/acpi/virt/IORT.memhp | Bin 0 -> 124 bytes tests/data/acpi/virt/IORT.numamem | Bin 0 -> 124 bytes tests/data/acpi/virt/IORT.pxb | Bin 0 -> 124 bytes 35 files changed, 2144 insertions(+), 210 deletions(-) create mode 100644 hw/intc/arm_gicv3_its.c create mode 100644 tests/data/acpi/virt/IORT create mode 100644 tests/data/acpi/virt/IORT.memhp create mode 100644 tests/data/acpi/virt/IORT.numamem create mode 100644 tests/data/acpi/virt/IORT.pxb