1637ec831SVincenzo Frascino /* SPDX-License-Identifier: GPL-2.0 */
2637ec831SVincenzo Frascino /*
3637ec831SVincenzo Frascino * Copyright (C) 2020 ARM Ltd.
4637ec831SVincenzo Frascino */
5637ec831SVincenzo Frascino #ifndef __ASM_MTE_H
6637ec831SVincenzo Frascino #define __ASM_MTE_H
7637ec831SVincenzo Frascino
885f49caeSVincenzo Frascino #include <asm/compiler.h>
985f49caeSVincenzo Frascino #include <asm/mte-def.h>
1085f49caeSVincenzo Frascino
11637ec831SVincenzo Frascino #ifndef __ASSEMBLY__
12637ec831SVincenzo Frascino
1385f49caeSVincenzo Frascino #include <linux/bitfield.h>
1438ddf7daSPeter Collingbourne #include <linux/kasan-enabled.h>
1534bfeea4SCatalin Marinas #include <linux/page-flags.h>
1638ddf7daSPeter Collingbourne #include <linux/sched.h>
1785f49caeSVincenzo Frascino #include <linux/types.h>
1834bfeea4SCatalin Marinas
1934bfeea4SCatalin Marinas #include <asm/pgtable-types.h>
2034bfeea4SCatalin Marinas
2134bfeea4SCatalin Marinas void mte_clear_page_tags(void *addr);
2218ddbaa0SCatalin Marinas unsigned long mte_copy_tags_from_user(void *to, const void __user *from,
2318ddbaa0SCatalin Marinas unsigned long n);
2418ddbaa0SCatalin Marinas unsigned long mte_copy_tags_to_user(void __user *to, void *from,
2518ddbaa0SCatalin Marinas unsigned long n);
2636943abaSSteven Price int mte_save_tags(struct page *page);
2736943abaSSteven Price void mte_save_page_tags(const void *page_addr, void *tag_storage);
28d77e59a8SCatalin Marinas void mte_restore_tags(swp_entry_t entry, struct page *page);
2936943abaSSteven Price void mte_restore_page_tags(void *page_addr, const void *tag_storage);
3036943abaSSteven Price void mte_invalidate_tags(int type, pgoff_t offset);
3136943abaSSteven Price void mte_invalidate_tags_area(int type);
3236943abaSSteven Price void *mte_allocate_tag_storage(void);
3336943abaSSteven Price void mte_free_tag_storage(char *storage);
3434bfeea4SCatalin Marinas
35637ec831SVincenzo Frascino #ifdef CONFIG_ARM64_MTE
36637ec831SVincenzo Frascino
3734bfeea4SCatalin Marinas /* track which pages have valid allocation tags */
3834bfeea4SCatalin Marinas #define PG_mte_tagged PG_arch_2
39d77e59a8SCatalin Marinas /* simple lock to avoid multiple threads tagging the same page */
40d77e59a8SCatalin Marinas #define PG_mte_lock PG_arch_3
4134bfeea4SCatalin Marinas
set_page_mte_tagged(struct page * page)42e059853dSCatalin Marinas static inline void set_page_mte_tagged(struct page *page)
43e059853dSCatalin Marinas {
44e059853dSCatalin Marinas /*
45e059853dSCatalin Marinas * Ensure that the tags written prior to this function are visible
46e059853dSCatalin Marinas * before the page flags update.
47e059853dSCatalin Marinas */
48e059853dSCatalin Marinas smp_wmb();
49e059853dSCatalin Marinas set_bit(PG_mte_tagged, &page->flags);
50e059853dSCatalin Marinas }
51e059853dSCatalin Marinas
page_mte_tagged(struct page * page)52e059853dSCatalin Marinas static inline bool page_mte_tagged(struct page *page)
53e059853dSCatalin Marinas {
54e059853dSCatalin Marinas bool ret = test_bit(PG_mte_tagged, &page->flags);
55e059853dSCatalin Marinas
56e059853dSCatalin Marinas /*
57e059853dSCatalin Marinas * If the page is tagged, ensure ordering with a likely subsequent
58e059853dSCatalin Marinas * read of the tags.
59e059853dSCatalin Marinas */
60e059853dSCatalin Marinas if (ret)
61e059853dSCatalin Marinas smp_rmb();
62e059853dSCatalin Marinas return ret;
63e059853dSCatalin Marinas }
64e059853dSCatalin Marinas
65d77e59a8SCatalin Marinas /*
66d77e59a8SCatalin Marinas * Lock the page for tagging and return 'true' if the page can be tagged,
67d77e59a8SCatalin Marinas * 'false' if already tagged. PG_mte_tagged is never cleared and therefore the
68d77e59a8SCatalin Marinas * locking only happens once for page initialisation.
69d77e59a8SCatalin Marinas *
70d77e59a8SCatalin Marinas * The page MTE lock state:
71d77e59a8SCatalin Marinas *
72d77e59a8SCatalin Marinas * Locked: PG_mte_lock && !PG_mte_tagged
73d77e59a8SCatalin Marinas * Unlocked: !PG_mte_lock || PG_mte_tagged
74d77e59a8SCatalin Marinas *
75d77e59a8SCatalin Marinas * Acquire semantics only if the page is tagged (returning 'false').
76d77e59a8SCatalin Marinas */
try_page_mte_tagging(struct page * page)77d77e59a8SCatalin Marinas static inline bool try_page_mte_tagging(struct page *page)
78d77e59a8SCatalin Marinas {
79d77e59a8SCatalin Marinas if (!test_and_set_bit(PG_mte_lock, &page->flags))
80d77e59a8SCatalin Marinas return true;
81d77e59a8SCatalin Marinas
82d77e59a8SCatalin Marinas /*
83d77e59a8SCatalin Marinas * The tags are either being initialised or may have been initialised
84d77e59a8SCatalin Marinas * already. Check if the PG_mte_tagged flag has been set or wait
85d77e59a8SCatalin Marinas * otherwise.
86d77e59a8SCatalin Marinas */
87d77e59a8SCatalin Marinas smp_cond_load_acquire(&page->flags, VAL & (1UL << PG_mte_tagged));
88d77e59a8SCatalin Marinas
89d77e59a8SCatalin Marinas return false;
90d77e59a8SCatalin Marinas }
91d77e59a8SCatalin Marinas
92013bb59dSPeter Collingbourne void mte_zero_clear_page_tags(void *addr);
93*332c151cSPeter Collingbourne void mte_sync_tags(pte_t pte);
942563776bSVincenzo Frascino void mte_copy_page_tags(void *kto, const void *kfrom);
9520169862SPeter Collingbourne void mte_thread_init_user(void);
961c101da8SCatalin Marinas void mte_thread_switch(struct task_struct *next);
97973b9e37SPeter Collingbourne void mte_cpu_setup(void);
98eab0e6e1SVincenzo Frascino void mte_suspend_enter(void);
99973b9e37SPeter Collingbourne void mte_suspend_exit(void);
10093f067f6SCatalin Marinas long set_mte_ctrl(struct task_struct *task, unsigned long arg);
10193f067f6SCatalin Marinas long get_mte_ctrl(struct task_struct *task);
10218ddbaa0SCatalin Marinas int mte_ptrace_copy_tags(struct task_struct *child, long request,
10318ddbaa0SCatalin Marinas unsigned long addr, unsigned long data);
104f3ba50a7SCatalin Marinas size_t mte_probe_user_range(const char __user *uaddr, size_t size);
105637ec831SVincenzo Frascino
10685f49caeSVincenzo Frascino #else /* CONFIG_ARM64_MTE */
107637ec831SVincenzo Frascino
10834bfeea4SCatalin Marinas /* unused if !CONFIG_ARM64_MTE, silence the compiler */
10934bfeea4SCatalin Marinas #define PG_mte_tagged 0
11034bfeea4SCatalin Marinas
set_page_mte_tagged(struct page * page)111e059853dSCatalin Marinas static inline void set_page_mte_tagged(struct page *page)
112e059853dSCatalin Marinas {
113e059853dSCatalin Marinas }
page_mte_tagged(struct page * page)114e059853dSCatalin Marinas static inline bool page_mte_tagged(struct page *page)
115e059853dSCatalin Marinas {
116e059853dSCatalin Marinas return false;
117e059853dSCatalin Marinas }
try_page_mte_tagging(struct page * page)118d77e59a8SCatalin Marinas static inline bool try_page_mte_tagging(struct page *page)
119d77e59a8SCatalin Marinas {
120d77e59a8SCatalin Marinas return false;
121d77e59a8SCatalin Marinas }
mte_zero_clear_page_tags(void * addr)122013bb59dSPeter Collingbourne static inline void mte_zero_clear_page_tags(void *addr)
123013bb59dSPeter Collingbourne {
124013bb59dSPeter Collingbourne }
mte_sync_tags(pte_t pte)125*332c151cSPeter Collingbourne static inline void mte_sync_tags(pte_t pte)
12634bfeea4SCatalin Marinas {
12734bfeea4SCatalin Marinas }
mte_copy_page_tags(void * kto,const void * kfrom)1282563776bSVincenzo Frascino static inline void mte_copy_page_tags(void *kto, const void *kfrom)
1292563776bSVincenzo Frascino {
1302563776bSVincenzo Frascino }
mte_thread_init_user(void)13120169862SPeter Collingbourne static inline void mte_thread_init_user(void)
132637ec831SVincenzo Frascino {
133637ec831SVincenzo Frascino }
mte_thread_switch(struct task_struct * next)1341c101da8SCatalin Marinas static inline void mte_thread_switch(struct task_struct *next)
1351c101da8SCatalin Marinas {
1361c101da8SCatalin Marinas }
mte_suspend_enter(void)137eab0e6e1SVincenzo Frascino static inline void mte_suspend_enter(void)
138eab0e6e1SVincenzo Frascino {
139eab0e6e1SVincenzo Frascino }
mte_suspend_exit(void)140973b9e37SPeter Collingbourne static inline void mte_suspend_exit(void)
141973b9e37SPeter Collingbourne {
142973b9e37SPeter Collingbourne }
set_mte_ctrl(struct task_struct * task,unsigned long arg)14393f067f6SCatalin Marinas static inline long set_mte_ctrl(struct task_struct *task, unsigned long arg)
1441c101da8SCatalin Marinas {
1451c101da8SCatalin Marinas return 0;
1461c101da8SCatalin Marinas }
get_mte_ctrl(struct task_struct * task)14793f067f6SCatalin Marinas static inline long get_mte_ctrl(struct task_struct *task)
1481c101da8SCatalin Marinas {
1491c101da8SCatalin Marinas return 0;
1501c101da8SCatalin Marinas }
mte_ptrace_copy_tags(struct task_struct * child,long request,unsigned long addr,unsigned long data)15118ddbaa0SCatalin Marinas static inline int mte_ptrace_copy_tags(struct task_struct *child,
15218ddbaa0SCatalin Marinas long request, unsigned long addr,
15318ddbaa0SCatalin Marinas unsigned long data)
15418ddbaa0SCatalin Marinas {
15518ddbaa0SCatalin Marinas return -EIO;
15618ddbaa0SCatalin Marinas }
157637ec831SVincenzo Frascino
15885f49caeSVincenzo Frascino #endif /* CONFIG_ARM64_MTE */
159637ec831SVincenzo Frascino
mte_disable_tco_entry(struct task_struct * task)16038ddf7daSPeter Collingbourne static inline void mte_disable_tco_entry(struct task_struct *task)
16138ddf7daSPeter Collingbourne {
16238ddf7daSPeter Collingbourne if (!system_supports_mte())
16338ddf7daSPeter Collingbourne return;
16438ddf7daSPeter Collingbourne
16538ddf7daSPeter Collingbourne /*
16638ddf7daSPeter Collingbourne * Re-enable tag checking (TCO set on exception entry). This is only
16738ddf7daSPeter Collingbourne * necessary if MTE is enabled in either the kernel or the userspace
16838ddf7daSPeter Collingbourne * task in synchronous or asymmetric mode (SCTLR_EL1.TCF0 bit 0 is set
16938ddf7daSPeter Collingbourne * for both). With MTE disabled in the kernel and disabled or
17038ddf7daSPeter Collingbourne * asynchronous in userspace, tag check faults (including in uaccesses)
17138ddf7daSPeter Collingbourne * are not reported, therefore there is no need to re-enable checking.
17238ddf7daSPeter Collingbourne * This is beneficial on microarchitectures where re-enabling TCO is
17338ddf7daSPeter Collingbourne * expensive.
17438ddf7daSPeter Collingbourne */
17538ddf7daSPeter Collingbourne if (kasan_hw_tags_enabled() ||
17638ddf7daSPeter Collingbourne (task->thread.sctlr_user & (1UL << SCTLR_EL1_TCF0_SHIFT)))
17738ddf7daSPeter Collingbourne asm volatile(SET_PSTATE_TCO(0));
17838ddf7daSPeter Collingbourne }
17938ddf7daSPeter Collingbourne
180e60beb95SVincenzo Frascino #ifdef CONFIG_KASAN_HW_TAGS
18165812c69SVincenzo Frascino void mte_check_tfsr_el1(void);
18265812c69SVincenzo Frascino
mte_check_tfsr_entry(void)18365812c69SVincenzo Frascino static inline void mte_check_tfsr_entry(void)
18465812c69SVincenzo Frascino {
1858c8a3b5bSPeter Collingbourne if (!system_supports_mte())
1868c8a3b5bSPeter Collingbourne return;
1878c8a3b5bSPeter Collingbourne
18865812c69SVincenzo Frascino mte_check_tfsr_el1();
18965812c69SVincenzo Frascino }
19065812c69SVincenzo Frascino
mte_check_tfsr_exit(void)19165812c69SVincenzo Frascino static inline void mte_check_tfsr_exit(void)
19265812c69SVincenzo Frascino {
1938c8a3b5bSPeter Collingbourne if (!system_supports_mte())
1948c8a3b5bSPeter Collingbourne return;
1958c8a3b5bSPeter Collingbourne
19665812c69SVincenzo Frascino /*
19765812c69SVincenzo Frascino * The asynchronous faults are sync'ed automatically with
19865812c69SVincenzo Frascino * TFSR_EL1 on kernel entry but for exit an explicit dsb()
19965812c69SVincenzo Frascino * is required.
20065812c69SVincenzo Frascino */
20165812c69SVincenzo Frascino dsb(nsh);
20265812c69SVincenzo Frascino isb();
20365812c69SVincenzo Frascino
20465812c69SVincenzo Frascino mte_check_tfsr_el1();
20565812c69SVincenzo Frascino }
206e60beb95SVincenzo Frascino #else
mte_check_tfsr_el1(void)20765812c69SVincenzo Frascino static inline void mte_check_tfsr_el1(void)
20865812c69SVincenzo Frascino {
20965812c69SVincenzo Frascino }
mte_check_tfsr_entry(void)21065812c69SVincenzo Frascino static inline void mte_check_tfsr_entry(void)
21165812c69SVincenzo Frascino {
21265812c69SVincenzo Frascino }
mte_check_tfsr_exit(void)21365812c69SVincenzo Frascino static inline void mte_check_tfsr_exit(void)
21465812c69SVincenzo Frascino {
21565812c69SVincenzo Frascino }
216e60beb95SVincenzo Frascino #endif /* CONFIG_KASAN_HW_TAGS */
217e60beb95SVincenzo Frascino
218637ec831SVincenzo Frascino #endif /* __ASSEMBLY__ */
219637ec831SVincenzo Frascino #endif /* __ASM_MTE_H */
220