:p
atchew
Login
Hi; here's an arm pullreq. There's a combination of small stuff, plus the hvf patchset that adds vGIC and nested-virt support. Last time we tried to land this we got tripped up by some gitlab CI failures on jobs that only run on the main staging CI run, not on the "run CI in your own repo before sending" CI. Hopefully that won't happen again, but it's possible... thanks -- PMM The following changes since commit 282771e1f9b9b6e0147adf5f9d676325175b1767: Merge tag 'pull-riscv-to-apply-20260429-1' of https://github.com/alistair23/qemu into staging (2026-04-29 09:22:51 -0400) are available in the Git repository at: https://gitlab.com/pm215/qemu.git tags/pull-target-arm-20260501 for you to fetch changes up to 03f12c3244ceb862714707411fa842bf76df711d: hvf: arm: enable vGIC by default for virt-11.1 and later (2026-05-01 10:44:18 +0100) ---------------------------------------------------------------- target-arm queue: * hw/arm/fsl-imx8mp: Do not create redundant unimplemented devices * hw/arm/fsl-imx8mp: Fix parent of ocram memory region * sysbus: Remove unused irq routing notifier method * linux-user/arm: Restrict regpairs_aligned * qemu-options: Improve description for -smb option * preliminary work for FEAT_RME_GDI * docs/devel/decodetree: Fix formatting in "field examples" table * hw/net/allwinner-sun8i-emac: Flush queued packets when rx is enabled * hw/intc/arm_gicv3: Fix NS write to ICC_AP1Rn_EL1 when prebits < 7 * target/arm/kvm: Cache host CPU probe failure * hvf: support platform vGIC * hvf: support nested virtualization ---------------------------------------------------------------- Akihiko Odaki (1): target/arm/kvm: Cache host CPU probe failure Bernhard Beschow (3): hw/arm/fsl-imx8mp: Do not create redundant unimplemented devices hw/arm/fsl-imx8mp: Fix parent of ocram memory region Revert "sysbus: add irq_routing_notifier" Jim MacArthur (4): target/arm/cpu-features.c: New fields in AA64MMFR4 target/arm/cpu.h: New GPCCR fields target/arm/ptw.c: Add GDI spaces to the granule protection case tests/tcg/aarch64/system/rme_gdi.c: Very basic test of GDI Mohamed Mediouni (15): 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 when using the Apple vGIC hvf: arm: allow exposing minimal PMU for kernel-irqchip=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: arm: physical timer emulation hvf: enable nested virtualisation support hvf: arm: enable vGIC by default for virt-11.1 and later Pali Rohár (1): qemu-options: Improve description for -smb option Peter Maydell (1): docs/devel/decodetree: Fix formatting in "field examples" table Richard Henderson (1): linux-user/arm: Restrict regpairs_aligned liugan1 (1): hw/intc/arm_gicv3: Fix NS write to ICC_AP1Rn_EL1 when prebits < 7 宋文武 (1): hw/net/allwinner-sun8i-emac: Flush queued packets when rx is enabled accel/hvf/hvf-all.c | 63 +++ accel/stubs/hvf-stub.c | 12 + docs/devel/decodetree.rst | 44 +- hw/arm/fsl-imx8mp.c | 3 +- hw/arm/virt.c | 46 +- hw/core/sysbus.c | 6 - hw/intc/arm_gicv3_common.c | 4 + hw/intc/arm_gicv3_cpuif.c | 35 +- hw/intc/arm_gicv3_hvf.c | 835 +++++++++++++++++++++++++++++++++++++ hw/intc/arm_gicv3_hvf_stub.c | 25 ++ hw/intc/meson.build | 2 + hw/net/allwinner-sun8i-emac.c | 3 + include/hw/arm/virt.h | 2 + include/hw/core/boards.h | 1 + include/hw/core/sysbus.h | 1 - include/hw/intc/arm_gicv3_common.h | 4 + include/system/hvf.h | 8 + include/system/hvf_int.h | 1 + linux-user/user-internals.h | 2 +- qemu-options.hx | 19 +- system/vl.c | 2 + target/arm/cpu-features.h | 8 + target/arm/cpu.h | 5 + target/arm/hvf/hvf.c | 208 ++++++++- target/arm/hvf/sysreg.c.inc | 44 ++ target/arm/hvf_arm.h | 5 + target/arm/kvm.c | 38 +- target/arm/ptw.c | 20 + tests/tcg/aarch64/system/rme_gdi.c | 46 ++ 29 files changed, 1418 insertions(+), 74 deletions(-) create mode 100644 hw/intc/arm_gicv3_hvf.c create mode 100644 hw/intc/arm_gicv3_hvf_stub.c create mode 100644 tests/tcg/aarch64/system/rme_gdi.c
From: Bernhard Beschow <shentey@gmail.com> The GPT devices are implemented. No need to create redundant unimplemented devices. Fixes: f8b26121762c ("hw/arm/fsl-imx8mp: Implement general purpose timers") cc: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260308203516.160103-2-shentey@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx8mp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) case FSL_IMX8MP_GIC_DIST: case FSL_IMX8MP_GIC_REDIST: case FSL_IMX8MP_GPIO1 ... FSL_IMX8MP_GPIO5: + case FSL_IMX8MP_GPT1 ... FSL_IMX8MP_GPT6: case FSL_IMX8MP_ECSPI1 ... FSL_IMX8MP_ECSPI3: case FSL_IMX8MP_ENET1: case FSL_IMX8MP_I2C1 ... FSL_IMX8MP_I2C6: -- 2.43.0
From: Bernhard Beschow <shentey@gmail.com> Rather than having a NULL parent, let the containing SoC object be the parent. This cleans up the QOM composition tree a bit. Fixes: 1aaf3478684f ("hw/arm/fsl-imx8mp: Add on-chip RAM") cc: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-id: 20260308203516.160103-3-shentey@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx8mp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) fsl_imx8mp_memmap[FSL_IMX8MP_PCIE_PHY1].addr); /* On-Chip RAM */ - if (!memory_region_init_ram(&s->ocram, NULL, "imx8mp.ocram", + if (!memory_region_init_ram(&s->ocram, OBJECT(dev), "imx8mp.ocram", fsl_imx8mp_memmap[FSL_IMX8MP_OCRAM].size, errp)) { return; -- 2.43.0
From: Bernhard Beschow <shentey@gmail.com> The callback has been introduced in commit 715ca691daca ("sysbus: add irq_routing_notifier") for use in VFIO platform. Meanwhile, VFIO platform has been removed via commit 762c85543948 ("vfio: Remove 'vfio- platform'") which was its only user. Remove this unused code. This reverts commit 715ca691daca081108b33306faa6fa102f0df8d8. cc: Cédric Le Goater <clg@redhat.com> Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Message-id: 20260308203516.160103-6-shentey@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/core/sysbus.c | 6 ------ include/hw/core/sysbus.h | 1 - 2 files changed, 7 deletions(-) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index XXXXXXX..XXXXXXX 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -XXX,XX +XXX,XX @@ qemu_irq sysbus_get_connected_irq(const SysBusDevice *dev, int n) void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq) { - SysBusDeviceClass *sbd = SYS_BUS_DEVICE_GET_CLASS(dev); - qdev_connect_gpio_out_named(DEVICE(dev), SYSBUS_DEVICE_GPIO_IRQ, n, irq); - - if (sbd->connect_irq_notifier) { - sbd->connect_irq_notifier(dev, irq); - } } /* Check whether an MMIO region exists */ diff --git a/include/hw/core/sysbus.h b/include/hw/core/sysbus.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/core/sysbus.h +++ b/include/hw/core/sysbus.h @@ -XXX,XX +XXX,XX @@ struct SysBusDeviceClass { * omitted then. (This is not considered a fatal error.) */ char *(*explicit_ofw_unit_address)(const SysBusDevice *dev); - void (*connect_irq_notifier)(SysBusDevice *dev, qemu_irq irq); }; struct SysBusDevice { -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> The function regpairs_aligned is for extracting a 64-bit quantity from a pair of 32-bit registers and does not apply to AArch64. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260409035015.132370-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- linux-user/user-internals.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index XXXXXXX..XXXXXXX 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -XXX,XX +XXX,XX @@ void print_termios2(void *arg); #endif /* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */ -#ifdef TARGET_ARM +#if defined(TARGET_ARM) && !defined(TARGET_AARCH64) static inline int regpairs_aligned(CPUArchState *cpu_env, int num) { return cpu_env->eabi; -- 2.43.0
From: Pali Rohár <pali@kernel.org> Add #PRE and #NOFNR flags to LMHOSTS example line. Include information about LMHOSTS path on Windows 3.x. Windows NT 3.1 requires #NOFNR flag for successful hostname resolving as described in MS article Q103765 because qemu/smbd does not respond to netbios name verification queries. #PRE is suggested in that article too and decrease delay on Windows 3.x for the first connection. Signed-off-by: Pali Rohár <pali@kernel.org> [PMM: Expand documentation to note what these flags do, and clarify Windows LMHOSTS paths for different Windows versions, based on discussion in patch review, fix existing grammar nit] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- qemu-options.hx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index XXXXXXX..XXXXXXX 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -XXX,XX +XXX,XX @@ SRST ``smb=dir[,smbserver=addr]`` When using the user mode network stack, activate a built-in SMB - server so that Windows OSes can access to the host files in + server so that Windows OSes can access the host files in ``dir`` transparently. The IP address of the SMB server can be set to addr. By default the 4th IP in the guest network is used, i.e. x.x.x.4. @@ -XXX,XX +XXX,XX @@ SRST :: - 10.0.2.4 smbserver + 10.0.2.4 smbserver #PRE #NOFNR - must be added in the file ``C:\WINDOWS\LMHOSTS`` (for windows - 9x/Me) or ``C:\WINNT\SYSTEM32\DRIVERS\ETC\LMHOSTS`` (Windows - NT/2000). + must be added in the ``LMHOSTS`` file. In this line, ``#PRE`` + requests pre-caching of the mapping, which speeds up the initial + connection on some Windows versions. ``#NOFNR`` is necessary for + Windows NT 3.1 to tell it not to send QEMU NBNS query packets that + QEMU does not handle, and is harmlessly ignored on other versions. + + The ``LMHOSTS`` file may be in different locations depending on + the Windows version: + + - ``C:\WINDOWS\LMHOSTS`` for Windows 3x/9x/Me + - ``C:\WINNT\SYSTEM32\DRIVERS\ETC\LMHOSTS`` for Windows NT/2000 + - ``C:\WINDOWS\SYSTEM32\DRIVERS\ETC\LMHOSTS`` for Windows XP and newer Then ``dir`` can be accessed in ``\\smbserver\qemu``. -- 2.43.0
From: Jim MacArthur <jim.macarthur@linaro.org> Added RMEGDI for FEAT_RME_GDI and other new fields. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org> Message-id: 20260421-jmac-feat_rme_gdi-v3-1-ecd20c77eae1@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu-features.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ FIELD(ID_AA64MMFR3, SDERR, 52, 4) FIELD(ID_AA64MMFR3, ADERR, 56, 4) FIELD(ID_AA64MMFR3, SPEC_FPACC, 60, 4) +FIELD(ID_AA64MMFR4, POPS, 0, 4) +FIELD(ID_AA64MMFR4, EIESB, 4, 4) FIELD(ID_AA64MMFR4, ASID2, 8, 4) +FIELD(ID_AA64MMFR4, HACDBS, 12, 4) +FIELD(ID_AA64MMFR4, FGWTE3, 16, 4) +FIELD(ID_AA64MMFR4, NV_FRAC, 20, 4) FIELD(ID_AA64MMFR4, E2H0, 24, 4) +FIELD(ID_AA64MMFR4, RMEGDI, 28, 4) +FIELD(ID_AA64MMFR4, E3DSE, 36, 4) +FIELD(ID_AA64MMFR4, SRMASK, 44, 4) FIELD(ID_AA64DFR0, DEBUGVER, 0, 4) FIELD(ID_AA64DFR0, TRACEVER, 4, 4) -- 2.43.0
From: Jim MacArthur <jim.macarthur@linaro.org> Add SA, NSP, NA6, NA7 for GDI and GPCBW for good measure. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org> Message-id: 20260421-jmac-feat_rme_gdi-v3-2-ecd20c77eae1@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ FIELD(GPCCR, TBGPCD, 18, 1) FIELD(GPCCR, NSO, 19, 1) FIELD(GPCCR, L0GPTSZ, 20, 4) FIELD(GPCCR, APPSAA, 24, 1) +FIELD(GPCCR, SA, 25, 1) +FIELD(GPCCR, NSP, 26, 1) +FIELD(GPCCR, NA6, 27, 1) +FIELD(GPCCR, NA7, 28, 1) +FIELD(GPCCR, GPCBW, 29, 1) FIELD(MFAR, FPA, 12, 40) FIELD(MFAR, NSE, 62, 1) -- 2.43.0
From: Jim MacArthur <jim.macarthur@linaro.org> System Agent, Non-secure Protected and two other GPI field encodings. These are explicitly denied access for any processing element when the relevant GPCCR bit is set, and reserved values when the relevant GPCCR bit is zero. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org> Message-id: 20260421-jmac-feat_rme_gdi-v3-3-ecd20c77eae1@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/ptw.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -XXX,XX +XXX,XX @@ bool arm_granule_protection_check(ARMGranuleProtectionConfig config, break; case 0b1111: /* all access */ return true; + case 0b0100: /* system agent only */ + if (FIELD_EX64(gpccr, GPCCR, SA) == 0) { + goto fault_walk; + } + break; + case 0b0101: /* non-secure protected */ + if (FIELD_EX64(gpccr, GPCCR, NSP) == 0) { + goto fault_walk; + } + break; + case 0b0110: /* reserved if NA6==0, otherwise no access */ + if (FIELD_EX64(gpccr, GPCCR, NA6) == 0) { + goto fault_walk; + } + break; + case 0b0111: /* reserved if NA7==0, otherwise no access */ + if (FIELD_EX64(gpccr, GPCCR, NA7) == 0) { + goto fault_walk; + } + break; case 0b1000: /* secure */ if (!config.support_sel2) { goto fault_walk; -- 2.43.0
From: Jim MacArthur <jim.macarthur@linaro.org> Simply tests GDI's prerequisites; that if GDI is enabled then so are FEAT_RME and FEAT_RME_GPC2. Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260421-jmac-feat_rme_gdi-v3-4-ecd20c77eae1@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- tests/tcg/aarch64/system/rme_gdi.c | 46 ++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/tcg/aarch64/system/rme_gdi.c diff --git a/tests/tcg/aarch64/system/rme_gdi.c b/tests/tcg/aarch64/system/rme_gdi.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/tcg/aarch64/system/rme_gdi.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * FEAT_RME_GDI Feature presence and enabled bits test + * + * Copyright (c) 2026 Linaro Ltd + * + */ + +#include <stdint.h> +#include <minilib.h> + +#define ID_AA64PFR0_EL1 "S3_0_C0_C4_0" +#define ID_AA64MMFR4_EL1 "S3_0_C0_C7_4" + +int main() +{ + uint64_t mmfr4; + uint64_t pfr0; + int rme_status; + int rmegdi_status; + + asm("mrs %[pfr0], " ID_AA64PFR0_EL1 "\n\t" + : [pfr0] "=r" (pfr0)); + + /* rme_status is 1 for RME, 2 for RME + GPC2, 3 for RME+GPC3 */ + rme_status = (pfr0 >> 52) & 0xF; + + asm("mrs %[mmfr4], " ID_AA64MMFR4_EL1 "\n\t" + : [mmfr4] "=r" (mmfr4)); + + rmegdi_status = ((mmfr4 >> 28) & 0xF); + + if (rmegdi_status < 1) { + ml_printf("SKIP: GDI not implemented\n"); + return 0; + } + + /* Check FEAT_RME and FEAT_RME_GPC2 also present */ + if (rme_status < 2) { + ml_printf("FAIL: GDI is %d, but RME is %d; RME should be >= 2\n", + rmegdi_status, rme_status); + return 1; + } + return 0; +} -- 2.43.0
The rST syntax for a table uses ASCII art to draw the cell boundaries; then inside each cell the text is treated as a body element, so it is rendered the same way as text at the top level of a document. The "field examples" table was assuming a "literal document" format for its cell bodies; this meant that the single line cells were being rendered in plain text, not a fixed width font, and the multi line cells were rendered as definition-lists because of their "second and subsequent lines are indented" layout. Fix this by consistently using inline-code markup for the left column and literal blocks for the right column. (We want to be consistent within each column because a literal block renders differently to inline-code, with a green background.) Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- docs/devel/decodetree.rst | 44 ++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/docs/devel/decodetree.rst b/docs/devel/decodetree.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/devel/decodetree.rst +++ b/docs/devel/decodetree.rst @@ -XXX,XX +XXX,XX @@ A field with no ``fields`` and no ``!function`` is in error. Field examples: -+---------------------------+---------------------------------------------+ -| Input | Generated code | -+===========================+=============================================+ -| %disp 0:s16 | sextract(i, 0, 16) | -+---------------------------+---------------------------------------------+ -| %imm9 16:6 10:3 | extract(i, 16, 6) << 3 | extract(i, 10, 3) | -+---------------------------+---------------------------------------------+ -| %disp12 0:s1 1:1 2:10 | sextract(i, 0, 1) << 11 | | -| | extract(i, 1, 1) << 10 | | -| | extract(i, 2, 10) | -+---------------------------+---------------------------------------------+ -| %shimm8 5:s8 13:1 | expand_shimm8(sextract(i, 5, 8) << 1 | | -| !function=expand_shimm8 | extract(i, 13, 1)) | -+---------------------------+---------------------------------------------+ -| %sz_imm 10:2 sz:3 | expand_sz_imm(extract(i, 10, 2) << 3 | | -| !function=expand_sz_imm | extract(a->sz, 0, 3)) | -+---------------------------+---------------------------------------------+ ++-----------------------------+----------------------------------------------+ +| Input | Generated code | ++=============================+==============================================+ +| ``%disp 0:s16`` | :: | +| | | +| | sextract(i, 0, 16) | ++-----------------------------+----------------------------------------------+ +| ``%imm9 16:6 10:3`` | :: | +| | | +| | extract(i, 16, 6) << 3 | extract(i, 10, 3) | ++-----------------------------+----------------------------------------------+ +| ``%disp12 0:s1 1:1 2:10`` | :: | +| | | +| | sextract(i, 0, 1) << 11 | | +| | extract(i, 1, 1) << 10 | | +| | extract(i, 2, 10) | ++-----------------------------+----------------------------------------------+ +| ``%shimm8 5:s8 13:1 | :: | +| !function=expand_shimm8`` | | +| | expand_shimm8(sextract(i, 5, 8) << 1 | | +| | extract(i, 13, 1)) | ++-----------------------------+----------------------------------------------+ +| ``%sz_imm 10:2 sz:3 | :: | +| !function=expand_sz_imm`` | | +| | expand_sz_imm(extract(i, 10, 2) << 3 | | +| | extract(a->sz, 0, 3)) | ++-----------------------------+----------------------------------------------+ Argument Sets ============= -- 2.43.0
From: 宋文武 <iyzsong@member.fsf.org> The RX_CTL_0 register includes the RX_EN receive-enable bit, which allwinner_sun8i_emac_can_receive() checks. That means that if the guest sets it we need to call qemu_flush_queued_packets() as we might now be able to handle them. This fixes a bug where networking didn't work in u-boot on the orangepi-pc machine. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3459 Signed-off-by: 宋文武 <iyzsong@member.fsf.org> Message-id: 20260430040753.3337-1-iyzsong@envs.net Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: expanded commit message, removed unneeded RX_EN test] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/net/allwinner-sun8i-emac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c index XXXXXXX..XXXXXXX 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -XXX,XX +XXX,XX @@ static void allwinner_sun8i_emac_write(void *opaque, hwaddr offset, break; case REG_RX_CTL_0: /* Receive Control 0 */ s->rx_ctl0 = value; + if (allwinner_sun8i_emac_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } break; case REG_RX_CTL_1: /* Receive Control 1 */ s->rx_ctl1 = value | RX_CTL1_RX_MD; -- 2.43.0
From: liugan1 <liugan1@lixiang.com> The existing code uses a blanket `regno < 2` check to make ICC_AP1R0_EL1 and ICC_AP1R1_EL1 writes from Non-secure code WI (Write Ignore) when EL3 is present. This is intended to prevent NS code from claiming active interrupts in the Secure priority range, which could block Secure interrupt delivery. However, that check assumes prebits=7 (4 APR registers), where the NS priority range (128..255) maps entirely to AP1R2/AP1R3. Since commit 39f29e599355 ("hw/intc/arm_gicv3: Use correct number of priority bits for the CPU", first in 7.1), all QEMU AArch64 CPUs are initialised with gic_pribits=5 (one APR register), so NS priorities map to AP1R0 bits [16:31]. Blanket WI of the entire AP1R0 register prevents NS code from clearing its own NS active priority bits. Machines using hw_compat_7_0 (e.g. virt-7.0) still force pribits=8 via force-8-bit-prio and are therefore unaffected. A concrete consequence observed in virtualisation scenarios: when a guest VM acknowledges an SPI interrupt but does not perform EOI, is force-killed and restarted, the new guest's attempt to clear the residual active state by writing ICC_AP1R0_EL1=0 is silently ignored. The running priority (RPR) remains stuck at the old interrupt's priority, preventing all equal-or-lower priority interrupts (including timer interrupts) from being delivered, and hanging the guest. Fix this by computing the exact Secure/NS boundary within the APR bank based on prebits. For registers entirely in the Secure range, keep the WI behaviour. For the register that straddles the boundary, preserve only the Secure bits while allowing NS bits to be modified. For registers entirely in the NS range, allow full write access. The new logic produces identical behaviour to the old code when prebits=7, preserving existing behaviour for machines that use force-8-bit-prio. Fixes: 39f29e599355 ("hw/intc/arm_gicv3: Use correct number of priority bits for the CPU") Cc: qemu-stable@nongnu.org Signed-off-by: liugan1 <liugan1@lixiang.com> Message-id: 20260428083119.1400110-1-gs_liugan@163.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/intc/arm_gicv3_cpuif.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -XXX,XX +XXX,XX @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, * at a priority outside the Non-secure range (128..255), since this * would otherwise allow malicious NS code to block delivery of S interrupts * by writing a bad value to these registers. + * + * The NS priority range (128..255) maps to APR bits starting at + * aprbit = 0x80 >> (8 - prebits). Depending on prebits, this boundary + * may fall within AP1R0 or AP1R1, so we cannot simply WI the entire + * register. Instead we calculate which bits within each register + * correspond to the Secure range and preserve those, while allowing + * NS code to modify only the NS range bits. + * + * prebits=4: num_aprs=1, NS starts at AP1R0[8] + * prebits=5: num_aprs=1, NS starts at AP1R0[16] + * prebits=6: num_aprs=2, NS starts at AP1R1[0] + * prebits=7: num_aprs=4, NS starts at AP1R2[0] */ - if (grp == GICV3_G1NS && regno < 2 && arm_feature(env, ARM_FEATURE_EL3)) { - return; + if (grp == GICV3_G1NS && arm_feature(env, ARM_FEATURE_EL3)) { + int ns_start_bit = 0x80 >> (8 - cs->prebits); + int ns_start_regno = ns_start_bit / 32; + int ns_start_regbit = ns_start_bit % 32; + + if (regno < ns_start_regno) { + /* This entire register is in the Secure range: WI */ + return; + } else if (regno == ns_start_regno && ns_start_regbit > 0) { + /* + * This register is split: low bits are Secure, high bits are NS. + * Preserve the Secure bits (below ns_start_regbit) from the + * current value, and take the NS bits (at and above + * ns_start_regbit) from the written value. + */ + uint32_t secure_mask = MAKE_64BIT_MASK(0, ns_start_regbit); + + value = (cs->icc_apr[grp][regno] & secure_mask) | + (value & ~secure_mask); + } + /* else: regno > ns_start_regno, entire register is NS: allow write */ } if (cs->nmi_support) { -- 2.43.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> kvm_arm_set_cpu_features_from_host() does not properly handle host CPU probe failure with caching. The current algorithm can be summarized as follows: If dtb_compatible is not cached: If kvm_arm_create_scratch_host_vcpu() fails: Report failure Cache dtb_compatible If getting register values fails: Report failure Report success This algorithm has the following problems: - If kvm_arm_create_scratch_host_vcpu() fails, probing may be repeated. - If getting register values fails, later invocations incorrectly report success. Make two changes to fix them: - Cache dtb_compatible whenever a probe is attempted. - Record probe failure by assigning QEMU_KVM_ARM_TARGET_NONE to arm_host_cpu_features.target. Suggested-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260428-features-v1-1-1841b39da7e6@rsg.ci.i.u-tokyo.ac.jp Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/kvm.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) 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 uint32_t kvm_arm_sve_get_vls(int fd) return vls[0] & MAKE_64BIT_MASK(0, ARM_MAX_VQ); } -static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) +static void kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { /* Identify the feature bits corresponding to the host CPU, and * fill out the ARMHostCPUClass fields accordingly. To do this @@ -XXX,XX +XXX,XX @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) uint64_t features = 0; int err; + ahcf->target = QEMU_KVM_ARM_TARGET_NONE; + ahcf->dtb_compatible = "arm,armv8"; + + if (!kvm_enabled()) { + return; + } + /* * target = -1 informs kvm_arm_create_scratch_host_vcpu() * to use the preferred target @@ -XXX,XX +XXX,XX @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) } if (!kvm_arm_create_scratch_host_vcpu(fdarray, &init)) { - return false; + return; } - ahcf->target = init.target; - ahcf->dtb_compatible = "arm,armv8"; int fd = fdarray[2]; err = get_host_cpu_reg(fd, ahcf, ID_AA64PFR0_EL1_IDX); @@ -XXX,XX +XXX,XX @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) kvm_arm_destroy_scratch_host_vcpu(fdarray); if (err < 0) { - return false; + return; } /* @@ -XXX,XX +XXX,XX @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) features |= 1ULL << ARM_FEATURE_EL2; } + ahcf->target = init.target; ahcf->features = features; - - return true; } void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) @@ -XXX,XX +XXX,XX @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) CPUARMState *env = &cpu->env; if (!arm_host_cpu_features.dtb_compatible) { - if (!kvm_enabled() || - !kvm_arm_get_host_cpu_features(&arm_host_cpu_features)) { - /* We can't report this error yet, so flag that we need to - * in arm_cpu_realizefn(). - */ - cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE; - cpu->host_cpu_probe_failed = true; - return; - } + kvm_arm_get_host_cpu_features(&arm_host_cpu_features); } cpu->kvm_target = arm_host_cpu_features.target; + + if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) { + /* + * We can't report this error yet, so flag that we need to + * in arm_cpu_realizefn(). + */ + cpu->host_cpu_probe_failed = true; + return; + } + cpu->dtb_compatible = arm_host_cpu_features.dtb_compatible; cpu->isar = arm_host_cpu_features.isar; cpu->sve_vq.supported = arm_host_cpu_features.sve_vq_supported; -- 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> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-id: 20260429190532.26538-2-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/intc/arm_gicv3_hvf.c | 749 +++++++++++++++++++++++++++++ hw/intc/meson.build | 1 + include/hw/intc/arm_gicv3_common.h | 1 + 3 files changed, 751 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> + +/* + * For the GIC, override the check outright, as availability is checked + * elsewhere + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + +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) + +#pragma clang diagnostic pop 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> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-id: 20260429190532.26538-3-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 | 94 ++++++++++++++++++++++++++++-- hw/intc/arm_gicv3_hvf_stub.c | 25 ++++++++ hw/intc/meson.build | 1 + include/hw/intc/arm_gicv3_common.h | 3 + 5 files changed, 120 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 parent_obj; + 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_malloc0(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 void hvf_gic_opaque_state_free(void *opaque) +{ + HVFGICv3State *gic = opaque; + free(gic->state); +} + +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> Message-id: 20260429190532.26538-4-mohamed@unpredictable.fr Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- accel/hvf/hvf-all.c | 46 ++++++++++++++++++++++++++++++++++++++ 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, 74 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; + hvf_kernel_irqchip = false; + 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: 20260429190532.26538-5-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 22 ++++++++++++++++++++++ 1 file changed, 22 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()) { + if (__builtin_available(macOS 15.0, *)) { + /* + * 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); + ret = hv_gic_create(cfg); + if (ret != HV_SUCCESS) { + error_report("error creating platform VGIC"); + goto cleanup; + } + os_release(cfg); + } else { + error_report("HVF: Unsupported OS for platform vGIC."); + ret = HV_UNSUPPORTED; + goto cleanup; + } + } cleanup: os_release(config); -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> Add hvf_arm_el2_supported for querying EL2 availability. An hvf_nested_virt_enable workaround is added as nested virt has to be enabled early on HVF. And adds hvf_nested_virt_enabled. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-id: 20260429190532.26538-6-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- accel/hvf/hvf-all.c | 6 ++++++ accel/stubs/hvf-stub.c | 11 +++++++++++ hw/arm/virt.c | 5 +++++ include/system/hvf.h | 5 +++++ target/arm/hvf/hvf.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 67 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; + +void hvf_nested_virt_enable(bool nested_virt) +{ + hvf_nested_virt = 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; + +void hvf_nested_virt_enable(bool nested_virt) +{ + /* + * This is called unconditionally from hw/arm/virt.c + * because we don't know if HVF is going to be used + * as that step of initialisation happens later. + * As such, do nothing here instead of marking as unreachable. + */ +} 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) #else /* !CONFIG_HVF_IS_POSSIBLE */ #define hvf_enabled() 0 #define hvf_irqchip_in_kernel() 0 +#define hvf_nested_virt_enabled() 0 #endif /* !CONFIG_HVF_IS_POSSIBLE */ +void hvf_nested_virt_enable(bool nested_virt); + #define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf") typedef struct HVFState HVFState; 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; + if (__builtin_available(macOS 15.0, *)) { + hv_return_t ret = hv_vm_config_get_el2_supported(&is_nested_virt_supported); + assert_hvf_ok(ret); + } else { + return false; + } + 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 (__builtin_available(macOS 15.0, *)) { + 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()) { if (__builtin_available(macOS 15.0, *)) { @@ -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; uint64_t 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: 20260429190532.26538-7-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. However, despite what that documentation says this is actually gated on using the Apple vGIC instead of nested virtualisation per se. Apple introduced both at the same time. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-id: 20260429190532.26538-8-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_irqchip_in_kernel() && 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_irqchip_in_kernel() && 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 the Apple vGIC, 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: 20260429190532.26538-9-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 19 +++++++++++++++++++ 1 file changed, 19 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); + /* + * Windows wants at least the PMU's cycles counter to be available. + * + * With kernel-irqchip=off, we "emulate" the cycles counter + * in reference to time in QEMU. Having that, even with + * ID_AA64DFR0_EL1.PMUVer = 0 is enough to make Windows happy. + * + * As it's a very inaccurate implementation with its only purpose + * being making Windows boot, expose ID_AA64DFR0_EL1.PMUVer = 0 + * when kernel-irqchip=off. + * + * When kernel-irqchip=on *and* ID_AA64DFR0_EL1.PMUVer = 1, + * the OS provides its own PMU emulation, which is currently + * a cycles counter only emulation. + */ + if (hvf_irqchip_in_kernel()) { + 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: 20260429190532.26538-10-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> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260429190532.26538-11-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 61 +++++++++++++++++++++++++++++++++---- target/arm/hvf/sysreg.c.inc | 44 ++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 6 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[] = { * * 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. + * + * Nested virt registers are handled via a runtime check, so override the + * guarded availability check done by Clang. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + #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(...) +#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 + +#pragma clang diagnostic pop #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) /* 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]; + 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: 20260429190532.26538-12-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> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260429190532.26538-13-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 5 +++++ target/arm/hvf_arm.h | 5 +++++ 2 files changed, 10 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) FIELD_DP64_IDREG(&host_isar, ID_AA64DFR0, PMUVER, 0x1); } + if (hvf_nested_virt_enabled()) { + /* 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> Enable this through leveraging TCG's physical timer emulation. This allows nested virtualisation to work with a kernel-irqchip=off + GICv2. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Message-id: 20260429190532.26538-14-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 | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 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 @@ void hvf_arm_init_debug(void) #define SYSREG_OSDLR_EL1 SYSREG(2, 0, 1, 3, 4) #define SYSREG_LORC_EL1 SYSREG(3, 0, 10, 4, 3) #define SYSREG_CNTPCT_EL0 SYSREG(3, 3, 14, 0, 1) +#define SYSREG_CNTP_TVAL_EL0 SYSREG(3, 3, 14, 2, 0) #define SYSREG_CNTP_CTL_EL0 SYSREG(3, 3, 14, 2, 1) +#define SYSREG_CNTP_CVAL_EL0 SYSREG(3, 3, 14, 2, 2) #define SYSREG_PMCR_EL0 SYSREG(3, 3, 9, 12, 0) #define SYSREG_PMUSERENR_EL0 SYSREG(3, 3, 9, 14, 0) #define SYSREG_PMCNTENSET_EL0 SYSREG(3, 3, 9, 12, 1) @@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) switch (reg) { case SYSREG_CNTPCT_EL0: - *val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / - gt_cntfrq_period_ns(arm_cpu); - return 0; + case SYSREG_CNTP_CTL_EL0: + case SYSREG_CNTP_CVAL_EL0: + case SYSREG_CNTP_TVAL_EL0: + assert(!hvf_irqchip_in_kernel()); + /* Call the TCG sysreg handler. */ + if (hvf_sysreg_read_cp(cpu, "PTimer", reg, val)) { + return 0; + } + break; case SYSREG_OSLSR_EL1: *val = env->cp15.oslsr_el1; return 0; @@ -XXX,XX +XXX,XX @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) env->cp15.oslsr_el1 = val & 1; return 0; case SYSREG_CNTP_CTL_EL0: - /* - * Guests should not rely on the physical counter, but macOS emits - * disable writes to it. Let it do so, but ignore the requests. - */ - qemu_log_mask(LOG_UNIMP, "Unsupported write to CNTP_CTL_EL0\n"); - return 0; + case SYSREG_CNTP_CVAL_EL0: + case SYSREG_CNTP_TVAL_EL0: + assert(!hvf_irqchip_in_kernel()); + /* Call the TCG sysreg handler. */ + if (hvf_sysreg_write_cp(cpu, "PTimer", reg, val)) { + return 0; + } + break; case SYSREG_OSDLR_EL1: /* Dummy register */ return 0; -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-id: 20260429190532.26538-15-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/virt.c | 3 ++- 1 file changed, 2 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()); -- 2.43.0
From: Mohamed Mediouni <mohamed@unpredictable.fr> Save states are incompatible between kernel-irqchip=on and off on HVF due to opaque vGIC state. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-id: 20260429190532.26538-16-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- accel/hvf/hvf-all.c | 11 +++++++++++ hw/arm/virt.c | 15 +++++++++++++++ include/hw/arm/virt.h | 2 ++ include/hw/core/boards.h | 1 + include/system/hvf_int.h | 1 + 5 files changed, 30 insertions(+) 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; +bool hvf_kernel_irqchip_override; void hvf_nested_virt_enable(bool nested_virt) { @@ -XXX,XX +XXX,XX @@ static int hvf_accel_init(AccelState *as, MachineState *ms) } } + if (mc->get_kernel_irqchip_default) { + bool kernel_irqchip_default = mc->get_kernel_irqchip_default(ms); + if (!hvf_kernel_irqchip_override) { + hvf_kernel_irqchip = kernel_irqchip_default; + } + } + ret = hvf_arch_vm_create(ms, (uint32_t)pa_range); if (ret == HV_DENIED) { error_report("Could not access HVF. Is the executable signed" @@ -XXX,XX +XXX,XX @@ static void hvf_set_kernel_irqchip(Object *obj, Visitor *v, Error **errp) { OnOffSplit mode; + + hvf_kernel_irqchip_override = true; if (!visit_type_OnOffSplit(v, name, &mode, errp)) { return; } @@ -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; + hvf_kernel_irqchip_override = false; hvf_kernel_irqchip = false; object_class_property_add(oc, "kernel-irqchip", "on|off|split", NULL, hvf_set_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 int virt_get_physical_address_range(MachineState *ms, return requested_ipa_size; } +static bool get_kernel_irqchip_default(const MachineState *ms) +{ + VirtMachineState *vms = VIRT_MACHINE(ms); + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); + if (hvf_allowed) { + return !vmc->hvf_no_kernel_irqchip_default; + } else { + return true; + } +} + static const char *virt_get_default_cpu_type(const MachineState *ms) { return tcg_enabled() ? ARM_CPU_TYPE_NAME("cortex-a15") @@ -XXX,XX +XXX,XX @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; mc->kvm_type = virt_kvm_type; mc->get_physical_address_range = virt_get_physical_address_range; + mc->get_kernel_irqchip_default = get_kernel_irqchip_default; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = virt_machine_get_hotplug_handler; hc->pre_plug = virt_machine_device_pre_plug_cb; @@ -XXX,XX +XXX,XX @@ DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) static void virt_machine_11_0_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + virt_machine_11_1_options(mc); compat_props_add(mc->compat_props, hw_compat_11_0, hw_compat_11_0_len); + vmc->hvf_no_kernel_irqchip_default = true; } DEFINE_VIRT_MACHINE(11, 0) diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -XXX,XX +XXX,XX @@ struct VirtMachineClass { bool no_tcg_lpa2; bool no_ns_el2_virt_timer_irq; bool no_nested_smmu; + /* HVF specific: support for kernel-irqchip=on introduced in QEMU 11.1 */ + bool hvf_no_kernel_irqchip_default; }; struct VirtMachineState { diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/core/boards.h +++ b/include/hw/core/boards.h @@ -XXX,XX +XXX,XX @@ struct MachineClass { int (*kvm_type)(MachineState *machine, const char *arg); int (*get_physical_address_range)(MachineState *machine, int default_ipa_size, int max_ipa_size); + bool (*get_kernel_irqchip_default) (const MachineState *machine); BlockInterfaceType block_default_type; int units_per_default_bus; diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/hvf_int.h +++ b/include/system/hvf_int.h @@ -XXX,XX +XXX,XX @@ bool hvf_arch_cpu_realize(CPUState *cpu, Error **errp); uint32_t hvf_arch_get_default_ipa_bit_size(void); uint32_t hvf_arch_get_max_ipa_bit_size(void); +extern bool hvf_kernel_irqchip_override; #endif -- 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