[PATCH] target/arm/hvf: implement MDCCSR_EL0 as RAZ

Showta Ishizaki posted 1 patch 3 weeks ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260905004800.5B560C92F2@bambi.snowrabbit.org
Maintainers: Alexander Graf <agraf@csgraf.de>, Peter Maydell <peter.maydell@linaro.org>
target/arm/hvf/hvf.c | 10 ++++++++++
1 file changed, 10 insertions(+)
[PATCH] target/arm/hvf: implement MDCCSR_EL0 as RAZ
Posted by Showta Ishizaki 3 weeks ago

A guest that reads MDCCSR_EL0 under hvf is killed.  The register has no
case in hvf_sysreg_read(), so it reaches the unhandled path and hvf
injects an undefined instruction:

    trace_hvf_unhandled_sysreg_read(env->pc, reg, ...);
    hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized(), 1);

The TCG path does not do this.  debug_helper.c defines MDCCSR_EL0 as
ARM_CP_CONST with resetvalue 0, and the comment on the neighbouring DCC
registers gives the reason:

    However we implement RAZ/WI behaviour with trapping to prevent
    spurious SIGILLs if the guest OS does access them as the support
    cannot be probed for.

That argument holds for hvf as much as for TCG, so do the same there.

The read only reaches QEMU since 37863fff59 ("hvf: arm: enable vGIC by
default for virt-11.1 and later").  With -M virt,kernel-irqchip=off the
trace point does not fire at all, so before that change the register was
never trapped out.

NetBSD/aarch64 hits this on every boot.  vmt(4) probes for the VMware
backdoor, which on arm64 is a read of MDCCSR_EL0 with a magic value in
x7, and the injected undefined instruction is fatal in kernel mode:

    [   1.0000000] cpu0 at acpi0: unknown CPU (ID = 0x610f0000), id 0x0
    [   1.0000000] panic: Trap: fatal Unknown Reason (Illegal Instruction):
      pc=ffffc000003fa318 sp=ffffc000010262e0 esr=02000000
    [   1.0000000] fp ffffc000010262f0 vmt_probe() at netbsd:vmt_probe+0x3c
    [   1.0000000] fp ffffc00001026340 vmt_match() at netbsd:vmt_match+0x1c

The pc in the panic is the same pc the trace point reports, and the
encoding it reports is MDCCSR_EL0:

    hvf_unhandled_sysreg_read unhandled sysreg read at pc=0xffffc000003fa318:
      0x0020c002 (op0=2 op1=3 crn=0 crm=1 op2=0)

With this patch the same image boots to login on -M virt -accel hvf, and
the trace point does not fire.

Fixes: 37863fff59e0 ("hvf: arm: enable vGIC by default for virt-11.1 and later")
Signed-off-by: Showta Ishizaki <zakinko@snowrabbit.org>
---
 target/arm/hvf/hvf.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index da3ec521fc..b00a7e1a84 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -234,6 +234,7 @@ void hvf_arm_init_debug(void)
 #define SYSREG_ICC_SRE_EL1       SYSREG(3, 0, 12, 12, 5)
 
 #define SYSREG_MDSCR_EL1      SYSREG(2, 0, 0, 2, 2)
+#define SYSREG_MDCCSR_EL0     SYSREG(2, 3, 0, 1, 0)
 #define SYSREG_DBGBVR0_EL1    SYSREG(2, 0, 0, 0, 4)
 #define SYSREG_DBGBCR0_EL1    SYSREG(2, 0, 0, 0, 5)
 #define SYSREG_DBGWVR0_EL1    SYSREG(2, 0, 0, 0, 6)
@@ -1756,6 +1757,15 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val)
     case SYSREG_MDCCINT_EL1:
         assert_hvf_ok(hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_MDCCINT_EL1, val));
         return 0;
+    case SYSREG_MDCCSR_EL0:
+        /*
+         * The Debug Communications Channel is not implemented, so RAZ,
+         * which is what the TCG path in debug_helper.c does.  A guest
+         * cannot probe for DCC support, so injecting an undefined
+         * instruction here turns a legal read into a fatal trap.
+         */
+        *val = 0;
+        return 0;
     case SYSREG_ICC_AP0R0_EL1:
     case SYSREG_ICC_AP0R1_EL1:
     case SYSREG_ICC_AP0R2_EL1:
