162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 ARM Ltd. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#ifndef __ASM_MTE_KASAN_H 662306a36Sopenharmony_ci#define __ASM_MTE_KASAN_H 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/compiler.h> 962306a36Sopenharmony_ci#include <asm/cputype.h> 1062306a36Sopenharmony_ci#include <asm/mte-def.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#ifndef __ASSEMBLY__ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#ifdef CONFIG_KASAN_HW_TAGS 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* Whether the MTE asynchronous mode is enabled. */ 1962306a36Sopenharmony_ciDECLARE_STATIC_KEY_FALSE(mte_async_or_asymm_mode); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic inline bool system_uses_mte_async_or_asymm_mode(void) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci return static_branch_unlikely(&mte_async_or_asymm_mode); 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#else /* CONFIG_KASAN_HW_TAGS */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic inline bool system_uses_mte_async_or_asymm_mode(void) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return false; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#endif /* CONFIG_KASAN_HW_TAGS */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#ifdef CONFIG_ARM64_MTE 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * The Tag Check Flag (TCF) mode for MTE is per EL, hence TCF0 3962306a36Sopenharmony_ci * affects EL0 and TCF affects EL1 irrespective of which TTBR is 4062306a36Sopenharmony_ci * used. 4162306a36Sopenharmony_ci * The kernel accesses TTBR0 usually with LDTR/STTR instructions 4262306a36Sopenharmony_ci * when UAO is available, so these would act as EL0 accesses using 4362306a36Sopenharmony_ci * TCF0. 4462306a36Sopenharmony_ci * However futex.h code uses exclusives which would be executed as 4562306a36Sopenharmony_ci * EL1, this can potentially cause a tag check fault even if the 4662306a36Sopenharmony_ci * user disables TCF0. 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * To address the problem we set the PSTATE.TCO bit in uaccess_enable() 4962306a36Sopenharmony_ci * and reset it in uaccess_disable(). 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * The Tag check override (TCO) bit disables temporarily the tag checking 5262306a36Sopenharmony_ci * preventing the issue. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic inline void mte_disable_tco(void) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(0), 5762306a36Sopenharmony_ci ARM64_MTE, CONFIG_KASAN_HW_TAGS)); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline void mte_enable_tco(void) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(1), 6362306a36Sopenharmony_ci ARM64_MTE, CONFIG_KASAN_HW_TAGS)); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * These functions disable tag checking only if in MTE async mode 6862306a36Sopenharmony_ci * since the sync mode generates exceptions synchronously and the 6962306a36Sopenharmony_ci * nofault or load_unaligned_zeropad can handle them. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cistatic inline void __mte_disable_tco_async(void) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci if (system_uses_mte_async_or_asymm_mode()) 7462306a36Sopenharmony_ci mte_disable_tco(); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic inline void __mte_enable_tco_async(void) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci if (system_uses_mte_async_or_asymm_mode()) 8062306a36Sopenharmony_ci mte_enable_tco(); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* 8462306a36Sopenharmony_ci * These functions are meant to be only used from KASAN runtime through 8562306a36Sopenharmony_ci * the arch_*() interface defined in asm/memory.h. 8662306a36Sopenharmony_ci * These functions don't include system_supports_mte() checks, 8762306a36Sopenharmony_ci * as KASAN only calls them when MTE is supported and enabled. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic inline u8 mte_get_ptr_tag(void *ptr) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci /* Note: The format of KASAN tags is 0xF<x> */ 9362306a36Sopenharmony_ci u8 tag = 0xF0 | (u8)(((u64)(ptr)) >> MTE_TAG_SHIFT); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return tag; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* Get allocation tag for the address. */ 9962306a36Sopenharmony_cistatic inline u8 mte_get_mem_tag(void *addr) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci asm(__MTE_PREAMBLE "ldg %0, [%0]" 10262306a36Sopenharmony_ci : "+r" (addr)); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return mte_get_ptr_tag(addr); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* Generate a random tag. */ 10862306a36Sopenharmony_cistatic inline u8 mte_get_random_tag(void) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci void *addr; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci asm(__MTE_PREAMBLE "irg %0, %0" 11362306a36Sopenharmony_ci : "=r" (addr)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return mte_get_ptr_tag(addr); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic inline u64 __stg_post(u64 p) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci asm volatile(__MTE_PREAMBLE "stg %0, [%0], #16" 12162306a36Sopenharmony_ci : "+r"(p) 12262306a36Sopenharmony_ci : 12362306a36Sopenharmony_ci : "memory"); 12462306a36Sopenharmony_ci return p; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic inline u64 __stzg_post(u64 p) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci asm volatile(__MTE_PREAMBLE "stzg %0, [%0], #16" 13062306a36Sopenharmony_ci : "+r"(p) 13162306a36Sopenharmony_ci : 13262306a36Sopenharmony_ci : "memory"); 13362306a36Sopenharmony_ci return p; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic inline void __dc_gva(u64 p) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci asm volatile(__MTE_PREAMBLE "dc gva, %0" : : "r"(p) : "memory"); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic inline void __dc_gzva(u64 p) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci asm volatile(__MTE_PREAMBLE "dc gzva, %0" : : "r"(p) : "memory"); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * Assign allocation tags for a region of memory based on the pointer tag. 14862306a36Sopenharmony_ci * Note: The address must be non-NULL and MTE_GRANULE_SIZE aligned and 14962306a36Sopenharmony_ci * size must be MTE_GRANULE_SIZE aligned. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic inline void mte_set_mem_tag_range(void *addr, size_t size, u8 tag, 15262306a36Sopenharmony_ci bool init) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci u64 curr, mask, dczid, dczid_bs, dczid_dzp, end1, end2, end3; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Read DC G(Z)VA block size from the system register. */ 15762306a36Sopenharmony_ci dczid = read_cpuid(DCZID_EL0); 15862306a36Sopenharmony_ci dczid_bs = 4ul << (dczid & 0xf); 15962306a36Sopenharmony_ci dczid_dzp = (dczid >> 4) & 1; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci curr = (u64)__tag_set(addr, tag); 16262306a36Sopenharmony_ci mask = dczid_bs - 1; 16362306a36Sopenharmony_ci /* STG/STZG up to the end of the first block. */ 16462306a36Sopenharmony_ci end1 = curr | mask; 16562306a36Sopenharmony_ci end3 = curr + size; 16662306a36Sopenharmony_ci /* DC GVA / GZVA in [end1, end2) */ 16762306a36Sopenharmony_ci end2 = end3 & ~mask; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * The following code uses STG on the first DC GVA block even if the 17162306a36Sopenharmony_ci * start address is aligned - it appears to be faster than an alignment 17262306a36Sopenharmony_ci * check + conditional branch. Also, if the range size is at least 2 DC 17362306a36Sopenharmony_ci * GVA blocks, the first two loops can use post-condition to save one 17462306a36Sopenharmony_ci * branch each. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci#define SET_MEMTAG_RANGE(stg_post, dc_gva) \ 17762306a36Sopenharmony_ci do { \ 17862306a36Sopenharmony_ci if (!dczid_dzp && size >= 2 * dczid_bs) {\ 17962306a36Sopenharmony_ci do { \ 18062306a36Sopenharmony_ci curr = stg_post(curr); \ 18162306a36Sopenharmony_ci } while (curr < end1); \ 18262306a36Sopenharmony_ci \ 18362306a36Sopenharmony_ci do { \ 18462306a36Sopenharmony_ci dc_gva(curr); \ 18562306a36Sopenharmony_ci curr += dczid_bs; \ 18662306a36Sopenharmony_ci } while (curr < end2); \ 18762306a36Sopenharmony_ci } \ 18862306a36Sopenharmony_ci \ 18962306a36Sopenharmony_ci while (curr < end3) \ 19062306a36Sopenharmony_ci curr = stg_post(curr); \ 19162306a36Sopenharmony_ci } while (0) 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (init) 19462306a36Sopenharmony_ci SET_MEMTAG_RANGE(__stzg_post, __dc_gzva); 19562306a36Sopenharmony_ci else 19662306a36Sopenharmony_ci SET_MEMTAG_RANGE(__stg_post, __dc_gva); 19762306a36Sopenharmony_ci#undef SET_MEMTAG_RANGE 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_civoid mte_enable_kernel_sync(void); 20162306a36Sopenharmony_civoid mte_enable_kernel_async(void); 20262306a36Sopenharmony_civoid mte_enable_kernel_asymm(void); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci#else /* CONFIG_ARM64_MTE */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic inline void mte_disable_tco(void) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic inline void mte_enable_tco(void) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic inline void __mte_disable_tco_async(void) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic inline void __mte_enable_tco_async(void) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic inline u8 mte_get_ptr_tag(void *ptr) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci return 0xFF; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic inline u8 mte_get_mem_tag(void *addr) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci return 0xFF; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic inline u8 mte_get_random_tag(void) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci return 0xFF; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic inline void mte_set_mem_tag_range(void *addr, size_t size, 23862306a36Sopenharmony_ci u8 tag, bool init) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic inline void mte_enable_kernel_sync(void) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic inline void mte_enable_kernel_async(void) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic inline void mte_enable_kernel_asymm(void) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci#endif /* CONFIG_ARM64_MTE */ 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci#endif /* __ASSEMBLY__ */ 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci#endif /* __ASM_MTE_KASAN_H */ 259