* [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases
@ 2026-09-16 10:00 Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 01/10] target/arm/emulate: add ISV=0 emulation library with load/store immediate Mohamed Mediouni
` (9 more replies)
0 siblings, 10 replies; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini
v8:
Refreshed for new macOS 27.
Now a standalone series.
Added new patch for newer macOS guests - to allow FileVault off.
v7:
Support for newer guest OSes.
Added not mergable patch for private ISA enablement.
Not grabbing a memory reference and still using flatview_for_each_range
for this one, which works fine as live migration is disallowed anyway.
v6:
Rework the patches a bit, with making sure that there's no compiler warnings
v5:
Address review comments and rebase.
v4:
Rebase and resend without the RFC tag as I didn't get feedback.
Drop patch 1 (version I posted was incomplete anyways). Last patch isn't worth it too tbh.
v2->v3:
Remove the Apple M4 support workaround part for now, to be part of another patchset perhaps
Address comments
v1->v2:
Remove some lines that shouldn't have been present...
Lucas Amaral (4):
target/arm/emulate: add ISV=0 emulation library with load/store
immediate
target/arm/emulate: add load/store register offset
target/arm/emulate: add load/store pair
target/arm/hvf, whpx: wire ISV=0 emulation for data aborts
Mohamed Mediouni (6):
vmapple: apple-gfx: make it work on the latest macOS release
hw/vmapple: aes: convert MAX_LEN to a #define
Revert "hw/arm: Do not build VMapple machine by default"
vmapple: add gicv2m
vmapple, gicv2m: add macOS compat quirk
hw/vmapple: virtio-blk: allow FileVault off
configs/devices/aarch64-softmmu/default.mak | 1 -
hw/display/apple-gfx-mmio.m | 58 ++-
hw/display/apple-gfx.h | 16 +
hw/display/apple-gfx.m | 90 +++-
hw/intc/arm_gicv2m.c | 5 +
hw/vmapple/aes.c | 3 +-
hw/vmapple/virtio-blk.c | 18 +
hw/vmapple/vmapple.c | 25 ++
target/arm/emulate/a64-ldst.decode | 226 ++++++++++
target/arm/emulate/arm_emulate.c | 451 ++++++++++++++++++++
target/arm/emulate/arm_emulate.h | 30 ++
target/arm/emulate/meson.build | 8 +
target/arm/hvf/hvf.c | 46 +-
target/arm/meson.build | 1 +
target/arm/whpx/whpx-all.c | 61 ++-
15 files changed, 1007 insertions(+), 32 deletions(-)
create mode 100644 target/arm/emulate/a64-ldst.decode
create mode 100644 target/arm/emulate/arm_emulate.c
create mode 100644 target/arm/emulate/arm_emulate.h
create mode 100644 target/arm/emulate/meson.build
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 22+ messages in thread
* [RFC PATCH v8 01/10] target/arm/emulate: add ISV=0 emulation library with load/store immediate
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-17 20:32 ` Akihiko Odaki
2026-09-16 10:00 ` [RFC PATCH v8 02/10] target/arm/emulate: add load/store register offset Mohamed Mediouni
` (8 subsequent siblings)
9 siblings, 1 reply; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini,
Lucas Amaral
From: Lucas Amaral <lucaaamaral@gmail.com>
Add a shared emulation library for AArch64 load/store instructions that
cause ISV=0 data aborts under hardware virtualization (HVF, WHPX).
When the Instruction Syndrome Valid bit is clear, the hypervisor cannot
determine the faulting instruction's target register or access size from
the syndrome alone. This library fetches and decodes the instruction
using a decodetree-generated decoder, then emulates it by accessing the
vCPU's register file (CPUARMState) and memory (cpu_memory_rw_debug)
directly.
This patch establishes the framework and adds load/store single with
immediate addressing — the most common ISV=0 trigger. Subsequent
patches add register-offset, pair, exclusive, and atomic instructions.
Instruction coverage:
- STR/LDR (GPR): unscaled, post-indexed, unprivileged, pre-indexed,
unsigned offset — all sizes (8/16/32/64-bit), sign/zero extension
- STR/LDR (SIMD/FP): same addressing modes, 8-128 bit elements
- PRFM: prefetch treated as NOP
- DC cache maintenance (SYS CRn=C7): NOP on MMIO
This library uses its own a64-ldst.decode rather than sharing
target/arm/tcg/a64.decode. TCG's trans_* functions are a compiler:
they emit IR ops into a translation block for later execution. This
library's trans_* functions are an interpreter: they execute directly
against the vCPU register file and memory. The decodetree-generated
dispatcher calls trans_* by name, so both cannot coexist in the same
translation unit. Decode patterns are kept consistent with TCG's
where possible.
Decodetree differences from TCG:
- &ldst_imm adds a 'u' flag to distinguish 9-bit signed vs 12-bit
unsigned immediate forms. TCG uses %uimm_scaled to pre-scale
the unsigned immediate at decode time; here imm:12 is extracted
raw and the handler scales it.
Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
---
target/arm/emulate/a64-ldst.decode | 129 ++++++++++++++++
target/arm/emulate/arm_emulate.c | 237 +++++++++++++++++++++++++++++
target/arm/emulate/arm_emulate.h | 30 ++++
target/arm/emulate/meson.build | 8 +
target/arm/meson.build | 1 +
5 files changed, 405 insertions(+)
create mode 100644 target/arm/emulate/a64-ldst.decode
create mode 100644 target/arm/emulate/arm_emulate.c
create mode 100644 target/arm/emulate/arm_emulate.h
create mode 100644 target/arm/emulate/meson.build
diff --git a/target/arm/emulate/a64-ldst.decode b/target/arm/emulate/a64-ldst.decode
new file mode 100644
index 0000000000..c887dcba7d
--- /dev/null
+++ b/target/arm/emulate/a64-ldst.decode
@@ -0,0 +1,129 @@
+# AArch64 load/store instruction patterns for ISV=0 emulation
+#
+# Copyright (c) 2026 Lucas Amaral <lucaaamaral@gmail.com>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+### Argument sets
+
+# Load/store immediate (unscaled, pre/post-index, unprivileged, unsigned offset)
+# 'u' flag: 0 = 9-bit signed immediate (byte offset), 1 = 12-bit unsigned (needs << sz)
+&ldst_imm rt rn imm sz sign w p unpriv ext u
+
+### Format templates
+
+# Load/store immediate (9-bit signed)
+@ldst_imm .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm u=0 unpriv=0 p=0 w=0
+@ldst_imm_pre .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm u=0 unpriv=0 p=0 w=1
+@ldst_imm_post .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm u=0 unpriv=0 p=1 w=1
+@ldst_imm_user .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm u=0 unpriv=1 p=0 w=0
+
+# Load/store unsigned offset (12-bit, handler scales by << sz)
+@ldst_uimm .. ... . .. .. imm:12 rn:5 rt:5 &ldst_imm u=1 unpriv=0 p=0 w=0
+
+### Load/store register — unscaled immediate (LDUR/STUR)
+
+# GPR
+STR_i sz:2 111 0 00 00 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0
+LDR_i 00 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=0
+LDR_i 01 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=1
+LDR_i 10 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=2
+LDR_i 11 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=3
+LDR_i 00 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=0
+LDR_i 01 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=1
+LDR_i 10 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=2
+LDR_i 00 111 0 00 11 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=1 sz=0
+LDR_i 01 111 0 00 11 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=1 sz=1
+
+# SIMD/FP
+STR_v_i sz:2 111 1 00 00 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0
+STR_v_i 00 111 1 00 10 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=4
+LDR_v_i sz:2 111 1 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0
+LDR_v_i 00 111 1 00 11 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=4
+
+### Load/store register — post-indexed
+
+# GPR
+STR_i sz:2 111 0 00 00 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0
+LDR_i 00 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=0
+LDR_i 01 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=1
+LDR_i 10 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=2
+LDR_i 11 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=3
+LDR_i 00 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=0
+LDR_i 01 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=1
+LDR_i 10 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=2
+LDR_i 00 111 0 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=1 sz=0
+LDR_i 01 111 0 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=1 sz=1
+
+# SIMD/FP
+STR_v_i sz:2 111 1 00 00 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0
+STR_v_i 00 111 1 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=4
+LDR_v_i sz:2 111 1 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0
+LDR_v_i 00 111 1 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=4
+
+### Load/store register — unprivileged
+
+# GPR only (no SIMD/FP unprivileged forms)
+STR_i sz:2 111 0 00 00 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=0
+LDR_i 00 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=0
+LDR_i 01 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=1
+LDR_i 10 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=2
+LDR_i 11 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=0 sz=3
+LDR_i 00 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=0
+LDR_i 01 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=1
+LDR_i 10 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=2
+LDR_i 00 111 0 00 11 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=1 sz=0
+LDR_i 01 111 0 00 11 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=1 sz=1
+
+### Load/store register — pre-indexed
+
+# GPR
+STR_i sz:2 111 0 00 00 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0
+LDR_i 00 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=0
+LDR_i 01 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=1
+LDR_i 10 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=2
+LDR_i 11 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=3
+LDR_i 00 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=0
+LDR_i 01 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=1
+LDR_i 10 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=2
+LDR_i 00 111 0 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=1 sz=0
+LDR_i 01 111 0 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=1 sz=1
+
+# SIMD/FP
+STR_v_i sz:2 111 1 00 00 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0
+STR_v_i 00 111 1 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4
+LDR_v_i sz:2 111 1 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0
+LDR_v_i 00 111 1 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4
+
+### PRFM — unscaled immediate: prefetch is a NOP
+
+NOP 11 111 0 00 10 0 --------- 00 ----- -----
+
+### Load/store register — unsigned offset
+
+# GPR
+STR_i sz:2 111 0 01 00 ............ ..... ..... @ldst_uimm sign=0 ext=0
+LDR_i 00 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=0
+LDR_i 01 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=1
+LDR_i 10 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=2
+LDR_i 11 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=3
+LDR_i 00 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=0
+LDR_i 01 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=1
+LDR_i 10 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=2
+LDR_i 00 111 0 01 11 ............ ..... ..... @ldst_uimm sign=1 ext=1 sz=0
+LDR_i 01 111 0 01 11 ............ ..... ..... @ldst_uimm sign=1 ext=1 sz=1
+
+# PRFM — unsigned offset
+NOP 11 111 0 01 10 ------------ ----- -----
+
+# SIMD/FP
+STR_v_i sz:2 111 1 01 00 ............ ..... ..... @ldst_uimm sign=0 ext=0
+STR_v_i 00 111 1 01 10 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4
+LDR_v_i sz:2 111 1 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0
+LDR_v_i 00 111 1 01 11 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4
+
+### System instructions — DC cache maintenance
+
+# SYS with CRn=C7 covers all data cache operations (DC CIVAC, CVAC, etc.).
+# On MMIO regions, cache maintenance is a harmless no-op.
+NOP 1101 0101 0000 1 --- 0111 ---- --- -----
diff --git a/target/arm/emulate/arm_emulate.c b/target/arm/emulate/arm_emulate.c
new file mode 100644
index 0000000000..02fefc30bf
--- /dev/null
+++ b/target/arm/emulate/arm_emulate.c
@@ -0,0 +1,237 @@
+/*
+ * AArch64 instruction emulation for ISV=0 data aborts
+ *
+ * Copyright (c) 2026 Lucas Amaral <lucaaamaral@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "arm_emulate.h"
+#include "target/arm/cpu.h"
+#include "exec/cpu-common.h"
+#include "exec/target_page.h"
+
+/* TODO: assumes LE guest data layout (sufficient for HVF/WHPX, both LE-only) */
+
+/* Named "DisasContext" as required by the decodetree code generator */
+typedef struct {
+ CPUState *cpu;
+ CPUARMState *env;
+ ArmEmulResult result;
+} DisasContext;
+
+#include "decode-a64-ldst.c.inc"
+
+/* GPR data access (Rt, Rs, Rt2) -- register 31 = XZR */
+
+static uint64_t gpr_read(DisasContext *ctx, int reg)
+{
+ if (reg == 31) {
+ return 0; /* XZR */
+ }
+ return ctx->env->xregs[reg];
+}
+
+static void gpr_write(DisasContext *ctx, int reg, uint64_t val)
+{
+ if (reg == 31) {
+ return; /* XZR -- discard */
+ }
+ ctx->env->xregs[reg] = val;
+ ctx->cpu->vcpu_dirty = true;
+}
+
+/* Base register access (Rn) -- register 31 = SP */
+
+static uint64_t base_read(DisasContext *ctx, int rn)
+{
+ return ctx->env->xregs[rn];
+}
+
+static void base_write(DisasContext *ctx, int rn, uint64_t val)
+{
+ ctx->env->xregs[rn] = val;
+ ctx->cpu->vcpu_dirty = true;
+}
+
+/* SIMD/FP register access */
+
+static void fpreg_read(DisasContext *ctx, int reg, void *buf, int size)
+{
+ memcpy(buf, &ctx->env->vfp.zregs[reg], size);
+}
+
+static void fpreg_write(DisasContext *ctx, int reg, const void *buf, int size)
+{
+ memset(&ctx->env->vfp.zregs[reg], 0, sizeof(ctx->env->vfp.zregs[reg]));
+ memcpy(&ctx->env->vfp.zregs[reg], buf, size);
+ ctx->cpu->vcpu_dirty = true;
+}
+
+/* Memory access wrappers */
+
+static int mem_read(DisasContext *ctx, uint64_t va, void *buf, int size)
+{
+ if (((va & ~TARGET_PAGE_MASK) + size) > TARGET_PAGE_SIZE) {
+ ctx->result = ARM_EMUL_ERR_MEM;
+ return -1;
+ }
+ int ret = cpu_memory_rw_debug(ctx->cpu, va, buf, size, false);
+ if (ret != 0) {
+ ctx->result = ARM_EMUL_ERR_MEM;
+ }
+ return ret;
+}
+
+static int mem_write(DisasContext *ctx, uint64_t va, const void *buf, int size)
+{
+ if (((va & ~TARGET_PAGE_MASK) + size) > TARGET_PAGE_SIZE) {
+ ctx->result = ARM_EMUL_ERR_MEM;
+ return -1;
+ }
+ int ret = cpu_memory_rw_debug(ctx->cpu, va, (void *)buf, size, true);
+ if (ret != 0) {
+ ctx->result = ARM_EMUL_ERR_MEM;
+ }
+ return ret;
+}
+
+/* Sign/zero extension helpers */
+
+static uint64_t sign_extend(uint64_t val, int from_bits)
+{
+ int shift = 64 - from_bits;
+ return (int64_t)(val << shift) >> shift;
+}
+
+/* Apply sign/zero extension */
+static uint64_t load_extend(uint64_t val, int sz, int sign, int ext)
+{
+ int data_bits = 8 << sz;
+
+ if (sign) {
+ val = sign_extend(val, data_bits);
+ if (ext) {
+ /* Sign-extend to 32 bits (W register) */
+ val &= 0xFFFFFFFF;
+ }
+ } else if (ext) {
+ /* Zero-extend to 32 bits (W register) */
+ val &= 0xFFFFFFFF;
+ }
+ return val;
+}
+
+/* Load/store single -- immediate (GPR) (DDI 0487 C3.3.8 -- C3.3.13) */
+
+static bool trans_STR_i(DisasContext *ctx, arg_ldst_imm *a)
+{
+ int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
+ int64_t offset = a->u ? ((int64_t)(uint64_t)a->imm << a->sz)
+ : (int64_t)a->imm;
+ uint64_t base = base_read(ctx, a->rn);
+ uint64_t va = a->p ? base : base + offset;
+
+ uint64_t val = gpr_read(ctx, a->rt);
+ if (mem_write(ctx, va, &val, esize) != 0) {
+ return true;
+ }
+
+ if (a->w) {
+ base_write(ctx, a->rn, base + offset);
+ }
+ return true;
+}
+
+static bool trans_LDR_i(DisasContext *ctx, arg_ldst_imm *a)
+{
+ int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
+ int64_t offset = a->u ? ((int64_t)(uint64_t)a->imm << a->sz)
+ : (int64_t)a->imm;
+ uint64_t base = base_read(ctx, a->rn);
+ uint64_t va = a->p ? base : base + offset;
+ uint64_t val = 0;
+
+ if (mem_read(ctx, va, &val, esize) != 0) {
+ return true;
+ }
+
+ val = load_extend(val, a->sz, a->sign, a->ext);
+ gpr_write(ctx, a->rt, val);
+
+ if (a->w) {
+ base_write(ctx, a->rn, base + offset);
+ }
+ return true;
+}
+
+/*
+ * Load/store single -- immediate (SIMD/FP)
+ * STR_v_i / LDR_v_i (DDI 0487 C3.3.10)
+ */
+
+static bool trans_STR_v_i(DisasContext *ctx, arg_ldst_imm *a)
+{
+ int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
+ int64_t offset = a->u ? ((int64_t)(uint64_t)a->imm << a->sz)
+ : (int64_t)a->imm;
+ uint64_t base = base_read(ctx, a->rn);
+ uint64_t va = a->p ? base : base + offset;
+ uint8_t buf[16];
+
+ fpreg_read(ctx, a->rt, buf, esize);
+ if (mem_write(ctx, va, buf, esize) != 0) {
+ return true;
+ }
+
+ if (a->w) {
+ base_write(ctx, a->rn, base + offset);
+ }
+ return true;
+}
+
+static bool trans_LDR_v_i(DisasContext *ctx, arg_ldst_imm *a)
+{
+ int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
+ int64_t offset = a->u ? ((int64_t)(uint64_t)a->imm << a->sz)
+ : (int64_t)a->imm;
+ uint64_t base = base_read(ctx, a->rn);
+ uint64_t va = a->p ? base : base + offset;
+ uint8_t buf[16];
+
+ if (mem_read(ctx, va, buf, esize) != 0) {
+ return true;
+ }
+
+ fpreg_write(ctx, a->rt, buf, esize);
+
+ if (a->w) {
+ base_write(ctx, a->rn, base + offset);
+ }
+ return true;
+}
+
+/* PRFM, DC cache maintenance -- treated as NOP */
+static bool trans_NOP(DisasContext *ctx, arg_NOP *a)
+{
+ (void)ctx;
+ (void)a;
+ return true;
+}
+
+/* Entry point */
+
+ArmEmulResult arm_emul_insn(CPUArchState *env, uint32_t insn)
+{
+ DisasContext ctx = {
+ .cpu = env_cpu(env),
+ .env = env,
+ .result = ARM_EMUL_OK,
+ };
+
+ if (!decode_a64_ldst(&ctx, insn)) {
+ return ARM_EMUL_UNHANDLED;
+ }
+
+ return ctx.result;
+}
diff --git a/target/arm/emulate/arm_emulate.h b/target/arm/emulate/arm_emulate.h
new file mode 100644
index 0000000000..7fe298394f
--- /dev/null
+++ b/target/arm/emulate/arm_emulate.h
@@ -0,0 +1,30 @@
+/*
+ * AArch64 instruction emulation library
+ *
+ * Copyright (c) 2026 Lucas Amaral <lucaaamaral@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef ARM_EMULATE_H
+#define ARM_EMULATE_H
+
+#include "qemu/osdep.h"
+
+/**
+ * ArmEmulResult - return status from arm_emul_insn()
+ */
+typedef enum {
+ ARM_EMUL_OK, /* Instruction emulated successfully */
+ ARM_EMUL_UNHANDLED, /* Instruction not recognized by decoder */
+ ARM_EMUL_ERR_MEM, /* Memory access failed */
+} ArmEmulResult;
+
+/**
+ * arm_emul_insn - decode and emulate one AArch64 instruction
+ *
+ * Caller must synchronize CPU state and fetch @insn before calling.
+ */
+ArmEmulResult arm_emul_insn(CPUArchState *env, uint32_t insn);
+
+#endif /* ARM_EMULATE_H */
diff --git a/target/arm/emulate/meson.build b/target/arm/emulate/meson.build
new file mode 100644
index 0000000000..e5455bd206
--- /dev/null
+++ b/target/arm/emulate/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+gen_a64_ldst = decodetree.process('a64-ldst.decode',
+ extra_args: ['--static-decode=decode_a64_ldst'])
+
+arm_common_system_ss.add(when: 'TARGET_AARCH64', if_true: [
+ gen_a64_ldst, files('arm_emulate.c')
+])
diff --git a/target/arm/meson.build b/target/arm/meson.build
index 0369f96b4c..1963c0ec21 100644
--- a/target/arm/meson.build
+++ b/target/arm/meson.build
@@ -61,6 +61,7 @@ arm_common_system_ss.add(files(
'ptw.c',
))
+subdir('emulate')
subdir('hvf')
subdir('whpx')
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC PATCH v8 02/10] target/arm/emulate: add load/store register offset
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 01/10] target/arm/emulate: add ISV=0 emulation library with load/store immediate Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 03/10] target/arm/emulate: add load/store pair Mohamed Mediouni
` (7 subsequent siblings)
9 siblings, 0 replies; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini,
Lucas Amaral
From: Lucas Amaral <lucaaamaral@gmail.com>
Add emulation for load/store register offset addressing mode
(DDI 0487 C3.3.9). The offset register value is extended via
UXTB/UXTH/UXTW/UXTX/SXTB/SXTH/SXTW/SXTX and optionally
shifted by the element size.
Instruction coverage:
- STR/LDR (GPR): register offset with extend, all sizes
- STR/LDR (SIMD/FP): register offset with extend, 8-128 bit
- PRFM register offset: NOP
Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
---
target/arm/emulate/a64-ldst.decode | 29 ++++++++
target/arm/emulate/arm_emulate.c | 103 +++++++++++++++++++++++++++++
2 files changed, 132 insertions(+)
diff --git a/target/arm/emulate/a64-ldst.decode b/target/arm/emulate/a64-ldst.decode
index c887dcba7d..af6babe14a 100644
--- a/target/arm/emulate/a64-ldst.decode
+++ b/target/arm/emulate/a64-ldst.decode
@@ -10,6 +10,9 @@
# 'u' flag: 0 = 9-bit signed immediate (byte offset), 1 = 12-bit unsigned (needs << sz)
&ldst_imm rt rn imm sz sign w p unpriv ext u
+# Load/store register offset
+&ldst rm rn rt sign ext sz opt s
+
### Format templates
# Load/store immediate (9-bit signed)
@@ -21,6 +24,9 @@
# Load/store unsigned offset (12-bit, handler scales by << sz)
@ldst_uimm .. ... . .. .. imm:12 rn:5 rt:5 &ldst_imm u=1 unpriv=0 p=0 w=0
+# Load/store register offset
+@ldst .. ... . .. .. . rm:5 opt:3 s:1 .. rn:5 rt:5 &ldst
+
### Load/store register — unscaled immediate (LDUR/STUR)
# GPR
@@ -122,6 +128,29 @@ STR_v_i 00 111 1 01 10 ............ ..... ..... @ldst_uimm sign=
LDR_v_i sz:2 111 1 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0
LDR_v_i 00 111 1 01 11 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4
+### Load/store register — register offset
+
+# GPR
+STR sz:2 111 0 00 00 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0
+LDR 00 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=1 sz=0
+LDR 01 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=1 sz=1
+LDR 10 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=1 sz=2
+LDR 11 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=3
+LDR 00 111 0 00 10 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=0 sz=0
+LDR 01 111 0 00 10 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=0 sz=1
+LDR 10 111 0 00 10 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=0 sz=2
+LDR 00 111 0 00 11 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=1 sz=0
+LDR 01 111 0 00 11 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=1 sz=1
+
+# PRFM — register offset
+NOP 11 111 0 00 10 1 ----- -1- - 10 ----- -----
+
+# SIMD/FP
+STR_v sz:2 111 1 00 00 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0
+STR_v 00 111 1 00 10 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=4
+LDR_v sz:2 111 1 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0
+LDR_v 00 111 1 00 11 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=4
+
### System instructions — DC cache maintenance
# SYS with CRn=C7 covers all data cache operations (DC CIVAC, CVAC, etc.).
diff --git a/target/arm/emulate/arm_emulate.c b/target/arm/emulate/arm_emulate.c
index 02fefc30bf..bf09e2a67b 100644
--- a/target/arm/emulate/arm_emulate.c
+++ b/target/arm/emulate/arm_emulate.c
@@ -211,6 +211,109 @@ static bool trans_LDR_v_i(DisasContext *ctx, arg_ldst_imm *a)
return true;
}
+/* Register offset extension (DDI 0487 C6.2.131) */
+
+static uint64_t extend_reg(uint64_t val, int option, int shift)
+{
+ switch (option) {
+ case 0: /* UXTB */
+ val = (uint8_t)val;
+ break;
+ case 1: /* UXTH */
+ val = (uint16_t)val;
+ break;
+ case 2: /* UXTW */
+ val = (uint32_t)val;
+ break;
+ case 3: /* UXTX / LSL */
+ break;
+ case 4: /* SXTB */
+ val = (int64_t)(int8_t)val;
+ break;
+ case 5: /* SXTH */
+ val = (int64_t)(int16_t)val;
+ break;
+ case 6: /* SXTW */
+ val = (int64_t)(int32_t)val;
+ break;
+ case 7: /* SXTX */
+ break;
+ }
+ return val << shift;
+}
+
+/*
+ * Load/store single -- register offset (GPR)
+ * STR / LDR (DDI 0487 C3.3.9)
+ */
+
+static bool trans_STR(DisasContext *ctx, arg_ldst *a)
+{
+ int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
+ int shift = a->s ? a->sz : 0;
+ uint64_t rm_val = gpr_read(ctx, a->rm);
+ uint64_t offset = extend_reg(rm_val, a->opt, shift);
+ uint64_t va = base_read(ctx, a->rn) + offset;
+
+ uint64_t val = gpr_read(ctx, a->rt);
+ mem_write(ctx, va, &val, esize);
+ return true;
+}
+
+static bool trans_LDR(DisasContext *ctx, arg_ldst *a)
+{
+ int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
+ int shift = a->s ? a->sz : 0;
+ uint64_t rm_val = gpr_read(ctx, a->rm);
+ uint64_t offset = extend_reg(rm_val, a->opt, shift);
+ uint64_t va = base_read(ctx, a->rn) + offset;
+ uint64_t val = 0;
+
+ if (mem_read(ctx, va, &val, esize) != 0) {
+ return true;
+ }
+
+ val = load_extend(val, a->sz, a->sign, a->ext);
+ gpr_write(ctx, a->rt, val);
+ return true;
+}
+
+/*
+ * Load/store single -- register offset (SIMD/FP)
+ * STR_v / LDR_v (DDI 0487 C3.3.10)
+ */
+
+static bool trans_STR_v(DisasContext *ctx, arg_ldst *a)
+{
+ int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
+ int shift = a->s ? a->sz : 0;
+ uint64_t rm_val = gpr_read(ctx, a->rm);
+ uint64_t offset = extend_reg(rm_val, a->opt, shift);
+ uint64_t va = base_read(ctx, a->rn) + offset;
+ uint8_t buf[16];
+
+ fpreg_read(ctx, a->rt, buf, esize);
+ mem_write(ctx, va, buf, esize);
+ return true;
+}
+
+static bool trans_LDR_v(DisasContext *ctx, arg_ldst *a)
+{
+ int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
+ int shift = a->s ? a->sz : 0;
+ uint64_t rm_val = gpr_read(ctx, a->rm);
+ uint64_t offset = extend_reg(rm_val, a->opt, shift);
+ uint64_t va = base_read(ctx, a->rn) + offset;
+ uint8_t buf[16];
+
+ if (mem_read(ctx, va, buf, esize) != 0) {
+ return true;
+ }
+
+ fpreg_write(ctx, a->rt, buf, esize);
+ return true;
+}
+
/* PRFM, DC cache maintenance -- treated as NOP */
static bool trans_NOP(DisasContext *ctx, arg_NOP *a)
{
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC PATCH v8 03/10] target/arm/emulate: add load/store pair
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 01/10] target/arm/emulate: add ISV=0 emulation library with load/store immediate Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 02/10] target/arm/emulate: add load/store register offset Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-17 20:33 ` Akihiko Odaki
2026-09-16 10:00 ` [RFC PATCH v8 04/10] target/arm/hvf, whpx: wire ISV=0 emulation for data aborts Mohamed Mediouni
` (6 subsequent siblings)
9 siblings, 1 reply; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini,
Lucas Amaral
From: Lucas Amaral <lucaaamaral@gmail.com>
Add emulation for load/store pair instructions (DDI 0487 C3.3.14 --
C3.3.16). All addressing modes are covered: non-temporal (STNP/LDNP),
post-indexed, signed offset, and pre-indexed.
Instruction coverage:
- STP/LDP (GPR): 32/64-bit pairs, all addressing modes
- STP/LDP (SIMD/FP): 32/64/128-bit pairs, all addressing modes
- LDPSW: sign-extending 32-bit pair load
- STGP: store allocation tag pair (tag operation is NOP for MMIO)
Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
---
target/arm/emulate/a64-ldst.decode | 68 ++++++++++++++++++
target/arm/emulate/arm_emulate.c | 111 +++++++++++++++++++++++++++++
2 files changed, 179 insertions(+)
diff --git a/target/arm/emulate/a64-ldst.decode b/target/arm/emulate/a64-ldst.decode
index af6babe14a..f3de3f86cb 100644
--- a/target/arm/emulate/a64-ldst.decode
+++ b/target/arm/emulate/a64-ldst.decode
@@ -10,6 +10,9 @@
# 'u' flag: 0 = 9-bit signed immediate (byte offset), 1 = 12-bit unsigned (needs << sz)
&ldst_imm rt rn imm sz sign w p unpriv ext u
+# Load/store pair (GPR and SIMD/FP)
+&ldstpair rt2 rt rn imm sz sign w p
+
# Load/store register offset
&ldst rm rn rt sign ext sz opt s
@@ -24,6 +27,9 @@
# Load/store unsigned offset (12-bit, handler scales by << sz)
@ldst_uimm .. ... . .. .. imm:12 rn:5 rt:5 &ldst_imm u=1 unpriv=0 p=0 w=0
+# Load/store pair: imm7 is signed, scaled by element size in handler
+@ldstpair .. ... . ... . imm:s7 rt2:5 rn:5 rt:5 &ldstpair
+
# Load/store register offset
@ldst .. ... . .. .. . rm:5 opt:3 s:1 .. rn:5 rt:5 &ldst
@@ -128,6 +134,68 @@ STR_v_i 00 111 1 01 10 ............ ..... ..... @ldst_uimm sign=
LDR_v_i sz:2 111 1 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0
LDR_v_i 00 111 1 01 11 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4
+### Load/store pair — non-temporal (STNP/LDNP)
+
+# STNP/LDNP: offset only, no writeback. Non-temporal hint ignored.
+STP 00 101 0 000 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
+LDP 00 101 0 000 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
+STP 10 101 0 000 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
+LDP 10 101 0 000 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
+STP_v 00 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
+LDP_v 00 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
+STP_v 01 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
+LDP_v 01 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
+STP_v 10 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0
+LDP_v 10 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0
+
+### Load/store pair — post-indexed
+
+STP 00 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1
+LDP 00 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1
+LDP 01 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=1 w=1
+STP 10 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
+LDP 10 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
+STP_v 00 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1
+LDP_v 00 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1
+STP_v 01 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
+LDP_v 01 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
+STP_v 10 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=1 w=1
+LDP_v 10 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=1 w=1
+
+### Load/store pair — signed offset
+
+STP 00 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
+LDP 00 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
+LDP 01 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=0 w=0
+STP 10 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
+LDP 10 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
+STP_v 00 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
+LDP_v 00 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
+STP_v 01 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
+LDP_v 01 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
+STP_v 10 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0
+LDP_v 10 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0
+
+### Load/store pair — pre-indexed
+
+STP 00 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1
+LDP 00 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1
+LDP 01 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=0 w=1
+STP 10 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
+LDP 10 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
+STP_v 00 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1
+LDP_v 00 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1
+STP_v 01 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
+LDP_v 01 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
+STP_v 10 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=1
+LDP_v 10 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=1
+
+### Load/store pair — STGP (store allocation tag + pair)
+
+STGP 01 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
+STGP 01 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
+STGP 01 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
+
### Load/store register — register offset
# GPR
diff --git a/target/arm/emulate/arm_emulate.c b/target/arm/emulate/arm_emulate.c
index bf09e2a67b..6c63a0d0d1 100644
--- a/target/arm/emulate/arm_emulate.c
+++ b/target/arm/emulate/arm_emulate.c
@@ -122,6 +122,117 @@ static uint64_t load_extend(uint64_t val, int sz, int sign, int ext)
return val;
}
+/*
+ * Load/store pair: STP, LDP, STNP, LDNP, STGP, LDPSW
+ * (DDI 0487 C3.3.14 -- C3.3.16)
+ */
+
+static bool trans_STP(DisasContext *ctx, arg_ldstpair *a)
+{
+ int esize = 1 << a->sz; /* 4 or 8 bytes */
+ int64_t offset = (int64_t)a->imm << a->sz;
+ uint64_t base = base_read(ctx, a->rn);
+ uint64_t va = a->p ? base : base + offset; /* post-index: unmodified base */
+ uint8_t buf[16]; /* max 2 x 8 bytes */
+
+ uint64_t v1 = gpr_read(ctx, a->rt);
+ uint64_t v2 = gpr_read(ctx, a->rt2);
+ memcpy(buf, &v1, esize);
+ memcpy(buf + esize, &v2, esize);
+
+ if (mem_write(ctx, va, buf, 2 * esize) != 0) {
+ return true;
+ }
+
+ if (a->w) {
+ base_write(ctx, a->rn, base + offset);
+ }
+ return true;
+}
+
+static bool trans_LDP(DisasContext *ctx, arg_ldstpair *a)
+{
+ int esize = 1 << a->sz;
+ int64_t offset = (int64_t)a->imm << a->sz;
+ uint64_t base = base_read(ctx, a->rn);
+ uint64_t va = a->p ? base : base + offset;
+ uint8_t buf[16];
+ uint64_t v1 = 0, v2 = 0;
+
+ if (mem_read(ctx, va, buf, 2 * esize) != 0) {
+ return true;
+ }
+ memcpy(&v1, buf, esize);
+ memcpy(&v2, buf + esize, esize);
+
+ /* LDPSW: sign-extend 32-bit values to 64-bit (sign=1, sz=2) */
+ if (a->sign) {
+ v1 = sign_extend(v1, 8 * esize);
+ v2 = sign_extend(v2, 8 * esize);
+ }
+
+ gpr_write(ctx, a->rt, v1);
+ gpr_write(ctx, a->rt2, v2);
+
+ if (a->w) {
+ base_write(ctx, a->rn, base + offset);
+ }
+ return true;
+}
+
+/* STGP: tag operation is a NOP for emulation; data stored via STP */
+static bool trans_STGP(DisasContext *ctx, arg_ldstpair *a)
+{
+ return trans_STP(ctx, a);
+}
+
+/*
+ * SIMD/FP load/store pair: STP_v, LDP_v
+ * (DDI 0487 C3.3.14 -- C3.3.16)
+ */
+
+static bool trans_STP_v(DisasContext *ctx, arg_ldstpair *a)
+{
+ int esize = 1 << a->sz; /* 4, 8, or 16 bytes */
+ int64_t offset = (int64_t)a->imm << a->sz;
+ uint64_t base = base_read(ctx, a->rn);
+ uint64_t va = a->p ? base : base + offset;
+ uint8_t buf[32]; /* max 2 x 16 bytes */
+
+ fpreg_read(ctx, a->rt, buf, esize);
+ fpreg_read(ctx, a->rt2, buf + esize, esize);
+
+ if (mem_write(ctx, va, buf, 2 * esize) != 0) {
+ return true;
+ }
+
+ if (a->w) {
+ base_write(ctx, a->rn, base + offset);
+ }
+ return true;
+}
+
+static bool trans_LDP_v(DisasContext *ctx, arg_ldstpair *a)
+{
+ int esize = 1 << a->sz;
+ int64_t offset = (int64_t)a->imm << a->sz;
+ uint64_t base = base_read(ctx, a->rn);
+ uint64_t va = a->p ? base : base + offset;
+ uint8_t buf[32];
+
+ if (mem_read(ctx, va, buf, 2 * esize) != 0) {
+ return true;
+ }
+
+ fpreg_write(ctx, a->rt, buf, esize);
+ fpreg_write(ctx, a->rt2, buf + esize, esize);
+
+ if (a->w) {
+ base_write(ctx, a->rn, base + offset);
+ }
+ return true;
+}
+
/* Load/store single -- immediate (GPR) (DDI 0487 C3.3.8 -- C3.3.13) */
static bool trans_STR_i(DisasContext *ctx, arg_ldst_imm *a)
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC PATCH v8 04/10] target/arm/hvf, whpx: wire ISV=0 emulation for data aborts
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
` (2 preceding siblings ...)
2026-09-16 10:00 ` [RFC PATCH v8 03/10] target/arm/emulate: add load/store pair Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-17 20:37 ` Akihiko Odaki
2026-09-16 10:00 ` [RFC PATCH v8 05/10] vmapple: apple-gfx: make it work on the latest macOS release Mohamed Mediouni
` (5 subsequent siblings)
9 siblings, 1 reply; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini,
Lucas Amaral
From: Lucas Amaral <lucaaamaral@gmail.com>
When a data abort with ISV=0 occurs during MMIO emulation, the
syndrome register does not carry the access size or target register.
Previously this hit an assert(isv) and killed the VM.
Replace the assert with instruction fetch + decode + emulate using the
shared library in target/arm/emulate/. The faulting instruction is read
from guest memory via cpu_memory_rw_debug(), decoded by the decodetree-
generated decoder, and emulated against the vCPU register file.
Both HVF (macOS) and WHPX (Windows Hyper-V) use the same pattern:
1. cpu_synchronize_state() to flush hypervisor registers
2. Fetch 4-byte instruction at env->pc
3. arm_emul_insn(env, insn)
4. On success, advance PC past the emulated instruction
If the instruction is unhandled or a memory error occurs, a synchronous
external abort is injected into the guest via syn_data_abort_no_iss()
with fnv=1 and fsc=0x10, matching the syndrome that KVM uses in
kvm_inject_arm_sea(). The guest kernel's fault handler then reports
the error through its normal data abort path.
WHPX adds a whpx_inject_data_abort() helper and adjusts the
whpx_handle_mmio() return convention so the caller skips PC advancement
when an exception has been injected.
Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
---
target/arm/hvf/hvf.c | 46 ++++++++++++++++++++++++++--
target/arm/whpx/whpx-all.c | 61 +++++++++++++++++++++++++++++++++++++-
2 files changed, 103 insertions(+), 4 deletions(-)
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index b00a7e1a84..a087e920e9 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -34,6 +34,7 @@
#include "arm-powerctl.h"
#include "target/arm/cpu.h"
#include "target/arm/internals.h"
+#include "emulate/arm_emulate.h"
#include "target/arm/multiprocessing.h"
#include "target/arm/gtimer.h"
#include "target/arm/trace.h"
@@ -2441,10 +2442,49 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp)
assert(!s1ptw);
/*
- * TODO: ISV will be 0 for SIMD or SVE accesses.
- * Inject the exception into the guest.
+ * ISV=0: syndrome doesn't carry access size/register info.
+ * Fetch and emulate via target/arm/emulate/.
*/
- assert(isv);
+ if (!isv) {
+ ARMCPU *arm_cpu = ARM_CPU(cpu);
+ CPUARMState *env = &arm_cpu->env;
+ uint32_t insn;
+ ArmEmulResult r;
+
+ cpu_synchronize_state(cpu);
+
+ if (cpu_memory_rw_debug(cpu, env->pc,
+ (uint8_t *)&insn, 4, false) != 0) {
+ bool same_el = arm_current_el(env) == 1;
+ uint32_t esr = syn_data_abort_no_iss(same_el,
+ 1, 0, 0, 0, iswrite, 0x10);
+
+ error_report("HVF: cannot read insn at pc=0x%" PRIx64,
+ (uint64_t)env->pc);
+ env->exception.vaddress = excp->virtual_address;
+ hvf_raise_exception(cpu, EXCP_DATA_ABORT, esr, 1);
+ break;
+ }
+
+ r = arm_emul_insn(env, insn);
+ if (r == ARM_EMUL_UNHANDLED || r == ARM_EMUL_ERR_MEM) {
+ bool same_el = arm_current_el(env) == 1;
+ uint32_t esr = syn_data_abort_no_iss(same_el,
+ 1, 0, 0, 0, iswrite, 0x10);
+
+ error_report("HVF: ISV=0 %s insn 0x%08x at "
+ "pc=0x%" PRIx64 ", injecting data abort",
+ r == ARM_EMUL_UNHANDLED ? "unhandled"
+ : "memory error",
+ insn, (uint64_t)env->pc);
+ env->exception.vaddress = excp->virtual_address;
+ hvf_raise_exception(cpu, EXCP_DATA_ABORT, esr, 1);
+ break;
+ }
+
+ advance_pc = true;
+ break;
+ }
/*
* Emulate MMIO.
diff --git a/target/arm/whpx/whpx-all.c b/target/arm/whpx/whpx-all.c
index d106400686..c916df35a7 100644
--- a/target/arm/whpx/whpx-all.c
+++ b/target/arm/whpx/whpx-all.c
@@ -29,6 +29,7 @@
#include "syndrome.h"
#include "target/arm/cpregs.h"
#include "internals.h"
+#include "emulate/arm_emulate.h"
#include "system/whpx-internal.h"
#include "system/whpx-accel-ops.h"
@@ -350,6 +351,27 @@ static void whpx_set_gp_reg(CPUState *cpu, int rt, uint64_t val)
whpx_set_reg(cpu, reg, reg_val);
}
+/*
+ * Inject a synchronous external abort (data abort) into the guest.
+ * Used when ISV=0 instruction emulation fails. Matches the syndrome
+ * that KVM uses in kvm_inject_arm_sea().
+ */
+static void whpx_inject_data_abort(CPUState *cpu, bool iswrite)
+{
+ ARMCPU *arm_cpu = ARM_CPU(cpu);
+ CPUARMState *env = &arm_cpu->env;
+ bool same_el = arm_current_el(env) == 1;
+ uint32_t esr = syn_data_abort_no_iss(same_el, 1, 0, 0, 0, iswrite, 0x10);
+
+ cpu->exception_index = EXCP_DATA_ABORT;
+ env->exception.target_el = 1;
+ env->exception.syndrome = esr;
+
+ bql_lock();
+ arm_cpu_do_interrupt(cpu);
+ bql_unlock();
+}
+
static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
{
uint64_t syndrome = ctx->Syndrome;
@@ -365,7 +387,40 @@ static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
assert(syn_get_ec(syndrome) == EC_DATAABORT);
assert(!cm);
- assert(isv);
+
+ /*
+ * ISV=0: syndrome doesn't carry access size/register info.
+ * Fetch and decode the faulting instruction via the emulation library.
+ */
+ if (!isv) {
+ ARMCPU *arm_cpu = ARM_CPU(cpu);
+ CPUARMState *env = &arm_cpu->env;
+ uint32_t insn;
+ ArmEmulResult r;
+
+ cpu_synchronize_state(cpu);
+
+ if (cpu_memory_rw_debug(cpu, env->pc,
+ (uint8_t *)&insn, 4, false) != 0) {
+ error_report("WHPX: cannot read insn at pc=0x%" PRIx64,
+ (uint64_t)env->pc);
+ whpx_inject_data_abort(cpu, iswrite);
+ return 1;
+ }
+
+ r = arm_emul_insn(env, insn);
+ if (r == ARM_EMUL_UNHANDLED || r == ARM_EMUL_ERR_MEM) {
+ error_report("WHPX: ISV=0 %s insn 0x%08x at "
+ "pc=0x%" PRIx64 ", injecting data abort",
+ r == ARM_EMUL_UNHANDLED ? "unhandled"
+ : "memory error",
+ insn, (uint64_t)env->pc);
+ whpx_inject_data_abort(cpu, iswrite);
+ return 1;
+ }
+
+ return 0;
+ }
if (iswrite) {
val = whpx_get_gp_reg(cpu, srt);
@@ -451,6 +506,10 @@ int whpx_vcpu_run(CPUState *cpu)
}
ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
+ if (ret > 0) {
+ advance_pc = false;
+ ret = 0;
+ }
break;
case WHvRunVpExitReasonCanceled:
cpu->exception_index = EXCP_INTERRUPT;
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC PATCH v8 05/10] vmapple: apple-gfx: make it work on the latest macOS release
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
` (3 preceding siblings ...)
2026-09-16 10:00 ` [RFC PATCH v8 04/10] target/arm/hvf, whpx: wire ISV=0 emulation for data aborts Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-17 20:37 ` Akihiko Odaki
2026-09-16 10:00 ` [RFC PATCH v8 06/10] hw/vmapple: aes: convert MAX_LEN to a #define Mohamed Mediouni
` (4 subsequent siblings)
9 siblings, 1 reply; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini
Follow changes in memory management introduced on macOS 15.4.
The legacy memory management API has been removed for the IOSurface mapper on that macOS version.
Also enable process isolation for a sandboxed GPU process when on a new OS.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/display/apple-gfx-mmio.m | 58 ++++++++++++++++++------
hw/display/apple-gfx.h | 16 +++++++
hw/display/apple-gfx.m | 90 ++++++++++++++++++++++++++++++++-----
3 files changed, 138 insertions(+), 26 deletions(-)
diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m
index 58beaadd1f..19e2d38a1e 100644
--- a/hw/display/apple-gfx-mmio.m
+++ b/hw/display/apple-gfx-mmio.m
@@ -19,6 +19,7 @@
#include "hw/core/irq.h"
#include "apple-gfx.h"
#include "trace.h"
+#include "system/address-spaces.h"
#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
@@ -36,12 +37,19 @@ typedef bool(^IOSFCMapMemory)(uint64_t phys, uint64_t len, bool ro, void **va,
@interface PGDeviceDescriptor (IOSurfaceMapper)
@property (readwrite, nonatomic) bool usingIOSurfaceMapper;
+@property (readwrite, nonatomic) bool enableArgumentBuffers;
+@property (readwrite, nonatomic) bool enableProcessIsolation;
+@property (readwrite, nonatomic) bool enableProtectedContent;
+
+@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
@end
@interface PGIOSurfaceHostDeviceDescriptor : NSObject
-(PGIOSurfaceHostDeviceDescriptor *)init;
@property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory;
@property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory;
+@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
+@property (readwrite, nonatomic) unsigned long long mmioLength;
@property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt;
@end
@@ -183,19 +191,32 @@ static bool apple_gfx_mmio_unmap_surface_memory(void *ptr)
[PGIOSurfaceHostDeviceDescriptor new];
PGIOSurfaceHostDevice *iosfc_host_dev;
- iosfc_desc.mapMemory =
- ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
- *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
-
- trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
-
- return *va != NULL;
- };
-
- iosfc_desc.unmapMemory =
- ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
- return apple_gfx_mmio_unmap_surface_memory(va);
- };
+ /*
+ * The legacy memory management API is no longer present
+ * for the IOSurface mapper as of macOS 15.4.
+ */
+ if (@available(macOS 15.4, *)) {
+ PGMemoryMapDescriptor *memory_map_descriptor = [PGMemoryMapDescriptor new];
+ FlatView* fv = address_space_to_flatview(&address_space_memory);
+ flatview_for_each_range(fv, apple_gfx_register_memory_cb, memory_map_descriptor);
+ /* the device model defines this as a single-page MMIO region, hence 16KB */
+ iosfc_desc.mmioLength = 0x10000;
+ iosfc_desc.memoryMapDescriptor = memory_map_descriptor;
+ } else {
+ iosfc_desc.mapMemory =
+ ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
+ *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
+
+ trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
+
+ return *va != NULL;
+ };
+
+ iosfc_desc.unmapMemory =
+ ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
+ return apple_gfx_mmio_unmap_surface_memory(va);
+ };
+ }
iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) {
trace_apple_gfx_iosfc_raise_irq(vector);
@@ -223,13 +244,22 @@ static void apple_gfx_mmio_realize(DeviceState *dev, Error **errp)
};
desc.usingIOSurfaceMapper = true;
- s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
+ /*
+ * Process isolation needs PGMemoryMapDescriptor instead of
+ * the legacy memory management interface present in releases
+ * older than macOS 15.4.
+ */
+ if (@available(macOS 15.4, *)) {
+ desc.enableProcessIsolation = true;
+ }
if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) {
[s->pgiosfc release];
s->pgiosfc = nil;
}
+ s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
+
[desc release];
desc = nil;
}
diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h
index 3197bd853d..384aee0c5f 100644
--- a/hw/display/apple-gfx.h
+++ b/hw/display/apple-gfx.h
@@ -12,6 +12,7 @@
#include "system/memory.h"
#include "hw/core/qdev-properties.h"
#include "ui/surface.h"
+#include "objc/NSObject.h"
#define TYPE_APPLE_GFX_MMIO "apple-gfx-mmio"
#define TYPE_APPLE_GFX_PCI "apple-gfx-pci"
@@ -23,6 +24,17 @@
@protocol MTLTexture;
@protocol MTLCommandQueue;
+typedef struct PGGuestPhysicalRange_s
+{
+ uint64_t physicalAddress;
+ uint64_t physicalLength;
+ void *hostAddress;
+} PGGuestPhysicalRange_t;
+
+@interface PGMemoryMapDescriptor : NSObject
+-(void)addRange:(PGGuestPhysicalRange_t) range;
+@end
+
typedef QTAILQ_HEAD(, PGTask_s) PGTaskList;
typedef struct AppleGFXDisplayMode {
@@ -68,6 +80,10 @@ void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
uint64_t length, bool read_only,
MemoryRegion **mapping_in_region);
+bool apple_gfx_register_memory_cb(Int128 start, Int128 len,
+ const MemoryRegion *mr,
+ hwaddr offset_in_region, void *opaque);
+
extern const PropertyInfo qdev_prop_apple_gfx_display_mode;
#endif
diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m
index be0061b9db..4ef8f6dbf3 100644
--- a/hw/display/apple-gfx.m
+++ b/hw/display/apple-gfx.m
@@ -21,6 +21,7 @@
#include "system/address-spaces.h"
#include "system/dma.h"
#include "migration/blocker.h"
+#include "system/memory.h"
#include "ui/console.h"
#include "apple-gfx.h"
#include "trace.h"
@@ -79,6 +80,7 @@ static dispatch_queue_t get_background_queue(void)
GPtrArray *mapped_regions;
};
+API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len)
{
mach_vm_address_t task_mem;
@@ -102,6 +104,7 @@ static dispatch_queue_t get_background_queue(void)
return task;
}
+API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
{
GPtrArray *regions = task->mapped_regions;
@@ -151,6 +154,7 @@ static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
return host_ptr;
}
+API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
uint64_t virtual_offset,
PGPhysicalMemoryRange_t *ranges,
@@ -207,6 +211,7 @@ static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
return success;
}
+API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
static void apple_gfx_task_unmap_memory(AppleGFXState *s, PGTask_t *task,
uint64_t virtual_offset, uint64_t length)
{
@@ -596,6 +601,36 @@ void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name)
/* TODO: PVG framework supports serialising device state: integrate it! */
}
+@interface PGDeviceDescriptor (IOSurfaceMapper)
+@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
+@end
+
+bool apple_gfx_register_memory_cb(Int128 start, Int128 len,
+ const MemoryRegion *mr,
+ hwaddr offset_in_region, void *opaque) {
+ PGGuestPhysicalRange_t range;
+ PGMemoryMapDescriptor *memory_map_descriptor = opaque;
+ if (memory_access_is_direct(mr, true, MEMTXATTRS_UNSPECIFIED)) {
+ range.physicalAddress = start;
+ range.physicalLength = len;
+ range.hostAddress = memory_region_get_ram_ptr(mr);
+ [memory_map_descriptor addRange:range];
+ }
+ return false;
+}
+
+static void apple_gfx_register_memory(AppleGFXState *s,
+ PGDeviceDescriptor *desc)
+{
+ PGMemoryMapDescriptor* memoryMapDescriptor = [PGMemoryMapDescriptor new];
+
+ FlatView* fv = address_space_to_flatview(&address_space_memory);
+ flatview_for_each_range(fv, apple_gfx_register_memory_cb, memoryMapDescriptor);
+
+ desc.memoryMapDescriptor = memoryMapDescriptor;
+}
+
+API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s,
PGDeviceDescriptor *desc)
{
@@ -667,16 +702,25 @@ static void new_frame_handler_bh(void *opaque)
BQL_LOCK_GUARD();
set_mode(s, sizeInPixels.x, sizeInPixels.y);
};
- disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
- PGDisplayCoord_t hotspot) {
- AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
- job->s = s;
- job->glyph = glyph;
- job->hotspot = hotspot;
- [glyph retain];
- aio_bh_schedule_oneshot(qemu_get_aio_context(),
- set_cursor_glyph, job);
- };
+
+ if (@available(macOS 27.0, *)) {
+
+ } else {
+#ifndef __MAC_27_0
+ disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
+ PGDisplayCoord_t hotspot) {
+ AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
+ job->s = s;
+ job->glyph = glyph;
+ job->hotspot = hotspot;
+ [glyph retain];
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ set_cursor_glyph, job);
+ };
+#else
+ abort();
+#endif
+ }
disp_desc.cursorShowHandler = ^(BOOL show) {
trace_apple_gfx_cursor_show(show);
qatomic_set(&s->cursor_show, show);
@@ -763,11 +807,33 @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
desc.device = s->mtl;
- apple_gfx_register_task_mapping_handlers(s, desc);
+ /*
+ * The legacy memory management interface doesn't allow for
+ * vGPU sandboxing. As such, always use the new interface
+ * on macOS 15.4 onwards.
+ */
+ if (@available(macOS 15.4, *)) {
+ apple_gfx_register_memory(s, desc);
+ } else {
+#ifndef __MAC_27_0
+ apple_gfx_register_task_mapping_handlers(s, desc);
+#else
+ abort();
+#endif
+ }
s->cursor_show = true;
- s->pgdev = PGNewDeviceWithDescriptor(desc);
+ if (@available(macOS 15.2, *)) {
+ s->pgdev = PGCreateDeviceWithDescriptor(desc);
+ }
+ else {
+#ifndef __MAC_27_0
+ s->pgdev = PGNewDeviceWithDescriptor(desc);
+#else
+ abort();
+#endif
+ }
disp_desc = apple_gfx_prepare_display_descriptor(s);
/*
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC PATCH v8 06/10] hw/vmapple: aes: convert MAX_LEN to a #define
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
` (4 preceding siblings ...)
2026-09-16 10:00 ` [RFC PATCH v8 05/10] vmapple: apple-gfx: make it work on the latest macOS release Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-16 11:20 ` Peter Maydell
2026-09-16 10:00 ` [RFC PATCH v8 07/10] Revert "hw/arm: Do not build VMapple machine by default" Mohamed Mediouni
` (3 subsequent siblings)
9 siblings, 1 reply; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini
Working around this compiler warning:
../hw/vmapple/aes.c:257:14: warning: variable length array folded to constant array as an extension [-Wgnu-folding-constant]
257 | char hex[MAX_LEN * 2 + 1] = "";
| ^~~~~~~~~~~~~~~
1 warning generated.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/vmapple/aes.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/hw/vmapple/aes.c b/hw/vmapple/aes.c
index 553e688adb..9d062d5a7f 100644
--- a/hw/vmapple/aes.c
+++ b/hw/vmapple/aes.c
@@ -253,7 +253,7 @@ static bool cmd_iv(AESState *s)
static void dump_data(const char *desc, const void *p, size_t len)
{
- static const size_t MAX_LEN = 0x1000;
+#define MAX_LEN 0x1000
char hex[MAX_LEN * 2 + 1] = "";
if (len > MAX_LEN) {
@@ -262,6 +262,7 @@ static void dump_data(const char *desc, const void *p, size_t len)
qemu_hexdump_to_buffer(hex, sizeof(hex), p, len);
trace_aes_dump_data(desc, hex);
+#undef MAX_LEN
}
static bool cmd_data(AESState *s)
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC PATCH v8 07/10] Revert "hw/arm: Do not build VMapple machine by default"
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
` (5 preceding siblings ...)
2026-09-16 10:00 ` [RFC PATCH v8 06/10] hw/vmapple: aes: convert MAX_LEN to a #define Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 08/10] vmapple: add gicv2m Mohamed Mediouni
` (2 subsequent siblings)
9 siblings, 0 replies; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini
The vmapple virtual machine is now functional on newer macOS host releases.
Revert commit 49551752e860f5e403cdacac11ee1d218141fd3d.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
configs/devices/aarch64-softmmu/default.mak | 1 -
1 file changed, 1 deletion(-)
diff --git a/configs/devices/aarch64-softmmu/default.mak b/configs/devices/aarch64-softmmu/default.mak
index ad8028cfd4..93f4022ad6 100644
--- a/configs/devices/aarch64-softmmu/default.mak
+++ b/configs/devices/aarch64-softmmu/default.mak
@@ -9,4 +9,3 @@ include ../arm-softmmu/default.mak
# CONFIG_XLNX_VERSAL=n
# CONFIG_SBSA_REF=n
# CONFIG_NPCM8XX=n
-CONFIG_VMAPPLE=n
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC PATCH v8 08/10] vmapple: add gicv2m
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
` (6 preceding siblings ...)
2026-09-16 10:00 ` [RFC PATCH v8 07/10] Revert "hw/arm: Do not build VMapple machine by default" Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 09/10] vmapple, gicv2m: add macOS compat quirk Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 10/10] hw/vmapple: virtio-blk: allow FileVault off Mohamed Mediouni
9 siblings, 0 replies; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/vmapple/vmapple.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c
index 607181f517..2467c79ab3 100644
--- a/hw/vmapple/vmapple.c
+++ b/hw/vmapple/vmapple.c
@@ -82,6 +82,7 @@ enum {
VMAPPLE_MEM,
VMAPPLE_GIC_DIST,
VMAPPLE_GIC_REDIST,
+ VMAPPLE_GICV2M,
VMAPPLE_UART,
VMAPPLE_RTC,
VMAPPLE_PCIE,
@@ -103,6 +104,7 @@ static const MemMapEntry memmap[] = {
[VMAPPLE_GIC_DIST] = { 0x10000000, 0x00010000 },
[VMAPPLE_GIC_REDIST] = { 0x10010000, 0x00400000 },
+ [VMAPPLE_GICV2M] = { 0x1FFF0000, 0x00010000 },
[VMAPPLE_UART] = { 0x20010000, 0x00010000 },
[VMAPPLE_RTC] = { 0x20050000, 0x00001000 },
@@ -128,6 +130,7 @@ static const int irqmap[] = {
[VMAPPLE_APV_GFX] = 0x11,
[VMAPPLE_AES_1] = 0x12,
[VMAPPLE_PCIE] = 0x20,
+ [VMAPPLE_GICV2M] = 0x80
};
#define GPEX_NUM_IRQS 16
@@ -285,6 +288,26 @@ static void create_gic(VMAppleMachineState *vms, MemoryRegion *mem)
}
}
+#define NUM_GICV2M_SPIS 128
+
+static void create_gicv2m(VMAppleMachineState *vms)
+{
+ int i;
+ int irq = vms->irqmap[VMAPPLE_GICV2M];
+ DeviceState *dev;
+
+ dev = qdev_new("arm-gicv2m");
+ qdev_prop_set_uint32(dev, "base-spi", irq);
+ qdev_prop_set_uint32(dev, "num-spi", NUM_GICV2M_SPIS);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VMAPPLE_GICV2M].base);
+
+ for (i = 0; i < NUM_GICV2M_SPIS; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
+ qdev_get_gpio_in(vms->gic, irq + i));
+ }
+}
+
static void create_uart(const VMAppleMachineState *vms, int uart,
MemoryRegion *mem, Chardev *chr)
{
@@ -495,6 +518,7 @@ static void mach_vmapple_init(MachineState *machine)
machine->ram);
create_gic(vms, sysmem);
+ create_gicv2m(vms);
create_bdif(vms, sysmem);
create_pvpanic(vms, sysmem);
create_aes(vms, sysmem);
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC PATCH v8 09/10] vmapple, gicv2m: add macOS compat quirk
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
` (7 preceding siblings ...)
2026-09-16 10:00 ` [RFC PATCH v8 08/10] vmapple: add gicv2m Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-16 10:51 ` Peter Maydell
2026-09-16 10:00 ` [RFC PATCH v8 10/10] hw/vmapple: virtio-blk: allow FileVault off Mohamed Mediouni
9 siblings, 1 reply; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini
macOS expects bit 31 of MSI_TYPER to be set.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/intc/arm_gicv2m.c | 5 +++++
hw/vmapple/vmapple.c | 1 +
2 files changed, 6 insertions(+)
diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c
index b343e17ca5..10598d8d50 100644
--- a/hw/intc/arm_gicv2m.c
+++ b/hw/intc/arm_gicv2m.c
@@ -57,6 +57,7 @@ struct ARMGICv2mState {
uint32_t base_spi;
uint32_t num_spi;
+ uint32_t macos_compat;
};
static void gicv2m_set_irq(void *opaque, int irq)
@@ -81,6 +82,9 @@ static uint64_t gicv2m_read(void *opaque, hwaddr offset,
case V2M_MSI_TYPER:
val = (s->base_spi + 32) << 16;
val |= s->num_spi;
+ if (s->macos_compat) {
+ val |= BIT(31);
+ }
return val;
case V2M_MSI_IIDR:
/* We don't have any valid implementor so we leave that field as zero
@@ -173,6 +177,7 @@ static void gicv2m_init(Object *obj)
static const Property gicv2m_properties[] = {
DEFINE_PROP_UINT32("base-spi", ARMGICv2mState, base_spi, 0),
DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64),
+ DEFINE_PROP_UINT32("x-macos-compat", ARMGICv2mState, macos_compat, 0),
};
static void gicv2m_class_init(ObjectClass *klass, const void *data)
diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c
index 2467c79ab3..072a49ac4f 100644
--- a/hw/vmapple/vmapple.c
+++ b/hw/vmapple/vmapple.c
@@ -299,6 +299,7 @@ static void create_gicv2m(VMAppleMachineState *vms)
dev = qdev_new("arm-gicv2m");
qdev_prop_set_uint32(dev, "base-spi", irq);
qdev_prop_set_uint32(dev, "num-spi", NUM_GICV2M_SPIS);
+ qdev_prop_set_uint32(dev, "x-macos-compat", 1);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VMAPPLE_GICV2M].base);
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC PATCH v8 10/10] hw/vmapple: virtio-blk: allow FileVault off
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
` (8 preceding siblings ...)
2026-09-16 10:00 ` [RFC PATCH v8 09/10] vmapple, gicv2m: add macOS compat quirk Mohamed Mediouni
@ 2026-09-16 10:00 ` Mohamed Mediouni
2026-09-17 20:37 ` Akihiko Odaki
9 siblings, 1 reply; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:00 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Mohamed Mediouni, Alexander Graf, Paolo Bonzini
Otherwise, a newer macOS version with FileVault
disabled in the guest does not boot.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/vmapple/virtio-blk.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c
index 9de9aaae0b..906c2b2ec8 100644
--- a/hw/vmapple/virtio-blk.c
+++ b/hw/vmapple/virtio-blk.c
@@ -119,12 +119,17 @@ static const Property vmapple_virtio_blk_pci_properties[] = {
VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED),
};
+#define VMAPPLE_PCI_VNDR_SIZE 14
+/* Disable requirement for FileVault to be enabled on the guest */
+#define VMAPPLE_PCI_VNDR_LEGACY_FEATURES_DISABLE_ENCRYPTION (1 << 0)
+
static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
ERRP_GUARD();
VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
VirtIOBlkConf *conf = &dev->vdev.parent_obj.conf;
+ int offset;
if (dev->variant == VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED) {
error_setg(errp, "vmapple virtio block device variant unspecified");
@@ -162,6 +167,19 @@ static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **err
pci_config_set_vendor_id(vpci_dev->pci_dev.config, PCI_VENDOR_ID_APPLE);
pci_config_set_device_id(vpci_dev->pci_dev.config,
PCI_DEVICE_ID_APPLE_VIRTIO_BLK);
+
+ offset = pci_add_capability(&vpci_dev->pci_dev, PCI_CAP_ID_VNDR, 0,
+ VMAPPLE_PCI_VNDR_SIZE, errp);
+ if (offset < 0) {
+ return;
+ }
+
+ pci_set_byte(vpci_dev->pci_dev.config + offset + 2,
+ VMAPPLE_PCI_VNDR_SIZE);
+ pci_set_byte(vpci_dev->pci_dev.config + offset + 3, 9);
+ pci_set_word(vpci_dev->pci_dev.config + offset + 4, PCI_VENDOR_ID_APPLE);
+ pci_set_byte(vpci_dev->pci_dev.config + offset + 6,
+ VMAPPLE_PCI_VNDR_LEGACY_FEATURES_DISABLE_ENCRYPTION);
}
static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass,
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 09/10] vmapple, gicv2m: add macOS compat quirk
2026-09-16 10:00 ` [RFC PATCH v8 09/10] vmapple, gicv2m: add macOS compat quirk Mohamed Mediouni
@ 2026-09-16 10:51 ` Peter Maydell
2026-09-16 10:56 ` Mohamed Mediouni
0 siblings, 1 reply; 22+ messages in thread
From: Peter Maydell @ 2026-09-16 10:51 UTC (permalink / raw)
To: Mohamed Mediouni
Cc: qemu-devel, Michael S. Tsirkin, Roman Bolshakov, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Alexander Graf, Paolo Bonzini
On Wed, 16 Sept 2026 at 11:00, Mohamed Mediouni
<mohamed@unpredictable.fr> wrote:
>
> macOS expects bit 31 of MSI_TYPER to be set.
>
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
This is a macos bug -- the Arm Base System Architecture
spec which defines what the GICv2M registers are is clear
that MSI_TYPER bits [31:26] are RES0. I'm not keen on working
around guest bugs here, especially of the "random proprietary
guest has mysterious undocumented requirement" kind: insisting
on bit 31 being set would presumably break on a real hardware
GICv2M as well.
If there's some documented "Apple GICv2m" variant of this
hardware, then we could potentially model it, but I'd
rather do that as a "here's the spec we're modelling,
implement it" than a one-off point hack.
-- PMM
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 09/10] vmapple, gicv2m: add macOS compat quirk
2026-09-16 10:51 ` Peter Maydell
@ 2026-09-16 10:56 ` Mohamed Mediouni
0 siblings, 0 replies; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-16 10:56 UTC (permalink / raw)
To: Peter Maydell
Cc: qemu-devel, Michael S. Tsirkin, Roman Bolshakov, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Alexander Graf, Paolo Bonzini
> On 16. Sep 2026, at 12:51, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Wed, 16 Sept 2026 at 11:00, Mohamed Mediouni
> <mohamed@unpredictable.fr> wrote:
>>
>> macOS expects bit 31 of MSI_TYPER to be set.
>>
>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
>> ---
>
> This is a macos bug -- the Arm Base System Architecture
> spec which defines what the GICv2M registers are is clear
> that MSI_TYPER bits [31:26] are RES0. I'm not keen on working
> around guest bugs here, especially of the "random proprietary
> guest has mysterious undocumented requirement" kind: insisting
> on bit 31 being set would presumably break on a real hardware
> GICv2M as well.
>
> If there's some documented "Apple GICv2m" variant of this
> hardware, then we could potentially model it, but I'd
> rather do that as a "here's the spec we're modelling,
> implement it" than a one-off point hack.
>
> — PMM
Hi,
vmapple as a whole is a reverse-engineered target without a public specification of any kind…
other than this patch the system is happy, but without it, there’s a kernel panic really
early on during boot…
There are no specific Apple GICv2m registers.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 06/10] hw/vmapple: aes: convert MAX_LEN to a #define
2026-09-16 10:00 ` [RFC PATCH v8 06/10] hw/vmapple: aes: convert MAX_LEN to a #define Mohamed Mediouni
@ 2026-09-16 11:20 ` Peter Maydell
2026-09-16 11:34 ` Daniel P. Berrangé
0 siblings, 1 reply; 22+ messages in thread
From: Peter Maydell @ 2026-09-16 11:20 UTC (permalink / raw)
To: Mohamed Mediouni
Cc: qemu-devel, Michael S. Tsirkin, Roman Bolshakov, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Akihiko Odaki, Alexander Graf, Paolo Bonzini
On Wed, 16 Sept 2026 at 11:00, Mohamed Mediouni
<mohamed@unpredictable.fr> wrote:
>
> Working around this compiler warning:
>
> ../hw/vmapple/aes.c:257:14: warning: variable length array folded to constant array as an extension [-Wgnu-folding-constant]
> 257 | char hex[MAX_LEN * 2 + 1] = "";
> | ^~~~~~~~~~~~~~~
> 1 warning generated.
I'm surprised this wasn't already caught by our '-Wvla' setting:
we don't want to have variable length arrays at all.
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> hw/vmapple/aes.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/hw/vmapple/aes.c b/hw/vmapple/aes.c
> index 553e688adb..9d062d5a7f 100644
> --- a/hw/vmapple/aes.c
> +++ b/hw/vmapple/aes.c
> @@ -253,7 +253,7 @@ static bool cmd_iv(AESState *s)
>
> static void dump_data(const char *desc, const void *p, size_t len)
> {
> - static const size_t MAX_LEN = 0x1000;
> +#define MAX_LEN 0x1000
> char hex[MAX_LEN * 2 + 1] = "";
>
> if (len > MAX_LEN) {
> @@ -262,6 +262,7 @@ static void dump_data(const char *desc, const void *p, size_t len)
>
> qemu_hexdump_to_buffer(hex, sizeof(hex), p, len);
> trace_aes_dump_data(desc, hex);
> +#undef MAX_LEN
> }
MAX_LEN is an awkwardly generic name. I would define it outside
the function, something like
/* Maximum amount of AES data to dump in trace events */
#define MAX_AES_DUMP_DATA_LEN 0x1000
Then you don't need to #undef it as there's no risk of a clash.
(Really this function ought not to do the binary-to-hex
conversion of all the data unless the trace event is actually
enabled, which docs/devel/tracing.rst says you can do, and
it would maybe be nice to emit at least some truncated data
in the "more than 1K of data" case, but let's stick to fixing
the compile issues for the moment.)
thanks
-- PMM
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 06/10] hw/vmapple: aes: convert MAX_LEN to a #define
2026-09-16 11:20 ` Peter Maydell
@ 2026-09-16 11:34 ` Daniel P. Berrangé
0 siblings, 0 replies; 22+ messages in thread
From: Daniel P. Berrangé @ 2026-09-16 11:34 UTC (permalink / raw)
To: Peter Maydell
Cc: Mohamed Mediouni, qemu-devel, Michael S. Tsirkin, Roman Bolshakov,
Peter Xu, Philippe Mathieu-Daudé, qemu-arm,
Phil Dennis-Jordan, Akihiko Odaki, Alexander Graf, Paolo Bonzini
On Wed, Sep 16, 2026 at 12:20:33PM +0100, Peter Maydell wrote:
> On Wed, 16 Sept 2026 at 11:00, Mohamed Mediouni
> <mohamed@unpredictable.fr> wrote:
> >
> > Working around this compiler warning:
> >
> > ../hw/vmapple/aes.c:257:14: warning: variable length array folded to constant array as an extension [-Wgnu-folding-constant]
> > 257 | char hex[MAX_LEN * 2 + 1] = "";
> > | ^~~~~~~~~~~~~~~
> > 1 warning generated.
>
> I'm surprised this wasn't already caught by our '-Wvla' setting:
> we don't want to have variable length arrays at all.
The -Wgnu-folding-constant warning seems to be saying that
GCC will always evaluate this expresson at compile time
eliminating the variable length array. Presumably that
expansion happens before -Wvla gets to see the code.
IOW, this warning is saying that we're not portable to compilers
other than GCC/CLang, which is just fine by QEMU's intent.
If we don't want to change the code, it looks valid to add
-Wno-gnu-folding-constant, since we dont care about
portability to other compilers.
>
> > Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> > ---
> > hw/vmapple/aes.c | 3 ++-
> > 1 file changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/vmapple/aes.c b/hw/vmapple/aes.c
> > index 553e688adb..9d062d5a7f 100644
> > --- a/hw/vmapple/aes.c
> > +++ b/hw/vmapple/aes.c
> > @@ -253,7 +253,7 @@ static bool cmd_iv(AESState *s)
> >
> > static void dump_data(const char *desc, const void *p, size_t len)
> > {
> > - static const size_t MAX_LEN = 0x1000;
> > +#define MAX_LEN 0x1000
> > char hex[MAX_LEN * 2 + 1] = "";
> >
> > if (len > MAX_LEN) {
> > @@ -262,6 +262,7 @@ static void dump_data(const char *desc, const void *p, size_t len)
> >
> > qemu_hexdump_to_buffer(hex, sizeof(hex), p, len);
> > trace_aes_dump_data(desc, hex);
> > +#undef MAX_LEN
> > }
>
> MAX_LEN is an awkwardly generic name. I would define it outside
> the function, something like
>
> /* Maximum amount of AES data to dump in trace events */
> #define MAX_AES_DUMP_DATA_LEN 0x1000
>
> Then you don't need to #undef it as there's no risk of a clash.
>
> (Really this function ought not to do the binary-to-hex
> conversion of all the data unless the trace event is actually
> enabled, which docs/devel/tracing.rst says you can do, and
> it would maybe be nice to emit at least some truncated data
> in the "more than 1K of data" case, but let's stick to fixing
> the compile issues for the moment.)
>
> thanks
> -- PMM
>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 01/10] target/arm/emulate: add ISV=0 emulation library with load/store immediate
2026-09-16 10:00 ` [RFC PATCH v8 01/10] target/arm/emulate: add ISV=0 emulation library with load/store immediate Mohamed Mediouni
@ 2026-09-17 20:32 ` Akihiko Odaki
0 siblings, 0 replies; 22+ messages in thread
From: Akihiko Odaki @ 2026-09-17 20:32 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Alexander Graf, Paolo Bonzini, Lucas Amaral
On 2026/09/16 19:00, Mohamed Mediouni wrote:
> From: Lucas Amaral <lucaaamaral@gmail.com>
>
> Add a shared emulation library for AArch64 load/store instructions that
> cause ISV=0 data aborts under hardware virtualization (HVF, WHPX).
>
> When the Instruction Syndrome Valid bit is clear, the hypervisor cannot
> determine the faulting instruction's target register or access size from
> the syndrome alone. This library fetches and decodes the instruction
> using a decodetree-generated decoder, then emulates it by accessing the
> vCPU's register file (CPUARMState) and memory (cpu_memory_rw_debug)
> directly.
Interesting.
>
> This patch establishes the framework and adds load/store single with
> immediate addressing — the most common ISV=0 trigger. Subsequent
> patches add register-offset, pair, exclusive, and atomic instructions.
>
> Instruction coverage:
> - STR/LDR (GPR): unscaled, post-indexed, unprivileged, pre-indexed,
> unsigned offset — all sizes (8/16/32/64-bit), sign/zero extension
> - STR/LDR (SIMD/FP): same addressing modes, 8-128 bit elements
> - PRFM: prefetch treated as NOP
> - DC cache maintenance (SYS CRn=C7): NOP on MMIO
>
> This library uses its own a64-ldst.decode rather than sharing
> target/arm/tcg/a64.decode. TCG's trans_* functions are a compiler:
> they emit IR ops into a translation block for later execution. This
> library's trans_* functions are an interpreter: they execute directly
> against the vCPU register file and memory. The decodetree-generated
> dispatcher calls trans_* by name, so both cannot coexist in the same
> translation unit. Decode patterns are kept consistent with TCG's
> where possible.
>
> Decodetree differences from TCG:
> - &ldst_imm adds a 'u' flag to distinguish 9-bit signed vs 12-bit
> unsigned immediate forms. TCG uses %uimm_scaled to pre-scale
> the unsigned immediate at decode time; here imm:12 is extracted
> raw and the handler scales it.
>
> Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
> ---
> target/arm/emulate/a64-ldst.decode | 129 ++++++++++++++++
> target/arm/emulate/arm_emulate.c | 237 +++++++++++++++++++++++++++++
> target/arm/emulate/arm_emulate.h | 30 ++++
> target/arm/emulate/meson.build | 8 +
> target/arm/meson.build | 1 +
> 5 files changed, 405 insertions(+)
> create mode 100644 target/arm/emulate/a64-ldst.decode
> create mode 100644 target/arm/emulate/arm_emulate.c
> create mode 100644 target/arm/emulate/arm_emulate.h
> create mode 100644 target/arm/emulate/meson.build
>
> diff --git a/target/arm/emulate/a64-ldst.decode b/target/arm/emulate/a64-ldst.decode
> new file mode 100644
> index 0000000000..c887dcba7d
> --- /dev/null
> +++ b/target/arm/emulate/a64-ldst.decode
> @@ -0,0 +1,129 @@
> +# AArch64 load/store instruction patterns for ISV=0 emulation
> +#
> +# Copyright (c) 2026 Lucas Amaral <lucaaamaral@gmail.com>
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +### Argument sets
> +
> +# Load/store immediate (unscaled, pre/post-index, unprivileged, unsigned offset)
> +# 'u' flag: 0 = 9-bit signed immediate (byte offset), 1 = 12-bit unsigned (needs << sz)
> +&ldst_imm rt rn imm sz sign w p unpriv ext u
> +
> +### Format templates
> +
> +# Load/store immediate (9-bit signed)
> +@ldst_imm .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm u=0 unpriv=0 p=0 w=0
> +@ldst_imm_pre .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm u=0 unpriv=0 p=0 w=1
> +@ldst_imm_post .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm u=0 unpriv=0 p=1 w=1
> +@ldst_imm_user .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm u=0 unpriv=1 p=0 w=0
> +
> +# Load/store unsigned offset (12-bit, handler scales by << sz)
> +@ldst_uimm .. ... . .. .. imm:12 rn:5 rt:5 &ldst_imm u=1 unpriv=0 p=0 w=0
> +
> +### Load/store register — unscaled immediate (LDUR/STUR)
> +
> +# GPR
> +STR_i sz:2 111 0 00 00 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0
> +LDR_i 00 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=0
> +LDR_i 01 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=1
> +LDR_i 10 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=2
> +LDR_i 11 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=3
> +LDR_i 00 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=0
> +LDR_i 01 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=1
> +LDR_i 10 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=2
> +LDR_i 00 111 0 00 11 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=1 sz=0
> +LDR_i 01 111 0 00 11 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=1 sz=1
> +
> +# SIMD/FP
> +STR_v_i sz:2 111 1 00 00 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0
> +STR_v_i 00 111 1 00 10 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=4
> +LDR_v_i sz:2 111 1 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0
> +LDR_v_i 00 111 1 00 11 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=4
> +
> +### Load/store register — post-indexed
> +
> +# GPR
> +STR_i sz:2 111 0 00 00 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0
> +LDR_i 00 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=0
> +LDR_i 01 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=1
> +LDR_i 10 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=2
> +LDR_i 11 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=3
> +LDR_i 00 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=0
> +LDR_i 01 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=1
> +LDR_i 10 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=2
> +LDR_i 00 111 0 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=1 sz=0
> +LDR_i 01 111 0 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=1 sz=1
> +
> +# SIMD/FP
> +STR_v_i sz:2 111 1 00 00 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0
> +STR_v_i 00 111 1 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=4
> +LDR_v_i sz:2 111 1 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0
> +LDR_v_i 00 111 1 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=4
> +
> +### Load/store register — unprivileged
> +
> +# GPR only (no SIMD/FP unprivileged forms)
> +STR_i sz:2 111 0 00 00 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=0
> +LDR_i 00 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=0
> +LDR_i 01 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=1
> +LDR_i 10 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=2
> +LDR_i 11 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=0 sz=3
> +LDR_i 00 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=0
> +LDR_i 01 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=1
> +LDR_i 10 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=2
> +LDR_i 00 111 0 00 11 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=1 sz=0
> +LDR_i 01 111 0 00 11 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=1 sz=1
> +
> +### Load/store register — pre-indexed
> +
> +# GPR
> +STR_i sz:2 111 0 00 00 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0
> +LDR_i 00 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=0
> +LDR_i 01 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=1
> +LDR_i 10 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=2
> +LDR_i 11 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=3
> +LDR_i 00 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=0
> +LDR_i 01 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=1
> +LDR_i 10 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=2
> +LDR_i 00 111 0 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=1 sz=0
> +LDR_i 01 111 0 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=1 sz=1
> +
> +# SIMD/FP
> +STR_v_i sz:2 111 1 00 00 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0
> +STR_v_i 00 111 1 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4
> +LDR_v_i sz:2 111 1 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0
> +LDR_v_i 00 111 1 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4
> +
> +### PRFM — unscaled immediate: prefetch is a NOP
> +
> +NOP 11 111 0 00 10 0 --------- 00 ----- -----
> +
> +### Load/store register — unsigned offset
> +
> +# GPR
> +STR_i sz:2 111 0 01 00 ............ ..... ..... @ldst_uimm sign=0 ext=0
> +LDR_i 00 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=0
> +LDR_i 01 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=1
> +LDR_i 10 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=2
> +LDR_i 11 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=3
> +LDR_i 00 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=0
> +LDR_i 01 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=1
> +LDR_i 10 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=2
> +LDR_i 00 111 0 01 11 ............ ..... ..... @ldst_uimm sign=1 ext=1 sz=0
> +LDR_i 01 111 0 01 11 ............ ..... ..... @ldst_uimm sign=1 ext=1 sz=1
> +
> +# PRFM — unsigned offset
> +NOP 11 111 0 01 10 ------------ ----- -----
> +
> +# SIMD/FP
> +STR_v_i sz:2 111 1 01 00 ............ ..... ..... @ldst_uimm sign=0 ext=0
> +STR_v_i 00 111 1 01 10 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4
> +LDR_v_i sz:2 111 1 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0
> +LDR_v_i 00 111 1 01 11 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4
> +
> +### System instructions — DC cache maintenance
> +
> +# SYS with CRn=C7 covers all data cache operations (DC CIVAC, CVAC, etc.).
> +# On MMIO regions, cache maintenance is a harmless no-op.
> +NOP 1101 0101 0000 1 --- 0111 ---- --- -----
The cache-maintenance wildcard silently discards DC ZVA, the Data Cache
Zero by Address instruction. It matches e.g., dc zva,x2; trans_NOP()
then reports success without memory access. A stage-2 fault while
zeroing MMIO mapped as Normal memory reaches this path with CM=0, and
callers advance PC without performing the zeros. At minimum this must
remain unhandled unless implemented correctly. ARM ARM DDI0487L.a
C5.3.32, printed pp C5-996–997, explicitly specifies CM=0, byte stores,
and an alignment fault for Device memory.
> diff --git a/target/arm/emulate/arm_emulate.c b/target/arm/emulate/arm_emulate.c
> new file mode 100644
> index 0000000000..02fefc30bf
> --- /dev/null
> +++ b/target/arm/emulate/arm_emulate.c
> @@ -0,0 +1,237 @@
> +/*
> + * AArch64 instruction emulation for ISV=0 data aborts
> + *
> + * Copyright (c) 2026 Lucas Amaral <lucaaamaral@gmail.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "arm_emulate.h"
> +#include "target/arm/cpu.h"
> +#include "exec/cpu-common.h"
> +#include "exec/target_page.h"
> +
> +/* TODO: assumes LE guest data layout (sufficient for HVF/WHPX, both LE-only) */
> +
> +/* Named "DisasContext" as required by the decodetree code generator */
> +typedef struct {
> + CPUState *cpu;
> + CPUARMState *env;
> + ArmEmulResult result;
> +} DisasContext;
> +
> +#include "decode-a64-ldst.c.inc"
> +
> +/* GPR data access (Rt, Rs, Rt2) -- register 31 = XZR */
> +
> +static uint64_t gpr_read(DisasContext *ctx, int reg)
> +{
> + if (reg == 31) {
> + return 0; /* XZR */
> + }
> + return ctx->env->xregs[reg];
> +}
> +
> +static void gpr_write(DisasContext *ctx, int reg, uint64_t val)
> +{
> + if (reg == 31) {
> + return; /* XZR -- discard */
> + }
> + ctx->env->xregs[reg] = val;
> + ctx->cpu->vcpu_dirty = true;
> +}
> +
> +/* Base register access (Rn) -- register 31 = SP */
> +
> +static uint64_t base_read(DisasContext *ctx, int rn)
> +{
> + return ctx->env->xregs[rn];
> +}
> +
> +static void base_write(DisasContext *ctx, int rn, uint64_t val)
> +{
> + ctx->env->xregs[rn] = val;
> + ctx->cpu->vcpu_dirty = true;
> +}
> +
> +/* SIMD/FP register access */
> +
> +static void fpreg_read(DisasContext *ctx, int reg, void *buf, int size)
> +{
> + memcpy(buf, &ctx->env->vfp.zregs[reg], size);
> +}
> +
> +static void fpreg_write(DisasContext *ctx, int reg, const void *buf, int size)
> +{
> + memset(&ctx->env->vfp.zregs[reg], 0, sizeof(ctx->env->vfp.zregs[reg]));
> + memcpy(&ctx->env->vfp.zregs[reg], buf, size);
> + ctx->cpu->vcpu_dirty = true;
> +}
> +
> +/* Memory access wrappers */
> +
> +static int mem_read(DisasContext *ctx, uint64_t va, void *buf, int size)
> +{
> + if (((va & ~TARGET_PAGE_MASK) + size) > TARGET_PAGE_SIZE) {
This and the corresponding write guard rejects valid accesses crossing a
4 KiB boundary.
With patch 3, ldp x0,x1,[x2] at x2=0xff8 demonstrates this for an
aligned pair—even wholly inside a guest’s 16 KiB page.
cpu_memory_rw_debug() already processes accesses in translated chunks.
ARM ARM DDI0487L.a B2.8.2 and B2.8.2.1.1 explicitly define pair
alignment using the individual element size.
Regards,
Akihiko Odaki
> + ctx->result = ARM_EMUL_ERR_MEM;
> + return -1;
> + }
> + int ret = cpu_memory_rw_debug(ctx->cpu, va, buf, size, false);
> + if (ret != 0) {
> + ctx->result = ARM_EMUL_ERR_MEM;
> + }
> + return ret;
> +}
> +
> +static int mem_write(DisasContext *ctx, uint64_t va, const void *buf, int size)
> +{
> + if (((va & ~TARGET_PAGE_MASK) + size) > TARGET_PAGE_SIZE) {
> + ctx->result = ARM_EMUL_ERR_MEM;
> + return -1;
> + }
> + int ret = cpu_memory_rw_debug(ctx->cpu, va, (void *)buf, size, true);
> + if (ret != 0) {
> + ctx->result = ARM_EMUL_ERR_MEM;
> + }
> + return ret;
> +}
> +
> +/* Sign/zero extension helpers */
> +
> +static uint64_t sign_extend(uint64_t val, int from_bits)
> +{
> + int shift = 64 - from_bits;
> + return (int64_t)(val << shift) >> shift;
> +}
> +
> +/* Apply sign/zero extension */
> +static uint64_t load_extend(uint64_t val, int sz, int sign, int ext)
> +{
> + int data_bits = 8 << sz;
> +
> + if (sign) {
> + val = sign_extend(val, data_bits);
> + if (ext) {
> + /* Sign-extend to 32 bits (W register) */
> + val &= 0xFFFFFFFF;
> + }
> + } else if (ext) {
> + /* Zero-extend to 32 bits (W register) */
> + val &= 0xFFFFFFFF;
> + }
> + return val;
> +}
> +
> +/* Load/store single -- immediate (GPR) (DDI 0487 C3.3.8 -- C3.3.13) */
> +
> +static bool trans_STR_i(DisasContext *ctx, arg_ldst_imm *a)
> +{
> + int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
> + int64_t offset = a->u ? ((int64_t)(uint64_t)a->imm << a->sz)
> + : (int64_t)a->imm;
> + uint64_t base = base_read(ctx, a->rn);
> + uint64_t va = a->p ? base : base + offset;
> +
> + uint64_t val = gpr_read(ctx, a->rt);
> + if (mem_write(ctx, va, &val, esize) != 0) {
> + return true;
> + }
> +
> + if (a->w) {
> + base_write(ctx, a->rn, base + offset);
> + }
> + return true;
> +}
> +
> +static bool trans_LDR_i(DisasContext *ctx, arg_ldst_imm *a)
> +{
> + int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
> + int64_t offset = a->u ? ((int64_t)(uint64_t)a->imm << a->sz)
> + : (int64_t)a->imm;
> + uint64_t base = base_read(ctx, a->rn);
> + uint64_t va = a->p ? base : base + offset;
> + uint64_t val = 0;
> +
> + if (mem_read(ctx, va, &val, esize) != 0) {
> + return true;
> + }
> +
> + val = load_extend(val, a->sz, a->sign, a->ext);
> + gpr_write(ctx, a->rt, val);
> +
> + if (a->w) {
> + base_write(ctx, a->rn, base + offset);
> + }
> + return true;
> +}
> +
> +/*
> + * Load/store single -- immediate (SIMD/FP)
> + * STR_v_i / LDR_v_i (DDI 0487 C3.3.10)
> + */
> +
> +static bool trans_STR_v_i(DisasContext *ctx, arg_ldst_imm *a)
> +{
> + int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
> + int64_t offset = a->u ? ((int64_t)(uint64_t)a->imm << a->sz)
> + : (int64_t)a->imm;
> + uint64_t base = base_read(ctx, a->rn);
> + uint64_t va = a->p ? base : base + offset;
> + uint8_t buf[16];
> +
> + fpreg_read(ctx, a->rt, buf, esize);
> + if (mem_write(ctx, va, buf, esize) != 0) {
> + return true;
> + }
> +
> + if (a->w) {
> + base_write(ctx, a->rn, base + offset);
> + }
> + return true;
> +}
> +
> +static bool trans_LDR_v_i(DisasContext *ctx, arg_ldst_imm *a)
> +{
> + int esize = (a->sz <= 3) ? (1 << a->sz) : 16;
> + int64_t offset = a->u ? ((int64_t)(uint64_t)a->imm << a->sz)
> + : (int64_t)a->imm;
> + uint64_t base = base_read(ctx, a->rn);
> + uint64_t va = a->p ? base : base + offset;
> + uint8_t buf[16];
> +
> + if (mem_read(ctx, va, buf, esize) != 0) {
> + return true;
> + }
> +
> + fpreg_write(ctx, a->rt, buf, esize);
> +
> + if (a->w) {
> + base_write(ctx, a->rn, base + offset);
> + }
> + return true;
> +}
> +
> +/* PRFM, DC cache maintenance -- treated as NOP */
> +static bool trans_NOP(DisasContext *ctx, arg_NOP *a)
> +{
> + (void)ctx;
> + (void)a;
> + return true;
> +}
> +
> +/* Entry point */
> +
> +ArmEmulResult arm_emul_insn(CPUArchState *env, uint32_t insn)
> +{
> + DisasContext ctx = {
> + .cpu = env_cpu(env),
> + .env = env,
> + .result = ARM_EMUL_OK,
> + };
> +
> + if (!decode_a64_ldst(&ctx, insn)) {
> + return ARM_EMUL_UNHANDLED;
> + }
> +
> + return ctx.result;
> +}
> diff --git a/target/arm/emulate/arm_emulate.h b/target/arm/emulate/arm_emulate.h
> new file mode 100644
> index 0000000000..7fe298394f
> --- /dev/null
> +++ b/target/arm/emulate/arm_emulate.h
> @@ -0,0 +1,30 @@
> +/*
> + * AArch64 instruction emulation library
> + *
> + * Copyright (c) 2026 Lucas Amaral <lucaaamaral@gmail.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef ARM_EMULATE_H
> +#define ARM_EMULATE_H
> +
> +#include "qemu/osdep.h"
> +
> +/**
> + * ArmEmulResult - return status from arm_emul_insn()
> + */
> +typedef enum {
> + ARM_EMUL_OK, /* Instruction emulated successfully */
> + ARM_EMUL_UNHANDLED, /* Instruction not recognized by decoder */
> + ARM_EMUL_ERR_MEM, /* Memory access failed */
> +} ArmEmulResult;
> +
> +/**
> + * arm_emul_insn - decode and emulate one AArch64 instruction
> + *
> + * Caller must synchronize CPU state and fetch @insn before calling.
> + */
> +ArmEmulResult arm_emul_insn(CPUArchState *env, uint32_t insn);
> +
> +#endif /* ARM_EMULATE_H */
> diff --git a/target/arm/emulate/meson.build b/target/arm/emulate/meson.build
> new file mode 100644
> index 0000000000..e5455bd206
> --- /dev/null
> +++ b/target/arm/emulate/meson.build
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +gen_a64_ldst = decodetree.process('a64-ldst.decode',
> + extra_args: ['--static-decode=decode_a64_ldst'])
> +
> +arm_common_system_ss.add(when: 'TARGET_AARCH64', if_true: [
> + gen_a64_ldst, files('arm_emulate.c')
> +])
> diff --git a/target/arm/meson.build b/target/arm/meson.build
> index 0369f96b4c..1963c0ec21 100644
> --- a/target/arm/meson.build
> +++ b/target/arm/meson.build
> @@ -61,6 +61,7 @@ arm_common_system_ss.add(files(
> 'ptw.c',
> ))
>
> +subdir('emulate')
> subdir('hvf')
> subdir('whpx')
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 03/10] target/arm/emulate: add load/store pair
2026-09-16 10:00 ` [RFC PATCH v8 03/10] target/arm/emulate: add load/store pair Mohamed Mediouni
@ 2026-09-17 20:33 ` Akihiko Odaki
0 siblings, 0 replies; 22+ messages in thread
From: Akihiko Odaki @ 2026-09-17 20:33 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Alexander Graf, Paolo Bonzini, Lucas Amaral
On 2026/09/16 19:00, Mohamed Mediouni wrote:
> From: Lucas Amaral <lucaaamaral@gmail.com>
>
> Add emulation for load/store pair instructions (DDI 0487 C3.3.14 --
> C3.3.16). All addressing modes are covered: non-temporal (STNP/LDNP),
> post-indexed, signed offset, and pre-indexed.
>
> Instruction coverage:
> - STP/LDP (GPR): 32/64-bit pairs, all addressing modes
> - STP/LDP (SIMD/FP): 32/64/128-bit pairs, all addressing modes
> - LDPSW: sign-extending 32-bit pair load
> - STGP: store allocation tag pair (tag operation is NOP for MMIO)
>
> Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
> ---
> target/arm/emulate/a64-ldst.decode | 68 ++++++++++++++++++
> target/arm/emulate/arm_emulate.c | 111 +++++++++++++++++++++++++++++
> 2 files changed, 179 insertions(+)
>
> diff --git a/target/arm/emulate/a64-ldst.decode b/target/arm/emulate/a64-ldst.decode
> index af6babe14a..f3de3f86cb 100644
> --- a/target/arm/emulate/a64-ldst.decode
> +++ b/target/arm/emulate/a64-ldst.decode
> @@ -10,6 +10,9 @@
> # 'u' flag: 0 = 9-bit signed immediate (byte offset), 1 = 12-bit unsigned (needs << sz)
> &ldst_imm rt rn imm sz sign w p unpriv ext u
>
> +# Load/store pair (GPR and SIMD/FP)
> +&ldstpair rt2 rt rn imm sz sign w p
> +
> # Load/store register offset
> &ldst rm rn rt sign ext sz opt s
>
> @@ -24,6 +27,9 @@
> # Load/store unsigned offset (12-bit, handler scales by << sz)
> @ldst_uimm .. ... . .. .. imm:12 rn:5 rt:5 &ldst_imm u=1 unpriv=0 p=0 w=0
>
> +# Load/store pair: imm7 is signed, scaled by element size in handler
> +@ldstpair .. ... . ... . imm:s7 rt2:5 rn:5 rt:5 &ldstpair
> +
> # Load/store register offset
> @ldst .. ... . .. .. . rm:5 opt:3 s:1 .. rn:5 rt:5 &ldst
>
> @@ -128,6 +134,68 @@ STR_v_i 00 111 1 01 10 ............ ..... ..... @ldst_uimm sign=
> LDR_v_i sz:2 111 1 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0
> LDR_v_i 00 111 1 01 11 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4
>
> +### Load/store pair — non-temporal (STNP/LDNP)
> +
> +# STNP/LDNP: offset only, no writeback. Non-temporal hint ignored.
> +STP 00 101 0 000 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
> +LDP 00 101 0 000 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
> +STP 10 101 0 000 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
> +LDP 10 101 0 000 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
> +STP_v 00 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
> +LDP_v 00 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
> +STP_v 01 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
> +LDP_v 01 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
> +STP_v 10 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0
> +LDP_v 10 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0
> +
> +### Load/store pair — post-indexed
> +
> +STP 00 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1
> +LDP 00 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1
> +LDP 01 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=1 w=1
> +STP 10 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
> +LDP 10 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
> +STP_v 00 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1
> +LDP_v 00 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1
> +STP_v 01 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
> +LDP_v 01 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
> +STP_v 10 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=1 w=1
> +LDP_v 10 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=1 w=1
> +
> +### Load/store pair — signed offset
> +
> +STP 00 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
> +LDP 00 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
> +LDP 01 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=0 w=0
> +STP 10 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
> +LDP 10 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
> +STP_v 00 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
> +LDP_v 00 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0
> +STP_v 01 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
> +LDP_v 01 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
> +STP_v 10 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0
> +LDP_v 10 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0
> +
> +### Load/store pair — pre-indexed
> +
> +STP 00 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1
> +LDP 00 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1
> +LDP 01 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=0 w=1
> +STP 10 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
> +LDP 10 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
> +STP_v 00 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1
> +LDP_v 00 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1
> +STP_v 01 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
> +LDP_v 01 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
> +STP_v 10 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=1
> +LDP_v 10 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=1
> +
> +### Load/store pair — STGP (store allocation tag + pair)
> +
> +STGP 01 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1
> +STGP 01 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0
> +STGP 01 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1
> +
> ### Load/store register — register offset
>
> # GPR
> diff --git a/target/arm/emulate/arm_emulate.c b/target/arm/emulate/arm_emulate.c
> index bf09e2a67b..6c63a0d0d1 100644
> --- a/target/arm/emulate/arm_emulate.c
> +++ b/target/arm/emulate/arm_emulate.c
> @@ -122,6 +122,117 @@ static uint64_t load_extend(uint64_t val, int sz, int sign, int ext)
> return val;
> }
>
> +/*
> + * Load/store pair: STP, LDP, STNP, LDNP, STGP, LDPSW
> + * (DDI 0487 C3.3.14 -- C3.3.16)
> + */
> +
> +static bool trans_STP(DisasContext *ctx, arg_ldstpair *a)
> +{
> + int esize = 1 << a->sz; /* 4 or 8 bytes */
> + int64_t offset = (int64_t)a->imm << a->sz;
> + uint64_t base = base_read(ctx, a->rn);
> + uint64_t va = a->p ? base : base + offset; /* post-index: unmodified base */
> + uint8_t buf[16]; /* max 2 x 8 bytes */
> +
> + uint64_t v1 = gpr_read(ctx, a->rt);
> + uint64_t v2 = gpr_read(ctx, a->rt2);
> + memcpy(buf, &v1, esize);
> + memcpy(buf + esize, &v2, esize);
> +
> + if (mem_write(ctx, va, buf, 2 * esize) != 0) {
> + return true;
> + }
> +
> + if (a->w) {
> + base_write(ctx, a->rn, base + offset);
> + }
> + return true;
> +}
> +
> +static bool trans_LDP(DisasContext *ctx, arg_ldstpair *a)
> +{
> + int esize = 1 << a->sz;
> + int64_t offset = (int64_t)a->imm << a->sz;
> + uint64_t base = base_read(ctx, a->rn);
> + uint64_t va = a->p ? base : base + offset;
> + uint8_t buf[16];
> + uint64_t v1 = 0, v2 = 0;
> +
> + if (mem_read(ctx, va, buf, 2 * esize) != 0) {
> + return true;
> + }
> + memcpy(&v1, buf, esize);
> + memcpy(&v2, buf + esize, esize);
> +
> + /* LDPSW: sign-extend 32-bit values to 64-bit (sign=1, sz=2) */
> + if (a->sign) {
> + v1 = sign_extend(v1, 8 * esize);
> + v2 = sign_extend(v2, 8 * esize);
> + }
> +
> + gpr_write(ctx, a->rt, v1);
> + gpr_write(ctx, a->rt2, v2);
> +
> + if (a->w) {
> + base_write(ctx, a->rn, base + offset);
> + }
> + return true;
> +}
> +
> +/* STGP: tag operation is a NOP for emulation; data stored via STP */
> +static bool trans_STGP(DisasContext *ctx, arg_ldstpair *a)
> +{
> + return trans_STP(ctx, a);
This uses half the required immediate offset. It delegates directly to
STP, which scales by sz=3. STGP requires scaling by the 16-byte tag
granule, independently of its 8-byte register elements. Consequently
both the destination address and writeback are wrong for every nonzero
immediate. Existing TCG trans_STGP in target/arm/tcg/translate-a64.c
correctly uses LOG2_TAG_GRANULE; ARM ARM DDI0487L.a C6.2.366 confirms it.
> +}
> +
> +/*
> + * SIMD/FP load/store pair: STP_v, LDP_v
> + * (DDI 0487 C3.3.14 -- C3.3.16)
> + */
> +
> +static bool trans_STP_v(DisasContext *ctx, arg_ldstpair *a)
> +{
> + int esize = 1 << a->sz; /* 4, 8, or 16 bytes */
> + int64_t offset = (int64_t)a->imm << a->sz;
> + uint64_t base = base_read(ctx, a->rn);
> + uint64_t va = a->p ? base : base + offset;
> + uint8_t buf[32]; /* max 2 x 16 bytes */
> +
> + fpreg_read(ctx, a->rt, buf, esize);
> + fpreg_read(ctx, a->rt2, buf + esize, esize);
> +
> + if (mem_write(ctx, va, buf, 2 * esize) != 0) {
> + return true;
> + }
> +
> + if (a->w) {
> + base_write(ctx, a->rn, base + offset);
> + }
> + return true;
> +}
> +
> +static bool trans_LDP_v(DisasContext *ctx, arg_ldstpair *a)
> +{
> + int esize = 1 << a->sz;
> + int64_t offset = (int64_t)a->imm << a->sz;
> + uint64_t base = base_read(ctx, a->rn);
> + uint64_t va = a->p ? base : base + offset;
> + uint8_t buf[32];
> +
> + if (mem_read(ctx, va, buf, 2 * esize) != 0) {
> + return true;
> + }
> +
> + fpreg_write(ctx, a->rt, buf, esize);
> + fpreg_write(ctx, a->rt2, buf + esize, esize);
> +
> + if (a->w) {
> + base_write(ctx, a->rn, base + offset);
> + }
> + return true;
> +}
> +
> /* Load/store single -- immediate (GPR) (DDI 0487 C3.3.8 -- C3.3.13) */
>
> static bool trans_STR_i(DisasContext *ctx, arg_ldst_imm *a)
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 04/10] target/arm/hvf, whpx: wire ISV=0 emulation for data aborts
2026-09-16 10:00 ` [RFC PATCH v8 04/10] target/arm/hvf, whpx: wire ISV=0 emulation for data aborts Mohamed Mediouni
@ 2026-09-17 20:37 ` Akihiko Odaki
0 siblings, 0 replies; 22+ messages in thread
From: Akihiko Odaki @ 2026-09-17 20:37 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Alexander Graf, Paolo Bonzini, Lucas Amaral
On 2026/09/16 19:00, Mohamed Mediouni wrote:
> From: Lucas Amaral <lucaaamaral@gmail.com>
>
> When a data abort with ISV=0 occurs during MMIO emulation, the
> syndrome register does not carry the access size or target register.
> Previously this hit an assert(isv) and killed the VM.
>
> Replace the assert with instruction fetch + decode + emulate using the
> shared library in target/arm/emulate/. The faulting instruction is read
> from guest memory via cpu_memory_rw_debug(), decoded by the decodetree-
> generated decoder, and emulated against the vCPU register file.
>
> Both HVF (macOS) and WHPX (Windows Hyper-V) use the same pattern:
> 1. cpu_synchronize_state() to flush hypervisor registers
> 2. Fetch 4-byte instruction at env->pc
> 3. arm_emul_insn(env, insn)
> 4. On success, advance PC past the emulated instruction
>
> If the instruction is unhandled or a memory error occurs, a synchronous
> external abort is injected into the guest via syn_data_abort_no_iss()
> with fnv=1 and fsc=0x10, matching the syndrome that KVM uses in
> kvm_inject_arm_sea(). The guest kernel's fault handler then reports
> the error through its normal data abort path.
>
> WHPX adds a whpx_inject_data_abort() helper and adjusts the
> whpx_handle_mmio() return convention so the caller skips PC advancement
> when an exception has been injected.
>
> Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
> ---
> target/arm/hvf/hvf.c | 46 ++++++++++++++++++++++++++--
> target/arm/whpx/whpx-all.c | 61 +++++++++++++++++++++++++++++++++++++-
> 2 files changed, 103 insertions(+), 4 deletions(-)
>
> diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
> index b00a7e1a84..a087e920e9 100644
> --- a/target/arm/hvf/hvf.c
> +++ b/target/arm/hvf/hvf.c
> @@ -34,6 +34,7 @@
> #include "arm-powerctl.h"
> #include "target/arm/cpu.h"
> #include "target/arm/internals.h"
> +#include "emulate/arm_emulate.h"
> #include "target/arm/multiprocessing.h"
> #include "target/arm/gtimer.h"
> #include "target/arm/trace.h"
> @@ -2441,10 +2442,49 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp)
> assert(!s1ptw);
>
> /*
> - * TODO: ISV will be 0 for SIMD or SVE accesses.
> - * Inject the exception into the guest.
> + * ISV=0: syndrome doesn't carry access size/register info.
> + * Fetch and emulate via target/arm/emulate/.
> */
> - assert(isv);
> + if (!isv) {
> + ARMCPU *arm_cpu = ARM_CPU(cpu);
> + CPUARMState *env = &arm_cpu->env;
> + uint32_t insn;
> + ArmEmulResult r;
> +
> + cpu_synchronize_state(cpu);
> +
> + if (cpu_memory_rw_debug(cpu, env->pc,
> + (uint8_t *)&insn, 4, false) != 0) {
> + bool same_el = arm_current_el(env) == 1;
This hardcodes exception target EL1, including failures at guest EL2
with virtualization=on. arm_cpu_do_interrupt() then selects VBAR_EL1,
overwrites EL1 exception registers, and changes execution to EL1. Derive
the abort target and same_el from the current EL and exception routing.
The instruction-fetch failure branch has the same defect.
Regards,
Akihiko Odaki
> + uint32_t esr = syn_data_abort_no_iss(same_el,
> + 1, 0, 0, 0, iswrite, 0x10);
> +
> + error_report("HVF: cannot read insn at pc=0x%" PRIx64,
> + (uint64_t)env->pc);
> + env->exception.vaddress = excp->virtual_address;
> + hvf_raise_exception(cpu, EXCP_DATA_ABORT, esr, 1);
> + break;
> + }
> +
> + r = arm_emul_insn(env, insn);
> + if (r == ARM_EMUL_UNHANDLED || r == ARM_EMUL_ERR_MEM) {
> + bool same_el = arm_current_el(env) == 1;
> + uint32_t esr = syn_data_abort_no_iss(same_el,
> + 1, 0, 0, 0, iswrite, 0x10);
> +
> + error_report("HVF: ISV=0 %s insn 0x%08x at "
> + "pc=0x%" PRIx64 ", injecting data abort",
> + r == ARM_EMUL_UNHANDLED ? "unhandled"
> + : "memory error",
> + insn, (uint64_t)env->pc);
> + env->exception.vaddress = excp->virtual_address;
> + hvf_raise_exception(cpu, EXCP_DATA_ABORT, esr, 1);
> + break;
> + }
> +
> + advance_pc = true;
> + break;
> + }
>
> /*
> * Emulate MMIO.
> diff --git a/target/arm/whpx/whpx-all.c b/target/arm/whpx/whpx-all.c
> index d106400686..c916df35a7 100644
> --- a/target/arm/whpx/whpx-all.c
> +++ b/target/arm/whpx/whpx-all.c
> @@ -29,6 +29,7 @@
> #include "syndrome.h"
> #include "target/arm/cpregs.h"
> #include "internals.h"
> +#include "emulate/arm_emulate.h"
>
> #include "system/whpx-internal.h"
> #include "system/whpx-accel-ops.h"
> @@ -350,6 +351,27 @@ static void whpx_set_gp_reg(CPUState *cpu, int rt, uint64_t val)
> whpx_set_reg(cpu, reg, reg_val);
> }
>
> +/*
> + * Inject a synchronous external abort (data abort) into the guest.
> + * Used when ISV=0 instruction emulation fails. Matches the syndrome
> + * that KVM uses in kvm_inject_arm_sea().
> + */
> +static void whpx_inject_data_abort(CPUState *cpu, bool iswrite)
> +{
> + ARMCPU *arm_cpu = ARM_CPU(cpu);
> + CPUARMState *env = &arm_cpu->env;
> + bool same_el = arm_current_el(env) == 1;
> + uint32_t esr = syn_data_abort_no_iss(same_el, 1, 0, 0, 0, iswrite, 0x10);
> +
> + cpu->exception_index = EXCP_DATA_ABORT;
> + env->exception.target_el = 1;
> + env->exception.syndrome = esr;
> +
> + bql_lock();
> + arm_cpu_do_interrupt(cpu);
> + bql_unlock();
> +}
> +
> static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
> {
> uint64_t syndrome = ctx->Syndrome;
> @@ -365,7 +387,40 @@ static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
>
> assert(syn_get_ec(syndrome) == EC_DATAABORT);
> assert(!cm);
> - assert(isv);
> +
> + /*
> + * ISV=0: syndrome doesn't carry access size/register info.
> + * Fetch and decode the faulting instruction via the emulation library.
> + */
> + if (!isv) {
> + ARMCPU *arm_cpu = ARM_CPU(cpu);
> + CPUARMState *env = &arm_cpu->env;
> + uint32_t insn;
> + ArmEmulResult r;
> +
> + cpu_synchronize_state(cpu);
> +
> + if (cpu_memory_rw_debug(cpu, env->pc,
> + (uint8_t *)&insn, 4, false) != 0) {
> + error_report("WHPX: cannot read insn at pc=0x%" PRIx64,
> + (uint64_t)env->pc);
> + whpx_inject_data_abort(cpu, iswrite);
> + return 1;
> + }
> +
> + r = arm_emul_insn(env, insn);
> + if (r == ARM_EMUL_UNHANDLED || r == ARM_EMUL_ERR_MEM) {
> + error_report("WHPX: ISV=0 %s insn 0x%08x at "
> + "pc=0x%" PRIx64 ", injecting data abort",
> + r == ARM_EMUL_UNHANDLED ? "unhandled"
> + : "memory error",
> + insn, (uint64_t)env->pc);
> + whpx_inject_data_abort(cpu, iswrite);
> + return 1;
> + }
> +
> + return 0;
> + }
>
> if (iswrite) {
> val = whpx_get_gp_reg(cpu, srt);
> @@ -451,6 +506,10 @@ int whpx_vcpu_run(CPUState *cpu)
> }
>
> ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
> + if (ret > 0) {
> + advance_pc = false;
> + ret = 0;
> + }
> break;
> case WHvRunVpExitReasonCanceled:
> cpu->exception_index = EXCP_INTERRUPT;
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 05/10] vmapple: apple-gfx: make it work on the latest macOS release
2026-09-16 10:00 ` [RFC PATCH v8 05/10] vmapple: apple-gfx: make it work on the latest macOS release Mohamed Mediouni
@ 2026-09-17 20:37 ` Akihiko Odaki
2026-09-17 21:49 ` Mohamed Mediouni
0 siblings, 1 reply; 22+ messages in thread
From: Akihiko Odaki @ 2026-09-17 20:37 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Alexander Graf, Paolo Bonzini
On 2026/09/16 19:00, Mohamed Mediouni wrote:
> Follow changes in memory management introduced on macOS 15.4.
>
> The legacy memory management API has been removed for the IOSurface mapper on that macOS version.
>
> Also enable process isolation for a sandboxed GPU process when on a new OS.
>
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> hw/display/apple-gfx-mmio.m | 58 ++++++++++++++++++------
> hw/display/apple-gfx.h | 16 +++++++
> hw/display/apple-gfx.m | 90 ++++++++++++++++++++++++++++++++-----
> 3 files changed, 138 insertions(+), 26 deletions(-)
>
> diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m
> index 58beaadd1f..19e2d38a1e 100644
> --- a/hw/display/apple-gfx-mmio.m
> +++ b/hw/display/apple-gfx-mmio.m
> @@ -19,6 +19,7 @@
> #include "hw/core/irq.h"
> #include "apple-gfx.h"
> #include "trace.h"
> +#include "system/address-spaces.h"
>
> #import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
>
> @@ -36,12 +37,19 @@ typedef bool(^IOSFCMapMemory)(uint64_t phys, uint64_t len, bool ro, void **va,
>
> @interface PGDeviceDescriptor (IOSurfaceMapper)
> @property (readwrite, nonatomic) bool usingIOSurfaceMapper;
> +@property (readwrite, nonatomic) bool enableArgumentBuffers;
> +@property (readwrite, nonatomic) bool enableProcessIsolation;
> +@property (readwrite, nonatomic) bool enableProtectedContent;
> +
> +@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
> @end
>
> @interface PGIOSurfaceHostDeviceDescriptor : NSObject
> -(PGIOSurfaceHostDeviceDescriptor *)init;
> @property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory;
> @property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory;
> +@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
> +@property (readwrite, nonatomic) unsigned long long mmioLength;
> @property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt;
> @end
>
> @@ -183,19 +191,32 @@ static bool apple_gfx_mmio_unmap_surface_memory(void *ptr)
> [PGIOSurfaceHostDeviceDescriptor new];
> PGIOSurfaceHostDevice *iosfc_host_dev;
>
> - iosfc_desc.mapMemory =
> - ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
> - *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
> -
> - trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
> -
> - return *va != NULL;
> - };
> -
> - iosfc_desc.unmapMemory =
> - ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
> - return apple_gfx_mmio_unmap_surface_memory(va);
> - };
> + /*
> + * The legacy memory management API is no longer present
> + * for the IOSurface mapper as of macOS 15.4.
> + */
> + if (@available(macOS 15.4, *)) {
> + PGMemoryMapDescriptor *memory_map_descriptor = [PGMemoryMapDescriptor new];
> + FlatView* fv = address_space_to_flatview(&address_space_memory);
> + flatview_for_each_range(fv, apple_gfx_register_memory_cb, memory_map_descriptor);
> + /* the device model defines this as a single-page MMIO region, hence 16KB */
> + iosfc_desc.mmioLength = 0x10000;
> + iosfc_desc.memoryMapDescriptor = memory_map_descriptor;
> + } else {
> + iosfc_desc.mapMemory =
> + ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
> + *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
> +
> + trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
> +
> + return *va != NULL;
> + };
> +
> + iosfc_desc.unmapMemory =
> + ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
> + return apple_gfx_mmio_unmap_surface_memory(va);
> + };
> + }
>
> iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) {
> trace_apple_gfx_iosfc_raise_irq(vector);
> @@ -223,13 +244,22 @@ static void apple_gfx_mmio_realize(DeviceState *dev, Error **errp)
> };
>
> desc.usingIOSurfaceMapper = true;
> - s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
> + /*
> + * Process isolation needs PGMemoryMapDescriptor instead of
> + * the legacy memory management interface present in releases
> + * older than macOS 15.4.
> + */
> + if (@available(macOS 15.4, *)) {
> + desc.enableProcessIsolation = true;
> + }
>
> if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) {
> [s->pgiosfc release];
> s->pgiosfc = nil;
> }
>
> + s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
> +
> [desc release];
> desc = nil;
> }
> diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h
> index 3197bd853d..384aee0c5f 100644
> --- a/hw/display/apple-gfx.h
> +++ b/hw/display/apple-gfx.h
> @@ -12,6 +12,7 @@
> #include "system/memory.h"
> #include "hw/core/qdev-properties.h"
> #include "ui/surface.h"
> +#include "objc/NSObject.h"
>
> #define TYPE_APPLE_GFX_MMIO "apple-gfx-mmio"
> #define TYPE_APPLE_GFX_PCI "apple-gfx-pci"
> @@ -23,6 +24,17 @@
> @protocol MTLTexture;
> @protocol MTLCommandQueue;
>
> +typedef struct PGGuestPhysicalRange_s
> +{
> + uint64_t physicalAddress;
> + uint64_t physicalLength;
> + void *hostAddress;
> +} PGGuestPhysicalRange_t;
> +
> +@interface PGMemoryMapDescriptor : NSObject
> +-(void)addRange:(PGGuestPhysicalRange_t) range;
> +@end
> +
> typedef QTAILQ_HEAD(, PGTask_s) PGTaskList;
>
> typedef struct AppleGFXDisplayMode {
> @@ -68,6 +80,10 @@ void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
> uint64_t length, bool read_only,
> MemoryRegion **mapping_in_region);
>
> +bool apple_gfx_register_memory_cb(Int128 start, Int128 len,
> + const MemoryRegion *mr,
> + hwaddr offset_in_region, void *opaque);
> +
> extern const PropertyInfo qdev_prop_apple_gfx_display_mode;
>
> #endif
> diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m
> index be0061b9db..4ef8f6dbf3 100644
> --- a/hw/display/apple-gfx.m
> +++ b/hw/display/apple-gfx.m
> @@ -21,6 +21,7 @@
> #include "system/address-spaces.h"
> #include "system/dma.h"
> #include "migration/blocker.h"
> +#include "system/memory.h"
> #include "ui/console.h"
> #include "apple-gfx.h"
> #include "trace.h"
> @@ -79,6 +80,7 @@ static dispatch_queue_t get_background_queue(void)
> GPtrArray *mapped_regions;
> };
>
> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
git am detected trailing whitespaces:
$ b4 shazam 20260916100021.73825-1-mohamed@unpredictable.fr
Looking up 20260916100021.73825-1-mohamed@unpredictable.fr
Checking for newer revisions
Grabbing search results from lore.kernel.org
Analyzing 15 messages in the thread
Looking for additional code-review trailers on lore.kernel.org
Analyzing 94 code-review messages
Checking attestation on all messages, may take a moment...
---
✓ [PATCH RFC v8 1/10] target/arm/emulate: add ISV=0 emulation library
with load/store immediate
✓ [PATCH RFC v8 2/10] target/arm/emulate: add load/store register offset
✓ [PATCH RFC v8 3/10] target/arm/emulate: add load/store pair
✓ [PATCH RFC v8 4/10] target/arm/hvf, whpx: wire ISV=0 emulation for
data aborts
✓ [PATCH RFC v8 5/10] vmapple: apple-gfx: make it work on the latest
macOS release
✓ [PATCH RFC v8 6/10] hw/vmapple: aes: convert MAX_LEN to a #define
✓ [PATCH RFC v8 7/10] Revert "hw/arm: Do not build VMapple machine by
default"
✓ [PATCH RFC v8 8/10] vmapple: add gicv2m
✓ [PATCH RFC v8 9/10] vmapple, gicv2m: add macOS compat quirk
✓ [PATCH RFC v8 10/10] hw/vmapple: virtio-blk: allow FileVault off
---
✓ Signed: DKIM/unpredictable.fr
---
Total patches: 10
---
Applying: target/arm/emulate: add ISV=0 emulation library with
load/store immediate
Applying: target/arm/emulate: add load/store register offset
Applying: target/arm/emulate: add load/store pair
Applying: target/arm/hvf, whpx: wire ISV=0 emulation for data aborts
Applying: vmapple: apple-gfx: make it work on the latest macOS release
Applying: hw/vmapple: aes: convert MAX_LEN to a #define
Applying: Revert "hw/arm: Do not build VMapple machine by default"
Applying: vmapple: add gicv2m
Applying: vmapple, gicv2m: add macOS compat quirk
Applying: hw/vmapple: virtio-blk: allow FileVault off
/home/me/q/var/qemu/.git/rebase-apply/patch:90: trailing whitespace.
/*
/home/me/q/var/qemu/.git/rebase-apply/patch:166: trailing whitespace.
API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
/home/me/q/var/qemu/.git/rebase-apply/patch:272: trailing whitespace.
/*
/home/me/q/var/qemu/.git/rebase-apply/patch:275: trailing whitespace.
* on macOS 15.4 onwards.
warning: 4 lines add whitespace errors.
> static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len)
> {
> mach_vm_address_t task_mem;
> @@ -102,6 +104,7 @@ static dispatch_queue_t get_background_queue(void)
> return task;
> }
>
> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
> static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
> {
> GPtrArray *regions = task->mapped_regions;
> @@ -151,6 +154,7 @@ static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
> return host_ptr;
> }
>
> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
> static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
> uint64_t virtual_offset,
> PGPhysicalMemoryRange_t *ranges,
> @@ -207,6 +211,7 @@ static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
> return success;
> }
>
> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
> static void apple_gfx_task_unmap_memory(AppleGFXState *s, PGTask_t *task,
> uint64_t virtual_offset, uint64_t length)
> {
> @@ -596,6 +601,36 @@ void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name)
> /* TODO: PVG framework supports serialising device state: integrate it! */
> }
>
> +@interface PGDeviceDescriptor (IOSurfaceMapper)
> +@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
> +@end
> +
> +bool apple_gfx_register_memory_cb(Int128 start, Int128 len,
> + const MemoryRegion *mr,
> + hwaddr offset_in_region, void *opaque) {
> + PGGuestPhysicalRange_t range;
> + PGMemoryMapDescriptor *memory_map_descriptor = opaque;
> + if (memory_access_is_direct(mr, true, MEMTXATTRS_UNSPECIFIED)) {
> + range.physicalAddress = start;
Please convert Int128 through its helpers. This assigns Int128 directly
into the two uint64_t fields. With CONFIG_TCG_INTERPRETER, QEMU
deliberately represents Int128 as a structure, even on hosts supporting
native __int128. Both assignments fail compilation. A focused Clang
check using the actual QEMU headers reproduced the incompatible-type
errors; int128_get64() provides the intended checked conversion.
> + range.physicalLength = len;
> + range.hostAddress = memory_region_get_ram_ptr(mr);
The FlatView slice offset, offset_in_region, needs to be honored when
registering RAM.
For apple-gfx-pci, ordinary PC RAM above 4 GiB aliases machine->ram
starting at below_4g_mem_size; GPU accesses to that range now alias low
RAM instead. Other clipped FlatView ranges have the same problem. The
previous mapping path adds the translated offset correctly.
> + [memory_map_descriptor addRange:range];
> + }
> + return false;
> +}
> +
> +static void apple_gfx_register_memory(AppleGFXState *s,
> + PGDeviceDescriptor *desc)
> +{
> + PGMemoryMapDescriptor* memoryMapDescriptor = [PGMemoryMapDescriptor new];
> +
> + FlatView* fv = address_space_to_flatview(&address_space_memory);
> + flatview_for_each_range(fv, apple_gfx_register_memory_cb, memoryMapDescriptor);
> +
> + desc.memoryMapDescriptor = memoryMapDescriptor;
> +}
> +
> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
> static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s,
> PGDeviceDescriptor *desc)
> {
> @@ -667,16 +702,25 @@ static void new_frame_handler_bh(void *opaque)
> BQL_LOCK_GUARD();
> set_mode(s, sizeInPixels.x, sizeInPixels.y);
> };
> - disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
> - PGDisplayCoord_t hotspot) {
> - AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
> - job->s = s;
> - job->glyph = glyph;
> - job->hotspot = hotspot;
> - [glyph retain];
> - aio_bh_schedule_oneshot(qemu_get_aio_context(),
> - set_cursor_glyph, job);
> - };
> +
> + if (@available(macOS 27.0, *)) {
> +
> + } else {
> +#ifndef __MAC_27_0
> + disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
> + PGDisplayCoord_t hotspot) {
> + AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
> + job->s = s;
> + job->glyph = glyph;
> + job->hotspot = hotspot;
> + [glyph retain];
> + aio_bh_schedule_oneshot(qemu_get_aio_context(),
> + set_cursor_glyph, job);
> + };
> +#else
> + abort();
> +#endif
I wonder what is this #ifdefs for. It tests SDK macro __MAC_27_0,
whereas the surrounding @available tests the runtime OS. A binary built
with that SDK aborts during display realization on every pre-27 host,
including macOS 26. Older hosts also encounter explicit aborts in the
memory/device fallback paths. Turning supported runtime fallback paths
into unconditional process termination depending when SDK is available
does not look correct.
Regards,
Akihiko Odaki
> + }
> disp_desc.cursorShowHandler = ^(BOOL show) {
> trace_apple_gfx_cursor_show(show);
> qatomic_set(&s->cursor_show, show);
> @@ -763,11 +807,33 @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
>
> desc.device = s->mtl;
>
> - apple_gfx_register_task_mapping_handlers(s, desc);
> + /*
> + * The legacy memory management interface doesn't allow for
> + * vGPU sandboxing. As such, always use the new interface
> + * on macOS 15.4 onwards.
> + */
> + if (@available(macOS 15.4, *)) {
> + apple_gfx_register_memory(s, desc);
> + } else {
> +#ifndef __MAC_27_0
> + apple_gfx_register_task_mapping_handlers(s, desc);
> +#else
> + abort();
> +#endif
> + }
>
> s->cursor_show = true;
>
> - s->pgdev = PGNewDeviceWithDescriptor(desc);
> + if (@available(macOS 15.2, *)) {
> + s->pgdev = PGCreateDeviceWithDescriptor(desc);
> + }
> + else {
> +#ifndef __MAC_27_0
> + s->pgdev = PGNewDeviceWithDescriptor(desc);
> +#else
> + abort();
> +#endif
> + }
>
> disp_desc = apple_gfx_prepare_display_descriptor(s);
> /*
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 10/10] hw/vmapple: virtio-blk: allow FileVault off
2026-09-16 10:00 ` [RFC PATCH v8 10/10] hw/vmapple: virtio-blk: allow FileVault off Mohamed Mediouni
@ 2026-09-17 20:37 ` Akihiko Odaki
0 siblings, 0 replies; 22+ messages in thread
From: Akihiko Odaki @ 2026-09-17 20:37 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel
Cc: Michael S. Tsirkin, Roman Bolshakov, Peter Maydell, Peter Xu,
Philippe Mathieu-Daudé, qemu-arm, Phil Dennis-Jordan,
Alexander Graf, Paolo Bonzini
On 2026/09/16 19:00, Mohamed Mediouni wrote:
> Otherwise, a newer macOS version with FileVault
> disabled in the guest does not boot.
>
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> hw/vmapple/virtio-blk.c | 18 ++++++++++++++++++
> 1 file changed, 18 insertions(+)
>
> diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c
> index 9de9aaae0b..906c2b2ec8 100644
> --- a/hw/vmapple/virtio-blk.c
> +++ b/hw/vmapple/virtio-blk.c
> @@ -119,12 +119,17 @@ static const Property vmapple_virtio_blk_pci_properties[] = {
> VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED),
> };
>
> +#define VMAPPLE_PCI_VNDR_SIZE 14
> +/* Disable requirement for FileVault to be enabled on the guest */
> +#define VMAPPLE_PCI_VNDR_LEGACY_FEATURES_DISABLE_ENCRYPTION (1 << 0)
> +
> static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
> {
> ERRP_GUARD();
> VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev);
> DeviceState *vdev = DEVICE(&dev->vdev);
> VirtIOBlkConf *conf = &dev->vdev.parent_obj.conf;
> + int offset;
>
> if (dev->variant == VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED) {
> error_setg(errp, "vmapple virtio block device variant unspecified");
> @@ -162,6 +167,19 @@ static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **err
> pci_config_set_vendor_id(vpci_dev->pci_dev.config, PCI_VENDOR_ID_APPLE);
> pci_config_set_device_id(vpci_dev->pci_dev.config,
> PCI_DEVICE_ID_APPLE_VIRTIO_BLK);
> +
> + offset = pci_add_capability(&vpci_dev->pci_dev, PCI_CAP_ID_VNDR, 0,
> + VMAPPLE_PCI_VNDR_SIZE, errp);
This exhausts conventional PCI capability space behind a PCIe root port
with modern-pio-notify=on and MSI-X enabled. Existing capabilities
consume 188 of 192 bytes; adding 14 calls pci_find_space(), which
returns zero and triggers assert(offset). The negative-result check
cannot handle this. Default root-bus placement fits.
Regards,
Akihiko Odaki
> + if (offset < 0) {
> + return;
> + }
> +
> + pci_set_byte(vpci_dev->pci_dev.config + offset + 2,
> + VMAPPLE_PCI_VNDR_SIZE);
> + pci_set_byte(vpci_dev->pci_dev.config + offset + 3, 9);
> + pci_set_word(vpci_dev->pci_dev.config + offset + 4, PCI_VENDOR_ID_APPLE);
> + pci_set_byte(vpci_dev->pci_dev.config + offset + 6,
> + VMAPPLE_PCI_VNDR_LEGACY_FEATURES_DISABLE_ENCRYPTION);
> }
>
> static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass,
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 05/10] vmapple: apple-gfx: make it work on the latest macOS release
2026-09-17 20:37 ` Akihiko Odaki
@ 2026-09-17 21:49 ` Mohamed Mediouni
2026-10-04 6:24 ` Akihiko Odaki
0 siblings, 1 reply; 22+ messages in thread
From: Mohamed Mediouni @ 2026-09-17 21:49 UTC (permalink / raw)
To: Akihiko Odaki
Cc: qemu-devel, Michael S. Tsirkin, Roman Bolshakov, Peter Maydell,
Peter Xu, Philippe Mathieu-Daudé, qemu-arm,
Phil Dennis-Jordan, Alexander Graf, Paolo Bonzini
> On 17. Sep 2026, at 22:37, Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> wrote:
>
> On 2026/09/16 19:00, Mohamed Mediouni wrote:
>> Follow changes in memory management introduced on macOS 15.4.
>> The legacy memory management API has been removed for the IOSurface mapper on that macOS version.
>> Also enable process isolation for a sandboxed GPU process when on a new OS.
>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
>> ---
>> hw/display/apple-gfx-mmio.m | 58 ++++++++++++++++++------
>> hw/display/apple-gfx.h | 16 +++++++
>> hw/display/apple-gfx.m | 90 ++++++++++++++++++++++++++++++++-----
>> 3 files changed, 138 insertions(+), 26 deletions(-)
>> diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m
>> index 58beaadd1f..19e2d38a1e 100644
>> --- a/hw/display/apple-gfx-mmio.m
>> +++ b/hw/display/apple-gfx-mmio.m
>> @@ -19,6 +19,7 @@
>> #include "hw/core/irq.h"
>> #include "apple-gfx.h"
>> #include "trace.h"
>> +#include "system/address-spaces.h"
>> #import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
>> @@ -36,12 +37,19 @@ typedef bool(^IOSFCMapMemory)(uint64_t phys, uint64_t len, bool ro, void **va,
>> @interface PGDeviceDescriptor (IOSurfaceMapper)
>> @property (readwrite, nonatomic) bool usingIOSurfaceMapper;
>> +@property (readwrite, nonatomic) bool enableArgumentBuffers;
>> +@property (readwrite, nonatomic) bool enableProcessIsolation;
>> +@property (readwrite, nonatomic) bool enableProtectedContent;
>> +
>> +@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
>> @end
>> @interface PGIOSurfaceHostDeviceDescriptor : NSObject
>> -(PGIOSurfaceHostDeviceDescriptor *)init;
>> @property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory;
>> @property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory;
>> +@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
>> +@property (readwrite, nonatomic) unsigned long long mmioLength;
>> @property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt;
>> @end
>> @@ -183,19 +191,32 @@ static bool apple_gfx_mmio_unmap_surface_memory(void *ptr)
>> [PGIOSurfaceHostDeviceDescriptor new];
>> PGIOSurfaceHostDevice *iosfc_host_dev;
>> - iosfc_desc.mapMemory =
>> - ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
>> - *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
>> -
>> - trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
>> -
>> - return *va != NULL;
>> - };
>> -
>> - iosfc_desc.unmapMemory =
>> - ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
>> - return apple_gfx_mmio_unmap_surface_memory(va);
>> - };
>> + /*
>> + * The legacy memory management API is no longer present
>> + * for the IOSurface mapper as of macOS 15.4.
>> + */
>> + if (@available(macOS 15.4, *)) {
>> + PGMemoryMapDescriptor *memory_map_descriptor = [PGMemoryMapDescriptor new];
>> + FlatView* fv = address_space_to_flatview(&address_space_memory);
>> + flatview_for_each_range(fv, apple_gfx_register_memory_cb, memory_map_descriptor);
>> + /* the device model defines this as a single-page MMIO region, hence 16KB */
>> + iosfc_desc.mmioLength = 0x10000;
>> + iosfc_desc.memoryMapDescriptor = memory_map_descriptor;
>> + } else {
>> + iosfc_desc.mapMemory =
>> + ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
>> + *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
>> +
>> + trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
>> +
>> + return *va != NULL;
>> + };
>> +
>> + iosfc_desc.unmapMemory =
>> + ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
>> + return apple_gfx_mmio_unmap_surface_memory(va);
>> + };
>> + }
>> iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) {
>> trace_apple_gfx_iosfc_raise_irq(vector);
>> @@ -223,13 +244,22 @@ static void apple_gfx_mmio_realize(DeviceState *dev, Error **errp)
>> };
>> desc.usingIOSurfaceMapper = true;
>> - s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
>> + /*
>> + * Process isolation needs PGMemoryMapDescriptor instead of
>> + * the legacy memory management interface present in releases
>> + * older than macOS 15.4.
>> + */
>> + if (@available(macOS 15.4, *)) {
>> + desc.enableProcessIsolation = true;
>> + }
>> if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) {
>> [s->pgiosfc release];
>> s->pgiosfc = nil;
>> }
>> + s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
>> +
>> [desc release];
>> desc = nil;
>> }
>> diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h
>> index 3197bd853d..384aee0c5f 100644
>> --- a/hw/display/apple-gfx.h
>> +++ b/hw/display/apple-gfx.h
>> @@ -12,6 +12,7 @@
>> #include "system/memory.h"
>> #include "hw/core/qdev-properties.h"
>> #include "ui/surface.h"
>> +#include "objc/NSObject.h"
>> #define TYPE_APPLE_GFX_MMIO "apple-gfx-mmio"
>> #define TYPE_APPLE_GFX_PCI "apple-gfx-pci"
>> @@ -23,6 +24,17 @@
>> @protocol MTLTexture;
>> @protocol MTLCommandQueue;
>> +typedef struct PGGuestPhysicalRange_s
>> +{
>> + uint64_t physicalAddress;
>> + uint64_t physicalLength;
>> + void *hostAddress;
>> +} PGGuestPhysicalRange_t;
>> +
>> +@interface PGMemoryMapDescriptor : NSObject
>> +-(void)addRange:(PGGuestPhysicalRange_t) range;
>> +@end
>> +
>> typedef QTAILQ_HEAD(, PGTask_s) PGTaskList;
>> typedef struct AppleGFXDisplayMode {
>> @@ -68,6 +80,10 @@ void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
>> uint64_t length, bool read_only,
>> MemoryRegion **mapping_in_region);
>> +bool apple_gfx_register_memory_cb(Int128 start, Int128 len,
>> + const MemoryRegion *mr,
>> + hwaddr offset_in_region, void *opaque);
>> +
>> extern const PropertyInfo qdev_prop_apple_gfx_display_mode;
>> #endif
>> diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m
>> index be0061b9db..4ef8f6dbf3 100644
>> --- a/hw/display/apple-gfx.m
>> +++ b/hw/display/apple-gfx.m
>> @@ -21,6 +21,7 @@
>> #include "system/address-spaces.h"
>> #include "system/dma.h"
>> #include "migration/blocker.h"
>> +#include "system/memory.h"
>> #include "ui/console.h"
>> #include "apple-gfx.h"
>> #include "trace.h"
>> @@ -79,6 +80,7 @@ static dispatch_queue_t get_background_queue(void)
>> GPtrArray *mapped_regions;
>> };
>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>
> git am detected trailing whitespaces:
>
> $ b4 shazam 20260916100021.73825-1-mohamed@unpredictable.fr
> Looking up 20260916100021.73825-1-mohamed@unpredictable.fr
> Checking for newer revisions
> Grabbing search results from lore.kernel.org
> Analyzing 15 messages in the thread
> Looking for additional code-review trailers on lore.kernel.org
> Analyzing 94 code-review messages
> Checking attestation on all messages, may take a moment...
> ---
> ✓ [PATCH RFC v8 1/10] target/arm/emulate: add ISV=0 emulation library with load/store immediate
> ✓ [PATCH RFC v8 2/10] target/arm/emulate: add load/store register offset
> ✓ [PATCH RFC v8 3/10] target/arm/emulate: add load/store pair
> ✓ [PATCH RFC v8 4/10] target/arm/hvf, whpx: wire ISV=0 emulation for data aborts
> ✓ [PATCH RFC v8 5/10] vmapple: apple-gfx: make it work on the latest macOS release
> ✓ [PATCH RFC v8 6/10] hw/vmapple: aes: convert MAX_LEN to a #define
> ✓ [PATCH RFC v8 7/10] Revert "hw/arm: Do not build VMapple machine by default"
> ✓ [PATCH RFC v8 8/10] vmapple: add gicv2m
> ✓ [PATCH RFC v8 9/10] vmapple, gicv2m: add macOS compat quirk
> ✓ [PATCH RFC v8 10/10] hw/vmapple: virtio-blk: allow FileVault off
> ---
> ✓ Signed: DKIM/unpredictable.fr
> ---
> Total patches: 10
> ---
> Applying: target/arm/emulate: add ISV=0 emulation library with load/store immediate
> Applying: target/arm/emulate: add load/store register offset
> Applying: target/arm/emulate: add load/store pair
> Applying: target/arm/hvf, whpx: wire ISV=0 emulation for data aborts
> Applying: vmapple: apple-gfx: make it work on the latest macOS release
> Applying: hw/vmapple: aes: convert MAX_LEN to a #define
> Applying: Revert "hw/arm: Do not build VMapple machine by default"
> Applying: vmapple: add gicv2m
> Applying: vmapple, gicv2m: add macOS compat quirk
> Applying: hw/vmapple: virtio-blk: allow FileVault off
> /home/me/q/var/qemu/.git/rebase-apply/patch:90: trailing whitespace.
> /*
> /home/me/q/var/qemu/.git/rebase-apply/patch:166: trailing whitespace.
> API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
> /home/me/q/var/qemu/.git/rebase-apply/patch:272: trailing whitespace.
> /*
> /home/me/q/var/qemu/.git/rebase-apply/patch:275: trailing whitespace.
> * on macOS 15.4 onwards.
> warning: 4 lines add whitespace errors.
>
>> static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len)
>> {
>> mach_vm_address_t task_mem;
>> @@ -102,6 +104,7 @@ static dispatch_queue_t get_background_queue(void)
>> return task;
>> }
>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>> static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
>> {
>> GPtrArray *regions = task->mapped_regions;
>> @@ -151,6 +154,7 @@ static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
>> return host_ptr;
>> }
>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>> static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
>> uint64_t virtual_offset,
>> PGPhysicalMemoryRange_t *ranges,
>> @@ -207,6 +211,7 @@ static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
>> return success;
>> }
>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>> static void apple_gfx_task_unmap_memory(AppleGFXState *s, PGTask_t *task,
>> uint64_t virtual_offset, uint64_t length)
>> {
>> @@ -596,6 +601,36 @@ void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name)
>> /* TODO: PVG framework supports serialising device state: integrate it! */
>> }
>> +@interface PGDeviceDescriptor (IOSurfaceMapper)
>> +@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
>> +@end
>> +
>> +bool apple_gfx_register_memory_cb(Int128 start, Int128 len,
>> + const MemoryRegion *mr,
>> + hwaddr offset_in_region, void *opaque) {
>> + PGGuestPhysicalRange_t range;
>> + PGMemoryMapDescriptor *memory_map_descriptor = opaque;
>> + if (memory_access_is_direct(mr, true, MEMTXATTRS_UNSPECIFIED)) {
>> + range.physicalAddress = start;
>
> Please convert Int128 through its helpers. This assigns Int128 directly into the two uint64_t fields. With CONFIG_TCG_INTERPRETER, QEMU deliberately represents Int128 as a structure, even on hosts supporting native __int128. Both assignments fail compilation. A focused Clang check using the actual QEMU headers reproduced the incompatible-type errors; int128_get64() provides the intended checked conversion.
>
>> + range.physicalLength = len;
>> + range.hostAddress = memory_region_get_ram_ptr(mr);
>
> The FlatView slice offset, offset_in_region, needs to be honored when registering RAM.
>
> For apple-gfx-pci, ordinary PC RAM above 4 GiB aliases machine->ram starting at below_4g_mem_size; GPU accesses to that range now alias low RAM instead. Other clipped FlatView ranges have the same problem. The previous mapping path adds the translated offset correctly.
>
>> + [memory_map_descriptor addRange:range];
>> + }
>> + return false;
>> +}
>> +
>> +static void apple_gfx_register_memory(AppleGFXState *s,
>> + PGDeviceDescriptor *desc)
>> +{
>> + PGMemoryMapDescriptor* memoryMapDescriptor = [PGMemoryMapDescriptor new];
>> +
>> + FlatView* fv = address_space_to_flatview(&address_space_memory);
>> + flatview_for_each_range(fv, apple_gfx_register_memory_cb, memoryMapDescriptor);
>> +
>> + desc.memoryMapDescriptor = memoryMapDescriptor;
>> +}
>> +
>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>> static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s,
>> PGDeviceDescriptor *desc)
>> {
>> @@ -667,16 +702,25 @@ static void new_frame_handler_bh(void *opaque)
>> BQL_LOCK_GUARD();
>> set_mode(s, sizeInPixels.x, sizeInPixels.y);
>> };
>> - disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
>> - PGDisplayCoord_t hotspot) {
>> - AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
>> - job->s = s;
>> - job->glyph = glyph;
>> - job->hotspot = hotspot;
>> - [glyph retain];
>> - aio_bh_schedule_oneshot(qemu_get_aio_context(),
>> - set_cursor_glyph, job);
>> - };
>> +
>> + if (@available(macOS 27.0, *)) {
>> +
>> + } else {
>> +#ifndef __MAC_27_0
>> + disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
>> + PGDisplayCoord_t hotspot) {
>> + AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
>> + job->s = s;
>> + job->glyph = glyph;
>> + job->hotspot = hotspot;
>> + [glyph retain];
>> + aio_bh_schedule_oneshot(qemu_get_aio_context(),
>> + set_cursor_glyph, job);
>> + };
>> +#else
>> + abort();
>> +#endif
>
> I wonder what is this #ifdefs for. It tests SDK macro __MAC_27_0, whereas the surrounding @available tests the runtime OS. A binary built with that SDK aborts during display realization on every pre-27 host, including macOS 26. Older hosts also encounter explicit aborts in the memory/device fallback paths. Turning supported runtime fallback paths into unconditional process termination depending when SDK is available does not look correct.
Hi,
Obsoleted APIs aren’t accessible from a newer SDK even for targeting older OS versions.
The compiler blocks it even when I use an if (@available) for the newer version before…
If you have an idea on how to work around that it’d be great.
>
> Regards,
> Akihiko Odaki
>
>> + }
>> disp_desc.cursorShowHandler = ^(BOOL show) {
>> trace_apple_gfx_cursor_show(show);
>> qatomic_set(&s->cursor_show, show);
>> @@ -763,11 +807,33 @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
>> desc.device = s->mtl;
>> - apple_gfx_register_task_mapping_handlers(s, desc);
>> + /*
>> + * The legacy memory management interface doesn't allow for
>> + * vGPU sandboxing. As such, always use the new interface
>> + * on macOS 15.4 onwards.
>> + */
>> + if (@available(macOS 15.4, *)) {
>> + apple_gfx_register_memory(s, desc);
>> + } else {
>> +#ifndef __MAC_27_0
>> + apple_gfx_register_task_mapping_handlers(s, desc);
>> +#else
>> + abort();
>> +#endif
>> + }
>> s->cursor_show = true;
>> - s->pgdev = PGNewDeviceWithDescriptor(desc);
>> + if (@available(macOS 15.2, *)) {
>> + s->pgdev = PGCreateDeviceWithDescriptor(desc);
>> + }
>> + else {
>> +#ifndef __MAC_27_0
>> + s->pgdev = PGNewDeviceWithDescriptor(desc);
>> +#else
>> + abort();
>> +#endif
>> + }
>> disp_desc = apple_gfx_prepare_display_descriptor(s);
>> /*
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC PATCH v8 05/10] vmapple: apple-gfx: make it work on the latest macOS release
2026-09-17 21:49 ` Mohamed Mediouni
@ 2026-10-04 6:24 ` Akihiko Odaki
0 siblings, 0 replies; 22+ messages in thread
From: Akihiko Odaki @ 2026-10-04 6:24 UTC (permalink / raw)
To: Mohamed Mediouni
Cc: qemu-devel, Michael S. Tsirkin, Roman Bolshakov, Peter Maydell,
Peter Xu, Philippe Mathieu-Daudé, qemu-arm,
Phil Dennis-Jordan, Alexander Graf, Paolo Bonzini
On 2026/09/18 6:49, Mohamed Mediouni wrote:
>
>
>> On 17. Sep 2026, at 22:37, Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> wrote:
>>
>> On 2026/09/16 19:00, Mohamed Mediouni wrote:
>>> Follow changes in memory management introduced on macOS 15.4.
>>> The legacy memory management API has been removed for the IOSurface mapper on that macOS version.
>>> Also enable process isolation for a sandboxed GPU process when on a new OS.
>>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
>>> ---
>>> hw/display/apple-gfx-mmio.m | 58 ++++++++++++++++++------
>>> hw/display/apple-gfx.h | 16 +++++++
>>> hw/display/apple-gfx.m | 90 ++++++++++++++++++++++++++++++++-----
>>> 3 files changed, 138 insertions(+), 26 deletions(-)
>>> diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m
>>> index 58beaadd1f..19e2d38a1e 100644
>>> --- a/hw/display/apple-gfx-mmio.m
>>> +++ b/hw/display/apple-gfx-mmio.m
>>> @@ -19,6 +19,7 @@
>>> #include "hw/core/irq.h"
>>> #include "apple-gfx.h"
>>> #include "trace.h"
>>> +#include "system/address-spaces.h"
>>> #import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
>>> @@ -36,12 +37,19 @@ typedef bool(^IOSFCMapMemory)(uint64_t phys, uint64_t len, bool ro, void **va,
>>> @interface PGDeviceDescriptor (IOSurfaceMapper)
>>> @property (readwrite, nonatomic) bool usingIOSurfaceMapper;
>>> +@property (readwrite, nonatomic) bool enableArgumentBuffers;
>>> +@property (readwrite, nonatomic) bool enableProcessIsolation;
>>> +@property (readwrite, nonatomic) bool enableProtectedContent;
>>> +
>>> +@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
>>> @end
>>> @interface PGIOSurfaceHostDeviceDescriptor : NSObject
>>> -(PGIOSurfaceHostDeviceDescriptor *)init;
>>> @property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory;
>>> @property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory;
>>> +@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
>>> +@property (readwrite, nonatomic) unsigned long long mmioLength;
>>> @property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt;
>>> @end
>>> @@ -183,19 +191,32 @@ static bool apple_gfx_mmio_unmap_surface_memory(void *ptr)
>>> [PGIOSurfaceHostDeviceDescriptor new];
>>> PGIOSurfaceHostDevice *iosfc_host_dev;
>>> - iosfc_desc.mapMemory =
>>> - ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
>>> - *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
>>> -
>>> - trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
>>> -
>>> - return *va != NULL;
>>> - };
>>> -
>>> - iosfc_desc.unmapMemory =
>>> - ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
>>> - return apple_gfx_mmio_unmap_surface_memory(va);
>>> - };
>>> + /*
>>> + * The legacy memory management API is no longer present
>>> + * for the IOSurface mapper as of macOS 15.4.
>>> + */
>>> + if (@available(macOS 15.4, *)) {
>>> + PGMemoryMapDescriptor *memory_map_descriptor = [PGMemoryMapDescriptor new];
>>> + FlatView* fv = address_space_to_flatview(&address_space_memory);
>>> + flatview_for_each_range(fv, apple_gfx_register_memory_cb, memory_map_descriptor);
>>> + /* the device model defines this as a single-page MMIO region, hence 16KB */
>>> + iosfc_desc.mmioLength = 0x10000;
>>> + iosfc_desc.memoryMapDescriptor = memory_map_descriptor;
>>> + } else {
>>> + iosfc_desc.mapMemory =
>>> + ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) {
>>> + *va = apple_gfx_mmio_map_surface_memory(phys, len, ro);
>>> +
>>> + trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va);
>>> +
>>> + return *va != NULL;
>>> + };
>>> +
>>> + iosfc_desc.unmapMemory =
>>> + ^bool(void *va, void *b, void *c, void *d, void *e, void *f) {
>>> + return apple_gfx_mmio_unmap_surface_memory(va);
>>> + };
>>> + }
>>> iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) {
>>> trace_apple_gfx_iosfc_raise_irq(vector);
>>> @@ -223,13 +244,22 @@ static void apple_gfx_mmio_realize(DeviceState *dev, Error **errp)
>>> };
>>> desc.usingIOSurfaceMapper = true;
>>> - s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
>>> + /*
>>> + * Process isolation needs PGMemoryMapDescriptor instead of
>>> + * the legacy memory management interface present in releases
>>> + * older than macOS 15.4.
>>> + */
>>> + if (@available(macOS 15.4, *)) {
>>> + desc.enableProcessIsolation = true;
>>> + }
>>> if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) {
>>> [s->pgiosfc release];
>>> s->pgiosfc = nil;
>>> }
>>> + s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s);
>>> +
>>> [desc release];
>>> desc = nil;
>>> }
>>> diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h
>>> index 3197bd853d..384aee0c5f 100644
>>> --- a/hw/display/apple-gfx.h
>>> +++ b/hw/display/apple-gfx.h
>>> @@ -12,6 +12,7 @@
>>> #include "system/memory.h"
>>> #include "hw/core/qdev-properties.h"
>>> #include "ui/surface.h"
>>> +#include "objc/NSObject.h"
>>> #define TYPE_APPLE_GFX_MMIO "apple-gfx-mmio"
>>> #define TYPE_APPLE_GFX_PCI "apple-gfx-pci"
>>> @@ -23,6 +24,17 @@
>>> @protocol MTLTexture;
>>> @protocol MTLCommandQueue;
>>> +typedef struct PGGuestPhysicalRange_s
>>> +{
>>> + uint64_t physicalAddress;
>>> + uint64_t physicalLength;
>>> + void *hostAddress;
>>> +} PGGuestPhysicalRange_t;
>>> +
>>> +@interface PGMemoryMapDescriptor : NSObject
>>> +-(void)addRange:(PGGuestPhysicalRange_t) range;
>>> +@end
>>> +
>>> typedef QTAILQ_HEAD(, PGTask_s) PGTaskList;
>>> typedef struct AppleGFXDisplayMode {
>>> @@ -68,6 +80,10 @@ void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
>>> uint64_t length, bool read_only,
>>> MemoryRegion **mapping_in_region);
>>> +bool apple_gfx_register_memory_cb(Int128 start, Int128 len,
>>> + const MemoryRegion *mr,
>>> + hwaddr offset_in_region, void *opaque);
>>> +
>>> extern const PropertyInfo qdev_prop_apple_gfx_display_mode;
>>> #endif
>>> diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m
>>> index be0061b9db..4ef8f6dbf3 100644
>>> --- a/hw/display/apple-gfx.m
>>> +++ b/hw/display/apple-gfx.m
>>> @@ -21,6 +21,7 @@
>>> #include "system/address-spaces.h"
>>> #include "system/dma.h"
>>> #include "migration/blocker.h"
>>> +#include "system/memory.h"
>>> #include "ui/console.h"
>>> #include "apple-gfx.h"
>>> #include "trace.h"
>>> @@ -79,6 +80,7 @@ static dispatch_queue_t get_background_queue(void)
>>> GPtrArray *mapped_regions;
>>> };
>>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>>
>> git am detected trailing whitespaces:
>>
>> $ b4 shazam 20260916100021.73825-1-mohamed@unpredictable.fr
>> Looking up 20260916100021.73825-1-mohamed@unpredictable.fr
>> Checking for newer revisions
>> Grabbing search results from lore.kernel.org
>> Analyzing 15 messages in the thread
>> Looking for additional code-review trailers on lore.kernel.org
>> Analyzing 94 code-review messages
>> Checking attestation on all messages, may take a moment...
>> ---
>> ✓ [PATCH RFC v8 1/10] target/arm/emulate: add ISV=0 emulation library with load/store immediate
>> ✓ [PATCH RFC v8 2/10] target/arm/emulate: add load/store register offset
>> ✓ [PATCH RFC v8 3/10] target/arm/emulate: add load/store pair
>> ✓ [PATCH RFC v8 4/10] target/arm/hvf, whpx: wire ISV=0 emulation for data aborts
>> ✓ [PATCH RFC v8 5/10] vmapple: apple-gfx: make it work on the latest macOS release
>> ✓ [PATCH RFC v8 6/10] hw/vmapple: aes: convert MAX_LEN to a #define
>> ✓ [PATCH RFC v8 7/10] Revert "hw/arm: Do not build VMapple machine by default"
>> ✓ [PATCH RFC v8 8/10] vmapple: add gicv2m
>> ✓ [PATCH RFC v8 9/10] vmapple, gicv2m: add macOS compat quirk
>> ✓ [PATCH RFC v8 10/10] hw/vmapple: virtio-blk: allow FileVault off
>> ---
>> ✓ Signed: DKIM/unpredictable.fr
>> ---
>> Total patches: 10
>> ---
>> Applying: target/arm/emulate: add ISV=0 emulation library with load/store immediate
>> Applying: target/arm/emulate: add load/store register offset
>> Applying: target/arm/emulate: add load/store pair
>> Applying: target/arm/hvf, whpx: wire ISV=0 emulation for data aborts
>> Applying: vmapple: apple-gfx: make it work on the latest macOS release
>> Applying: hw/vmapple: aes: convert MAX_LEN to a #define
>> Applying: Revert "hw/arm: Do not build VMapple machine by default"
>> Applying: vmapple: add gicv2m
>> Applying: vmapple, gicv2m: add macOS compat quirk
>> Applying: hw/vmapple: virtio-blk: allow FileVault off
>> /home/me/q/var/qemu/.git/rebase-apply/patch:90: trailing whitespace.
>> /*
>> /home/me/q/var/qemu/.git/rebase-apply/patch:166: trailing whitespace.
>> API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>> /home/me/q/var/qemu/.git/rebase-apply/patch:272: trailing whitespace.
>> /*
>> /home/me/q/var/qemu/.git/rebase-apply/patch:275: trailing whitespace.
>> * on macOS 15.4 onwards.
>> warning: 4 lines add whitespace errors.
>>
>>> static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len)
>>> {
>>> mach_vm_address_t task_mem;
>>> @@ -102,6 +104,7 @@ static dispatch_queue_t get_background_queue(void)
>>> return task;
>>> }
>>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>>> static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
>>> {
>>> GPtrArray *regions = task->mapped_regions;
>>> @@ -151,6 +154,7 @@ static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task)
>>> return host_ptr;
>>> }
>>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>>> static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
>>> uint64_t virtual_offset,
>>> PGPhysicalMemoryRange_t *ranges,
>>> @@ -207,6 +211,7 @@ static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task,
>>> return success;
>>> }
>>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>>> static void apple_gfx_task_unmap_memory(AppleGFXState *s, PGTask_t *task,
>>> uint64_t virtual_offset, uint64_t length)
>>> {
>>> @@ -596,6 +601,36 @@ void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name)
>>> /* TODO: PVG framework supports serialising device state: integrate it! */
>>> }
>>> +@interface PGDeviceDescriptor (IOSurfaceMapper)
>>> +@property (readwrite, nonatomic, copy, nullable) PGMemoryMapDescriptor* memoryMapDescriptor;
>>> +@end
>>> +
>>> +bool apple_gfx_register_memory_cb(Int128 start, Int128 len,
>>> + const MemoryRegion *mr,
>>> + hwaddr offset_in_region, void *opaque) {
>>> + PGGuestPhysicalRange_t range;
>>> + PGMemoryMapDescriptor *memory_map_descriptor = opaque;
>>> + if (memory_access_is_direct(mr, true, MEMTXATTRS_UNSPECIFIED)) {
>>> + range.physicalAddress = start;
>>
>> Please convert Int128 through its helpers. This assigns Int128 directly into the two uint64_t fields. With CONFIG_TCG_INTERPRETER, QEMU deliberately represents Int128 as a structure, even on hosts supporting native __int128. Both assignments fail compilation. A focused Clang check using the actual QEMU headers reproduced the incompatible-type errors; int128_get64() provides the intended checked conversion.
>>
>>> + range.physicalLength = len;
>>> + range.hostAddress = memory_region_get_ram_ptr(mr);
>>
>> The FlatView slice offset, offset_in_region, needs to be honored when registering RAM.
>>
>> For apple-gfx-pci, ordinary PC RAM above 4 GiB aliases machine->ram starting at below_4g_mem_size; GPU accesses to that range now alias low RAM instead. Other clipped FlatView ranges have the same problem. The previous mapping path adds the translated offset correctly.
>>
>>> + [memory_map_descriptor addRange:range];
>>> + }
>>> + return false;
>>> +}
>>> +
>>> +static void apple_gfx_register_memory(AppleGFXState *s,
>>> + PGDeviceDescriptor *desc)
>>> +{
>>> + PGMemoryMapDescriptor* memoryMapDescriptor = [PGMemoryMapDescriptor new];
>>> +
>>> + FlatView* fv = address_space_to_flatview(&address_space_memory);
>>> + flatview_for_each_range(fv, apple_gfx_register_memory_cb, memoryMapDescriptor);
>>> +
>>> + desc.memoryMapDescriptor = memoryMapDescriptor;
>>> +}
>>> +
>>> +API_OBSOLETED("Legacy ParavirtualizedGraphics", macos(11.0, 27.0, 27.0))
>>> static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s,
>>> PGDeviceDescriptor *desc)
>>> {
>>> @@ -667,16 +702,25 @@ static void new_frame_handler_bh(void *opaque)
>>> BQL_LOCK_GUARD();
>>> set_mode(s, sizeInPixels.x, sizeInPixels.y);
>>> };
>>> - disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
>>> - PGDisplayCoord_t hotspot) {
>>> - AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
>>> - job->s = s;
>>> - job->glyph = glyph;
>>> - job->hotspot = hotspot;
>>> - [glyph retain];
>>> - aio_bh_schedule_oneshot(qemu_get_aio_context(),
>>> - set_cursor_glyph, job);
>>> - };
>>> +
>>> + if (@available(macOS 27.0, *)) {
>>> +
>>> + } else {
>>> +#ifndef __MAC_27_0
>>> + disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph,
>>> + PGDisplayCoord_t hotspot) {
>>> + AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job));
>>> + job->s = s;
>>> + job->glyph = glyph;
>>> + job->hotspot = hotspot;
>>> + [glyph retain];
>>> + aio_bh_schedule_oneshot(qemu_get_aio_context(),
>>> + set_cursor_glyph, job);
>>> + };
>>> +#else
>>> + abort();
>>> +#endif
>>
>> I wonder what is this #ifdefs for. It tests SDK macro __MAC_27_0, whereas the surrounding @available tests the runtime OS. A binary built with that SDK aborts during display realization on every pre-27 host, including macOS 26. Older hosts also encounter explicit aborts in the memory/device fallback paths. Turning supported runtime fallback paths into unconditional process termination depending when SDK is available does not look correct.
>
> Hi,
>
> Obsoleted APIs aren’t accessible from a newer SDK even for targeting older OS versions.
> The compiler blocks it even when I use an if (@available) for the newer version before…
>
> If you have an idea on how to work around that it’d be great.
I checked this with the macOS 27 SDK and confirmed that it no longer
exports PGNewDeviceWithDescriptor. API_OBSOLETED() is not appropriate
here: it checks the minimum deployment target, not the SDK version.
Please use __MAC_27_0 consistently to conditionally compile code that
depends on the SDK version. If a build cannot support the runtime OS
version, report an error with error_setg() and fail device realization
instead of calling abort().
Regards,
Akihiko Odaki
>
>>
>> Regards,
>> Akihiko Odaki
>>
>>> + }
>>> disp_desc.cursorShowHandler = ^(BOOL show) {
>>> trace_apple_gfx_cursor_show(show);
>>> qatomic_set(&s->cursor_show, show);
>>> @@ -763,11 +807,33 @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
>>> desc.device = s->mtl;
>>> - apple_gfx_register_task_mapping_handlers(s, desc);
>>> + /*
>>> + * The legacy memory management interface doesn't allow for
>>> + * vGPU sandboxing. As such, always use the new interface
>>> + * on macOS 15.4 onwards.
>>> + */
>>> + if (@available(macOS 15.4, *)) {
>>> + apple_gfx_register_memory(s, desc);
>>> + } else {
>>> +#ifndef __MAC_27_0
>>> + apple_gfx_register_task_mapping_handlers(s, desc);
>>> +#else
>>> + abort();
>>> +#endif
>>> + }
>>> s->cursor_show = true;
>>> - s->pgdev = PGNewDeviceWithDescriptor(desc);
>>> + if (@available(macOS 15.2, *)) {
>>> + s->pgdev = PGCreateDeviceWithDescriptor(desc);
>>> + }
>>> + else {
>>> +#ifndef __MAC_27_0
>>> + s->pgdev = PGNewDeviceWithDescriptor(desc);
>>> +#else
>>> + abort();
>>> +#endif
>>> + }
>>> disp_desc = apple_gfx_prepare_display_descriptor(s);
>>> /*
>
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2026-10-04 6:26 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-16 10:00 [RFC PATCH v8 00/10] vmapple: making it work on the latest macOS host releases Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 01/10] target/arm/emulate: add ISV=0 emulation library with load/store immediate Mohamed Mediouni
2026-09-17 20:32 ` Akihiko Odaki
2026-09-16 10:00 ` [RFC PATCH v8 02/10] target/arm/emulate: add load/store register offset Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 03/10] target/arm/emulate: add load/store pair Mohamed Mediouni
2026-09-17 20:33 ` Akihiko Odaki
2026-09-16 10:00 ` [RFC PATCH v8 04/10] target/arm/hvf, whpx: wire ISV=0 emulation for data aborts Mohamed Mediouni
2026-09-17 20:37 ` Akihiko Odaki
2026-09-16 10:00 ` [RFC PATCH v8 05/10] vmapple: apple-gfx: make it work on the latest macOS release Mohamed Mediouni
2026-09-17 20:37 ` Akihiko Odaki
2026-09-17 21:49 ` Mohamed Mediouni
2026-10-04 6:24 ` Akihiko Odaki
2026-09-16 10:00 ` [RFC PATCH v8 06/10] hw/vmapple: aes: convert MAX_LEN to a #define Mohamed Mediouni
2026-09-16 11:20 ` Peter Maydell
2026-09-16 11:34 ` Daniel P. Berrangé
2026-09-16 10:00 ` [RFC PATCH v8 07/10] Revert "hw/arm: Do not build VMapple machine by default" Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 08/10] vmapple: add gicv2m Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 09/10] vmapple, gicv2m: add macOS compat quirk Mohamed Mediouni
2026-09-16 10:51 ` Peter Maydell
2026-09-16 10:56 ` Mohamed Mediouni
2026-09-16 10:00 ` [RFC PATCH v8 10/10] hw/vmapple: virtio-blk: allow FileVault off Mohamed Mediouni
2026-09-17 20:37 ` Akihiko Odaki
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.