-- 
2.50.1 (Apple Git-155)
Re: [PATCH] target/arm/hvf: implement MDCCSR_EL0 as RAZ
Posted by Mohamed Mediouni 3 weeks ago

> On 5. Sep 2026, at 02:48, Showta Ishizaki <zakinko@snowrabbit.org> wrote:
> 
> 
> A guest that reads MDCCSR_EL0 under hvf is killed.  The register has no
> case in hvf_sysreg_read(), so it reaches the unhandled path and hvf
> injects an undefined instruction:
> 
>    trace_hvf_unhandled_sysreg_read(env->pc, reg, ...);
>    hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized(), 1);
> 
> The TCG path does not do this.  debug_helper.c defines MDCCSR_EL0 as
> ARM_CP_CONST with resetvalue 0, and the comment on the neighbouring DCC
> registers gives the reason:
> 
>    However we implement RAZ/WI behaviour with trapping to prevent
>    spurious SIGILLs if the guest OS does access them as the support
>    cannot be probed for.
> 
> That argument holds for hvf as much as for TCG, so do the same there.
> 
> The read only reaches QEMU since 37863fff59 ("hvf: arm: enable vGIC by
> default for virt-11.1 and later").  With -M virt,kernel-irqchip=off the
> trace point does not fire at all, so before that change the register was
> never trapped out.
> 
> NetBSD/aarch64 hits this on every boot.  vmt(4) probes for the VMware
> backdoor, which on arm64 is a read of MDCCSR_EL0 with a magic value in
> x7, and the injected undefined instruction is fatal in kernel mode:
> 
>    [   1.0000000] cpu0 at acpi0: unknown CPU (ID = 0x610f0000), id 0x0
>    [   1.0000000] panic: Trap: fatal Unknown Reason (Illegal Instruction):
>      pc=ffffc000003fa318 sp=ffffc000010262e0 esr=02000000
>    [   1.0000000] fp ffffc000010262f0 vmt_probe() at netbsd:vmt_probe+0x3c
>    [   1.0000000] fp ffffc00001026340 vmt_match() at netbsd:vmt_match+0x1c
> 
> The pc in the panic is the same pc the trace point reports, and the
> encoding it reports is MDCCSR_EL0:
> 
>    hvf_unhandled_sysreg_read unhandled sysreg read at pc=0xffffc000003fa318:
>      0x0020c002 (op0=2 op1=3 crn=0 crm=1 op2=0)
> 
> With this patch the same image boots to login on -M virt -accel hvf, and
> the trace point does not fire.
> 
> Fixes: 37863fff59e0 ("hvf: arm: enable vGIC by default for virt-11.1 and later")
> Signed-off-by: Showta Ishizaki <zakinko@snowrabbit.org>

Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> target/arm/hvf/hvf.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
> 
> diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
> index da3ec521fc..b00a7e1a84 100644
> --- a/target/arm/hvf/hvf.c
> +++ b/target/arm/hvf/hvf.c
> @@ -234,6 +234,7 @@ void hvf_arm_init_debug(void)
> #define SYSREG_ICC_SRE_EL1       SYSREG(3, 0, 12, 12, 5)
> 
> #define SYSREG_MDSCR_EL1      SYSREG(2, 0, 0, 2, 2)
> +#define SYSREG_MDCCSR_EL0     SYSREG(2, 3, 0, 1, 0)
> #define SYSREG_DBGBVR0_EL1    SYSREG(2, 0, 0, 0, 4)
> #define SYSREG_DBGBCR0_EL1    SYSREG(2, 0, 0, 0, 5)
> #define SYSREG_DBGWVR0_EL1    SYSREG(2, 0, 0, 0, 6)
> @@ -1756,6 +1757,15 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val)
>     case SYSREG_MDCCINT_EL1:
>         assert_hvf_ok(hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_MDCCINT_EL1, val));
>         return 0;
> +    case SYSREG_MDCCSR_EL0:
> +        /*
> +         * The Debug Communications Channel is not implemented, so RAZ,
> +         * which is what the TCG path in debug_helper.c does.  A guest
> +         * cannot probe for DCC support, so injecting an undefined
> +         * instruction here turns a legal read into a fatal trap.
> +         */
> +        *val = 0;
> +        return 0;
>     case SYSREG_ICC_AP0R0_EL1:
>     case SYSREG_ICC_AP0R1_EL1:
>     case SYSREG_ICC_AP0R2_EL1:
> -- 
> 2.50.1 (Apple Git-155)
> 
> 
Re: [PATCH] target/arm/hvf: implement MDCCSR_EL0 as RAZ
Posted by Mohamed Mediouni 3 weeks ago

