* [PULL 01/28] hw/arm/fsl-imx8mp: Do not create redundant unimplemented devices
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 02/28] hw/arm/fsl-imx8mp: Fix parent of ocram memory region Peter Maydell
` (26 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 b36df82971..33749d7fe1 100644
--- a/hw/arm/fsl-imx8mp.c
+++ b/hw/arm/fsl-imx8mp.c
@@ -687,6 +687,7 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 02/28] hw/arm/fsl-imx8mp: Fix parent of ocram memory region
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
2026-05-01 10:14 ` [PULL 01/28] hw/arm/fsl-imx8mp: Do not create redundant unimplemented devices Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 03/28] Revert "sysbus: add irq_routing_notifier" Peter Maydell
` (25 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 33749d7fe1..7c03ed3c34 100644
--- a/hw/arm/fsl-imx8mp.c
+++ b/hw/arm/fsl-imx8mp.c
@@ -670,7 +670,7 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 03/28] Revert "sysbus: add irq_routing_notifier"
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
2026-05-01 10:14 ` [PULL 01/28] hw/arm/fsl-imx8mp: Do not create redundant unimplemented devices Peter Maydell
2026-05-01 10:14 ` [PULL 02/28] hw/arm/fsl-imx8mp: Fix parent of ocram memory region Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 04/28] linux-user/arm: Restrict regpairs_aligned Peter Maydell
` (24 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 3adf2f2faf..3e1160ee92 100644
--- a/hw/core/sysbus.c
+++ b/hw/core/sysbus.c
@@ -104,13 +104,7 @@ 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 c0d18d9e00..f3c4259d29 100644
--- a/include/hw/core/sysbus.h
+++ b/include/hw/core/sysbus.h
@@ -50,7 +50,6 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 04/28] linux-user/arm: Restrict regpairs_aligned
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (2 preceding siblings ...)
2026-05-01 10:14 ` [PULL 03/28] Revert "sysbus: add irq_routing_notifier" Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 05/28] qemu-options: Improve description for -smb option Peter Maydell
` (23 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 24d35998f0..e65373b204 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -136,7 +136,7 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 05/28] qemu-options: Improve description for -smb option
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (3 preceding siblings ...)
2026-05-01 10:14 ` [PULL 04/28] linux-user/arm: Restrict regpairs_aligned Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 06/28] target/arm/cpu-features.c: New fields in AA64MMFR4 Peter Maydell
` (22 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 e780bc2ac0..98e39a1ad4 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3415,7 +3415,7 @@ 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.
@@ -3424,11 +3424,20 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 06/28] target/arm/cpu-features.c: New fields in AA64MMFR4
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (4 preceding siblings ...)
2026-05-01 10:14 ` [PULL 05/28] qemu-options: Improve description for -smb option Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 07/28] target/arm/cpu.h: New GPCCR fields Peter Maydell
` (21 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 6e5212ff6c..88fe3ed287 100644
--- a/target/arm/cpu-features.h
+++ b/target/arm/cpu-features.h
@@ -346,8 +346,16 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 07/28] target/arm/cpu.h: New GPCCR fields
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (5 preceding siblings ...)
2026-05-01 10:14 ` [PULL 06/28] target/arm/cpu-features.c: New fields in AA64MMFR4 Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 08/28] target/arm/ptw.c: Add GDI spaces to the granule protection case Peter Maydell
` (20 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 be14a47c35..5e4f12f1e7 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -2091,6 +2091,11 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 08/28] target/arm/ptw.c: Add GDI spaces to the granule protection case
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (6 preceding siblings ...)
2026-05-01 10:14 ` [PULL 07/28] target/arm/cpu.h: New GPCCR fields Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 09/28] tests/tcg/aarch64/system/rme_gdi.c: Very basic test of GDI Peter Maydell
` (19 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 7b993bb5b3..316e201cfe 100644
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -510,6 +510,26 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 09/28] tests/tcg/aarch64/system/rme_gdi.c: Very basic test of GDI
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (7 preceding siblings ...)
2026-05-01 10:14 ` [PULL 08/28] target/arm/ptw.c: Add GDI spaces to the granule protection case Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 10/28] docs/devel/decodetree: Fix formatting in "field examples" table Peter Maydell
` (18 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 0000000000..e869943a13
--- /dev/null
+++ b/tests/tcg/aarch64/system/rme_gdi.c
@@ -0,0 +1,46 @@
+/* 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 10/28] docs/devel/decodetree: Fix formatting in "field examples" table
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (8 preceding siblings ...)
2026-05-01 10:14 ` [PULL 09/28] tests/tcg/aarch64/system/rme_gdi.c: Very basic test of GDI Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 11/28] hw/net/allwinner-sun8i-emac: Flush queued packets when rx is enabled Peter Maydell
` (17 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 98ad33a487..33cd6fe583 100644
--- a/docs/devel/decodetree.rst
+++ b/docs/devel/decodetree.rst
@@ -64,23 +64,33 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 11/28] hw/net/allwinner-sun8i-emac: Flush queued packets when rx is enabled
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (9 preceding siblings ...)
2026-05-01 10:14 ` [PULL 10/28] docs/devel/decodetree: Fix formatting in "field examples" table Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 12/28] hw/intc/arm_gicv3: Fix NS write to ICC_AP1Rn_EL1 when prebits < 7 Peter Maydell
` (16 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 9b7c67ae8e..8ddaa6101f 100644
--- a/hw/net/allwinner-sun8i-emac.c
+++ b/hw/net/allwinner-sun8i-emac.c
@@ -727,6 +727,9 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 12/28] hw/intc/arm_gicv3: Fix NS write to ICC_AP1Rn_EL1 when prebits < 7
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (10 preceding siblings ...)
2026-05-01 10:14 ` [PULL 11/28] hw/net/allwinner-sun8i-emac: Flush queued packets when rx is enabled Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 13/28] target/arm/kvm: Cache host CPU probe failure Peter Maydell
` (15 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 fcb3922fa0..921d1fdfde 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -1869,9 +1869,40 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 13/28] target/arm/kvm: Cache host CPU probe failure
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (11 preceding siblings ...)
2026-05-01 10:14 ` [PULL 12/28] hw/intc/arm_gicv3: Fix NS write to ICC_AP1Rn_EL1 when prebits < 7 Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 14/28] hw/intc: Add hvf vGIC interrupt controller support Peter Maydell
` (14 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 d4a68874b8..7d194ea112 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -273,7 +273,7 @@ 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
@@ -287,6 +287,13 @@ 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
@@ -326,11 +333,9 @@ 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);
@@ -454,7 +459,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
kvm_arm_destroy_scratch_host_vcpu(fdarray);
if (err < 0) {
- return false;
+ return;
}
/*
@@ -471,9 +476,8 @@ 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)
@@ -481,18 +485,20 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 14/28] hw/intc: Add hvf vGIC interrupt controller support
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (12 preceding siblings ...)
2026-05-01 10:14 ` [PULL 13/28] target/arm/kvm: Cache host CPU probe failure Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 15/28] hw/intc: arm_gicv3_hvf: save/restore Apple GIC state Peter Maydell
` (13 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 0000000000..22f19d274d
--- /dev/null
+++ b/hw/intc/arm_gicv3_hvf.c
@@ -0,0 +1,749 @@
+/* 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 96742df090..b7baf8a0f6 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -42,6 +42,7 @@ 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 c55cf18120..9adcab0a0c 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -315,6 +315,7 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 15/28] hw/intc: arm_gicv3_hvf: save/restore Apple GIC state
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (13 preceding siblings ...)
2026-05-01 10:14 ` [PULL 14/28] hw/intc: Add hvf vGIC interrupt controller support Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-07 8:08 ` Philippe Mathieu-Daudé
2026-06-13 11:45 ` Philippe Mathieu-Daudé
2026-05-01 10:14 ` [PULL 16/28] accel, hw/arm, include/system/hvf: infrastructure changes for HVF vGIC Peter Maydell
` (12 subsequent siblings)
27 siblings, 2 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 9200671c7a..9c3fb2f4bf 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -305,6 +305,7 @@ 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 22f19d274d..ae881092ea 100644
--- a/hw/intc/arm_gicv3_hvf.c
+++ b/hw/intc/arm_gicv3_hvf.c
@@ -13,6 +13,7 @@
#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"
@@ -37,8 +38,13 @@ 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);
/*
@@ -668,7 +674,7 @@ 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;
@@ -715,6 +721,86 @@ 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);
@@ -734,7 +820,7 @@ 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 0000000000..a587332c7c
--- /dev/null
+++ b/hw/intc/arm_gicv3_hvf_stub.c
@@ -0,0 +1,25 @@
+/* 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 b7baf8a0f6..c6de2d9d00 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -43,6 +43,7 @@ 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 9adcab0a0c..03ab3e8f2f 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -339,4 +339,7 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PULL 15/28] hw/intc: arm_gicv3_hvf: save/restore Apple GIC state
2026-05-01 10:14 ` [PULL 15/28] hw/intc: arm_gicv3_hvf: save/restore Apple GIC state Peter Maydell
@ 2026-05-07 8:08 ` Philippe Mathieu-Daudé
2026-06-13 11:45 ` Philippe Mathieu-Daudé
1 sibling, 0 replies; 34+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-05-07 8:08 UTC (permalink / raw)
To: Peter Maydell, qemu-devel, Paolo Bonzini
Cc: Mohamed Mediouni, Manos Pitsidianakis, Pierrick Bouvier, Peter Xu,
Fabiano Rosas
Hi,
On 1/5/26 12:14, Peter Maydell wrote:
> 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>
Revisiting this patch ;)
> 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 9200671c7a..9c3fb2f4bf 100644
> --- a/hw/intc/arm_gicv3_common.c
> +++ b/hw/intc/arm_gicv3_common.c
> @@ -305,6 +305,7 @@ static const VMStateDescription vmstate_gicv3 = {
> .subsections = (const VMStateDescription * const []) {
> &vmstate_gicv3_gicd_no_migration_shift_bug,
> &vmstate_gicv3_gicd_nmi,
> + &vmstate_gicv3_hvf,
Since HVF is a *host* feature, I wondered why not wrap with CONFIG_HVF
#ifdef'ry but that triggers the "attempt to use a poisoned identifier"
error.
So we really need a stub, right?
> NULL
> }
> };
> diff --git a/hw/intc/arm_gicv3_hvf_stub.c b/hw/intc/arm_gicv3_hvf_stub.c
> new file mode 100644
> index 0000000000..a587332c7c
> --- /dev/null
> +++ b/hw/intc/arm_gicv3_hvf_stub.c
> @@ -0,0 +1,25 @@
> +/* 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 b7baf8a0f6..c6de2d9d00 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -43,6 +43,7 @@ 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'))
IIRC if_false does not work as expected with multiple conditions.
Simpler is to directly use the stub_ss[] source set:
-- >8 --
-specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_false:
files('arm_gicv3_hvf_stub.c'))
+stub_ss.add(files('arm_gicv3_hvf_stub.c'))
---
(I'll post that patch)
> 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 9adcab0a0c..03ab3e8f2f 100644
> --- a/include/hw/intc/arm_gicv3_common.h
> +++ b/include/hw/intc/arm_gicv3_common.h
> @@ -339,4 +339,7 @@ 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
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PULL 15/28] hw/intc: arm_gicv3_hvf: save/restore Apple GIC state
2026-05-01 10:14 ` [PULL 15/28] hw/intc: arm_gicv3_hvf: save/restore Apple GIC state Peter Maydell
2026-05-07 8:08 ` Philippe Mathieu-Daudé
@ 2026-06-13 11:45 ` Philippe Mathieu-Daudé
2026-06-13 11:49 ` Mohamed Mediouni
1 sibling, 1 reply; 34+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-06-13 11:45 UTC (permalink / raw)
To: Mohamed Mediouni; +Cc: qemu-devel, Peter Maydell
Hi Mohamed,
On 1/5/26 12:14, Peter Maydell wrote:
> 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_hvf.c b/hw/intc/arm_gicv3_hvf.c
> index 22f19d274d..ae881092ea 100644
> --- a/hw/intc/arm_gicv3_hvf.c
> +++ b/hw/intc/arm_gicv3_hvf.c
> @@ -13,6 +13,7 @@
> #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"
> @@ -37,8 +38,13 @@ 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);
>
> /*
> @@ -668,7 +674,7 @@ 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;
>
> @@ -715,6 +721,86 @@ 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();
> +}
Unfortunately this commit (48396ad6ce9) breaks bisection:
../../hw/intc/arm_gicv3_hvf.c:730:29: error: call to undeclared function
'hvf_irqchip_in_kernel'; ISO C99 and later do not support implicit
function declarations [-Wimplicit-function-declaration]
730 | return hvf_enabled() && hvf_irqchip_in_kernel();
| ^
1 error generated.
The method is declared / defined in the following commit (2a3c965516f).
Please be careful to avoid that in future to avoid manual operations.
Regards,
Phil.
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PULL 15/28] hw/intc: arm_gicv3_hvf: save/restore Apple GIC state
2026-06-13 11:45 ` Philippe Mathieu-Daudé
@ 2026-06-13 11:49 ` Mohamed Mediouni
0 siblings, 0 replies; 34+ messages in thread
From: Mohamed Mediouni @ 2026-06-13 11:49 UTC (permalink / raw)
To: Philippe Mathieu-Daudé; +Cc: qemu-devel, Peter Maydell
> On 13. Jun 2026, at 13:45, Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> wrote:
>
> Hi Mohamed,
>
> On 1/5/26 12:14, Peter Maydell wrote:
>> 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_hvf.c b/hw/intc/arm_gicv3_hvf.c
>> index 22f19d274d..ae881092ea 100644
>> --- a/hw/intc/arm_gicv3_hvf.c
>> +++ b/hw/intc/arm_gicv3_hvf.c
>> @@ -13,6 +13,7 @@
>> #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"
>> @@ -37,8 +38,13 @@ 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);
>> /*
>> @@ -668,7 +674,7 @@ 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;
>> @@ -715,6 +721,86 @@ 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();
>> +}
> Unfortunately this commit (48396ad6ce9) breaks bisection:
>
> ../../hw/intc/arm_gicv3_hvf.c:730:29: error: call to undeclared function 'hvf_irqchip_in_kernel'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
> 730 | return hvf_enabled() && hvf_irqchip_in_kernel();
> | ^
> 1 error generated.
Hi,
My mistake. Will try to see in which rev of the patchset I intro’d this one…
and will see how to avoid it going forward...
On a side note, two bugs affecting HVF right now:
- looks like on M1 there’s an issue affecting detecting 36-bit PAs
think that bug was intro’d by me but will have to look more into it
- and qemu master is broken rn with HVF (and there’s an unmerged patch for
that)
>
> The method is declared / defined in the following commit (2a3c965516f).
>
> Please be careful to avoid that in future to avoid manual operations.
>
> Regards,
>
> Phil.
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PULL 16/28] accel, hw/arm, include/system/hvf: infrastructure changes for HVF vGIC
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (14 preceding siblings ...)
2026-05-01 10:14 ` [PULL 15/28] hw/intc: arm_gicv3_hvf: save/restore Apple GIC state Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 17/28] target/arm: hvf: instantiate GIC early Peter Maydell
` (11 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 5f357c6d19..add265e0c8 100644
--- a/accel/hvf/hvf-all.c
+++ b/accel/hvf/hvf-all.c
@@ -10,6 +10,8 @@
#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"
@@ -21,6 +23,7 @@
#include "trace.h"
bool hvf_allowed;
+bool hvf_kernel_irqchip;
const char *hvf_return_string(hv_return_t ret)
{
@@ -216,6 +219,43 @@ 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);
@@ -223,6 +263,12 @@ 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 42eadc5ca9..6bd08759ba 100644
--- a/accel/stubs/hvf-stub.c
+++ b/accel/stubs/hvf-stub.c
@@ -10,3 +10,4 @@
#include "system/hvf.h"
bool hvf_allowed;
+bool hvf_kernel_irqchip;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 77891f0820..47400214a2 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1163,7 +1163,7 @@ 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);
}
@@ -1186,7 +1186,8 @@ 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);
@@ -1197,7 +1198,7 @@ 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);
}
@@ -2444,7 +2445,15 @@ 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;
@@ -2486,6 +2495,8 @@ 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;
}
@@ -2505,6 +2516,10 @@ 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 9c3fb2f4bf..f7ba74e6d5 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -33,6 +33,7 @@
#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)
@@ -659,6 +660,8 @@ 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 d3dcf088b3..dc8da85979 100644
--- a/include/system/hvf.h
+++ b/include/system/hvf.h
@@ -26,8 +26,11 @@
#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 0e1fc217b4..516ed7890b 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -1781,6 +1781,8 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 17/28] target/arm: hvf: instantiate GIC early
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (15 preceding siblings ...)
2026-05-01 10:14 ` [PULL 16/28] accel, hw/arm, include/system/hvf: infrastructure changes for HVF vGIC Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 18/28] hw/arm, target/arm: nested virtualisation on HVF Peter Maydell
` (10 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 ef4c3671e9..a028c99e24 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -1230,6 +1230,28 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 18/28] hw/arm, target/arm: nested virtualisation on HVF
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (16 preceding siblings ...)
2026-05-01 10:14 ` [PULL 17/28] target/arm: hvf: instantiate GIC early Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 19/28] hvf: only call hvf_sync_vtimer() when running without the platform vGIC Peter Maydell
` (9 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 add265e0c8..da29aa3aa3 100644
--- a/accel/hvf/hvf-all.c
+++ b/accel/hvf/hvf-all.c
@@ -24,6 +24,12 @@
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 6bd08759ba..7643e8c5f5 100644
--- a/accel/stubs/hvf-stub.c
+++ b/accel/stubs/hvf-stub.c
@@ -11,3 +11,14 @@
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 47400214a2..ca50411020 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2987,6 +2987,11 @@ 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 dc8da85979..a961df8b95 100644
--- a/include/system/hvf.h
+++ b/include/system/hvf.h
@@ -28,11 +28,16 @@ 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 a028c99e24..09f41094e5 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -27,6 +27,7 @@
#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"
@@ -1103,6 +1104,10 @@ 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]);
@@ -1218,6 +1223,19 @@ 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;
@@ -1229,6 +1247,20 @@ 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, *)) {
@@ -1420,6 +1452,13 @@ 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.
*
@@ -1441,7 +1480,6 @@ 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],
@@ -1495,7 +1533,7 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 19/28] hvf: only call hvf_sync_vtimer() when running without the platform vGIC
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (17 preceding siblings ...)
2026-05-01 10:14 ` [PULL 18/28] hw/arm, target/arm: nested virtualisation on HVF Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 20/28] hvf: gate ARM_FEATURE_PMU register emulation when using the Apple vGIC Peter Maydell
` (8 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 09f41094e5..fa0a22fdc3 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -2368,7 +2368,9 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 20/28] hvf: gate ARM_FEATURE_PMU register emulation when using the Apple vGIC
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (18 preceding siblings ...)
2026-05-01 10:14 ` [PULL 19/28] hvf: only call hvf_sync_vtimer() when running without the platform vGIC Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 21/28] hvf: arm: allow exposing minimal PMU for kernel-irqchip=on Peter Maydell
` (7 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 fa0a22fdc3..9d64f2e1a5 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -1601,7 +1601,7 @@ 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;
@@ -1862,7 +1862,7 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 21/28] hvf: arm: allow exposing minimal PMU for kernel-irqchip=on
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (19 preceding siblings ...)
2026-05-01 10:14 ` [PULL 20/28] hvf: gate ARM_FEATURE_PMU register emulation when using the Apple vGIC Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:14 ` [PULL 22/28] target/arm: hvf: add asserts for code paths not leveraged when using the vGIC Peter Maydell
` (6 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 9d64f2e1a5..390a3529ff 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -1145,6 +1145,25 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 22/28] target/arm: hvf: add asserts for code paths not leveraged when using the vGIC
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (20 preceding siblings ...)
2026-05-01 10:14 ` [PULL 21/28] hvf: arm: allow exposing minimal PMU for kernel-irqchip=on Peter Maydell
@ 2026-05-01 10:14 ` Peter Maydell
2026-05-01 10:15 ` [PULL 23/28] hvf: sync registers used at EL2 Peter Maydell
` (5 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:14 UTC (permalink / raw)
To: qemu-devel
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 390a3529ff..38f88c1a80 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -1696,6 +1696,7 @@ 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;
@@ -1986,6 +1987,7 @@ 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;
@@ -2393,6 +2395,7 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 23/28] hvf: sync registers used at EL2
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (21 preceding siblings ...)
2026-05-01 10:14 ` [PULL 22/28] target/arm: hvf: add asserts for code paths not leveraged when using the vGIC Peter Maydell
@ 2026-05-01 10:15 ` Peter Maydell
2026-05-01 10:59 ` Stefan Hajnoczi
2026-05-01 10:15 ` [PULL 24/28] target/arm: hvf: pass through CNTHCTL_EL2 and MDCCINT_EL1 Peter Maydell
` (4 subsequent siblings)
27 siblings, 1 reply; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:15 UTC (permalink / raw)
To: qemu-devel
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 38f88c1a80..ecfe06cd8c 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -467,37 +467,75 @@ 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
@@ -1335,6 +1373,9 @@ 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"
@@ -1362,11 +1403,19 @@ 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;
@@ -1375,7 +1424,7 @@ 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 7a2f880f78..c11dbf274e 100644
--- a/target/arm/hvf/sysreg.c.inc
+++ b/target/arm/hvf/sysreg.c.inc
@@ -153,3 +153,47 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PULL 23/28] hvf: sync registers used at EL2
2026-05-01 10:15 ` [PULL 23/28] hvf: sync registers used at EL2 Peter Maydell
@ 2026-05-01 10:59 ` Stefan Hajnoczi
2026-05-01 23:20 ` Mohamed Mediouni
0 siblings, 1 reply; 34+ messages in thread
From: Stefan Hajnoczi @ 2026-05-01 10:59 UTC (permalink / raw)
To: Peter Maydell, mohamed; +Cc: qemu-devel
On Fri, May 1, 2026 at 6:18 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>
> 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.
Hi Mohamed and Peter,
Please take a look at the following CI failure:
In file included from ../target/arm/hvf/hvf.c:497:
../target/arm/hvf/sysreg.c.inc:187:1: error: static assertion failed
due to requirement '!(HV_SYS_REG_MDCR_EL2 != (((((1 << 28) | (19 <<
16) | ((3) << 14) | ((4) << 11) | ((1) << 7) | ((1) << 3) | ((1) <<
0)) | 13510798882111488ULL)) & 65535))': not expecting:
HV_SYS_REG_MDCR_EL2 != KVMID_TO_HVF(KVMID_AA64_SYS_REG64(3, 4, 1, 1,
1))
187 | DEF_SYSREG_EL2(HV_SYS_REG_MDCR_EL2, 3, 4, 1, 1, 1)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../target/arm/hvf/hvf.c:489:3: note: expanded from macro 'DEF_SYSREG_EL2'
489 | QEMU_BUILD_BUG_ON(HVF_ID !=
KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__)));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/private/var/folders/q_/v_6k8zks62lb0q_8f10mvn8h0000gn/T/cirrus-ci-build/include/qemu/compiler.h:74:30:
note: expanded from macro 'QEMU_BUILD_BUG_ON'
74 | #define QEMU_BUILD_BUG_ON(x) QEMU_BUILD_BUG_MSG(x, "not expecting: " #x)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/private/var/folders/q_/v_6k8zks62lb0q_8f10mvn8h0000gn/T/cirrus-ci-build/include/qemu/compiler.h:72:51:
note: expanded from macro 'QEMU_BUILD_BUG_MSG'
72 | #define QEMU_BUILD_BUG_MSG(x, msg) _Static_assert(!(x), msg)
| ^~~~
1 error generated.
https://gitlab.com/qemu-project/qemu/-/jobs/14176182142#L5618
Peter: Please resend the pull request with fixed patches or this patch removed.
Thanks,
Stefan
>
> 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 38f88c1a80..ecfe06cd8c 100644
> --- a/target/arm/hvf/hvf.c
> +++ b/target/arm/hvf/hvf.c
> @@ -467,37 +467,75 @@ 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
> @@ -1335,6 +1373,9 @@ 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"
>
> @@ -1362,11 +1403,19 @@ 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;
> @@ -1375,7 +1424,7 @@ 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 7a2f880f78..c11dbf274e 100644
> --- a/target/arm/hvf/sysreg.c.inc
> +++ b/target/arm/hvf/sysreg.c.inc
> @@ -153,3 +153,47 @@ 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
>
>
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PULL 23/28] hvf: sync registers used at EL2
2026-05-01 10:59 ` Stefan Hajnoczi
@ 2026-05-01 23:20 ` Mohamed Mediouni
0 siblings, 0 replies; 34+ messages in thread
From: Mohamed Mediouni @ 2026-05-01 23:20 UTC (permalink / raw)
To: Stefan Hajnoczi; +Cc: Peter Maydell, qemu-devel
> On 1. May 2026, at 12:59, Stefan Hajnoczi <stefanha@gmail.com> wrote:
>
> On Fri, May 1, 2026 at 6:18 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>>
>> 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.
>
> Hi Mohamed and Peter,
> Please take a look at the following CI failure:
>
> In file included from ../target/arm/hvf/hvf.c:497:
> ../target/arm/hvf/sysreg.c.inc:187:1: error: static assertion failed
> due to requirement '!(HV_SYS_REG_MDCR_EL2 != (((((1 << 28) | (19 <<
> 16) | ((3) << 14) | ((4) << 11) | ((1) << 7) | ((1) << 3) | ((1) <<
> 0)) | 13510798882111488ULL)) & 65535))': not expecting:
> HV_SYS_REG_MDCR_EL2 != KVMID_TO_HVF(KVMID_AA64_SYS_REG64(3, 4, 1, 1,
> 1))
> 187 | DEF_SYSREG_EL2(HV_SYS_REG_MDCR_EL2, 3, 4, 1, 1, 1)
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> ../target/arm/hvf/hvf.c:489:3: note: expanded from macro 'DEF_SYSREG_EL2'
> 489 | QEMU_BUILD_BUG_ON(HVF_ID !=
> KVMID_TO_HVF(KVMID_AA64_SYS_REG64(__VA_ARGS__)));
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> /private/var/folders/q_/v_6k8zks62lb0q_8f10mvn8h0000gn/T/cirrus-ci-build/include/qemu/compiler.h:74:30:
> note: expanded from macro 'QEMU_BUILD_BUG_ON'
> 74 | #define QEMU_BUILD_BUG_ON(x) QEMU_BUILD_BUG_MSG(x, "not expecting: " #x)
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> /private/var/folders/q_/v_6k8zks62lb0q_8f10mvn8h0000gn/T/cirrus-ci-build/include/qemu/compiler.h:72:51:
> note: expanded from macro 'QEMU_BUILD_BUG_MSG'
> 72 | #define QEMU_BUILD_BUG_MSG(x, msg) _Static_assert(!(x), msg)
> | ^~~~
> 1 error generated.
>
> https://gitlab.com/qemu-project/qemu/-/jobs/14176182142#L5618
Thank you, it turned out to be an issue with older macOS SDKs
that macOS CI still uses.
Did send a (standalone) patch: https://lore.kernel.org/qemu-devel/20260501231353.63184-1-mohamed@unpredictable.fr/T/#u (looks like patchew is down at time of writing)
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PULL 24/28] target/arm: hvf: pass through CNTHCTL_EL2 and MDCCINT_EL1
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (22 preceding siblings ...)
2026-05-01 10:15 ` [PULL 23/28] hvf: sync registers used at EL2 Peter Maydell
@ 2026-05-01 10:15 ` Peter Maydell
2026-05-01 10:15 ` [PULL 25/28] hvf: arm: disable SME when nested virt is active Peter Maydell
` (3 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:15 UTC (permalink / raw)
To: qemu-devel
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 ecfe06cd8c..1894be6b0e 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -296,6 +296,10 @@ 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)
@@ -1719,6 +1723,14 @@ 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:
@@ -2007,6 +2019,14 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 25/28] hvf: arm: disable SME when nested virt is active
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (23 preceding siblings ...)
2026-05-01 10:15 ` [PULL 24/28] target/arm: hvf: pass through CNTHCTL_EL2 and MDCCINT_EL1 Peter Maydell
@ 2026-05-01 10:15 ` Peter Maydell
2026-05-01 10:15 ` [PULL 26/28] hvf: arm: physical timer emulation Peter Maydell
` (2 subsequent siblings)
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:15 UTC (permalink / raw)
To: qemu-devel
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 1894be6b0e..6062e08c49 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -1206,6 +1206,11 @@ 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 8029d48caf..59e19f6e84 100644
--- a/target/arm/hvf_arm.h
+++ b/target/arm/hvf_arm.h
@@ -11,6 +11,7 @@
#ifndef QEMU_HVF_ARM_H
#define QEMU_HVF_ARM_H
+#include "system/hvf.h"
#include "target/arm/cpu-qom.h"
/**
@@ -35,6 +36,10 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 26/28] hvf: arm: physical timer emulation
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (24 preceding siblings ...)
2026-05-01 10:15 ` [PULL 25/28] hvf: arm: disable SME when nested virt is active Peter Maydell
@ 2026-05-01 10:15 ` Peter Maydell
2026-05-01 10:15 ` [PULL 27/28] hvf: enable nested virtualisation support Peter Maydell
2026-05-01 10:15 ` [PULL 28/28] hvf: arm: enable vGIC by default for virt-11.1 and later Peter Maydell
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:15 UTC (permalink / raw)
To: qemu-devel
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 6062e08c49..2252b96f83 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -189,7 +189,9 @@ 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)
@@ -1719,9 +1721,15 @@ 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;
@@ -2015,12 +2023,14 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 27/28] hvf: enable nested virtualisation support
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (25 preceding siblings ...)
2026-05-01 10:15 ` [PULL 26/28] hvf: arm: physical timer emulation Peter Maydell
@ 2026-05-01 10:15 ` Peter Maydell
2026-05-01 10:15 ` [PULL 28/28] hvf: arm: enable vGIC by default for virt-11.1 and later Peter Maydell
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:15 UTC (permalink / raw)
To: qemu-devel
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 ca50411020..ad0a459987 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2719,7 +2719,8 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PULL 28/28] hvf: arm: enable vGIC by default for virt-11.1 and later
2026-05-01 10:14 [PULL 00/28] target-arm queue Peter Maydell
` (26 preceding siblings ...)
2026-05-01 10:15 ` [PULL 27/28] hvf: enable nested virtualisation support Peter Maydell
@ 2026-05-01 10:15 ` Peter Maydell
27 siblings, 0 replies; 34+ messages in thread
From: Peter Maydell @ 2026-05-01 10:15 UTC (permalink / raw)
To: qemu-devel
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 da29aa3aa3..946dbca59d 100644
--- a/accel/hvf/hvf-all.c
+++ b/accel/hvf/hvf-all.c
@@ -25,6 +25,7 @@
bool hvf_allowed;
bool hvf_kernel_irqchip;
bool hvf_nested_virt;
+bool hvf_kernel_irqchip_override;
void hvf_nested_virt_enable(bool nested_virt)
{
@@ -204,6 +205,13 @@ 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"
@@ -230,6 +238,8 @@ 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;
}
@@ -269,6 +279,7 @@ 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 ad0a459987..fe19030886 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -3769,6 +3769,17 @@ 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")
@@ -3835,6 +3846,7 @@ 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;
@@ -4079,8 +4091,11 @@ 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 fc7950da85..13e135a460 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -138,6 +138,8 @@ 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 ca63304c95..29c68931d8 100644
--- a/include/hw/core/boards.h
+++ b/include/hw/core/boards.h
@@ -280,6 +280,7 @@ 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 2621164cb2..ad7d375109 100644
--- a/include/system/hvf_int.h
+++ b/include/system/hvf_int.h
@@ -112,4 +112,5 @@ 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
^ permalink raw reply related [flat|nested] 34+ messages in thread