Commit 59d4c98d authored by Vincenzo Frascino's avatar Vincenzo Frascino
Browse files

arm64: mte: Add in-kernel tag fault handler

Add the implementation of the in-kernel fault handler.

When a tag fault happens on a kernel address:
* MTE is disabled on the current CPU,
* the execution continues.

When a tag fault happens on a user address:
* the kernel executes do_bad_area() and panics.

The tag fault handler for kernel addresses is currently empty and will be
filled in by a future commit.

Signed-off-by: Vincenzo Frascino's avatarVincenzo Frascino <>
Co-developed-by: default avatarAndrey Konovalov <>
Signed-off-by: default avatarAndrey Konovalov <>
Reviewed-by: Catalin Marinas's avatarCatalin Marinas <>
Change-Id: I9b8aa79567f7c45f4d6a1290efcf34567e620717
parent 216da24f
......@@ -200,13 +200,36 @@ do { \
} while (0)
* The Tag Check Flag (TCF) mode for MTE is per EL, hence TCF0
* affects EL0 and TCF affects EL1 irrespective of which TTBR is
* used.
* The kernel accesses TTBR0 usually with LDTR/STTR instructions
* when UAO is available, so these would act as EL0 accesses using
* TCF0.
* However futex.h code uses exclusives which would be executed as
* EL1, this can potentially cause a tag check fault even if the
* user disables TCF0.
* To address the problem we set the PSTATE.TCO bit in uaccess_enable()
* and reset it in uaccess_disable().
* The Tag check override (TCO) bit disables temporarily the tag checking
* preventing the issue.
static inline void uaccess_disable(void)
asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(0),
static inline void uaccess_enable(void)
asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(1),
......@@ -33,6 +33,7 @@
#include <asm/debug-monitors.h>
#include <asm/esr.h>
#include <asm/kprobes.h>
#include <asm/mte.h>
#include <asm/processor.h>
#include <asm/sysreg.h>
#include <asm/system_misc.h>
......@@ -296,6 +297,44 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
static void report_tag_fault(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
static void do_tag_recovery(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
static bool reported = false;
if (!READ_ONCE(reported)) {
report_tag_fault(addr, esr, regs);
WRITE_ONCE(reported, true);
* Disable MTE Tag Checking on the local CPU for the current EL.
* It will be done lazily on the other CPUs when they will hit a
* tag fault.
sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, SCTLR_ELx_TCF_NONE);
static bool is_el1_mte_sync_tag_check_fault(unsigned int esr)
unsigned int ec = ESR_ELx_EC(esr);
unsigned int fsc = esr & ESR_ELx_FSC;
if (ec != ESR_ELx_EC_DABT_CUR)
return false;
if (fsc == ESR_ELx_FSC_MTE)
return true;
return false;
static void __do_kernel_fault(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
......@@ -312,6 +351,12 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr,
"Ignoring spurious kernel translation fault at virtual address %016lx\n", addr))
if (is_el1_mte_sync_tag_check_fault(esr)) {
do_tag_recovery(addr, esr, regs);
if (is_el1_permission_fault(addr, esr, regs)) {
if (esr & ESR_ELx_WNR)
msg = "write to read-only memory";
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment