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