xref: /openbmc/linux/tools/testing/selftests/x86/lam.c (revision b13513e7)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/syscall.h>
7 #include <time.h>
8 #include <signal.h>
9 #include <setjmp.h>
10 #include <sys/mman.h>
11 #include <sys/utsname.h>
12 #include <sys/wait.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <inttypes.h>
16 #include <sched.h>
17 
18 #include <sys/uio.h>
19 #include <linux/io_uring.h>
20 #include "../kselftest.h"
21 
22 #ifndef __x86_64__
23 # error This test is 64-bit only
24 #endif
25 
26 /* LAM modes, these definitions were copied from kernel code */
27 #define LAM_NONE                0
28 #define LAM_U57_BITS            6
29 
30 #define LAM_U57_MASK            (0x3fULL << 57)
31 /* arch prctl for LAM */
32 #define ARCH_GET_UNTAG_MASK     0x4001
33 #define ARCH_ENABLE_TAGGED_ADDR 0x4002
34 #define ARCH_GET_MAX_TAG_BITS   0x4003
35 #define ARCH_FORCE_TAGGED_SVA	0x4004
36 
37 /* Specified test function bits */
38 #define FUNC_MALLOC             0x1
39 #define FUNC_BITS               0x2
40 #define FUNC_MMAP               0x4
41 #define FUNC_SYSCALL            0x8
42 #define FUNC_URING              0x10
43 #define FUNC_INHERITE           0x20
44 #define FUNC_PASID              0x40
45 
46 #define TEST_MASK               0x7f
47 
48 #define LOW_ADDR                (0x1UL << 30)
49 #define HIGH_ADDR               (0x3UL << 48)
50 
51 #define MALLOC_LEN              32
52 
53 #define PAGE_SIZE               (4 << 10)
54 
55 #define STACK_SIZE		65536
56 
57 #define barrier() ({						\
58 		   __asm__ __volatile__("" : : : "memory");	\
59 })
60 
61 #define URING_QUEUE_SZ 1
62 #define URING_BLOCK_SZ 2048
63 
64 /* Pasid test define */
65 #define LAM_CMD_BIT 0x1
66 #define PAS_CMD_BIT 0x2
67 #define SVA_CMD_BIT 0x4
68 
69 #define PAS_CMD(cmd1, cmd2, cmd3) (((cmd3) << 8) | ((cmd2) << 4) | ((cmd1) << 0))
70 
71 struct testcases {
72 	unsigned int later;
73 	int expected; /* 2: SIGSEGV Error; 1: other errors */
74 	unsigned long lam;
75 	uint64_t addr;
76 	uint64_t cmd;
77 	int (*test_func)(struct testcases *test);
78 	const char *msg;
79 };
80 
81 /* Used by CQ of uring, source file handler and file's size */
82 struct file_io {
83 	int file_fd;
84 	off_t file_sz;
85 	struct iovec iovecs[];
86 };
87 
88 struct io_uring_queue {
89 	unsigned int *head;
90 	unsigned int *tail;
91 	unsigned int *ring_mask;
92 	unsigned int *ring_entries;
93 	unsigned int *flags;
94 	unsigned int *array;
95 	union {
96 		struct io_uring_cqe *cqes;
97 		struct io_uring_sqe *sqes;
98 	} queue;
99 	size_t ring_sz;
100 };
101 
102 struct io_ring {
103 	int ring_fd;
104 	struct io_uring_queue sq_ring;
105 	struct io_uring_queue cq_ring;
106 };
107 
108 int tests_cnt;
109 jmp_buf segv_env;
110 
segv_handler(int sig)111 static void segv_handler(int sig)
112 {
113 	ksft_print_msg("Get segmentation fault(%d).", sig);
114 
115 	siglongjmp(segv_env, 1);
116 }
117 
cpu_has_lam(void)118 static inline int cpu_has_lam(void)
119 {
120 	unsigned int cpuinfo[4];
121 
122 	__cpuid_count(0x7, 1, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]);
123 
124 	return (cpuinfo[0] & (1 << 26));
125 }
126 
127 /* Check 5-level page table feature in CPUID.(EAX=07H, ECX=00H):ECX.[bit 16] */
cpu_has_la57(void)128 static inline int cpu_has_la57(void)
129 {
130 	unsigned int cpuinfo[4];
131 
132 	__cpuid_count(0x7, 0, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]);
133 
134 	return (cpuinfo[2] & (1 << 16));
135 }
136 
137 /*
138  * Set tagged address and read back untag mask.
139  * check if the untagged mask is expected.
140  *
141  * @return:
142  * 0: Set LAM mode successfully
143  * others: failed to set LAM
144  */
set_lam(unsigned long lam)145 static int set_lam(unsigned long lam)
146 {
147 	int ret = 0;
148 	uint64_t ptr = 0;
149 
150 	if (lam != LAM_U57_BITS && lam != LAM_NONE)
151 		return -1;
152 
153 	/* Skip check return */
154 	syscall(SYS_arch_prctl, ARCH_ENABLE_TAGGED_ADDR, lam);
155 
156 	/* Get untagged mask */
157 	syscall(SYS_arch_prctl, ARCH_GET_UNTAG_MASK, &ptr);
158 
159 	/* Check mask returned is expected */
160 	if (lam == LAM_U57_BITS)
161 		ret = (ptr != ~(LAM_U57_MASK));
162 	else if (lam == LAM_NONE)
163 		ret = (ptr != -1ULL);
164 
165 	return ret;
166 }
167 
get_default_tag_bits(void)168 static unsigned long get_default_tag_bits(void)
169 {
170 	pid_t pid;
171 	int lam = LAM_NONE;
172 	int ret = 0;
173 
174 	pid = fork();
175 	if (pid < 0) {
176 		perror("Fork failed.");
177 	} else if (pid == 0) {
178 		/* Set LAM mode in child process */
179 		if (set_lam(LAM_U57_BITS) == 0)
180 			lam = LAM_U57_BITS;
181 		else
182 			lam = LAM_NONE;
183 		exit(lam);
184 	} else {
185 		wait(&ret);
186 		lam = WEXITSTATUS(ret);
187 	}
188 
189 	return lam;
190 }
191 
192 /*
193  * Set tagged address and read back untag mask.
194  * check if the untag mask is expected.
195  */
get_lam(void)196 static int get_lam(void)
197 {
198 	uint64_t ptr = 0;
199 	int ret = -1;
200 	/* Get untagged mask */
201 	if (syscall(SYS_arch_prctl, ARCH_GET_UNTAG_MASK, &ptr) == -1)
202 		return -1;
203 
204 	/* Check mask returned is expected */
205 	if (ptr == ~(LAM_U57_MASK))
206 		ret = LAM_U57_BITS;
207 	else if (ptr == -1ULL)
208 		ret = LAM_NONE;
209 
210 
211 	return ret;
212 }
213 
214 /* According to LAM mode, set metadata in high bits */
set_metadata(uint64_t src,unsigned long lam)215 static uint64_t set_metadata(uint64_t src, unsigned long lam)
216 {
217 	uint64_t metadata;
218 
219 	srand(time(NULL));
220 
221 	switch (lam) {
222 	case LAM_U57_BITS: /* Set metadata in bits 62:57 */
223 		/* Get a random non-zero value as metadata */
224 		metadata = (rand() % ((1UL << LAM_U57_BITS) - 1) + 1) << 57;
225 		metadata |= (src & ~(LAM_U57_MASK));
226 		break;
227 	default:
228 		metadata = src;
229 		break;
230 	}
231 
232 	return metadata;
233 }
234 
235 /*
236  * Set metadata in user pointer, compare new pointer with original pointer.
237  * both pointers should point to the same address.
238  *
239  * @return:
240  * 0: value on the pointer with metadate and value on original are same
241  * 1: not same.
242  */
handle_lam_test(void * src,unsigned int lam)243 static int handle_lam_test(void *src, unsigned int lam)
244 {
245 	char *ptr;
246 
247 	strcpy((char *)src, "USER POINTER");
248 
249 	ptr = (char *)set_metadata((uint64_t)src, lam);
250 	if (src == ptr)
251 		return 0;
252 
253 	/* Copy a string into the pointer with metadata */
254 	strcpy((char *)ptr, "METADATA POINTER");
255 
256 	return (!!strcmp((char *)src, (char *)ptr));
257 }
258 
259 
handle_max_bits(struct testcases * test)260 int handle_max_bits(struct testcases *test)
261 {
262 	unsigned long exp_bits = get_default_tag_bits();
263 	unsigned long bits = 0;
264 
265 	if (exp_bits != LAM_NONE)
266 		exp_bits = LAM_U57_BITS;
267 
268 	/* Get LAM max tag bits */
269 	if (syscall(SYS_arch_prctl, ARCH_GET_MAX_TAG_BITS, &bits) == -1)
270 		return 1;
271 
272 	return (exp_bits != bits);
273 }
274 
275 /*
276  * Test lam feature through dereference pointer get from malloc.
277  * @return 0: Pass test. 1: Get failure during test 2: Get SIGSEGV
278  */
handle_malloc(struct testcases * test)279 static int handle_malloc(struct testcases *test)
280 {
281 	char *ptr = NULL;
282 	int ret = 0;
283 
284 	if (test->later == 0 && test->lam != 0)
285 		if (set_lam(test->lam) == -1)
286 			return 1;
287 
288 	ptr = (char *)malloc(MALLOC_LEN);
289 	if (ptr == NULL) {
290 		perror("malloc() failure\n");
291 		return 1;
292 	}
293 
294 	/* Set signal handler */
295 	if (sigsetjmp(segv_env, 1) == 0) {
296 		signal(SIGSEGV, segv_handler);
297 		ret = handle_lam_test(ptr, test->lam);
298 	} else {
299 		ret = 2;
300 	}
301 
302 	if (test->later != 0 && test->lam != 0)
303 		if (set_lam(test->lam) == -1 && ret == 0)
304 			ret = 1;
305 
306 	free(ptr);
307 
308 	return ret;
309 }
310 
handle_mmap(struct testcases * test)311 static int handle_mmap(struct testcases *test)
312 {
313 	void *ptr;
314 	unsigned int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
315 	int ret = 0;
316 
317 	if (test->later == 0 && test->lam != 0)
318 		if (set_lam(test->lam) != 0)
319 			return 1;
320 
321 	ptr = mmap((void *)test->addr, PAGE_SIZE, PROT_READ | PROT_WRITE,
322 		   flags, -1, 0);
323 	if (ptr == MAP_FAILED) {
324 		if (test->addr == HIGH_ADDR)
325 			if (!cpu_has_la57())
326 				return 3; /* unsupport LA57 */
327 		return 1;
328 	}
329 
330 	if (test->later != 0 && test->lam != 0)
331 		if (set_lam(test->lam) != 0)
332 			ret = 1;
333 
334 	if (ret == 0) {
335 		if (sigsetjmp(segv_env, 1) == 0) {
336 			signal(SIGSEGV, segv_handler);
337 			ret = handle_lam_test(ptr, test->lam);
338 		} else {
339 			ret = 2;
340 		}
341 	}
342 
343 	munmap(ptr, PAGE_SIZE);
344 	return ret;
345 }
346 
handle_syscall(struct testcases * test)347 static int handle_syscall(struct testcases *test)
348 {
349 	struct utsname unme, *pu;
350 	int ret = 0;
351 
352 	if (test->later == 0 && test->lam != 0)
353 		if (set_lam(test->lam) != 0)
354 			return 1;
355 
356 	if (sigsetjmp(segv_env, 1) == 0) {
357 		signal(SIGSEGV, segv_handler);
358 		pu = (struct utsname *)set_metadata((uint64_t)&unme, test->lam);
359 		ret = uname(pu);
360 		if (ret < 0)
361 			ret = 1;
362 	} else {
363 		ret = 2;
364 	}
365 
366 	if (test->later != 0 && test->lam != 0)
367 		if (set_lam(test->lam) != -1 && ret == 0)
368 			ret = 1;
369 
370 	return ret;
371 }
372 
sys_uring_setup(unsigned int entries,struct io_uring_params * p)373 int sys_uring_setup(unsigned int entries, struct io_uring_params *p)
374 {
375 	return (int)syscall(__NR_io_uring_setup, entries, p);
376 }
377 
sys_uring_enter(int fd,unsigned int to,unsigned int min,unsigned int flags)378 int sys_uring_enter(int fd, unsigned int to, unsigned int min, unsigned int flags)
379 {
380 	return (int)syscall(__NR_io_uring_enter, fd, to, min, flags, NULL, 0);
381 }
382 
383 /* Init submission queue and completion queue */
mmap_io_uring(struct io_uring_params p,struct io_ring * s)384 int mmap_io_uring(struct io_uring_params p, struct io_ring *s)
385 {
386 	struct io_uring_queue *sring = &s->sq_ring;
387 	struct io_uring_queue *cring = &s->cq_ring;
388 
389 	sring->ring_sz = p.sq_off.array + p.sq_entries * sizeof(unsigned int);
390 	cring->ring_sz = p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe);
391 
392 	if (p.features & IORING_FEAT_SINGLE_MMAP) {
393 		if (cring->ring_sz > sring->ring_sz)
394 			sring->ring_sz = cring->ring_sz;
395 
396 		cring->ring_sz = sring->ring_sz;
397 	}
398 
399 	void *sq_ptr = mmap(0, sring->ring_sz, PROT_READ | PROT_WRITE,
400 			    MAP_SHARED | MAP_POPULATE, s->ring_fd,
401 			    IORING_OFF_SQ_RING);
402 
403 	if (sq_ptr == MAP_FAILED) {
404 		perror("sub-queue!");
405 		return 1;
406 	}
407 
408 	void *cq_ptr = sq_ptr;
409 
410 	if (!(p.features & IORING_FEAT_SINGLE_MMAP)) {
411 		cq_ptr = mmap(0, cring->ring_sz, PROT_READ | PROT_WRITE,
412 			      MAP_SHARED | MAP_POPULATE, s->ring_fd,
413 			      IORING_OFF_CQ_RING);
414 		if (cq_ptr == MAP_FAILED) {
415 			perror("cpl-queue!");
416 			munmap(sq_ptr, sring->ring_sz);
417 			return 1;
418 		}
419 	}
420 
421 	sring->head = sq_ptr + p.sq_off.head;
422 	sring->tail = sq_ptr + p.sq_off.tail;
423 	sring->ring_mask = sq_ptr + p.sq_off.ring_mask;
424 	sring->ring_entries = sq_ptr + p.sq_off.ring_entries;
425 	sring->flags = sq_ptr + p.sq_off.flags;
426 	sring->array = sq_ptr + p.sq_off.array;
427 
428 	/* Map a queue as mem map */
429 	s->sq_ring.queue.sqes = mmap(0, p.sq_entries * sizeof(struct io_uring_sqe),
430 				     PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
431 				     s->ring_fd, IORING_OFF_SQES);
432 	if (s->sq_ring.queue.sqes == MAP_FAILED) {
433 		munmap(sq_ptr, sring->ring_sz);
434 		if (sq_ptr != cq_ptr) {
435 			ksft_print_msg("failed to mmap uring queue!");
436 			munmap(cq_ptr, cring->ring_sz);
437 			return 1;
438 		}
439 	}
440 
441 	cring->head = cq_ptr + p.cq_off.head;
442 	cring->tail = cq_ptr + p.cq_off.tail;
443 	cring->ring_mask = cq_ptr + p.cq_off.ring_mask;
444 	cring->ring_entries = cq_ptr + p.cq_off.ring_entries;
445 	cring->queue.cqes = cq_ptr + p.cq_off.cqes;
446 
447 	return 0;
448 }
449 
450 /* Init io_uring queues */
setup_io_uring(struct io_ring * s)451 int setup_io_uring(struct io_ring *s)
452 {
453 	struct io_uring_params para;
454 
455 	memset(&para, 0, sizeof(para));
456 	s->ring_fd = sys_uring_setup(URING_QUEUE_SZ, &para);
457 	if (s->ring_fd < 0)
458 		return 1;
459 
460 	return mmap_io_uring(para, s);
461 }
462 
463 /*
464  * Get data from completion queue. the data buffer saved the file data
465  * return 0: success; others: error;
466  */
handle_uring_cq(struct io_ring * s)467 int handle_uring_cq(struct io_ring *s)
468 {
469 	struct file_io *fi = NULL;
470 	struct io_uring_queue *cring = &s->cq_ring;
471 	struct io_uring_cqe *cqe;
472 	unsigned int head;
473 	off_t len = 0;
474 
475 	head = *cring->head;
476 
477 	do {
478 		barrier();
479 		if (head == *cring->tail)
480 			break;
481 		/* Get the entry */
482 		cqe = &cring->queue.cqes[head & *s->cq_ring.ring_mask];
483 		fi = (struct file_io *)cqe->user_data;
484 		if (cqe->res < 0)
485 			break;
486 
487 		int blocks = (int)(fi->file_sz + URING_BLOCK_SZ - 1) / URING_BLOCK_SZ;
488 
489 		for (int i = 0; i < blocks; i++)
490 			len += fi->iovecs[i].iov_len;
491 
492 		head++;
493 	} while (1);
494 
495 	*cring->head = head;
496 	barrier();
497 
498 	return (len != fi->file_sz);
499 }
500 
501 /*
502  * Submit squeue. specify via IORING_OP_READV.
503  * the buffer need to be set metadata according to LAM mode
504  */
handle_uring_sq(struct io_ring * ring,struct file_io * fi,unsigned long lam)505 int handle_uring_sq(struct io_ring *ring, struct file_io *fi, unsigned long lam)
506 {
507 	int file_fd = fi->file_fd;
508 	struct io_uring_queue *sring = &ring->sq_ring;
509 	unsigned int index = 0, cur_block = 0, tail = 0, next_tail = 0;
510 	struct io_uring_sqe *sqe;
511 
512 	off_t remain = fi->file_sz;
513 	int blocks = (int)(remain + URING_BLOCK_SZ - 1) / URING_BLOCK_SZ;
514 
515 	while (remain) {
516 		off_t bytes = remain;
517 		void *buf;
518 
519 		if (bytes > URING_BLOCK_SZ)
520 			bytes = URING_BLOCK_SZ;
521 
522 		fi->iovecs[cur_block].iov_len = bytes;
523 
524 		if (posix_memalign(&buf, URING_BLOCK_SZ, URING_BLOCK_SZ))
525 			return 1;
526 
527 		fi->iovecs[cur_block].iov_base = (void *)set_metadata((uint64_t)buf, lam);
528 		remain -= bytes;
529 		cur_block++;
530 	}
531 
532 	next_tail = *sring->tail;
533 	tail = next_tail;
534 	next_tail++;
535 
536 	barrier();
537 
538 	index = tail & *ring->sq_ring.ring_mask;
539 
540 	sqe = &ring->sq_ring.queue.sqes[index];
541 	sqe->fd = file_fd;
542 	sqe->flags = 0;
543 	sqe->opcode = IORING_OP_READV;
544 	sqe->addr = (unsigned long)fi->iovecs;
545 	sqe->len = blocks;
546 	sqe->off = 0;
547 	sqe->user_data = (uint64_t)fi;
548 
549 	sring->array[index] = index;
550 	tail = next_tail;
551 
552 	if (*sring->tail != tail) {
553 		*sring->tail = tail;
554 		barrier();
555 	}
556 
557 	if (sys_uring_enter(ring->ring_fd, 1, 1, IORING_ENTER_GETEVENTS) < 0)
558 		return 1;
559 
560 	return 0;
561 }
562 
563 /*
564  * Test LAM in async I/O and io_uring, read current binery through io_uring
565  * Set metadata in pointers to iovecs buffer.
566  */
do_uring(unsigned long lam)567 int do_uring(unsigned long lam)
568 {
569 	struct io_ring *ring;
570 	struct file_io *fi;
571 	struct stat st;
572 	int ret = 1;
573 	char path[PATH_MAX] = {0};
574 
575 	/* get current process path */
576 	if (readlink("/proc/self/exe", path, PATH_MAX - 1) <= 0)
577 		return 1;
578 
579 	int file_fd = open(path, O_RDONLY);
580 
581 	if (file_fd < 0)
582 		return 1;
583 
584 	if (fstat(file_fd, &st) < 0)
585 		return 1;
586 
587 	off_t file_sz = st.st_size;
588 
589 	int blocks = (int)(file_sz + URING_BLOCK_SZ - 1) / URING_BLOCK_SZ;
590 
591 	fi = malloc(sizeof(*fi) + sizeof(struct iovec) * blocks);
592 	if (!fi)
593 		return 1;
594 
595 	fi->file_sz = file_sz;
596 	fi->file_fd = file_fd;
597 
598 	ring = malloc(sizeof(*ring));
599 	if (!ring)
600 		return 1;
601 
602 	memset(ring, 0, sizeof(struct io_ring));
603 
604 	if (setup_io_uring(ring))
605 		goto out;
606 
607 	if (handle_uring_sq(ring, fi, lam))
608 		goto out;
609 
610 	ret = handle_uring_cq(ring);
611 
612 out:
613 	free(ring);
614 
615 	for (int i = 0; i < blocks; i++) {
616 		if (fi->iovecs[i].iov_base) {
617 			uint64_t addr = ((uint64_t)fi->iovecs[i].iov_base);
618 
619 			switch (lam) {
620 			case LAM_U57_BITS: /* Clear bits 62:57 */
621 				addr = (addr & ~(LAM_U57_MASK));
622 				break;
623 			}
624 			free((void *)addr);
625 			fi->iovecs[i].iov_base = NULL;
626 		}
627 	}
628 
629 	free(fi);
630 
631 	return ret;
632 }
633 
handle_uring(struct testcases * test)634 int handle_uring(struct testcases *test)
635 {
636 	int ret = 0;
637 
638 	if (test->later == 0 && test->lam != 0)
639 		if (set_lam(test->lam) != 0)
640 			return 1;
641 
642 	if (sigsetjmp(segv_env, 1) == 0) {
643 		signal(SIGSEGV, segv_handler);
644 		ret = do_uring(test->lam);
645 	} else {
646 		ret = 2;
647 	}
648 
649 	return ret;
650 }
651 
fork_test(struct testcases * test)652 static int fork_test(struct testcases *test)
653 {
654 	int ret, child_ret;
655 	pid_t pid;
656 
657 	pid = fork();
658 	if (pid < 0) {
659 		perror("Fork failed.");
660 		ret = 1;
661 	} else if (pid == 0) {
662 		ret = test->test_func(test);
663 		exit(ret);
664 	} else {
665 		wait(&child_ret);
666 		ret = WEXITSTATUS(child_ret);
667 	}
668 
669 	return ret;
670 }
671 
handle_execve(struct testcases * test)672 static int handle_execve(struct testcases *test)
673 {
674 	int ret, child_ret;
675 	int lam = test->lam;
676 	pid_t pid;
677 
678 	pid = fork();
679 	if (pid < 0) {
680 		perror("Fork failed.");
681 		ret = 1;
682 	} else if (pid == 0) {
683 		char path[PATH_MAX] = {0};
684 
685 		/* Set LAM mode in parent process */
686 		if (set_lam(lam) != 0)
687 			return 1;
688 
689 		/* Get current binary's path and the binary was run by execve */
690 		if (readlink("/proc/self/exe", path, PATH_MAX - 1) <= 0)
691 			exit(-1);
692 
693 		/* run binary to get LAM mode and return to parent process */
694 		if (execlp(path, path, "-t 0x0", NULL) < 0) {
695 			perror("error on exec");
696 			exit(-1);
697 		}
698 	} else {
699 		wait(&child_ret);
700 		ret = WEXITSTATUS(child_ret);
701 		if (ret != LAM_NONE)
702 			return 1;
703 	}
704 
705 	return 0;
706 }
707 
handle_inheritance(struct testcases * test)708 static int handle_inheritance(struct testcases *test)
709 {
710 	int ret, child_ret;
711 	int lam = test->lam;
712 	pid_t pid;
713 
714 	/* Set LAM mode in parent process */
715 	if (set_lam(lam) != 0)
716 		return 1;
717 
718 	pid = fork();
719 	if (pid < 0) {
720 		perror("Fork failed.");
721 		return 1;
722 	} else if (pid == 0) {
723 		/* Set LAM mode in parent process */
724 		int child_lam = get_lam();
725 
726 		exit(child_lam);
727 	} else {
728 		wait(&child_ret);
729 		ret = WEXITSTATUS(child_ret);
730 
731 		if (lam != ret)
732 			return 1;
733 	}
734 
735 	return 0;
736 }
737 
thread_fn_get_lam(void * arg)738 static int thread_fn_get_lam(void *arg)
739 {
740 	return get_lam();
741 }
742 
thread_fn_set_lam(void * arg)743 static int thread_fn_set_lam(void *arg)
744 {
745 	struct testcases *test = arg;
746 
747 	return set_lam(test->lam);
748 }
749 
handle_thread(struct testcases * test)750 static int handle_thread(struct testcases *test)
751 {
752 	char stack[STACK_SIZE];
753 	int ret, child_ret;
754 	int lam = 0;
755 	pid_t pid;
756 
757 	/* Set LAM mode in parent process */
758 	if (!test->later) {
759 		lam = test->lam;
760 		if (set_lam(lam) != 0)
761 			return 1;
762 	}
763 
764 	pid = clone(thread_fn_get_lam, stack + STACK_SIZE,
765 		    SIGCHLD | CLONE_FILES | CLONE_FS | CLONE_VM, NULL);
766 	if (pid < 0) {
767 		perror("Clone failed.");
768 		return 1;
769 	}
770 
771 	waitpid(pid, &child_ret, 0);
772 	ret = WEXITSTATUS(child_ret);
773 
774 	if (lam != ret)
775 		return 1;
776 
777 	if (test->later) {
778 		if (set_lam(test->lam) != 0)
779 			return 1;
780 	}
781 
782 	return 0;
783 }
784 
handle_thread_enable(struct testcases * test)785 static int handle_thread_enable(struct testcases *test)
786 {
787 	char stack[STACK_SIZE];
788 	int ret, child_ret;
789 	int lam = test->lam;
790 	pid_t pid;
791 
792 	pid = clone(thread_fn_set_lam, stack + STACK_SIZE,
793 		    SIGCHLD | CLONE_FILES | CLONE_FS | CLONE_VM, test);
794 	if (pid < 0) {
795 		perror("Clone failed.");
796 		return 1;
797 	}
798 
799 	waitpid(pid, &child_ret, 0);
800 	ret = WEXITSTATUS(child_ret);
801 
802 	if (lam != ret)
803 		return 1;
804 
805 	return 0;
806 }
run_test(struct testcases * test,int count)807 static void run_test(struct testcases *test, int count)
808 {
809 	int i, ret = 0;
810 
811 	for (i = 0; i < count; i++) {
812 		struct testcases *t = test + i;
813 
814 		/* fork a process to run test case */
815 		tests_cnt++;
816 		ret = fork_test(t);
817 
818 		/* return 3 is not support LA57, the case should be skipped */
819 		if (ret == 3) {
820 			ksft_test_result_skip(t->msg);
821 			continue;
822 		}
823 
824 		if (ret != 0)
825 			ret = (t->expected == ret);
826 		else
827 			ret = !(t->expected);
828 
829 		ksft_test_result(ret, t->msg);
830 	}
831 }
832 
833 static struct testcases uring_cases[] = {
834 	{
835 		.later = 0,
836 		.lam = LAM_U57_BITS,
837 		.test_func = handle_uring,
838 		.msg = "URING: LAM_U57. Dereferencing pointer with metadata\n",
839 	},
840 	{
841 		.later = 1,
842 		.expected = 1,
843 		.lam = LAM_U57_BITS,
844 		.test_func = handle_uring,
845 		.msg = "URING:[Negative] Disable LAM. Dereferencing pointer with metadata.\n",
846 	},
847 };
848 
849 static struct testcases malloc_cases[] = {
850 	{
851 		.later = 0,
852 		.lam = LAM_U57_BITS,
853 		.test_func = handle_malloc,
854 		.msg = "MALLOC: LAM_U57. Dereferencing pointer with metadata\n",
855 	},
856 	{
857 		.later = 1,
858 		.expected = 2,
859 		.lam = LAM_U57_BITS,
860 		.test_func = handle_malloc,
861 		.msg = "MALLOC:[Negative] Disable LAM. Dereferencing pointer with metadata.\n",
862 	},
863 };
864 
865 static struct testcases bits_cases[] = {
866 	{
867 		.test_func = handle_max_bits,
868 		.msg = "BITS: Check default tag bits\n",
869 	},
870 };
871 
872 static struct testcases syscall_cases[] = {
873 	{
874 		.later = 0,
875 		.lam = LAM_U57_BITS,
876 		.test_func = handle_syscall,
877 		.msg = "SYSCALL: LAM_U57. syscall with metadata\n",
878 	},
879 	{
880 		.later = 1,
881 		.expected = 1,
882 		.lam = LAM_U57_BITS,
883 		.test_func = handle_syscall,
884 		.msg = "SYSCALL:[Negative] Disable LAM. Dereferencing pointer with metadata.\n",
885 	},
886 };
887 
888 static struct testcases mmap_cases[] = {
889 	{
890 		.later = 1,
891 		.expected = 0,
892 		.lam = LAM_U57_BITS,
893 		.addr = HIGH_ADDR,
894 		.test_func = handle_mmap,
895 		.msg = "MMAP: First mmap high address, then set LAM_U57.\n",
896 	},
897 	{
898 		.later = 0,
899 		.expected = 0,
900 		.lam = LAM_U57_BITS,
901 		.addr = HIGH_ADDR,
902 		.test_func = handle_mmap,
903 		.msg = "MMAP: First LAM_U57, then High address.\n",
904 	},
905 	{
906 		.later = 0,
907 		.expected = 0,
908 		.lam = LAM_U57_BITS,
909 		.addr = LOW_ADDR,
910 		.test_func = handle_mmap,
911 		.msg = "MMAP: First LAM_U57, then Low address.\n",
912 	},
913 };
914 
915 static struct testcases inheritance_cases[] = {
916 	{
917 		.expected = 0,
918 		.lam = LAM_U57_BITS,
919 		.test_func = handle_inheritance,
920 		.msg = "FORK: LAM_U57, child process should get LAM mode same as parent\n",
921 	},
922 	{
923 		.expected = 0,
924 		.lam = LAM_U57_BITS,
925 		.test_func = handle_thread,
926 		.msg = "THREAD: LAM_U57, child thread should get LAM mode same as parent\n",
927 	},
928 	{
929 		.expected = 1,
930 		.lam = LAM_U57_BITS,
931 		.test_func = handle_thread_enable,
932 		.msg = "THREAD: [NEGATIVE] Enable LAM in child.\n",
933 	},
934 	{
935 		.expected = 1,
936 		.later = 1,
937 		.lam = LAM_U57_BITS,
938 		.test_func = handle_thread,
939 		.msg = "THREAD: [NEGATIVE] Enable LAM in parent after thread created.\n",
940 	},
941 	{
942 		.expected = 0,
943 		.lam = LAM_U57_BITS,
944 		.test_func = handle_execve,
945 		.msg = "EXECVE: LAM_U57, child process should get disabled LAM mode\n",
946 	},
947 };
948 
cmd_help(void)949 static void cmd_help(void)
950 {
951 	printf("usage: lam [-h] [-t test list]\n");
952 	printf("\t-t test list: run tests specified in the test list, default:0x%x\n", TEST_MASK);
953 	printf("\t\t0x1:malloc; 0x2:max_bits; 0x4:mmap; 0x8:syscall; 0x10:io_uring; 0x20:inherit;\n");
954 	printf("\t-h: help\n");
955 }
956 
957 /* Check for file existence */
file_Exists(const char * fileName)958 uint8_t file_Exists(const char *fileName)
959 {
960 	struct stat buffer;
961 
962 	uint8_t ret = (stat(fileName, &buffer) == 0);
963 
964 	return ret;
965 }
966 
967 /* Sysfs idxd files */
968 const char *dsa_configs[] = {
969 	"echo 1 > /sys/bus/dsa/devices/dsa0/wq0.1/group_id",
970 	"echo shared > /sys/bus/dsa/devices/dsa0/wq0.1/mode",
971 	"echo 10 > /sys/bus/dsa/devices/dsa0/wq0.1/priority",
972 	"echo 16 > /sys/bus/dsa/devices/dsa0/wq0.1/size",
973 	"echo 15 > /sys/bus/dsa/devices/dsa0/wq0.1/threshold",
974 	"echo user > /sys/bus/dsa/devices/dsa0/wq0.1/type",
975 	"echo MyApp1 > /sys/bus/dsa/devices/dsa0/wq0.1/name",
976 	"echo 1 > /sys/bus/dsa/devices/dsa0/engine0.1/group_id",
977 	"echo dsa0 > /sys/bus/dsa/drivers/idxd/bind",
978 	/* bind files and devices, generated a device file in /dev */
979 	"echo wq0.1 > /sys/bus/dsa/drivers/user/bind",
980 };
981 
982 /* DSA device file */
983 const char *dsaDeviceFile = "/dev/dsa/wq0.1";
984 /* file for io*/
985 const char *dsaPasidEnable = "/sys/bus/dsa/devices/dsa0/pasid_enabled";
986 
987 /*
988  * DSA depends on kernel cmdline "intel_iommu=on,sm_on"
989  * return pasid_enabled (0: disable 1:enable)
990  */
Check_DSA_Kernel_Setting(void)991 int Check_DSA_Kernel_Setting(void)
992 {
993 	char command[256] = "";
994 	char buf[256] = "";
995 	char *ptr;
996 	int rv = -1;
997 
998 	snprintf(command, sizeof(command) - 1, "cat %s", dsaPasidEnable);
999 
1000 	FILE *cmd = popen(command, "r");
1001 
1002 	if (cmd) {
1003 		while (fgets(buf, sizeof(buf) - 1, cmd) != NULL);
1004 
1005 		pclose(cmd);
1006 		rv = strtol(buf, &ptr, 16);
1007 	}
1008 
1009 	return rv;
1010 }
1011 
1012 /*
1013  * Config DSA's sysfs files as shared DSA's WQ.
1014  * Generated a device file /dev/dsa/wq0.1
1015  * Return:  0 OK; 1 Failed; 3 Skip(SVA disabled).
1016  */
Dsa_Init_Sysfs(void)1017 int Dsa_Init_Sysfs(void)
1018 {
1019 	uint len = ARRAY_SIZE(dsa_configs);
1020 	const char **p = dsa_configs;
1021 
1022 	if (file_Exists(dsaDeviceFile) == 1)
1023 		return 0;
1024 
1025 	/* check the idxd driver */
1026 	if (file_Exists(dsaPasidEnable) != 1) {
1027 		printf("Please make sure idxd driver was loaded\n");
1028 		return 3;
1029 	}
1030 
1031 	/* Check SVA feature */
1032 	if (Check_DSA_Kernel_Setting() != 1) {
1033 		printf("Please enable SVA.(Add intel_iommu=on,sm_on in kernel cmdline)\n");
1034 		return 3;
1035 	}
1036 
1037 	/* Check the idxd device file on /dev/dsa/ */
1038 	for (int i = 0; i < len; i++) {
1039 		if (system(p[i]))
1040 			return 1;
1041 	}
1042 
1043 	/* After config, /dev/dsa/wq0.1 should be generated */
1044 	return (file_Exists(dsaDeviceFile) != 1);
1045 }
1046 
1047 /*
1048  * Open DSA device file, triger API: iommu_sva_alloc_pasid
1049  */
allocate_dsa_pasid(void)1050 void *allocate_dsa_pasid(void)
1051 {
1052 	int fd;
1053 	void *wq;
1054 
1055 	fd = open(dsaDeviceFile, O_RDWR);
1056 	if (fd < 0) {
1057 		perror("open");
1058 		return MAP_FAILED;
1059 	}
1060 
1061 	wq = mmap(NULL, 0x1000, PROT_WRITE,
1062 			   MAP_SHARED | MAP_POPULATE, fd, 0);
1063 	if (wq == MAP_FAILED)
1064 		perror("mmap");
1065 
1066 	return wq;
1067 }
1068 
set_force_svm(void)1069 int set_force_svm(void)
1070 {
1071 	int ret = 0;
1072 
1073 	ret = syscall(SYS_arch_prctl, ARCH_FORCE_TAGGED_SVA);
1074 
1075 	return ret;
1076 }
1077 
handle_pasid(struct testcases * test)1078 int handle_pasid(struct testcases *test)
1079 {
1080 	uint tmp = test->cmd;
1081 	uint runed = 0x0;
1082 	int ret = 0;
1083 	void *wq = NULL;
1084 
1085 	ret = Dsa_Init_Sysfs();
1086 	if (ret != 0)
1087 		return ret;
1088 
1089 	for (int i = 0; i < 3; i++) {
1090 		int err = 0;
1091 
1092 		if (tmp & 0x1) {
1093 			/* run set lam mode*/
1094 			if ((runed & 0x1) == 0)	{
1095 				err = set_lam(LAM_U57_BITS);
1096 				runed = runed | 0x1;
1097 			} else
1098 				err = 1;
1099 		} else if (tmp & 0x4) {
1100 			/* run force svm */
1101 			if ((runed & 0x4) == 0)	{
1102 				err = set_force_svm();
1103 				runed = runed | 0x4;
1104 			} else
1105 				err = 1;
1106 		} else if (tmp & 0x2) {
1107 			/* run allocate pasid */
1108 			if ((runed & 0x2) == 0) {
1109 				runed = runed | 0x2;
1110 				wq = allocate_dsa_pasid();
1111 				if (wq == MAP_FAILED)
1112 					err = 1;
1113 			} else
1114 				err = 1;
1115 		}
1116 
1117 		ret = ret + err;
1118 		if (ret > 0)
1119 			break;
1120 
1121 		tmp = tmp >> 4;
1122 	}
1123 
1124 	if (wq != MAP_FAILED && wq != NULL)
1125 		if (munmap(wq, 0x1000))
1126 			printf("munmap failed %d\n", errno);
1127 
1128 	if (runed != 0x7)
1129 		ret = 1;
1130 
1131 	return (ret != 0);
1132 }
1133 
1134 /*
1135  * Pasid test depends on idxd and SVA, kernel should enable iommu and sm.
1136  * command line(intel_iommu=on,sm_on)
1137  */
1138 static struct testcases pasid_cases[] = {
1139 	{
1140 		.expected = 1,
1141 		.cmd = PAS_CMD(LAM_CMD_BIT, PAS_CMD_BIT, SVA_CMD_BIT),
1142 		.test_func = handle_pasid,
1143 		.msg = "PASID: [Negative] Execute LAM, PASID, SVA in sequence\n",
1144 	},
1145 	{
1146 		.expected = 0,
1147 		.cmd = PAS_CMD(LAM_CMD_BIT, SVA_CMD_BIT, PAS_CMD_BIT),
1148 		.test_func = handle_pasid,
1149 		.msg = "PASID: Execute LAM, SVA, PASID in sequence\n",
1150 	},
1151 	{
1152 		.expected = 1,
1153 		.cmd = PAS_CMD(PAS_CMD_BIT, LAM_CMD_BIT, SVA_CMD_BIT),
1154 		.test_func = handle_pasid,
1155 		.msg = "PASID: [Negative] Execute PASID, LAM, SVA in sequence\n",
1156 	},
1157 	{
1158 		.expected = 0,
1159 		.cmd = PAS_CMD(PAS_CMD_BIT, SVA_CMD_BIT, LAM_CMD_BIT),
1160 		.test_func = handle_pasid,
1161 		.msg = "PASID: Execute PASID, SVA, LAM in sequence\n",
1162 	},
1163 	{
1164 		.expected = 0,
1165 		.cmd = PAS_CMD(SVA_CMD_BIT, LAM_CMD_BIT, PAS_CMD_BIT),
1166 		.test_func = handle_pasid,
1167 		.msg = "PASID: Execute SVA, LAM, PASID in sequence\n",
1168 	},
1169 	{
1170 		.expected = 0,
1171 		.cmd = PAS_CMD(SVA_CMD_BIT, PAS_CMD_BIT, LAM_CMD_BIT),
1172 		.test_func = handle_pasid,
1173 		.msg = "PASID: Execute SVA, PASID, LAM in sequence\n",
1174 	},
1175 };
1176 
main(int argc,char ** argv)1177 int main(int argc, char **argv)
1178 {
1179 	int c = 0;
1180 	unsigned int tests = TEST_MASK;
1181 
1182 	tests_cnt = 0;
1183 
1184 	if (!cpu_has_lam()) {
1185 		ksft_print_msg("Unsupported LAM feature!\n");
1186 		return -1;
1187 	}
1188 
1189 	while ((c = getopt(argc, argv, "ht:")) != -1) {
1190 		switch (c) {
1191 		case 't':
1192 			tests = strtoul(optarg, NULL, 16);
1193 			if (tests && !(tests & TEST_MASK)) {
1194 				ksft_print_msg("Invalid argument!\n");
1195 				return -1;
1196 			}
1197 			break;
1198 		case 'h':
1199 			cmd_help();
1200 			return 0;
1201 		default:
1202 			ksft_print_msg("Invalid argument\n");
1203 			return -1;
1204 		}
1205 	}
1206 
1207 	/*
1208 	 * When tests is 0, it is not a real test case;
1209 	 * the option used by test case(execve) to check the lam mode in
1210 	 * process generated by execve, the process read back lam mode and
1211 	 * check with lam mode in parent process.
1212 	 */
1213 	if (!tests)
1214 		return (get_lam());
1215 
1216 	/* Run test cases */
1217 	if (tests & FUNC_MALLOC)
1218 		run_test(malloc_cases, ARRAY_SIZE(malloc_cases));
1219 
1220 	if (tests & FUNC_BITS)
1221 		run_test(bits_cases, ARRAY_SIZE(bits_cases));
1222 
1223 	if (tests & FUNC_MMAP)
1224 		run_test(mmap_cases, ARRAY_SIZE(mmap_cases));
1225 
1226 	if (tests & FUNC_SYSCALL)
1227 		run_test(syscall_cases, ARRAY_SIZE(syscall_cases));
1228 
1229 	if (tests & FUNC_URING)
1230 		run_test(uring_cases, ARRAY_SIZE(uring_cases));
1231 
1232 	if (tests & FUNC_INHERITE)
1233 		run_test(inheritance_cases, ARRAY_SIZE(inheritance_cases));
1234 
1235 	if (tests & FUNC_PASID)
1236 		run_test(pasid_cases, ARRAY_SIZE(pasid_cases));
1237 
1238 	ksft_set_plan(tests_cnt);
1239 
1240 	return ksft_exit_pass();
1241 }
1242