| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371 |
- // SPDX-License-Identifier: GPL-2.0
- #define _GNU_SOURCE
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/syscall.h>
- #include <sys/ioctl.h>
- #include <time.h>
- #include <signal.h>
- #include <setjmp.h>
- #include <sys/mman.h>
- #include <sys/utsname.h>
- #include <sys/wait.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <inttypes.h>
- #include <sched.h>
- #include <sys/uio.h>
- #include <linux/io_uring.h>
- #include "kselftest.h"
- #ifndef __x86_64__
- # error This test is 64-bit only
- #endif
- /* LAM modes, these definitions were copied from kernel code */
- #define LAM_NONE 0
- #define LAM_U57_BITS 6
- #define LAM_U57_MASK (0x3fULL << 57)
- /* arch prctl for LAM */
- #define ARCH_GET_UNTAG_MASK 0x4001
- #define ARCH_ENABLE_TAGGED_ADDR 0x4002
- #define ARCH_GET_MAX_TAG_BITS 0x4003
- #define ARCH_FORCE_TAGGED_SVA 0x4004
- /* Specified test function bits */
- #define FUNC_MALLOC 0x1
- #define FUNC_BITS 0x2
- #define FUNC_MMAP 0x4
- #define FUNC_SYSCALL 0x8
- #define FUNC_URING 0x10
- #define FUNC_INHERITE 0x20
- #define FUNC_PASID 0x40
- /* get_user() pointer test cases */
- #define GET_USER_USER 0
- #define GET_USER_KERNEL_TOP 1
- #define GET_USER_KERNEL_BOT 2
- #define GET_USER_KERNEL 3
- #define TEST_MASK 0x7f
- #define L5_SIGN_EXT_MASK (0xFFUL << 56)
- #define L4_SIGN_EXT_MASK (0x1FFFFUL << 47)
- #define LOW_ADDR (0x1UL << 30)
- #define HIGH_ADDR (0x3UL << 48)
- #define MALLOC_LEN 32
- #define PAGE_SIZE (4 << 10)
- #define STACK_SIZE 65536
- #define barrier() ({ \
- __asm__ __volatile__("" : : : "memory"); \
- })
- #define URING_QUEUE_SZ 1
- #define URING_BLOCK_SZ 2048
- /* Pasid test define */
- #define LAM_CMD_BIT 0x1
- #define PAS_CMD_BIT 0x2
- #define SVA_CMD_BIT 0x4
- #define PAS_CMD(cmd1, cmd2, cmd3) (((cmd3) << 8) | ((cmd2) << 4) | ((cmd1) << 0))
- struct testcases {
- unsigned int later;
- int expected; /* 2: SIGSEGV Error; 1: other errors */
- unsigned long lam;
- uint64_t addr;
- uint64_t cmd;
- int (*test_func)(struct testcases *test);
- const char *msg;
- };
- /* Used by CQ of uring, source file handler and file's size */
- struct file_io {
- int file_fd;
- off_t file_sz;
- struct iovec iovecs[];
- };
- struct io_uring_queue {
- unsigned int *head;
- unsigned int *tail;
- unsigned int *ring_mask;
- unsigned int *ring_entries;
- unsigned int *flags;
- unsigned int *array;
- union {
- struct io_uring_cqe *cqes;
- struct io_uring_sqe *sqes;
- } queue;
- size_t ring_sz;
- };
- struct io_ring {
- int ring_fd;
- struct io_uring_queue sq_ring;
- struct io_uring_queue cq_ring;
- };
- int tests_cnt;
- jmp_buf segv_env;
- static void segv_handler(int sig)
- {
- ksft_print_msg("Get segmentation fault(%d).", sig);
- siglongjmp(segv_env, 1);
- }
- static inline int lam_is_available(void)
- {
- unsigned int cpuinfo[4];
- unsigned long bits = 0;
- int ret;
- __cpuid_count(0x7, 1, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]);
- /* Check if cpu supports LAM */
- if (!(cpuinfo[0] & (1 << 26))) {
- ksft_print_msg("LAM is not supported!\n");
- return 0;
- }
- /* Return 0 if CONFIG_ADDRESS_MASKING is not set */
- ret = syscall(SYS_arch_prctl, ARCH_GET_MAX_TAG_BITS, &bits);
- if (ret) {
- ksft_print_msg("LAM is disabled in the kernel!\n");
- return 0;
- }
- return 1;
- }
- static inline int la57_enabled(void)
- {
- int ret;
- void *p;
- p = mmap((void *)HIGH_ADDR, PAGE_SIZE, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
- ret = p == MAP_FAILED ? 0 : 1;
- munmap(p, PAGE_SIZE);
- return ret;
- }
- /*
- * Set tagged address and read back untag mask.
- * check if the untagged mask is expected.
- *
- * @return:
- * 0: Set LAM mode successfully
- * others: failed to set LAM
- */
- static int set_lam(unsigned long lam)
- {
- int ret = 0;
- uint64_t ptr = 0;
- if (lam != LAM_U57_BITS && lam != LAM_NONE)
- return -1;
- /* Skip check return */
- syscall(SYS_arch_prctl, ARCH_ENABLE_TAGGED_ADDR, lam);
- /* Get untagged mask */
- syscall(SYS_arch_prctl, ARCH_GET_UNTAG_MASK, &ptr);
- /* Check mask returned is expected */
- if (lam == LAM_U57_BITS)
- ret = (ptr != ~(LAM_U57_MASK));
- else if (lam == LAM_NONE)
- ret = (ptr != -1ULL);
- return ret;
- }
- static unsigned long get_default_tag_bits(void)
- {
- pid_t pid;
- int lam = LAM_NONE;
- int ret = 0;
- pid = fork();
- if (pid < 0) {
- perror("Fork failed.");
- } else if (pid == 0) {
- /* Set LAM mode in child process */
- if (set_lam(LAM_U57_BITS) == 0)
- lam = LAM_U57_BITS;
- else
- lam = LAM_NONE;
- exit(lam);
- } else {
- wait(&ret);
- lam = WEXITSTATUS(ret);
- }
- return lam;
- }
- /*
- * Set tagged address and read back untag mask.
- * check if the untag mask is expected.
- */
- static int get_lam(void)
- {
- uint64_t ptr = 0;
- int ret = -1;
- /* Get untagged mask */
- if (syscall(SYS_arch_prctl, ARCH_GET_UNTAG_MASK, &ptr) == -1)
- return -1;
- /* Check mask returned is expected */
- if (ptr == ~(LAM_U57_MASK))
- ret = LAM_U57_BITS;
- else if (ptr == -1ULL)
- ret = LAM_NONE;
- return ret;
- }
- /* According to LAM mode, set metadata in high bits */
- static uint64_t set_metadata(uint64_t src, unsigned long lam)
- {
- uint64_t metadata;
- srand(time(NULL));
- switch (lam) {
- case LAM_U57_BITS: /* Set metadata in bits 62:57 */
- /* Get a random non-zero value as metadata */
- metadata = (rand() % ((1UL << LAM_U57_BITS) - 1) + 1) << 57;
- metadata |= (src & ~(LAM_U57_MASK));
- break;
- default:
- metadata = src;
- break;
- }
- return metadata;
- }
- /*
- * Set metadata in user pointer, compare new pointer with original pointer.
- * both pointers should point to the same address.
- *
- * @return:
- * 0: value on the pointer with metadata and value on original are same
- * 1: not same.
- */
- static int handle_lam_test(void *src, unsigned int lam)
- {
- char *ptr;
- strcpy((char *)src, "USER POINTER");
- ptr = (char *)set_metadata((uint64_t)src, lam);
- if (src == ptr)
- return 0;
- /* Copy a string into the pointer with metadata */
- strcpy((char *)ptr, "METADATA POINTER");
- return (!!strcmp((char *)src, (char *)ptr));
- }
- int handle_max_bits(struct testcases *test)
- {
- unsigned long exp_bits = get_default_tag_bits();
- unsigned long bits = 0;
- if (exp_bits != LAM_NONE)
- exp_bits = LAM_U57_BITS;
- /* Get LAM max tag bits */
- if (syscall(SYS_arch_prctl, ARCH_GET_MAX_TAG_BITS, &bits) == -1)
- return 1;
- return (exp_bits != bits);
- }
- /*
- * Test lam feature through dereference pointer get from malloc.
- * @return 0: Pass test. 1: Get failure during test 2: Get SIGSEGV
- */
- static int handle_malloc(struct testcases *test)
- {
- char *ptr = NULL;
- int ret = 0;
- if (test->later == 0 && test->lam != 0)
- if (set_lam(test->lam) == -1)
- return 1;
- ptr = (char *)malloc(MALLOC_LEN);
- if (ptr == NULL) {
- perror("malloc() failure\n");
- return 1;
- }
- /* Set signal handler */
- if (sigsetjmp(segv_env, 1) == 0) {
- signal(SIGSEGV, segv_handler);
- ret = handle_lam_test(ptr, test->lam);
- } else {
- ret = 2;
- }
- if (test->later != 0 && test->lam != 0)
- if (set_lam(test->lam) == -1 && ret == 0)
- ret = 1;
- free(ptr);
- return ret;
- }
- static int handle_mmap(struct testcases *test)
- {
- void *ptr;
- unsigned int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
- int ret = 0;
- if (test->later == 0 && test->lam != 0)
- if (set_lam(test->lam) != 0)
- return 1;
- ptr = mmap((void *)test->addr, PAGE_SIZE, PROT_READ | PROT_WRITE,
- flags, -1, 0);
- if (ptr == MAP_FAILED) {
- if (test->addr == HIGH_ADDR)
- if (!la57_enabled())
- return 3; /* unsupport LA57 */
- return 1;
- }
- if (test->later != 0 && test->lam != 0)
- if (set_lam(test->lam) != 0)
- ret = 1;
- if (ret == 0) {
- if (sigsetjmp(segv_env, 1) == 0) {
- signal(SIGSEGV, segv_handler);
- ret = handle_lam_test(ptr, test->lam);
- } else {
- ret = 2;
- }
- }
- munmap(ptr, PAGE_SIZE);
- return ret;
- }
- static int handle_syscall(struct testcases *test)
- {
- struct utsname unme, *pu;
- int ret = 0;
- if (test->later == 0 && test->lam != 0)
- if (set_lam(test->lam) != 0)
- return 1;
- if (sigsetjmp(segv_env, 1) == 0) {
- signal(SIGSEGV, segv_handler);
- pu = (struct utsname *)set_metadata((uint64_t)&unme, test->lam);
- ret = uname(pu);
- if (ret < 0)
- ret = 1;
- } else {
- ret = 2;
- }
- if (test->later != 0 && test->lam != 0)
- if (set_lam(test->lam) != -1 && ret == 0)
- ret = 1;
- return ret;
- }
- static int get_user_syscall(struct testcases *test)
- {
- uint64_t ptr_address, bitmask;
- int fd, ret = 0;
- void *ptr;
- if (la57_enabled()) {
- bitmask = L5_SIGN_EXT_MASK;
- ptr_address = HIGH_ADDR;
- } else {
- bitmask = L4_SIGN_EXT_MASK;
- ptr_address = LOW_ADDR;
- }
- ptr = mmap((void *)ptr_address, PAGE_SIZE, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
- if (ptr == MAP_FAILED) {
- perror("failed to map byte to pass into get_user");
- return 1;
- }
- if (set_lam(test->lam) != 0) {
- ret = 2;
- goto error;
- }
- fd = memfd_create("lam_ioctl", 0);
- if (fd == -1) {
- munmap(ptr, PAGE_SIZE);
- exit(EXIT_FAILURE);
- }
- switch (test->later) {
- case GET_USER_USER:
- /* Control group - properly tagged user pointer */
- ptr = (void *)set_metadata((uint64_t)ptr, test->lam);
- break;
- case GET_USER_KERNEL_TOP:
- /* Kernel address with top bit cleared */
- bitmask &= (bitmask >> 1);
- ptr = (void *)((uint64_t)ptr | bitmask);
- break;
- case GET_USER_KERNEL_BOT:
- /* Kernel address with bottom sign-extension bit cleared */
- bitmask &= (bitmask << 1);
- ptr = (void *)((uint64_t)ptr | bitmask);
- break;
- case GET_USER_KERNEL:
- /* Try to pass a kernel address */
- ptr = (void *)((uint64_t)ptr | bitmask);
- break;
- default:
- printf("Invalid test case value passed!\n");
- break;
- }
- /*
- * Use FIOASYNC ioctl because it utilizes get_user() internally and is
- * very non-invasive to the system. Pass differently tagged pointers to
- * get_user() in order to verify that valid user pointers are going
- * through and invalid kernel/non-canonical pointers are not.
- */
- if (ioctl(fd, FIOASYNC, ptr) != 0)
- ret = 1;
- close(fd);
- error:
- munmap(ptr, PAGE_SIZE);
- return ret;
- }
- int sys_uring_setup(unsigned int entries, struct io_uring_params *p)
- {
- return (int)syscall(__NR_io_uring_setup, entries, p);
- }
- int sys_uring_enter(int fd, unsigned int to, unsigned int min, unsigned int flags)
- {
- return (int)syscall(__NR_io_uring_enter, fd, to, min, flags, NULL, 0);
- }
- /* Init submission queue and completion queue */
- int mmap_io_uring(struct io_uring_params p, struct io_ring *s)
- {
- struct io_uring_queue *sring = &s->sq_ring;
- struct io_uring_queue *cring = &s->cq_ring;
- sring->ring_sz = p.sq_off.array + p.sq_entries * sizeof(unsigned int);
- cring->ring_sz = p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe);
- if (p.features & IORING_FEAT_SINGLE_MMAP) {
- if (cring->ring_sz > sring->ring_sz)
- sring->ring_sz = cring->ring_sz;
- cring->ring_sz = sring->ring_sz;
- }
- void *sq_ptr = mmap(0, sring->ring_sz, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_POPULATE, s->ring_fd,
- IORING_OFF_SQ_RING);
- if (sq_ptr == MAP_FAILED) {
- perror("sub-queue!");
- return 1;
- }
- void *cq_ptr = sq_ptr;
- if (!(p.features & IORING_FEAT_SINGLE_MMAP)) {
- cq_ptr = mmap(0, cring->ring_sz, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_POPULATE, s->ring_fd,
- IORING_OFF_CQ_RING);
- if (cq_ptr == MAP_FAILED) {
- perror("cpl-queue!");
- munmap(sq_ptr, sring->ring_sz);
- return 1;
- }
- }
- sring->head = sq_ptr + p.sq_off.head;
- sring->tail = sq_ptr + p.sq_off.tail;
- sring->ring_mask = sq_ptr + p.sq_off.ring_mask;
- sring->ring_entries = sq_ptr + p.sq_off.ring_entries;
- sring->flags = sq_ptr + p.sq_off.flags;
- sring->array = sq_ptr + p.sq_off.array;
- /* Map a queue as mem map */
- s->sq_ring.queue.sqes = mmap(0, p.sq_entries * sizeof(struct io_uring_sqe),
- PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
- s->ring_fd, IORING_OFF_SQES);
- if (s->sq_ring.queue.sqes == MAP_FAILED) {
- munmap(sq_ptr, sring->ring_sz);
- if (sq_ptr != cq_ptr) {
- ksft_print_msg("failed to mmap uring queue!");
- munmap(cq_ptr, cring->ring_sz);
- return 1;
- }
- }
- cring->head = cq_ptr + p.cq_off.head;
- cring->tail = cq_ptr + p.cq_off.tail;
- cring->ring_mask = cq_ptr + p.cq_off.ring_mask;
- cring->ring_entries = cq_ptr + p.cq_off.ring_entries;
- cring->queue.cqes = cq_ptr + p.cq_off.cqes;
- return 0;
- }
- /* Init io_uring queues */
- int setup_io_uring(struct io_ring *s)
- {
- struct io_uring_params para;
- memset(¶, 0, sizeof(para));
- s->ring_fd = sys_uring_setup(URING_QUEUE_SZ, ¶);
- if (s->ring_fd < 0)
- return 1;
- return mmap_io_uring(para, s);
- }
- /*
- * Get data from completion queue. the data buffer saved the file data
- * return 0: success; others: error;
- */
- int handle_uring_cq(struct io_ring *s)
- {
- struct file_io *fi = NULL;
- struct io_uring_queue *cring = &s->cq_ring;
- struct io_uring_cqe *cqe;
- unsigned int head;
- off_t len = 0;
- head = *cring->head;
- do {
- barrier();
- if (head == *cring->tail)
- break;
- /* Get the entry */
- cqe = &cring->queue.cqes[head & *s->cq_ring.ring_mask];
- fi = (struct file_io *)cqe->user_data;
- if (cqe->res < 0)
- break;
- int blocks = (int)(fi->file_sz + URING_BLOCK_SZ - 1) / URING_BLOCK_SZ;
- for (int i = 0; i < blocks; i++)
- len += fi->iovecs[i].iov_len;
- head++;
- } while (1);
- *cring->head = head;
- barrier();
- return (len != fi->file_sz);
- }
- /*
- * Submit squeue. specify via IORING_OP_READV.
- * the buffer need to be set metadata according to LAM mode
- */
- int handle_uring_sq(struct io_ring *ring, struct file_io *fi, unsigned long lam)
- {
- int file_fd = fi->file_fd;
- struct io_uring_queue *sring = &ring->sq_ring;
- unsigned int index = 0, cur_block = 0, tail = 0, next_tail = 0;
- struct io_uring_sqe *sqe;
- off_t remain = fi->file_sz;
- int blocks = (int)(remain + URING_BLOCK_SZ - 1) / URING_BLOCK_SZ;
- while (remain) {
- off_t bytes = remain;
- void *buf;
- if (bytes > URING_BLOCK_SZ)
- bytes = URING_BLOCK_SZ;
- fi->iovecs[cur_block].iov_len = bytes;
- if (posix_memalign(&buf, URING_BLOCK_SZ, URING_BLOCK_SZ))
- return 1;
- fi->iovecs[cur_block].iov_base = (void *)set_metadata((uint64_t)buf, lam);
- remain -= bytes;
- cur_block++;
- }
- next_tail = *sring->tail;
- tail = next_tail;
- next_tail++;
- barrier();
- index = tail & *ring->sq_ring.ring_mask;
- sqe = &ring->sq_ring.queue.sqes[index];
- sqe->fd = file_fd;
- sqe->flags = 0;
- sqe->opcode = IORING_OP_READV;
- sqe->addr = (unsigned long)fi->iovecs;
- sqe->len = blocks;
- sqe->off = 0;
- sqe->user_data = (uint64_t)fi;
- sring->array[index] = index;
- tail = next_tail;
- if (*sring->tail != tail) {
- *sring->tail = tail;
- barrier();
- }
- if (sys_uring_enter(ring->ring_fd, 1, 1, IORING_ENTER_GETEVENTS) < 0)
- return 1;
- return 0;
- }
- /*
- * Test LAM in async I/O and io_uring, read current binery through io_uring
- * Set metadata in pointers to iovecs buffer.
- */
- int do_uring(unsigned long lam)
- {
- struct io_ring *ring;
- struct file_io *fi;
- struct stat st;
- int ret = 1;
- char path[PATH_MAX] = {0};
- /* get current process path */
- if (readlink("/proc/self/exe", path, PATH_MAX - 1) <= 0)
- return 1;
- int file_fd = open(path, O_RDONLY);
- if (file_fd < 0)
- return 1;
- if (fstat(file_fd, &st) < 0)
- goto cleanup;
- off_t file_sz = st.st_size;
- int blocks = (int)(file_sz + URING_BLOCK_SZ - 1) / URING_BLOCK_SZ;
- fi = malloc(sizeof(*fi) + sizeof(struct iovec) * blocks);
- if (!fi)
- goto cleanup;
- fi->file_sz = file_sz;
- fi->file_fd = file_fd;
- ring = malloc(sizeof(*ring));
- if (!ring) {
- free(fi);
- goto cleanup;
- }
- memset(ring, 0, sizeof(struct io_ring));
- if (setup_io_uring(ring))
- goto out;
- if (handle_uring_sq(ring, fi, lam))
- goto out;
- ret = handle_uring_cq(ring);
- out:
- free(ring);
- for (int i = 0; i < blocks; i++) {
- if (fi->iovecs[i].iov_base) {
- uint64_t addr = ((uint64_t)fi->iovecs[i].iov_base);
- switch (lam) {
- case LAM_U57_BITS: /* Clear bits 62:57 */
- addr = (addr & ~(LAM_U57_MASK));
- break;
- }
- free((void *)addr);
- fi->iovecs[i].iov_base = NULL;
- }
- }
- free(fi);
- cleanup:
- close(file_fd);
- return ret;
- }
- int handle_uring(struct testcases *test)
- {
- int ret = 0;
- if (test->later == 0 && test->lam != 0)
- if (set_lam(test->lam) != 0)
- return 1;
- if (sigsetjmp(segv_env, 1) == 0) {
- signal(SIGSEGV, segv_handler);
- ret = do_uring(test->lam);
- } else {
- ret = 2;
- }
- return ret;
- }
- static int fork_test(struct testcases *test)
- {
- int ret, child_ret;
- pid_t pid;
- pid = fork();
- if (pid < 0) {
- perror("Fork failed.");
- ret = 1;
- } else if (pid == 0) {
- ret = test->test_func(test);
- exit(ret);
- } else {
- wait(&child_ret);
- ret = WEXITSTATUS(child_ret);
- }
- return ret;
- }
- static int handle_execve(struct testcases *test)
- {
- int ret, child_ret;
- int lam = test->lam;
- pid_t pid;
- pid = fork();
- if (pid < 0) {
- perror("Fork failed.");
- ret = 1;
- } else if (pid == 0) {
- char path[PATH_MAX] = {0};
- /* Set LAM mode in parent process */
- if (set_lam(lam) != 0)
- return 1;
- /* Get current binary's path and the binary was run by execve */
- if (readlink("/proc/self/exe", path, PATH_MAX - 1) <= 0)
- exit(-1);
- /* run binary to get LAM mode and return to parent process */
- if (execlp(path, path, "-t 0x0", NULL) < 0) {
- perror("error on exec");
- exit(-1);
- }
- } else {
- wait(&child_ret);
- ret = WEXITSTATUS(child_ret);
- if (ret != LAM_NONE)
- return 1;
- }
- return 0;
- }
- static int handle_inheritance(struct testcases *test)
- {
- int ret, child_ret;
- int lam = test->lam;
- pid_t pid;
- /* Set LAM mode in parent process */
- if (set_lam(lam) != 0)
- return 1;
- pid = fork();
- if (pid < 0) {
- perror("Fork failed.");
- return 1;
- } else if (pid == 0) {
- /* Set LAM mode in parent process */
- int child_lam = get_lam();
- exit(child_lam);
- } else {
- wait(&child_ret);
- ret = WEXITSTATUS(child_ret);
- if (lam != ret)
- return 1;
- }
- return 0;
- }
- static int thread_fn_get_lam(void *arg)
- {
- return get_lam();
- }
- static int thread_fn_set_lam(void *arg)
- {
- struct testcases *test = arg;
- return set_lam(test->lam);
- }
- static int handle_thread(struct testcases *test)
- {
- char stack[STACK_SIZE];
- int ret, child_ret;
- int lam = 0;
- pid_t pid;
- /* Set LAM mode in parent process */
- if (!test->later) {
- lam = test->lam;
- if (set_lam(lam) != 0)
- return 1;
- }
- pid = clone(thread_fn_get_lam, stack + STACK_SIZE,
- SIGCHLD | CLONE_FILES | CLONE_FS | CLONE_VM, NULL);
- if (pid < 0) {
- perror("Clone failed.");
- return 1;
- }
- waitpid(pid, &child_ret, 0);
- ret = WEXITSTATUS(child_ret);
- if (lam != ret)
- return 1;
- if (test->later) {
- if (set_lam(test->lam) != 0)
- return 1;
- }
- return 0;
- }
- static int handle_thread_enable(struct testcases *test)
- {
- char stack[STACK_SIZE];
- int ret, child_ret;
- int lam = test->lam;
- pid_t pid;
- pid = clone(thread_fn_set_lam, stack + STACK_SIZE,
- SIGCHLD | CLONE_FILES | CLONE_FS | CLONE_VM, test);
- if (pid < 0) {
- perror("Clone failed.");
- return 1;
- }
- waitpid(pid, &child_ret, 0);
- ret = WEXITSTATUS(child_ret);
- if (lam != ret)
- return 1;
- return 0;
- }
- static void run_test(struct testcases *test, int count)
- {
- int i, ret = 0;
- for (i = 0; i < count; i++) {
- struct testcases *t = test + i;
- /* fork a process to run test case */
- tests_cnt++;
- ret = fork_test(t);
- /* return 3 is not support LA57, the case should be skipped */
- if (ret == 3) {
- ksft_test_result_skip("%s", t->msg);
- continue;
- }
- if (ret != 0)
- ret = (t->expected == ret);
- else
- ret = !(t->expected);
- ksft_test_result(ret, "%s", t->msg);
- }
- }
- static struct testcases uring_cases[] = {
- {
- .later = 0,
- .lam = LAM_U57_BITS,
- .test_func = handle_uring,
- .msg = "URING: LAM_U57. Dereferencing pointer with metadata\n",
- },
- {
- .later = 1,
- .expected = 1,
- .lam = LAM_U57_BITS,
- .test_func = handle_uring,
- .msg = "URING:[Negative] Disable LAM. Dereferencing pointer with metadata.\n",
- },
- };
- static struct testcases malloc_cases[] = {
- {
- .later = 0,
- .lam = LAM_U57_BITS,
- .test_func = handle_malloc,
- .msg = "MALLOC: LAM_U57. Dereferencing pointer with metadata\n",
- },
- {
- .later = 1,
- .expected = 2,
- .lam = LAM_U57_BITS,
- .test_func = handle_malloc,
- .msg = "MALLOC:[Negative] Disable LAM. Dereferencing pointer with metadata.\n",
- },
- };
- static struct testcases bits_cases[] = {
- {
- .test_func = handle_max_bits,
- .msg = "BITS: Check default tag bits\n",
- },
- };
- static struct testcases syscall_cases[] = {
- {
- .later = 0,
- .lam = LAM_U57_BITS,
- .test_func = handle_syscall,
- .msg = "SYSCALL: LAM_U57. syscall with metadata\n",
- },
- {
- .later = 1,
- .expected = 1,
- .lam = LAM_U57_BITS,
- .test_func = handle_syscall,
- .msg = "SYSCALL:[Negative] Disable LAM. Dereferencing pointer with metadata.\n",
- },
- {
- .later = GET_USER_USER,
- .lam = LAM_U57_BITS,
- .test_func = get_user_syscall,
- .msg = "GET_USER: get_user() and pass a properly tagged user pointer.\n",
- },
- {
- .later = GET_USER_KERNEL_TOP,
- .expected = 1,
- .lam = LAM_U57_BITS,
- .test_func = get_user_syscall,
- .msg = "GET_USER:[Negative] get_user() with a kernel pointer and the top bit cleared.\n",
- },
- {
- .later = GET_USER_KERNEL_BOT,
- .expected = 1,
- .lam = LAM_U57_BITS,
- .test_func = get_user_syscall,
- .msg = "GET_USER:[Negative] get_user() with a kernel pointer and the bottom sign-extension bit cleared.\n",
- },
- {
- .later = GET_USER_KERNEL,
- .expected = 1,
- .lam = LAM_U57_BITS,
- .test_func = get_user_syscall,
- .msg = "GET_USER:[Negative] get_user() and pass a kernel pointer.\n",
- },
- };
- static struct testcases mmap_cases[] = {
- {
- .later = 1,
- .expected = 0,
- .lam = LAM_U57_BITS,
- .addr = HIGH_ADDR,
- .test_func = handle_mmap,
- .msg = "MMAP: First mmap high address, then set LAM_U57.\n",
- },
- {
- .later = 0,
- .expected = 0,
- .lam = LAM_U57_BITS,
- .addr = HIGH_ADDR,
- .test_func = handle_mmap,
- .msg = "MMAP: First LAM_U57, then High address.\n",
- },
- {
- .later = 0,
- .expected = 0,
- .lam = LAM_U57_BITS,
- .addr = LOW_ADDR,
- .test_func = handle_mmap,
- .msg = "MMAP: First LAM_U57, then Low address.\n",
- },
- };
- static struct testcases inheritance_cases[] = {
- {
- .expected = 0,
- .lam = LAM_U57_BITS,
- .test_func = handle_inheritance,
- .msg = "FORK: LAM_U57, child process should get LAM mode same as parent\n",
- },
- {
- .expected = 0,
- .lam = LAM_U57_BITS,
- .test_func = handle_thread,
- .msg = "THREAD: LAM_U57, child thread should get LAM mode same as parent\n",
- },
- {
- .expected = 1,
- .lam = LAM_U57_BITS,
- .test_func = handle_thread_enable,
- .msg = "THREAD: [NEGATIVE] Enable LAM in child.\n",
- },
- {
- .expected = 1,
- .later = 1,
- .lam = LAM_U57_BITS,
- .test_func = handle_thread,
- .msg = "THREAD: [NEGATIVE] Enable LAM in parent after thread created.\n",
- },
- {
- .expected = 0,
- .lam = LAM_U57_BITS,
- .test_func = handle_execve,
- .msg = "EXECVE: LAM_U57, child process should get disabled LAM mode\n",
- },
- };
- static void cmd_help(void)
- {
- printf("usage: lam [-h] [-t test list]\n");
- printf("\t-t test list: run tests specified in the test list, default:0x%x\n", TEST_MASK);
- printf("\t\t0x1:malloc; 0x2:max_bits; 0x4:mmap; 0x8:syscall; 0x10:io_uring; 0x20:inherit;\n");
- printf("\t-h: help\n");
- }
- /* Check for file existence */
- uint8_t file_Exists(const char *fileName)
- {
- struct stat buffer;
- uint8_t ret = (stat(fileName, &buffer) == 0);
- return ret;
- }
- /* Sysfs idxd files */
- const char *dsa_configs[] = {
- "echo 1 > /sys/bus/dsa/devices/dsa0/wq0.1/group_id",
- "echo shared > /sys/bus/dsa/devices/dsa0/wq0.1/mode",
- "echo 10 > /sys/bus/dsa/devices/dsa0/wq0.1/priority",
- "echo 16 > /sys/bus/dsa/devices/dsa0/wq0.1/size",
- "echo 15 > /sys/bus/dsa/devices/dsa0/wq0.1/threshold",
- "echo user > /sys/bus/dsa/devices/dsa0/wq0.1/type",
- "echo MyApp1 > /sys/bus/dsa/devices/dsa0/wq0.1/name",
- "echo 1 > /sys/bus/dsa/devices/dsa0/engine0.1/group_id",
- "echo dsa0 > /sys/bus/dsa/drivers/idxd/bind",
- /* bind files and devices, generated a device file in /dev */
- "echo wq0.1 > /sys/bus/dsa/drivers/user/bind",
- };
- /* DSA device file */
- const char *dsaDeviceFile = "/dev/dsa/wq0.1";
- /* file for io*/
- const char *dsaPasidEnable = "/sys/bus/dsa/devices/dsa0/pasid_enabled";
- /*
- * DSA depends on kernel cmdline "intel_iommu=on,sm_on"
- * return pasid_enabled (0: disable 1:enable)
- */
- int Check_DSA_Kernel_Setting(void)
- {
- char command[256] = "";
- char buf[256] = "";
- char *ptr;
- int rv = -1;
- snprintf(command, sizeof(command) - 1, "cat %s", dsaPasidEnable);
- FILE *cmd = popen(command, "r");
- if (cmd) {
- while (fgets(buf, sizeof(buf) - 1, cmd) != NULL);
- pclose(cmd);
- rv = strtol(buf, &ptr, 16);
- }
- return rv;
- }
- /*
- * Config DSA's sysfs files as shared DSA's WQ.
- * Generated a device file /dev/dsa/wq0.1
- * Return: 0 OK; 1 Failed; 3 Skip(SVA disabled).
- */
- int Dsa_Init_Sysfs(void)
- {
- uint len = ARRAY_SIZE(dsa_configs);
- const char **p = dsa_configs;
- if (file_Exists(dsaDeviceFile) == 1)
- return 0;
- /* check the idxd driver */
- if (file_Exists(dsaPasidEnable) != 1) {
- printf("Please make sure idxd driver was loaded\n");
- return 3;
- }
- /* Check SVA feature */
- if (Check_DSA_Kernel_Setting() != 1) {
- printf("Please enable SVA.(Add intel_iommu=on,sm_on in kernel cmdline)\n");
- return 3;
- }
- /* Check the idxd device file on /dev/dsa/ */
- for (int i = 0; i < len; i++) {
- if (system(p[i]))
- return 1;
- }
- /* After config, /dev/dsa/wq0.1 should be generated */
- return (file_Exists(dsaDeviceFile) != 1);
- }
- /*
- * Open DSA device file, triger API: iommu_sva_alloc_pasid
- */
- void *allocate_dsa_pasid(void)
- {
- int fd;
- void *wq;
- fd = open(dsaDeviceFile, O_RDWR);
- if (fd < 0) {
- perror("open");
- return MAP_FAILED;
- }
- wq = mmap(NULL, 0x1000, PROT_WRITE,
- MAP_SHARED | MAP_POPULATE, fd, 0);
- close(fd);
- if (wq == MAP_FAILED)
- perror("mmap");
- return wq;
- }
- int set_force_svm(void)
- {
- int ret = 0;
- ret = syscall(SYS_arch_prctl, ARCH_FORCE_TAGGED_SVA);
- return ret;
- }
- int handle_pasid(struct testcases *test)
- {
- uint tmp = test->cmd;
- uint runed = 0x0;
- int ret = 0;
- void *wq = NULL;
- ret = Dsa_Init_Sysfs();
- if (ret != 0)
- return ret;
- for (int i = 0; i < 3; i++) {
- int err = 0;
- if (tmp & 0x1) {
- /* run set lam mode*/
- if ((runed & 0x1) == 0) {
- err = set_lam(LAM_U57_BITS);
- runed = runed | 0x1;
- } else
- err = 1;
- } else if (tmp & 0x4) {
- /* run force svm */
- if ((runed & 0x4) == 0) {
- err = set_force_svm();
- runed = runed | 0x4;
- } else
- err = 1;
- } else if (tmp & 0x2) {
- /* run allocate pasid */
- if ((runed & 0x2) == 0) {
- runed = runed | 0x2;
- wq = allocate_dsa_pasid();
- if (wq == MAP_FAILED)
- err = 1;
- } else
- err = 1;
- }
- ret = ret + err;
- if (ret > 0)
- break;
- tmp = tmp >> 4;
- }
- if (wq != MAP_FAILED && wq != NULL)
- if (munmap(wq, 0x1000))
- printf("munmap failed %d\n", errno);
- if (runed != 0x7)
- ret = 1;
- return (ret != 0);
- }
- /*
- * Pasid test depends on idxd and SVA, kernel should enable iommu and sm.
- * command line(intel_iommu=on,sm_on)
- */
- static struct testcases pasid_cases[] = {
- {
- .expected = 1,
- .cmd = PAS_CMD(LAM_CMD_BIT, PAS_CMD_BIT, SVA_CMD_BIT),
- .test_func = handle_pasid,
- .msg = "PASID: [Negative] Execute LAM, PASID, SVA in sequence\n",
- },
- {
- .expected = 0,
- .cmd = PAS_CMD(LAM_CMD_BIT, SVA_CMD_BIT, PAS_CMD_BIT),
- .test_func = handle_pasid,
- .msg = "PASID: Execute LAM, SVA, PASID in sequence\n",
- },
- {
- .expected = 1,
- .cmd = PAS_CMD(PAS_CMD_BIT, LAM_CMD_BIT, SVA_CMD_BIT),
- .test_func = handle_pasid,
- .msg = "PASID: [Negative] Execute PASID, LAM, SVA in sequence\n",
- },
- {
- .expected = 0,
- .cmd = PAS_CMD(PAS_CMD_BIT, SVA_CMD_BIT, LAM_CMD_BIT),
- .test_func = handle_pasid,
- .msg = "PASID: Execute PASID, SVA, LAM in sequence\n",
- },
- {
- .expected = 0,
- .cmd = PAS_CMD(SVA_CMD_BIT, LAM_CMD_BIT, PAS_CMD_BIT),
- .test_func = handle_pasid,
- .msg = "PASID: Execute SVA, LAM, PASID in sequence\n",
- },
- {
- .expected = 0,
- .cmd = PAS_CMD(SVA_CMD_BIT, PAS_CMD_BIT, LAM_CMD_BIT),
- .test_func = handle_pasid,
- .msg = "PASID: Execute SVA, PASID, LAM in sequence\n",
- },
- };
- int main(int argc, char **argv)
- {
- int c = 0;
- unsigned int tests = TEST_MASK;
- tests_cnt = 0;
- if (!lam_is_available())
- return KSFT_SKIP;
- while ((c = getopt(argc, argv, "ht:")) != -1) {
- switch (c) {
- case 't':
- tests = strtoul(optarg, NULL, 16);
- if (tests && !(tests & TEST_MASK)) {
- ksft_print_msg("Invalid argument!\n");
- return -1;
- }
- break;
- case 'h':
- cmd_help();
- return 0;
- default:
- ksft_print_msg("Invalid argument\n");
- return -1;
- }
- }
- /*
- * When tests is 0, it is not a real test case;
- * the option used by test case(execve) to check the lam mode in
- * process generated by execve, the process read back lam mode and
- * check with lam mode in parent process.
- */
- if (!tests)
- return (get_lam());
- /* Run test cases */
- if (tests & FUNC_MALLOC)
- run_test(malloc_cases, ARRAY_SIZE(malloc_cases));
- if (tests & FUNC_BITS)
- run_test(bits_cases, ARRAY_SIZE(bits_cases));
- if (tests & FUNC_MMAP)
- run_test(mmap_cases, ARRAY_SIZE(mmap_cases));
- if (tests & FUNC_SYSCALL)
- run_test(syscall_cases, ARRAY_SIZE(syscall_cases));
- if (tests & FUNC_URING)
- run_test(uring_cases, ARRAY_SIZE(uring_cases));
- if (tests & FUNC_INHERITE)
- run_test(inheritance_cases, ARRAY_SIZE(inheritance_cases));
- if (tests & FUNC_PASID)
- run_test(pasid_cases, ARRAY_SIZE(pasid_cases));
- ksft_set_plan(tests_cnt);
- ksft_exit_pass();
- }
|