> On 5. Sep 2026, at 08:37, Mohamed Mediouni <mohamed@unpredictable.fr> wrote:
> 
> 
> 
>> On 5. Sep 2026, at 02:48, Showta Ishizaki <zakinko@snowrabbit.org> wrote:
>> 
>> 
>> A guest that reads MDCCSR_EL0 under hvf is killed.  The register has no
>> case in hvf_sysreg_read(), so it reaches the unhandled path and hvf
>> injects an undefined instruction:
>> 
>>   trace_hvf_unhandled_sysreg_read(env->pc, reg, ...);
>>   hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized(), 1);
>> 
>> The TCG path does not do this.  debug_helper.c defines MDCCSR_EL0 as
>> ARM_CP_CONST with resetvalue 0, and the comment on the neighbouring DCC
>> registers gives the reason:
>> 
>>   However we implement RAZ/WI behaviour with trapping to prevent
>>   spurious SIGILLs if the guest OS does access them as the support
>>   cannot be probed for.
>> 
>> That argument holds for hvf as much as for TCG, so do the same there.
>> 
>> The read only reaches QEMU since 37863fff59 ("hvf: arm: enable vGIC by
>> default for virt-11.1 and later").  With -M virt,kernel-irqchip=off the
>> trace point does not fire at all, so before that change the register was
>> never trapped out.
>> 
>> NetBSD/aarch64 hits this on every boot.  vmt(4) probes for the VMware
>> backdoor, which on arm64 is a read of MDCCSR_EL0 with a magic value in
>> x7, and the injected undefined instruction is fatal in kernel mode:
>> 
>>   [   1.0000000] cpu0 at acpi0: unknown CPU (ID = 0x610f0000), id 0x0
>>   [   1.0000000] panic: Trap: fatal Unknown Reason (Illegal Instruction):
>>     pc=ffffc000003fa318 sp=ffffc000010262e0 esr=02000000
>>   [   1.0000000] fp ffffc000010262f0 vmt_probe() at netbsd:vmt_probe+0x3c
>>   [   1.0000000] fp ffffc00001026340 vmt_match() at netbsd:vmt_match+0x1c
>> 
>> The pc in the panic is the same pc the trace point reports, and the
>> encoding it reports is MDCCSR_EL0:
>> 
>>   hvf_unhandled_sysreg_read unhandled sysreg read at pc=0xffffc000003fa318:
>>     0x0020c002 (op0=2 op1=3 crn=0 crm=1 op2=0)
>> 
>> With this patch the same image boots to login on -M virt -accel hvf, and
>> the trace point does not fire.
>> 
>> Fixes: 37863fff59e0 ("hvf: arm: enable vGIC by default for virt-11.1 and later")
>> Signed-off-by: Showta Ishizaki <zakinko@snowrabbit.org>
> 
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
*Reviewed-by

Oops.

Will submit this as part of a rollup series or it’ll be taken on its own.
>> ---
>> target/arm/hvf/hvf.c | 10 ++++++++++
>> 1 file changed, 10 insertions(+)
>> 
>> diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
>> index da3ec521fc..b00a7e1a84 100644
>> --- a/target/arm/hvf/hvf.c
>> +++ b/target/arm/hvf/hvf.c
>> @@ -234,6 +234,7 @@ void hvf_arm_init_debug(void)
>> #define SYSREG_ICC_SRE_EL1       SYSREG(3, 0, 12, 12, 5)
>> 
>> #define SYSREG_MDSCR_EL1      SYSREG(2, 0, 0, 2, 2)
>> +#define SYSREG_MDCCSR_EL0     SYSREG(2, 3, 0, 1, 0)
>> #define SYSREG_DBGBVR0_EL1    SYSREG(2, 0, 0, 0, 4)
>> #define SYSREG_DBGBCR0_EL1    SYSREG(2, 0, 0, 0, 5)
>> #define SYSREG_DBGWVR0_EL1    SYSREG(2, 0, 0, 0, 6)
>> @@ -1756,6 +1757,15 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val)
>>    case SYSREG_MDCCINT_EL1:
>>        assert_hvf_ok(hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_MDCCINT_EL1, val));
>>        return 0;
>> +    case SYSREG_MDCCSR_EL0:
>> +        /*
>> +         * The Debug Communications Channel is not implemented, so RAZ,
>> +         * which is what the TCG path in debug_helper.c does.  A guest
>> +         * cannot probe for DCC support, so injecting an undefined
>> +         * instruction here turns a legal read into a fatal trap.
>> +         */
>> +        *val = 0;
>> +        return 0;
>>    case SYSREG_ICC_AP0R0_EL1:
>>    case SYSREG_ICC_AP0R1_EL1:
>>    case SYSREG_ICC_AP0R2_EL1:
>> -- 
>> 2.50.1 (Apple Git-155)
>> 
>> 
> 
> 
Re: [PATCH] target/arm/hvf: implement MDCCSR_EL0 as RAZ
Posted by Peter Maydell 2 weeks, 4 days ago
On Sat, 5 Sept 2026 at 07:40, Mohamed Mediouni <mohamed@unpredictable.fr> wrote:
>
>
>
> > On 5. Sep 2026, at 08:37, Mohamed Mediouni <mohamed@unpredictable.fr> wrote:
> >
> >
> >
> >> On 5. Sep 2026, at 02:48, Showta Ishizaki <zakinko@snowrabbit.org> wrote:
> >>
> >>
> >> A guest that reads MDCCSR_EL0 under hvf is killed.  The register has no
> >> case in hvf_sysreg_read(), so it reaches the unhandled path and hvf
> >> injects an undefined instruction:
> >>
> >>   trace_hvf_unhandled_sysreg_read(env->pc, reg, ...);
> >>   hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized(), 1);
> >>
> >> The TCG path does not do this.  debug_helper.c defines MDCCSR_EL0 as
> >> ARM_CP_CONST with resetvalue 0, and the comment on the neighbouring DCC
> >> registers gives the reason:
> >>
> >>   However we implement RAZ/WI behaviour with trapping to prevent
> >>   spurious SIGILLs if the guest OS does access them as the support
> >>   cannot be probed for.
> >>
> >> That argument holds for hvf as much as for TCG, so do the same there.
> >>
> >> The read only reaches QEMU since 37863fff59 ("hvf: arm: enable vGIC by
> >> default for virt-11.1 and later").  With -M virt,kernel-irqchip=off the
> >> trace point does not fire at all, so before that change the register was
> >> never trapped out.
> >>
> >> NetBSD/aarch64 hits this on every boot.  vmt(4) probes for the VMware
> >> backdoor, which on arm64 is a read of MDCCSR_EL0 with a magic value in
> >> x7, and the injected undefined instruction is fatal in kernel mode:
> >>
> >>   [   1.0000000] cpu0 at acpi0: unknown CPU (ID = 0x610f0000), id 0x0
> >>   [   1.0000000] panic: Trap: fatal Unknown Reason (Illegal Instruction):
> >>     pc=ffffc000003fa318 sp=ffffc000010262e0 esr=02000000
> >>   [   1.0000000] fp ffffc000010262f0 vmt_probe() at netbsd:vmt_probe+0x3c
> >>   [   1.0000000] fp ffffc00001026340 vmt_match() at netbsd:vmt_match+0x1c
> >>
> >> The pc in the panic is the same pc the trace point reports, and the
> >> encoding it reports is MDCCSR_EL0:
> >>
> >>   hvf_unhandled_sysreg_read unhandled sysreg read at pc=0xffffc000003fa318:
> >>     0x0020c002 (op0=2 op1=3 crn=0 crm=1 op2=0)
> >>
> >> With this patch the same image boots to login on -M virt -accel hvf, and
> >> the trace point does not fire.
> >>
> >> Fixes: 37863fff59e0 ("hvf: arm: enable vGIC by default for virt-11.1 and later")
> >> Signed-off-by: Showta Ishizaki <zakinko@snowrabbit.org>
> >
> > Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> *Reviewed-by
>
> Oops.
>
> Will submit this as part of a rollup series or it’ll be taken on its own.
> >> ---


Applied to target-arm.next, thanks. I've added a cc:stable tag
since we run into this with a netbsd guest.

-- PMM