selftests/x86/lam: Add inherit test cases for linear-address masking
authorWeihong Zhang <weihong.zhang@intel.com>
Sun, 12 Mar 2023 11:26:10 +0000 (14:26 +0300)
committerDave Hansen <dave.hansen@linux.intel.com>
Thu, 16 Mar 2023 20:08:40 +0000 (13:08 -0700)
LAM is enabled per-thread and gets inherited on fork(2)/clone(2). exec()
reverts LAM status to the default disabled state.

There are two test scenarios:

 - Fork test cases:

   These cases were used to test the inheritance of LAM for per-thread,
   Child process generated by fork() should inherit LAM feature from
   parent process, Child process can get the LAM mode same as parent
   process.

 - Execve test cases:

   Processes generated by execve() are different from processes
   generated by fork(), these processes revert LAM status to disabled
   status.

Signed-off-by: Weihong Zhang <weihong.zhang@intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20230312112612.31869-16-kirill.shutemov%40linux.intel.com
tools/testing/selftests/x86/lam.c

index 52750eb..ebabd43 100644 (file)
@@ -37,8 +37,9 @@
 #define FUNC_MMAP               0x4
 #define FUNC_SYSCALL            0x8
 #define FUNC_URING              0x10
+#define FUNC_INHERITE           0x20
 
-#define TEST_MASK               0x1f
+#define TEST_MASK               0x3f
 
 #define LOW_ADDR                (0x1UL << 30)
 #define HIGH_ADDR               (0x3UL << 48)
@@ -174,6 +175,28 @@ static unsigned long get_default_tag_bits(void)
        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)
 {
@@ -581,7 +604,7 @@ out:
 
                        switch (lam) {
                        case LAM_U57_BITS: /* Clear bits 62:57 */
-                               addr = (addr & ~(0x3fULL << 57));
+                               addr = (addr & ~(LAM_U57_MASK));
                                break;
                        }
                        free((void *)addr);
@@ -632,6 +655,72 @@ static int fork_test(struct testcases *test)
        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];
+
+               /* 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) <= 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 void run_test(struct testcases *test, int count)
 {
        int i, ret = 0;
@@ -740,11 +829,26 @@ static struct testcases mmap_cases[] = {
        },
 };
 
+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_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.\n");
+       printf("\t\t0x1:malloc; 0x2:max_bits; 0x4:mmap; 0x8:syscall; 0x10:io_uring; 0x20:inherit;\n");
        printf("\t-h: help\n");
 }
 
@@ -764,7 +868,7 @@ int main(int argc, char **argv)
                switch (c) {
                case 't':
                        tests = strtoul(optarg, NULL, 16);
-                       if (!(tests & TEST_MASK)) {
+                       if (tests && !(tests & TEST_MASK)) {
                                ksft_print_msg("Invalid argument!\n");
                                return -1;
                        }
@@ -778,6 +882,16 @@ int main(int argc, char **argv)
                }
        }
 
+       /*
+        * 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));
 
@@ -793,6 +907,9 @@ int main(int argc, char **argv)
        if (tests & FUNC_URING)
                run_test(uring_cases, ARRAY_SIZE(uring_cases));
 
+       if (tests & FUNC_INHERITE)
+               run_test(inheritance_cases, ARRAY_SIZE(inheritance_cases));
+
        ksft_set_plan(tests_cnt);
 
        return ksft_exit_pass();