* [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range
@ 2026-03-15 16:38 Zenghui Yu
2026-03-16 9:40 ` Alex Bennée
2026-03-20 10:52 ` Peter Maydell
0 siblings, 2 replies; 8+ messages in thread
From: Zenghui Yu @ 2026-03-15 16:38 UTC (permalink / raw)
To: qemu-arm, qemu-devel; +Cc: agraf, peter.maydell, Zenghui Yu
It seems that hvf doesn't deal with the abort generated when guest tries to
execute instructions outside of the valid physical memory range, for
unknown reason. The abort is forwarded to userspace and QEMU doesn't handle
it either, which ends up with faulting on the same instruction infinitely.
This was noticed by the kvm-unit-tests/selftest-vectors-kernel failure:
timeout -k 1s --foreground 90s /opt/homebrew/bin/qemu-system-aarch64 \
-nodefaults -machine virt -accel hvf -cpu host \
-device virtio-serial-device -device virtconsole,chardev=ctd \
-chardev testdev,id=ctd -device pci-testdev -display none \
-serial stdio -kernel arm/selftest.flat -smp 1 -append vectors-kernel
PASS: selftest: vectors-kernel: und
PASS: selftest: vectors-kernel: svc
qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
[...]
It's apparent that the guest is braindead and it's unsure what prevents hvf
from injecting an abort directly in that case. Try to deal with the insane
guest in QEMU by injecting an SEA back into it in the EC_INSNABORT
emulation path.
Signed-off-by: Zenghui Yu <zenghui.yu@linux.dev>
---
target/arm/hvf/hvf.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index aabc7d32c1..54d6ea469c 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -2332,9 +2332,32 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp)
bool ea = (syndrome >> 9) & 1;
bool s1ptw = (syndrome >> 7) & 1;
uint32_t ifsc = (syndrome >> 0) & 0x3f;
+ uint64_t ipa = excp->physical_address;
+ AddressSpace *as = cpu_get_address_space(cpu, ARMASIdx_NS);
+ hwaddr xlat;
+ MemoryRegion *mr;
+
+ cpu_synchronize_state(cpu);
trace_hvf_insn_abort(env->pc, set, fnv, ea, s1ptw, ifsc);
+ /*
+ * TODO: If s1ptw, this is an error in the guest os page tables.
+ * Inject the exception into the guest.
+ */
+ assert(!s1ptw);
+
+ mr = address_space_translate(as, ipa, &xlat, NULL, false,
+ MEMTXATTRS_UNSPECIFIED);
+ if (unlikely(!memory_region_is_ram(mr))) {
+ uint32_t syn;
+
+ /* inject an SEA back into the guest */
+ syn = syn_insn_abort(arm_current_el(env) == 1, ea, false, 0x10);
+ hvf_raise_exception(cpu, EXCP_PREFETCH_ABORT, syn, 1);
+ break;
+ }
+
/* fall through */
}
default:
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range
2026-03-15 16:38 [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range Zenghui Yu
@ 2026-03-16 9:40 ` Alex Bennée
2026-03-16 10:05 ` Mohamed Mediouni
2026-03-16 10:54 ` Zenghui Yu
2026-03-20 10:52 ` Peter Maydell
1 sibling, 2 replies; 8+ messages in thread
From: Alex Bennée @ 2026-03-16 9:40 UTC (permalink / raw)
To: Zenghui Yu; +Cc: qemu-arm, qemu-devel, agraf, peter.maydell
Zenghui Yu <zenghui.yu@linux.dev> writes:
> It seems that hvf doesn't deal with the abort generated when guest tries to
> execute instructions outside of the valid physical memory range, for
> unknown reason. The abort is forwarded to userspace and QEMU doesn't handle
> it either, which ends up with faulting on the same instruction infinitely.
>
> This was noticed by the kvm-unit-tests/selftest-vectors-kernel failure:
>
> timeout -k 1s --foreground 90s /opt/homebrew/bin/qemu-system-aarch64 \
> -nodefaults -machine virt -accel hvf -cpu host \
> -device virtio-serial-device -device virtconsole,chardev=ctd \
> -chardev testdev,id=ctd -device pci-testdev -display none \
> -serial stdio -kernel arm/selftest.flat -smp 1 -append
> vectors-kernel
Have you got patches for teaching kvm-unit-tests about hvf or are you
running these all manually? I tried building on the Mac we have but it
failed the build and the docs only mention x86.
>
> PASS: selftest: vectors-kernel: und
> PASS: selftest: vectors-kernel: svc
> qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
I think this is running:
static bool check_pabt(void)
{
enum vector v = check_vector_prep();
install_exception_handler(v, ESR_EL1_EC_IABT_EL1, pabt_handler);
test_exception("adrp x9, check_pabt_invalid_paddr\n"
"add x9, x9, :lo12:check_pabt_invalid_paddr\n"
"ldr x9, [x9]\n",
"blr x9\n",
"", "x9", "x30");
install_exception_handler(v, ESR_EL1_EC_IABT_EL1, NULL);
return pabt_works;
}
which is expecting 0x21 - instruction abort at the same exception level.
I wonder why there is the difference.
> [...]
>
> It's apparent that the guest is braindead and it's unsure what prevents hvf
> from injecting an abort directly in that case. Try to deal with the insane
> guest in QEMU by injecting an SEA back into it in the EC_INSNABORT
> emulation path.
>
> Signed-off-by: Zenghui Yu <zenghui.yu@linux.dev>
> ---
> target/arm/hvf/hvf.c | 23 +++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
>
> diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
> index aabc7d32c1..54d6ea469c 100644
> --- a/target/arm/hvf/hvf.c
> +++ b/target/arm/hvf/hvf.c
> @@ -2332,9 +2332,32 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp)
> bool ea = (syndrome >> 9) & 1;
> bool s1ptw = (syndrome >> 7) & 1;
> uint32_t ifsc = (syndrome >> 0) & 0x3f;
> + uint64_t ipa = excp->physical_address;
> + AddressSpace *as = cpu_get_address_space(cpu, ARMASIdx_NS);
> + hwaddr xlat;
> + MemoryRegion *mr;
> +
> + cpu_synchronize_state(cpu);
>
> trace_hvf_insn_abort(env->pc, set, fnv, ea, s1ptw, ifsc);
>
> + /*
> + * TODO: If s1ptw, this is an error in the guest os page tables.
> + * Inject the exception into the guest.
> + */
> + assert(!s1ptw);
> +
> + mr = address_space_translate(as, ipa, &xlat, NULL, false,
> + MEMTXATTRS_UNSPECIFIED);
> + if (unlikely(!memory_region_is_ram(mr))) {
> + uint32_t syn;
> +
> + /* inject an SEA back into the guest */
> + syn = syn_insn_abort(arm_current_el(env) == 1, ea, false, 0x10);
> + hvf_raise_exception(cpu, EXCP_PREFETCH_ABORT, syn, 1);
> + break;
> + }
> +
> /* fall through */
> }
> default:
I need the check the exception paths for KVM and TCG. I guess for the
KVM case it is all in the kernel?
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range
2026-03-16 9:40 ` Alex Bennée
@ 2026-03-16 10:05 ` Mohamed Mediouni
2026-03-16 10:54 ` Zenghui Yu
1 sibling, 0 replies; 8+ messages in thread
From: Mohamed Mediouni @ 2026-03-16 10:05 UTC (permalink / raw)
To: Alex Bennée; +Cc: Zenghui Yu, qemu-arm, qemu-devel, agraf, peter.maydell
[-- Attachment #1: Type: text/plain, Size: 5059 bytes --]
> On 16. Mar 2026, at 10:40, Alex Bennée <alex.bennee@linaro.org> wrote:
>
> Zenghui Yu <zenghui.yu@linux.dev> writes:
>
>> It seems that hvf doesn't deal with the abort generated when guest tries to
>> execute instructions outside of the valid physical memory range, for
>> unknown reason. The abort is forwarded to userspace and QEMU doesn't handle
>> it either, which ends up with faulting on the same instruction infinitely.
>>
Hello,
This one is a bit complicated.
We don’t fully implement the whole vmapple device model for example
- and I have hacks locally around that to run new macOS releases...
I think at this point that a lot of the exception story would be better off
in a common library between backends, like the upcoming target/arm/emulate for isv=0….
Interestingly, behaviour on x86 also has expectations around accessing an unmapped memory region, and _that_
is actually leveraged by the Linux kernel with https://github.com/torvalds/linux/commit/ab8131028710d009ab93d6bffd2a2749ade909b0
only checking CPUID…
>> This was noticed by the kvm-unit-tests/selftest-vectors-kernel failure:
>>
>> timeout -k 1s --foreground 90s /opt/homebrew/bin/qemu-system-aarch64 \
>> -nodefaults -machine virt -accel hvf -cpu host \
>> -device virtio-serial-device -device virtconsole,chardev=ctd \
>> -chardev testdev,id=ctd -device pci-testdev -display none \
>> -serial stdio -kernel arm/selftest.flat -smp 1 -append
>> vectors-kernel
>
> Have you got patches for teaching kvm-unit-tests about hvf or are you
> running these all manually? I tried building on the Mac we have but it
> failed the build and the docs only mention x86.
It builds fine from aarch64-elf cross toolchain and runs as usual with accel=hvf.
>
>>
>> PASS: selftest: vectors-kernel: und
>> PASS: selftest: vectors-kernel: svc
>> qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
>> qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
>> qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
>
> I think this is running:
>
> static bool check_pabt(void)
> {
> enum vector v = check_vector_prep();
>
> install_exception_handler(v, ESR_EL1_EC_IABT_EL1, pabt_handler);
>
> test_exception("adrp x9, check_pabt_invalid_paddr\n"
> "add x9, x9, :lo12:check_pabt_invalid_paddr\n"
> "ldr x9, [x9]\n",
> "blr x9\n",
> "", "x9", "x30");
>
> install_exception_handler(v, ESR_EL1_EC_IABT_EL1, NULL);
>
> return pabt_works;
> }
>
> which is expecting 0x21 - instruction abort at the same exception level.
> I wonder why there is the difference.
>
>
>> [...]
>>
>> It's apparent that the guest is braindead and it's unsure what prevents hvf
>> from injecting an abort directly in that case. Try to deal with the insane
>> guest in QEMU by injecting an SEA back into it in the EC_INSNABORT
>> emulation path.
>>
>> Signed-off-by: Zenghui Yu <zenghui.yu@linux.dev>
>> ---
>> target/arm/hvf/hvf.c | 23 +++++++++++++++++++++++
>> 1 file changed, 23 insertions(+)
>>
>> diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
>> index aabc7d32c1..54d6ea469c 100644
>> --- a/target/arm/hvf/hvf.c
>> +++ b/target/arm/hvf/hvf.c
>> @@ -2332,9 +2332,32 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp)
>> bool ea = (syndrome >> 9) & 1;
>> bool s1ptw = (syndrome >> 7) & 1;
>> uint32_t ifsc = (syndrome >> 0) & 0x3f;
>> + uint64_t ipa = excp->physical_address;
>> + AddressSpace *as = cpu_get_address_space(cpu, ARMASIdx_NS);
>> + hwaddr xlat;
>> + MemoryRegion *mr;
>> +
>> + cpu_synchronize_state(cpu);
>>
>> trace_hvf_insn_abort(env->pc, set, fnv, ea, s1ptw, ifsc);
>>
>> + /*
>> + * TODO: If s1ptw, this is an error in the guest os page tables.
>> + * Inject the exception into the guest.
>> + */
>> + assert(!s1ptw);
>> +
>> + mr = address_space_translate(as, ipa, &xlat, NULL, false,
>> + MEMTXATTRS_UNSPECIFIED);
>> + if (unlikely(!memory_region_is_ram(mr))) {
>> + uint32_t syn;
>> +
>> + /* inject an SEA back into the guest */
>> + syn = syn_insn_abort(arm_current_el(env) == 1, ea, false, 0x10);
>> + hvf_raise_exception(cpu, EXCP_PREFETCH_ABORT, syn, 1);
>> + break;
>> + }
>> +
>> /* fall through */
>> }
>> default:
>
> I need the check the exception paths for KVM and TCG. I guess for the
> KVM case it is all in the kernel?
KVM does a whole lot in the kernel that other backends don’t.
Perhaps it’d be a good idea to introduce a reduced capability KVM cap that shrinks
the number of things KVM does.
> --
> Alex Bennée
> Virtualisation Tech Lead @ Linaro
>
[-- Attachment #2: Type: text/html, Size: 8304 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range
2026-03-16 9:40 ` Alex Bennée
2026-03-16 10:05 ` Mohamed Mediouni
@ 2026-03-16 10:54 ` Zenghui Yu
1 sibling, 0 replies; 8+ messages in thread
From: Zenghui Yu @ 2026-03-16 10:54 UTC (permalink / raw)
To: Alex Bennée; +Cc: qemu-arm, qemu-devel, agraf, peter.maydell
Hi Alex,
On 3/16/26 5:40 PM, Alex Bennée wrote:
> Zenghui Yu <zenghui.yu@linux.dev> writes:
>
> > It seems that hvf doesn't deal with the abort generated when guest tries to
> > execute instructions outside of the valid physical memory range, for
> > unknown reason. The abort is forwarded to userspace and QEMU doesn't handle
> > it either, which ends up with faulting on the same instruction infinitely.
> >
> > This was noticed by the kvm-unit-tests/selftest-vectors-kernel failure:
> >
> > timeout -k 1s --foreground 90s /opt/homebrew/bin/qemu-system-aarch64 \
> > -nodefaults -machine virt -accel hvf -cpu host \
> > -device virtio-serial-device -device virtconsole,chardev=ctd \
> > -chardev testdev,id=ctd -device pci-testdev -display none \
> > -serial stdio -kernel arm/selftest.flat -smp 1 -append vectors-kernel
>
> Have you got patches for teaching kvm-unit-tests about hvf or are you
> running these all manually? I tried building on the Mac we have but it
> failed the build and the docs only mention x86.
No more patches. This is how I build and test it on my M1:
./configure --arch=aarch64 --page-size=16k --cross-prefix=aarch64-elf-
make && make standalone
QEMU=qemu-system-aarch64 ACCEL=hvf ./run_tests.sh
The command line above is copied from logs/selftest-vectors-kernel.log.
> > PASS: selftest: vectors-kernel: und
> > PASS: selftest: vectors-kernel: svc
> > qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> > qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> > qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
>
> I think this is running:
>
> static bool check_pabt(void)
> {
> enum vector v = check_vector_prep();
>
> install_exception_handler(v, ESR_EL1_EC_IABT_EL1, pabt_handler);
>
> test_exception("adrp x9, check_pabt_invalid_paddr\n"
> "add x9, x9, :lo12:check_pabt_invalid_paddr\n"
> "ldr x9, [x9]\n",
> "blr x9\n",
> "", "x9", "x30");
>
> install_exception_handler(v, ESR_EL1_EC_IABT_EL1, NULL);
>
> return pabt_works;
> }
>
> which is expecting 0x21 - instruction abort at the same exception level.
> I wonder why there is the difference.
It's the guest that executes an instruction outside of the valid memory
range. From hypervisor's perspective, it's an instruction abort from a
lower EL, so we get the syndrome saying that EC=0x20. We then decide
that we should emulate the hardware behavior by injecting an SEA into
the insane guest.
And from guest's perspective, it's an instruction abort caused by itself
(thus EC=0x21 - instruction abort taken without a change in EL).
> > [...]
> >
> > It's apparent that the guest is braindead and it's unsure what prevents hvf
> > from injecting an abort directly in that case. Try to deal with the insane
> > guest in QEMU by injecting an SEA back into it in the EC_INSNABORT
> > emulation path.
> >
> > Signed-off-by: Zenghui Yu <zenghui.yu@linux.dev>
> > ---
> > target/arm/hvf/hvf.c | 23 +++++++++++++++++++++++
> > 1 file changed, 23 insertions(+)
> >
> > diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
> > index aabc7d32c1..54d6ea469c 100644
> > --- a/target/arm/hvf/hvf.c
> > +++ b/target/arm/hvf/hvf.c
> > @@ -2332,9 +2332,32 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp)
> > bool ea = (syndrome >> 9) & 1;
> > bool s1ptw = (syndrome >> 7) & 1;
> > uint32_t ifsc = (syndrome >> 0) & 0x3f;
> > + uint64_t ipa = excp->physical_address;
> > + AddressSpace *as = cpu_get_address_space(cpu, ARMASIdx_NS);
> > + hwaddr xlat;
> > + MemoryRegion *mr;
> > +
> > + cpu_synchronize_state(cpu);
> >
> > trace_hvf_insn_abort(env->pc, set, fnv, ea, s1ptw, ifsc);
> >
> > + /*
> > + * TODO: If s1ptw, this is an error in the guest os page tables.
> > + * Inject the exception into the guest.
> > + */
> > + assert(!s1ptw);
> > +
> > + mr = address_space_translate(as, ipa, &xlat, NULL, false,
> > + MEMTXATTRS_UNSPECIFIED);
> > + if (unlikely(!memory_region_is_ram(mr))) {
> > + uint32_t syn;
> > +
> > + /* inject an SEA back into the guest */
> > + syn = syn_insn_abort(arm_current_el(env) == 1, ea, false, 0x10);
> > + hvf_raise_exception(cpu, EXCP_PREFETCH_ABORT, syn, 1);
> > + break;
> > + }
> > +
> > /* fall through */
> > }
> > default:
>
> I need the check the exception paths for KVM and TCG. I guess for the
> KVM case it is all in the kernel?
Yup, in kvm_handle_guest_abort().
Thanks,
Zenghui
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range
2026-03-15 16:38 [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range Zenghui Yu
2026-03-16 9:40 ` Alex Bennée
@ 2026-03-20 10:52 ` Peter Maydell
2026-03-21 17:09 ` Zenghui Yu
1 sibling, 1 reply; 8+ messages in thread
From: Peter Maydell @ 2026-03-20 10:52 UTC (permalink / raw)
To: Zenghui Yu; +Cc: qemu-arm, qemu-devel, agraf
On Sun, 15 Mar 2026 at 16:39, Zenghui Yu <zenghui.yu@linux.dev> wrote:
>
> It seems that hvf doesn't deal with the abort generated when guest tries to
> execute instructions outside of the valid physical memory range, for
> unknown reason. The abort is forwarded to userspace and QEMU doesn't handle
> it either, which ends up with faulting on the same instruction infinitely.
>
> This was noticed by the kvm-unit-tests/selftest-vectors-kernel failure:
>
> timeout -k 1s --foreground 90s /opt/homebrew/bin/qemu-system-aarch64 \
> -nodefaults -machine virt -accel hvf -cpu host \
> -device virtio-serial-device -device virtconsole,chardev=ctd \
> -chardev testdev,id=ctd -device pci-testdev -display none \
> -serial stdio -kernel arm/selftest.flat -smp 1 -append vectors-kernel
>
> PASS: selftest: vectors-kernel: und
> PASS: selftest: vectors-kernel: svc
> qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> [...]
>
> It's apparent that the guest is braindead and it's unsure what prevents hvf
> from injecting an abort directly in that case. Try to deal with the insane
> guest in QEMU by injecting an SEA back into it in the EC_INSNABORT
> emulation path.
Shouldn't that be an AddressSize fault, not an external abort?
My guess would be that hvf is handing us the EC_INSNABORT
cases for the same reason it hands us EC_DATABORT cases --
we might have some ability to emulate the access. We probably
also get this for cases like "guest tries to execute out of
an MMIO device".
What happens for a data access to this kind of
out-of-the-physical-memory-range address? Does hvf
pass it back to us, or handle it internally?
Is the problem here a bogus virtual address from the guest's
point of view, or a valid virtual address that the guest's
page tables have translated to an invalid (intermediate)
physical address ?
> Signed-off-by: Zenghui Yu <zenghui.yu@linux.dev>
> ---
> target/arm/hvf/hvf.c | 23 +++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
>
> diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
> index aabc7d32c1..54d6ea469c 100644
> --- a/target/arm/hvf/hvf.c
> +++ b/target/arm/hvf/hvf.c
> @@ -2332,9 +2332,32 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp)
> bool ea = (syndrome >> 9) & 1;
> bool s1ptw = (syndrome >> 7) & 1;
> uint32_t ifsc = (syndrome >> 0) & 0x3f;
> + uint64_t ipa = excp->physical_address;
> + AddressSpace *as = cpu_get_address_space(cpu, ARMASIdx_NS);
> + hwaddr xlat;
> + MemoryRegion *mr;
> +
> + cpu_synchronize_state(cpu);
>
> trace_hvf_insn_abort(env->pc, set, fnv, ea, s1ptw, ifsc);
>
> + /*
> + * TODO: If s1ptw, this is an error in the guest os page tables.
> + * Inject the exception into the guest.
> + */
> + assert(!s1ptw);
> +
> + mr = address_space_translate(as, ipa, &xlat, NULL, false,
> + MEMTXATTRS_UNSPECIFIED);
> + if (unlikely(!memory_region_is_ram(mr))) {
This doesn't look like the right kind of check, given the
stated problem. Addresses can be in range but not have RAM.
> + uint32_t syn;
> +
> + /* inject an SEA back into the guest */
> + syn = syn_insn_abort(arm_current_el(env) == 1, ea, false, 0x10);
> + hvf_raise_exception(cpu, EXCP_PREFETCH_ABORT, syn, 1);
> + break;
> + }
> +
> /* fall through */
This "fall through" remains not correct, I think, and it's kind
of a big part of the problem here -- if we get an EC_INSNABORT
handed to us by hvf, then we could:
* stop execution, exiting QEMU (as a "situation we can't
handle and don't know what to do with")
* advance the PC over the insn (questionable...)
* feed some kind of exception into the guest
but "continue execution of the guest without changing PC at all"
is definitely wrong. A fix for this problem ought to involve
changing the EC_INSNABORT case so that it no lenger does that
"fall through to default" at all.
thanks
-- PMM
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range
2026-03-20 10:52 ` Peter Maydell
@ 2026-03-21 17:09 ` Zenghui Yu
2026-03-21 17:26 ` Mohamed Mediouni
0 siblings, 1 reply; 8+ messages in thread
From: Zenghui Yu @ 2026-03-21 17:09 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-arm, qemu-devel, agraf
Hi Peter,
On 3/20/26 6:52 PM, Peter Maydell wrote:
> On Sun, 15 Mar 2026 at 16:39, Zenghui Yu <zenghui.yu@linux.dev> wrote:
> >
> > It seems that hvf doesn't deal with the abort generated when guest tries to
> > execute instructions outside of the valid physical memory range, for
> > unknown reason. The abort is forwarded to userspace and QEMU doesn't handle
> > it either, which ends up with faulting on the same instruction infinitely.
> >
> > This was noticed by the kvm-unit-tests/selftest-vectors-kernel failure:
> >
> > timeout -k 1s --foreground 90s /opt/homebrew/bin/qemu-system-aarch64 \
> > -nodefaults -machine virt -accel hvf -cpu host \
> > -device virtio-serial-device -device virtconsole,chardev=ctd \
> > -chardev testdev,id=ctd -device pci-testdev -display none \
> > -serial stdio -kernel arm/selftest.flat -smp 1 -append vectors-kernel
> >
> > PASS: selftest: vectors-kernel: und
> > PASS: selftest: vectors-kernel: svc
> > qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> > qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> > qemu-system-aarch64: 0xffffc000: unhandled exception ec=0x20
> > [...]
> >
> > It's apparent that the guest is braindead and it's unsure what prevents hvf
> > from injecting an abort directly in that case. Try to deal with the insane
> > guest in QEMU by injecting an SEA back into it in the EC_INSNABORT
> > emulation path.
>
> Shouldn't that be an AddressSize fault, not an external abort?
I should have described this problem more clearly, see below.
> My guess would be that hvf is handing us the EC_INSNABORT
> cases for the same reason it hands us EC_DATABORT cases --
> we might have some ability to emulate the access. We probably
> also get this for cases like "guest tries to execute out of
> an MMIO device".
>
> What happens for a data access to this kind of
> out-of-the-physical-memory-range address? Does hvf
> pass it back to us, or handle it internally?
>
> Is the problem here a bogus virtual address from the guest's
> point of view, or a valid virtual address that the guest's
> page tables have translated to an invalid (intermediate)
> physical address ?
After adding `--trace "hvf_vm_map" --trace "hvf_vm_unmap"` to the
testing command line, I got:
hvf_vm_map paddr:0x0000000000000000 size:0x04000000 vaddr:0x112a34000
flags:0x05/R-X
hvf_vm_map paddr:0x0000000004000000 size:0x04000000 vaddr:0x116a38000
flags:0x05/R-X
hvf_vm_map paddr:0x0000000040000000 size:0x08000000 vaddr:0x10aa30000
flags:0x07/RWX
The guest then maps a VA 0xffffc000 to IPA 0x48000000 (an IPA that
hasn't been "registered" to hvf by hv_vm_map(), and I imprecisely refer
to it as an insn in invalid memory range) and sets PC to 0xffffc000,
expecting to receive an insn abort with IFSC equals to 0x10 (i.e., an
SEA). So the problem here is "a valid virtual address that the guest's
page tables have translated to an invalid (intermediate) physical
address".
This is what check_vectors()/check_pabt_init()/check_pabt() have tested,
if you can be bothered to have a look at kvm-unit-tests. ;-)
As for the AddressSize fault, I checked that on M1, we expose
ID_AA64MMFR0_EL1.PARange as 0b0001 to guest, so the advertised PA size
is 36bits (i.e., 64GB).
After hacking KUT to let the guest map a VA to IPA 0x1000000000 (an IPA
right after 64GB) and execute an insn on that VA, the guest receives an
insn abort with IFSC equals to 0x03 (Hello, AddressSize fault!). We can
_infer_ from that that the AddressSize fault is injected internally by
hvf.
I haven't tried the "data access" side, sorry. Without some docs
describing which syndromes can be forwarded to userspace, and more
importantly, given my limited understanding of hvf, I think I'd better
stop making incomplete hacks (like this patch) to hvf. :-)
> > Signed-off-by: Zenghui Yu <zenghui.yu@linux.dev>
> > ---
> > target/arm/hvf/hvf.c | 23 +++++++++++++++++++++++
> > 1 file changed, 23 insertions(+)
> >
> > diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
> > index aabc7d32c1..54d6ea469c 100644
> > --- a/target/arm/hvf/hvf.c
> > +++ b/target/arm/hvf/hvf.c
> > @@ -2332,9 +2332,32 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp)
> > bool ea = (syndrome >> 9) & 1;
> > bool s1ptw = (syndrome >> 7) & 1;
> > uint32_t ifsc = (syndrome >> 0) & 0x3f;
> > + uint64_t ipa = excp->physical_address;
> > + AddressSpace *as = cpu_get_address_space(cpu, ARMASIdx_NS);
> > + hwaddr xlat;
> > + MemoryRegion *mr;
> > +
> > + cpu_synchronize_state(cpu);
> >
> > trace_hvf_insn_abort(env->pc, set, fnv, ea, s1ptw, ifsc);
> >
> > + /*
> > + * TODO: If s1ptw, this is an error in the guest os page tables.
> > + * Inject the exception into the guest.
> > + */
> > + assert(!s1ptw);
> > +
> > + mr = address_space_translate(as, ipa, &xlat, NULL, false,
> > + MEMTXATTRS_UNSPECIFIED);
> > + if (unlikely(!memory_region_is_ram(mr))) {
>
> This doesn't look like the right kind of check, given the
> stated problem. Addresses can be in range but not have RAM.
>
> > + uint32_t syn;
> > +
> > + /* inject an SEA back into the guest */
> > + syn = syn_insn_abort(arm_current_el(env) == 1, ea, false, 0x10);
> > + hvf_raise_exception(cpu, EXCP_PREFETCH_ABORT, syn, 1);
> > + break;
> > + }
> > +
> > /* fall through */
>
> This "fall through" remains not correct, I think, and it's kind
> of a big part of the problem here -- if we get an EC_INSNABORT
> handed to us by hvf, then we could:
> * stop execution, exiting QEMU (as a "situation we can't
> handle and don't know what to do with")
> * advance the PC over the insn (questionable...)
> * feed some kind of exception into the guest
>
> but "continue execution of the guest without changing PC at all"
> is definitely wrong. A fix for this problem ought to involve
> changing the EC_INSNABORT case so that it no lenger does that
> "fall through to default" at all.
I completely agree with this. Thanks for your suggestion, Peter!
Zenghui
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range
2026-03-21 17:09 ` Zenghui Yu
@ 2026-03-21 17:26 ` Mohamed Mediouni
2026-03-21 17:39 ` Zenghui Yu
0 siblings, 1 reply; 8+ messages in thread
From: Mohamed Mediouni @ 2026-03-21 17:26 UTC (permalink / raw)
To: Zenghui Yu; +Cc: Peter Maydell, qemu-arm, qemu-devel, agraf
> On 21. Mar 2026, at 18:09, Zenghui Yu <zenghui.yu@linux.dev> wrote:
>
> After hacking KUT to let the guest map a VA to IPA 0x1000000000 (an IPA
> right after 64GB) and execute an insn on that VA, the guest receives an
> insn abort with IFSC equals to 0x03 (Hello, AddressSize fault!). We can
> _infer_ from that that the AddressSize fault is injected internally by
> hvf.
Hello,
As far as I tell:
If the translation fails at stage-1, the fault is routed by hardware
to the guest directly instead of going to the hypervisor.
In the case where it’s not a valid IPA in the first place - instead
of an unmapped one - the hardware triggers a fault to the guest instead
of going to the hypervisor.
If the stage-1 translation is successful but the IPA is valid and not
mapped, _then_ it gets trapped to the hypervisor, and gets routed to
user-space for VMM handling.
The EC_DATAABORT case happens on MMIO faults but the EC_INSNABORT
case is… more speculative for QEMU today.
I can see cases for doing it for on-demand page mappings in user-space
though… where you’d map the page and then resume from the same PC.
With the current model QEMU uses I’d expect not handling EC_INSNABORT
at all to not result in a functionality gap.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range
2026-03-21 17:26 ` Mohamed Mediouni
@ 2026-03-21 17:39 ` Zenghui Yu
0 siblings, 0 replies; 8+ messages in thread
From: Zenghui Yu @ 2026-03-21 17:39 UTC (permalink / raw)
To: Mohamed Mediouni; +Cc: Peter Maydell, qemu-arm, qemu-devel, agraf
On 3/22/26 1:26 AM, Mohamed Mediouni wrote:
>
> > On 21. Mar 2026, at 18:09, Zenghui Yu <zenghui.yu@linux.dev> wrote:
> >
> > After hacking KUT to let the guest map a VA to IPA 0x1000000000 (an IPA
> > right after 64GB) and execute an insn on that VA, the guest receives an
> > insn abort with IFSC equals to 0x03 (Hello, AddressSize fault!). We can
> > _infer_ from that that the AddressSize fault is injected internally by
> > hvf.
>
> Hello,
>
> As far as I tell:
>
> If the translation fails at stage-1, the fault is routed by hardware
> to the guest directly instead of going to the hypervisor.
Oops,, you're right. Sorry for the noise on the AddressSize fault part.
Thanks,
Zenghui
> In the case where it’s not a valid IPA in the first place - instead
> of an unmapped one - the hardware triggers a fault to the guest instead
> of going to the hypervisor.
>
> If the stage-1 translation is successful but the IPA is valid and not
> mapped, _then_ it gets trapped to the hypervisor, and gets routed to
> user-space for VMM handling.
>
> The EC_DATAABORT case happens on MMIO faults but the EC_INSNABORT
> case is… more speculative for QEMU today.
>
> I can see cases for doing it for on-demand page mappings in user-space
> though… where you’d map the page and then resume from the same PC.
>
> With the current model QEMU uses I’d expect not handling EC_INSNABORT
> at all to not result in a functionality gap.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-03-21 17:41 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-15 16:38 [PATCH rfc] hvf: arm: Inject SEA when executing insn in invalid memory range Zenghui Yu
2026-03-16 9:40 ` Alex Bennée
2026-03-16 10:05 ` Mohamed Mediouni
2026-03-16 10:54 ` Zenghui Yu
2026-03-20 10:52 ` Peter Maydell
2026-03-21 17:09 ` Zenghui Yu
2026-03-21 17:26 ` Mohamed Mediouni
2026-03-21 17:39 ` Zenghui Yu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox