1e9b60476SAmit Daniel Kachhap // SPDX-License-Identifier: GPL-2.0
2e9b60476SAmit Daniel Kachhap // Copyright (C) 2020 ARM Limited
3e9b60476SAmit Daniel Kachhap 
4e9b60476SAmit Daniel Kachhap #include <fcntl.h>
5e9b60476SAmit Daniel Kachhap #include <sched.h>
6e9b60476SAmit Daniel Kachhap #include <signal.h>
7e9b60476SAmit Daniel Kachhap #include <stdio.h>
8e9b60476SAmit Daniel Kachhap #include <stdlib.h>
9e9b60476SAmit Daniel Kachhap #include <unistd.h>
10e9b60476SAmit Daniel Kachhap 
11e9b60476SAmit Daniel Kachhap #include <linux/auxvec.h>
12e9b60476SAmit Daniel Kachhap #include <sys/auxv.h>
13e9b60476SAmit Daniel Kachhap #include <sys/mman.h>
14e9b60476SAmit Daniel Kachhap #include <sys/prctl.h>
15e9b60476SAmit Daniel Kachhap 
16e9b60476SAmit Daniel Kachhap #include <asm/hwcap.h>
17e9b60476SAmit Daniel Kachhap 
18e9b60476SAmit Daniel Kachhap #include "kselftest.h"
19e9b60476SAmit Daniel Kachhap #include "mte_common_util.h"
20e9b60476SAmit Daniel Kachhap #include "mte_def.h"
21e9b60476SAmit Daniel Kachhap 
22e9b60476SAmit Daniel Kachhap #define INIT_BUFFER_SIZE       256
23e9b60476SAmit Daniel Kachhap 
24e9b60476SAmit Daniel Kachhap struct mte_fault_cxt cur_mte_cxt;
25e9b60476SAmit Daniel Kachhap static unsigned int mte_cur_mode;
26e9b60476SAmit Daniel Kachhap static unsigned int mte_cur_pstate_tco;
27e9b60476SAmit Daniel Kachhap 
28e9b60476SAmit Daniel Kachhap void mte_default_handler(int signum, siginfo_t *si, void *uc)
29e9b60476SAmit Daniel Kachhap {
30e9b60476SAmit Daniel Kachhap 	unsigned long addr = (unsigned long)si->si_addr;
31e9b60476SAmit Daniel Kachhap 
32e9b60476SAmit Daniel Kachhap 	if (signum == SIGSEGV) {
33e9b60476SAmit Daniel Kachhap #ifdef DEBUG
34e9b60476SAmit Daniel Kachhap 		ksft_print_msg("INFO: SIGSEGV signal at pc=%lx, fault addr=%lx, si_code=%lx\n",
35e9b60476SAmit Daniel Kachhap 				((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code);
36e9b60476SAmit Daniel Kachhap #endif
37e9b60476SAmit Daniel Kachhap 		if (si->si_code == SEGV_MTEAERR) {
38e9b60476SAmit Daniel Kachhap 			if (cur_mte_cxt.trig_si_code == si->si_code)
39e9b60476SAmit Daniel Kachhap 				cur_mte_cxt.fault_valid = true;
40e9b60476SAmit Daniel Kachhap 			return;
41e9b60476SAmit Daniel Kachhap 		}
42e9b60476SAmit Daniel Kachhap 		/* Compare the context for precise error */
43e9b60476SAmit Daniel Kachhap 		else if (si->si_code == SEGV_MTESERR) {
44e9b60476SAmit Daniel Kachhap 			if (cur_mte_cxt.trig_si_code == si->si_code &&
45e9b60476SAmit Daniel Kachhap 			    ((cur_mte_cxt.trig_range >= 0 &&
46e9b60476SAmit Daniel Kachhap 			      addr >= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) &&
47e9b60476SAmit Daniel Kachhap 			      addr <= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) ||
48e9b60476SAmit Daniel Kachhap 			     (cur_mte_cxt.trig_range < 0 &&
49e9b60476SAmit Daniel Kachhap 			      addr <= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) &&
50e9b60476SAmit Daniel Kachhap 			      addr >= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)))) {
51e9b60476SAmit Daniel Kachhap 				cur_mte_cxt.fault_valid = true;
52e9b60476SAmit Daniel Kachhap 				/* Adjust the pc by 4 */
53e9b60476SAmit Daniel Kachhap 				((ucontext_t *)uc)->uc_mcontext.pc += 4;
54e9b60476SAmit Daniel Kachhap 			} else {
55e9b60476SAmit Daniel Kachhap 				ksft_print_msg("Invalid MTE synchronous exception caught!\n");
56e9b60476SAmit Daniel Kachhap 				exit(1);
57e9b60476SAmit Daniel Kachhap 			}
58e9b60476SAmit Daniel Kachhap 		} else {
59e9b60476SAmit Daniel Kachhap 			ksft_print_msg("Unknown SIGSEGV exception caught!\n");
60e9b60476SAmit Daniel Kachhap 			exit(1);
61e9b60476SAmit Daniel Kachhap 		}
62e9b60476SAmit Daniel Kachhap 	} else if (signum == SIGBUS) {
63e9b60476SAmit Daniel Kachhap 		ksft_print_msg("INFO: SIGBUS signal at pc=%lx, fault addr=%lx, si_code=%lx\n",
64e9b60476SAmit Daniel Kachhap 				((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code);
65e9b60476SAmit Daniel Kachhap 		if ((cur_mte_cxt.trig_range >= 0 &&
66e9b60476SAmit Daniel Kachhap 		     addr >= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) &&
67e9b60476SAmit Daniel Kachhap 		     addr <= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) ||
68e9b60476SAmit Daniel Kachhap 		    (cur_mte_cxt.trig_range < 0 &&
69e9b60476SAmit Daniel Kachhap 		     addr <= MT_CLEAR_TAG(cur_mte_cxt.trig_addr) &&
70e9b60476SAmit Daniel Kachhap 		     addr >= (MT_CLEAR_TAG(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range))) {
71e9b60476SAmit Daniel Kachhap 			cur_mte_cxt.fault_valid = true;
72e9b60476SAmit Daniel Kachhap 			/* Adjust the pc by 4 */
73e9b60476SAmit Daniel Kachhap 			((ucontext_t *)uc)->uc_mcontext.pc += 4;
74e9b60476SAmit Daniel Kachhap 		}
75e9b60476SAmit Daniel Kachhap 	}
76e9b60476SAmit Daniel Kachhap }
77e9b60476SAmit Daniel Kachhap 
78e9b60476SAmit Daniel Kachhap void mte_register_signal(int signal, void (*handler)(int, siginfo_t *, void *))
79e9b60476SAmit Daniel Kachhap {
80e9b60476SAmit Daniel Kachhap 	struct sigaction sa;
81e9b60476SAmit Daniel Kachhap 
82e9b60476SAmit Daniel Kachhap 	sa.sa_sigaction = handler;
83e9b60476SAmit Daniel Kachhap 	sa.sa_flags = SA_SIGINFO;
84e9b60476SAmit Daniel Kachhap 	sigemptyset(&sa.sa_mask);
85e9b60476SAmit Daniel Kachhap 	sigaction(signal, &sa, NULL);
86e9b60476SAmit Daniel Kachhap }
87e9b60476SAmit Daniel Kachhap 
88e9b60476SAmit Daniel Kachhap void mte_wait_after_trig(void)
89e9b60476SAmit Daniel Kachhap {
90e9b60476SAmit Daniel Kachhap 	sched_yield();
91e9b60476SAmit Daniel Kachhap }
92e9b60476SAmit Daniel Kachhap 
93e9b60476SAmit Daniel Kachhap void *mte_insert_tags(void *ptr, size_t size)
94e9b60476SAmit Daniel Kachhap {
95e9b60476SAmit Daniel Kachhap 	void *tag_ptr;
96e9b60476SAmit Daniel Kachhap 	int align_size;
97e9b60476SAmit Daniel Kachhap 
98e9b60476SAmit Daniel Kachhap 	if (!ptr || (unsigned long)(ptr) & MT_ALIGN_GRANULE) {
99e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: Addr=%lx: invalid\n", ptr);
100e9b60476SAmit Daniel Kachhap 		return NULL;
101e9b60476SAmit Daniel Kachhap 	}
102e9b60476SAmit Daniel Kachhap 	align_size = MT_ALIGN_UP(size);
103e9b60476SAmit Daniel Kachhap 	tag_ptr = mte_insert_random_tag(ptr);
104e9b60476SAmit Daniel Kachhap 	mte_set_tag_address_range(tag_ptr, align_size);
105e9b60476SAmit Daniel Kachhap 	return tag_ptr;
106e9b60476SAmit Daniel Kachhap }
107e9b60476SAmit Daniel Kachhap 
108e9b60476SAmit Daniel Kachhap void mte_clear_tags(void *ptr, size_t size)
109e9b60476SAmit Daniel Kachhap {
110e9b60476SAmit Daniel Kachhap 	if (!ptr || (unsigned long)(ptr) & MT_ALIGN_GRANULE) {
111e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: Addr=%lx: invalid\n", ptr);
112e9b60476SAmit Daniel Kachhap 		return;
113e9b60476SAmit Daniel Kachhap 	}
114e9b60476SAmit Daniel Kachhap 	size = MT_ALIGN_UP(size);
115e9b60476SAmit Daniel Kachhap 	ptr = (void *)MT_CLEAR_TAG((unsigned long)ptr);
116e9b60476SAmit Daniel Kachhap 	mte_clear_tag_address_range(ptr, size);
117e9b60476SAmit Daniel Kachhap }
118e9b60476SAmit Daniel Kachhap 
119e9b60476SAmit Daniel Kachhap static void *__mte_allocate_memory_range(size_t size, int mem_type, int mapping,
120e9b60476SAmit Daniel Kachhap 					 size_t range_before, size_t range_after,
121e9b60476SAmit Daniel Kachhap 					 bool tags, int fd)
122e9b60476SAmit Daniel Kachhap {
123e9b60476SAmit Daniel Kachhap 	void *ptr;
124e9b60476SAmit Daniel Kachhap 	int prot_flag, map_flag;
125e9b60476SAmit Daniel Kachhap 	size_t entire_size = size + range_before + range_after;
126e9b60476SAmit Daniel Kachhap 
127e9b60476SAmit Daniel Kachhap 	if (mem_type != USE_MALLOC && mem_type != USE_MMAP &&
128e9b60476SAmit Daniel Kachhap 	    mem_type != USE_MPROTECT) {
129e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: Invalid allocate request\n");
130e9b60476SAmit Daniel Kachhap 		return NULL;
131e9b60476SAmit Daniel Kachhap 	}
132e9b60476SAmit Daniel Kachhap 	if (mem_type == USE_MALLOC)
133e9b60476SAmit Daniel Kachhap 		return malloc(entire_size) + range_before;
134e9b60476SAmit Daniel Kachhap 
135e9b60476SAmit Daniel Kachhap 	prot_flag = PROT_READ | PROT_WRITE;
136e9b60476SAmit Daniel Kachhap 	if (mem_type == USE_MMAP)
137e9b60476SAmit Daniel Kachhap 		prot_flag |= PROT_MTE;
138e9b60476SAmit Daniel Kachhap 
139e9b60476SAmit Daniel Kachhap 	map_flag = mapping;
140e9b60476SAmit Daniel Kachhap 	if (fd == -1)
141e9b60476SAmit Daniel Kachhap 		map_flag = MAP_ANONYMOUS | map_flag;
142e9b60476SAmit Daniel Kachhap 	if (!(mapping & MAP_SHARED))
143e9b60476SAmit Daniel Kachhap 		map_flag |= MAP_PRIVATE;
144e9b60476SAmit Daniel Kachhap 	ptr = mmap(NULL, entire_size, prot_flag, map_flag, fd, 0);
145e9b60476SAmit Daniel Kachhap 	if (ptr == MAP_FAILED) {
146e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: mmap allocation\n");
147e9b60476SAmit Daniel Kachhap 		return NULL;
148e9b60476SAmit Daniel Kachhap 	}
149e9b60476SAmit Daniel Kachhap 	if (mem_type == USE_MPROTECT) {
150e9b60476SAmit Daniel Kachhap 		if (mprotect(ptr, entire_size, prot_flag | PROT_MTE)) {
151e9b60476SAmit Daniel Kachhap 			munmap(ptr, size);
152e9b60476SAmit Daniel Kachhap 			ksft_print_msg("FAIL: mprotect PROT_MTE property\n");
153e9b60476SAmit Daniel Kachhap 			return NULL;
154e9b60476SAmit Daniel Kachhap 		}
155e9b60476SAmit Daniel Kachhap 	}
156e9b60476SAmit Daniel Kachhap 	if (tags)
157e9b60476SAmit Daniel Kachhap 		ptr = mte_insert_tags(ptr + range_before, size);
158e9b60476SAmit Daniel Kachhap 	return ptr;
159e9b60476SAmit Daniel Kachhap }
160e9b60476SAmit Daniel Kachhap 
161e9b60476SAmit Daniel Kachhap void *mte_allocate_memory_tag_range(size_t size, int mem_type, int mapping,
162e9b60476SAmit Daniel Kachhap 				    size_t range_before, size_t range_after)
163e9b60476SAmit Daniel Kachhap {
164e9b60476SAmit Daniel Kachhap 	return __mte_allocate_memory_range(size, mem_type, mapping, range_before,
165e9b60476SAmit Daniel Kachhap 					   range_after, true, -1);
166e9b60476SAmit Daniel Kachhap }
167e9b60476SAmit Daniel Kachhap 
168e9b60476SAmit Daniel Kachhap void *mte_allocate_memory(size_t size, int mem_type, int mapping, bool tags)
169e9b60476SAmit Daniel Kachhap {
170e9b60476SAmit Daniel Kachhap 	return __mte_allocate_memory_range(size, mem_type, mapping, 0, 0, tags, -1);
171e9b60476SAmit Daniel Kachhap }
172e9b60476SAmit Daniel Kachhap 
173e9b60476SAmit Daniel Kachhap void *mte_allocate_file_memory(size_t size, int mem_type, int mapping, bool tags, int fd)
174e9b60476SAmit Daniel Kachhap {
175e9b60476SAmit Daniel Kachhap 	int index;
176e9b60476SAmit Daniel Kachhap 	char buffer[INIT_BUFFER_SIZE];
177e9b60476SAmit Daniel Kachhap 
178e9b60476SAmit Daniel Kachhap 	if (mem_type != USE_MPROTECT && mem_type != USE_MMAP) {
179e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: Invalid mmap file request\n");
180e9b60476SAmit Daniel Kachhap 		return NULL;
181e9b60476SAmit Daniel Kachhap 	}
182e9b60476SAmit Daniel Kachhap 	/* Initialize the file for mappable size */
183e9b60476SAmit Daniel Kachhap 	lseek(fd, 0, SEEK_SET);
184e9b60476SAmit Daniel Kachhap 	for (index = INIT_BUFFER_SIZE; index < size; index += INIT_BUFFER_SIZE)
185e9b60476SAmit Daniel Kachhap 		write(fd, buffer, INIT_BUFFER_SIZE);
186e9b60476SAmit Daniel Kachhap 	index -= INIT_BUFFER_SIZE;
187e9b60476SAmit Daniel Kachhap 	write(fd, buffer, size - index);
188e9b60476SAmit Daniel Kachhap 	return __mte_allocate_memory_range(size, mem_type, mapping, 0, 0, tags, fd);
189e9b60476SAmit Daniel Kachhap }
190e9b60476SAmit Daniel Kachhap 
191e9b60476SAmit Daniel Kachhap void *mte_allocate_file_memory_tag_range(size_t size, int mem_type, int mapping,
192e9b60476SAmit Daniel Kachhap 					 size_t range_before, size_t range_after, int fd)
193e9b60476SAmit Daniel Kachhap {
194e9b60476SAmit Daniel Kachhap 	int index;
195e9b60476SAmit Daniel Kachhap 	char buffer[INIT_BUFFER_SIZE];
196e9b60476SAmit Daniel Kachhap 	int map_size = size + range_before + range_after;
197e9b60476SAmit Daniel Kachhap 
198e9b60476SAmit Daniel Kachhap 	if (mem_type != USE_MPROTECT && mem_type != USE_MMAP) {
199e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: Invalid mmap file request\n");
200e9b60476SAmit Daniel Kachhap 		return NULL;
201e9b60476SAmit Daniel Kachhap 	}
202e9b60476SAmit Daniel Kachhap 	/* Initialize the file for mappable size */
203e9b60476SAmit Daniel Kachhap 	lseek(fd, 0, SEEK_SET);
204e9b60476SAmit Daniel Kachhap 	for (index = INIT_BUFFER_SIZE; index < map_size; index += INIT_BUFFER_SIZE)
205e9b60476SAmit Daniel Kachhap 		write(fd, buffer, INIT_BUFFER_SIZE);
206e9b60476SAmit Daniel Kachhap 	index -= INIT_BUFFER_SIZE;
207e9b60476SAmit Daniel Kachhap 	write(fd, buffer, map_size - index);
208e9b60476SAmit Daniel Kachhap 	return __mte_allocate_memory_range(size, mem_type, mapping, range_before,
209e9b60476SAmit Daniel Kachhap 					   range_after, true, fd);
210e9b60476SAmit Daniel Kachhap }
211e9b60476SAmit Daniel Kachhap 
212e9b60476SAmit Daniel Kachhap static void __mte_free_memory_range(void *ptr, size_t size, int mem_type,
213e9b60476SAmit Daniel Kachhap 				    size_t range_before, size_t range_after, bool tags)
214e9b60476SAmit Daniel Kachhap {
215e9b60476SAmit Daniel Kachhap 	switch (mem_type) {
216e9b60476SAmit Daniel Kachhap 	case USE_MALLOC:
217e9b60476SAmit Daniel Kachhap 		free(ptr - range_before);
218e9b60476SAmit Daniel Kachhap 		break;
219e9b60476SAmit Daniel Kachhap 	case USE_MMAP:
220e9b60476SAmit Daniel Kachhap 	case USE_MPROTECT:
221e9b60476SAmit Daniel Kachhap 		if (tags)
222e9b60476SAmit Daniel Kachhap 			mte_clear_tags(ptr, size);
223e9b60476SAmit Daniel Kachhap 		munmap(ptr - range_before, size + range_before + range_after);
224e9b60476SAmit Daniel Kachhap 		break;
225e9b60476SAmit Daniel Kachhap 	default:
226e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: Invalid free request\n");
227e9b60476SAmit Daniel Kachhap 		break;
228e9b60476SAmit Daniel Kachhap 	}
229e9b60476SAmit Daniel Kachhap }
230e9b60476SAmit Daniel Kachhap 
231e9b60476SAmit Daniel Kachhap void mte_free_memory_tag_range(void *ptr, size_t size, int mem_type,
232e9b60476SAmit Daniel Kachhap 			       size_t range_before, size_t range_after)
233e9b60476SAmit Daniel Kachhap {
234e9b60476SAmit Daniel Kachhap 	__mte_free_memory_range(ptr, size, mem_type, range_before, range_after, true);
235e9b60476SAmit Daniel Kachhap }
236e9b60476SAmit Daniel Kachhap 
237e9b60476SAmit Daniel Kachhap void mte_free_memory(void *ptr, size_t size, int mem_type, bool tags)
238e9b60476SAmit Daniel Kachhap {
239e9b60476SAmit Daniel Kachhap 	__mte_free_memory_range(ptr, size, mem_type, 0, 0, tags);
240e9b60476SAmit Daniel Kachhap }
241e9b60476SAmit Daniel Kachhap 
242e9b60476SAmit Daniel Kachhap void mte_initialize_current_context(int mode, uintptr_t ptr, ssize_t range)
243e9b60476SAmit Daniel Kachhap {
244e9b60476SAmit Daniel Kachhap 	cur_mte_cxt.fault_valid = false;
245e9b60476SAmit Daniel Kachhap 	cur_mte_cxt.trig_addr = ptr;
246e9b60476SAmit Daniel Kachhap 	cur_mte_cxt.trig_range = range;
247e9b60476SAmit Daniel Kachhap 	if (mode == MTE_SYNC_ERR)
248e9b60476SAmit Daniel Kachhap 		cur_mte_cxt.trig_si_code = SEGV_MTESERR;
249e9b60476SAmit Daniel Kachhap 	else if (mode == MTE_ASYNC_ERR)
250e9b60476SAmit Daniel Kachhap 		cur_mte_cxt.trig_si_code = SEGV_MTEAERR;
251e9b60476SAmit Daniel Kachhap 	else
252e9b60476SAmit Daniel Kachhap 		cur_mte_cxt.trig_si_code = 0;
253e9b60476SAmit Daniel Kachhap }
254e9b60476SAmit Daniel Kachhap 
255e9b60476SAmit Daniel Kachhap int mte_switch_mode(int mte_option, unsigned long incl_mask)
256e9b60476SAmit Daniel Kachhap {
257e9b60476SAmit Daniel Kachhap 	unsigned long en = 0;
258e9b60476SAmit Daniel Kachhap 
259e9b60476SAmit Daniel Kachhap 	if (!(mte_option == MTE_SYNC_ERR || mte_option == MTE_ASYNC_ERR ||
260e9b60476SAmit Daniel Kachhap 	      mte_option == MTE_NONE_ERR || incl_mask <= MTE_ALLOW_NON_ZERO_TAG)) {
261e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: Invalid mte config option\n");
262e9b60476SAmit Daniel Kachhap 		return -EINVAL;
263e9b60476SAmit Daniel Kachhap 	}
264e9b60476SAmit Daniel Kachhap 	en = PR_TAGGED_ADDR_ENABLE;
265e9b60476SAmit Daniel Kachhap 	if (mte_option == MTE_SYNC_ERR)
266e9b60476SAmit Daniel Kachhap 		en |= PR_MTE_TCF_SYNC;
267e9b60476SAmit Daniel Kachhap 	else if (mte_option == MTE_ASYNC_ERR)
268e9b60476SAmit Daniel Kachhap 		en |= PR_MTE_TCF_ASYNC;
269e9b60476SAmit Daniel Kachhap 	else if (mte_option == MTE_NONE_ERR)
270e9b60476SAmit Daniel Kachhap 		en |= PR_MTE_TCF_NONE;
271e9b60476SAmit Daniel Kachhap 
272e9b60476SAmit Daniel Kachhap 	en |= (incl_mask << PR_MTE_TAG_SHIFT);
273e9b60476SAmit Daniel Kachhap 	/* Enable address tagging ABI, mte error reporting mode and tag inclusion mask. */
274e9b60476SAmit Daniel Kachhap 	if (!prctl(PR_SET_TAGGED_ADDR_CTRL, en, 0, 0, 0) == 0) {
275e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL:prctl PR_SET_TAGGED_ADDR_CTRL for mte mode\n");
276e9b60476SAmit Daniel Kachhap 		return -EINVAL;
277e9b60476SAmit Daniel Kachhap 	}
278e9b60476SAmit Daniel Kachhap 	return 0;
279e9b60476SAmit Daniel Kachhap }
280e9b60476SAmit Daniel Kachhap 
281e9b60476SAmit Daniel Kachhap #define ID_AA64PFR1_MTE_SHIFT		8
282e9b60476SAmit Daniel Kachhap #define ID_AA64PFR1_MTE			2
283e9b60476SAmit Daniel Kachhap 
284e9b60476SAmit Daniel Kachhap int mte_default_setup(void)
285e9b60476SAmit Daniel Kachhap {
286e9b60476SAmit Daniel Kachhap 	unsigned long hwcaps = getauxval(AT_HWCAP);
287e9b60476SAmit Daniel Kachhap 	unsigned long en = 0;
288e9b60476SAmit Daniel Kachhap 	int ret;
289e9b60476SAmit Daniel Kachhap 
290e9b60476SAmit Daniel Kachhap 	if (!(hwcaps & HWCAP_CPUID)) {
291e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: CPUID registers unavailable\n");
292e9b60476SAmit Daniel Kachhap 		return KSFT_FAIL;
293e9b60476SAmit Daniel Kachhap 	}
294e9b60476SAmit Daniel Kachhap 	/* Read ID_AA64PFR1_EL1 register */
295e9b60476SAmit Daniel Kachhap 	asm volatile("mrs %0, id_aa64pfr1_el1" : "=r"(hwcaps) : : "memory");
296e9b60476SAmit Daniel Kachhap 	if (((hwcaps >> ID_AA64PFR1_MTE_SHIFT) & MT_TAG_MASK) != ID_AA64PFR1_MTE) {
297e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: MTE features unavailable\n");
298e9b60476SAmit Daniel Kachhap 		return KSFT_SKIP;
299e9b60476SAmit Daniel Kachhap 	}
300e9b60476SAmit Daniel Kachhap 	/* Get current mte mode */
301e9b60476SAmit Daniel Kachhap 	ret = prctl(PR_GET_TAGGED_ADDR_CTRL, en, 0, 0, 0);
302e9b60476SAmit Daniel Kachhap 	if (ret < 0) {
303e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL:prctl PR_GET_TAGGED_ADDR_CTRL with error =%d\n", ret);
304e9b60476SAmit Daniel Kachhap 		return KSFT_FAIL;
305e9b60476SAmit Daniel Kachhap 	}
306e9b60476SAmit Daniel Kachhap 	if (ret & PR_MTE_TCF_SYNC)
307e9b60476SAmit Daniel Kachhap 		mte_cur_mode = MTE_SYNC_ERR;
308e9b60476SAmit Daniel Kachhap 	else if (ret & PR_MTE_TCF_ASYNC)
309e9b60476SAmit Daniel Kachhap 		mte_cur_mode = MTE_ASYNC_ERR;
310e9b60476SAmit Daniel Kachhap 	else if (ret & PR_MTE_TCF_NONE)
311e9b60476SAmit Daniel Kachhap 		mte_cur_mode = MTE_NONE_ERR;
312e9b60476SAmit Daniel Kachhap 
313e9b60476SAmit Daniel Kachhap 	mte_cur_pstate_tco = mte_get_pstate_tco();
314e9b60476SAmit Daniel Kachhap 	/* Disable PSTATE.TCO */
315e9b60476SAmit Daniel Kachhap 	mte_disable_pstate_tco();
316e9b60476SAmit Daniel Kachhap 	return 0;
317e9b60476SAmit Daniel Kachhap }
318e9b60476SAmit Daniel Kachhap 
319e9b60476SAmit Daniel Kachhap void mte_restore_setup(void)
320e9b60476SAmit Daniel Kachhap {
321e9b60476SAmit Daniel Kachhap 	mte_switch_mode(mte_cur_mode, MTE_ALLOW_NON_ZERO_TAG);
322e9b60476SAmit Daniel Kachhap 	if (mte_cur_pstate_tco == MT_PSTATE_TCO_EN)
323e9b60476SAmit Daniel Kachhap 		mte_enable_pstate_tco();
324e9b60476SAmit Daniel Kachhap 	else if (mte_cur_pstate_tco == MT_PSTATE_TCO_DIS)
325e9b60476SAmit Daniel Kachhap 		mte_disable_pstate_tco();
326e9b60476SAmit Daniel Kachhap }
327e9b60476SAmit Daniel Kachhap 
328e9b60476SAmit Daniel Kachhap int create_temp_file(void)
329e9b60476SAmit Daniel Kachhap {
330e9b60476SAmit Daniel Kachhap 	int fd;
331e9b60476SAmit Daniel Kachhap 	char filename[] = "/dev/shm/tmp_XXXXXX";
332e9b60476SAmit Daniel Kachhap 
333e9b60476SAmit Daniel Kachhap 	/* Create a file in the tmpfs filesystem */
334e9b60476SAmit Daniel Kachhap 	fd = mkstemp(&filename[0]);
335e9b60476SAmit Daniel Kachhap 	if (fd == -1) {
336e9b60476SAmit Daniel Kachhap 		ksft_print_msg("FAIL: Unable to open temporary file\n");
337e9b60476SAmit Daniel Kachhap 		return 0;
338e9b60476SAmit Daniel Kachhap 	}
339e9b60476SAmit Daniel Kachhap 	unlink(&filename[0]);
340e9b60476SAmit Daniel Kachhap 	return fd;
341e9b60476SAmit Daniel Kachhap }
342