[PATCH v1 4/5] arm64: Inject UNDEF when accessing MTE sysregs with MTE disabled
Fuad Tabba
tabba at google.com
Thu Nov 27 06:41:24 PST 2025
Hi Marc,
On Thu, 27 Nov 2025 at 14:17, Marc Zyngier <maz at kernel.org> wrote:
>
> On Thu, 27 Nov 2025 12:22:09 +0000,
> Fuad Tabba <tabba at google.com> wrote:
> >
> > When MTE hardware is present but disabled via software (arm64.nomte or
> > CONFIG_ARM64_MTE=n), HCR_EL2.ATA is cleared to prevent use of MTE
> > instructions. However, this alone doesn't fully emulate hardware that
> > lacks MTE support.
> >
> > With HCR_EL2.ATA cleared, accesses to certain MTE system registers trap
> > to EL2 with exception class ESR_ELx_EC_SYS64. To faithfully emulate
> > hardware without MTE (where such accesses would cause an Undefined
> > Instruction exception), inject UNDEF into the host.
> >
> > Signed-off-by: Fuad Tabba <tabba at google.com>
> > ---
> > arch/arm64/kvm/hyp/nvhe/hyp-main.c | 44 ++++++++++++++++++++++++++++++
> > 1 file changed, 44 insertions(+)
> >
> > diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > index 29430c031095..f542e4c17156 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > @@ -686,6 +686,46 @@ static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
> > kvm_skip_host_instr();
> > }
> >
> > +static void inject_undef64(void)
> > +{
> > + unsigned long sctlr, vbar, old, new;
> > + u64 offset, esr;
> > +
> > + vbar = read_sysreg_el1(SYS_VBAR);
> > + sctlr = read_sysreg_el1(SYS_SCTLR);
> > + old = read_sysreg_el2(SYS_SPSR);
> > + new = get_except64_cpsr(old, system_supports_mte(), sctlr, PSR_MODE_EL1h);
> > + offset = get_except64_offset(old, PSR_MODE_EL1h, except_type_sync);
> > + esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL;
> > +
> > + write_sysreg_el1(esr, SYS_ESR);
> > + write_sysreg_el1(read_sysreg_el2(SYS_ELR), SYS_ELR);
> > + write_sysreg_el1(old, SYS_SPSR);
> > + write_sysreg_el2(vbar + offset, SYS_ELR);
> > + write_sysreg_el2(new, SYS_SPSR);
> > +}
> > +
> > +static bool handle_host_mte(u64 esr)
> > +{
> > + /* If we're here for any reason other than MTE, then it's a bug. */
> > +
> > + if (read_sysreg(HCR_EL2) & HCR_ATA)
> > + return false;
> > +
> > + switch (esr_sys64_to_sysreg(esr)) {
> > + case SYS_RGSR_EL1:
> > + case SYS_GCR_EL1:
> > + case SYS_TFSR_EL1:
> > + case SYS_TFSRE0_EL1:
>
> How about other things, such as DC GVA? Don't you need to trap and
> UNDEF it (which has the side effect of also trapping DC ZVA)?
>
> Same question for all the DC {C,I,CI}GVA{C,P} instructions.
As far as I could tell, none of these are trapped by ATA. The spec
says that in the absence of MTE, their behavior is undefined --- which
is the same for the ones I'm actually handling here...
The reasons I've only handled these is that, when booting a system
with a misadvertised MTE, the kernel accesses these registers, and
injecting an UNDEF resulted in a nicer failure mode.
What do you suggest, drop this patch (and the one before it), since
trying to access MTE is UNDEF, and the kernel that does it is just
shooting itself in the foot (no security implications)? Or edit the
commit message to make it clear that this is best effort, based on
what ATA traps?
Cheers,
/fuad
>
> Thanks,
>
> M.
>
> --
> Without deviation from the norm, progress is not possible.
More information about the linux-arm-kernel
mailing list