xref: /linux/tools/testing/selftests/arm64/mte/check_prctl.c (revision b4ada0618eed0fbd1b1630f73deb048c592b06a1)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2022 ARM Limited
3 
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <string.h>
7 
8 #include <sys/auxv.h>
9 #include <sys/prctl.h>
10 
11 #include <asm/hwcap.h>
12 
13 #include "kselftest.h"
14 
15 #ifndef AT_HWCAP3
16 #define AT_HWCAP3 29
17 #endif
18 
19 static int set_tagged_addr_ctrl(int val)
20 {
21 	int ret;
22 
23 	ret = prctl(PR_SET_TAGGED_ADDR_CTRL, val, 0, 0, 0);
24 	if (ret < 0)
25 		ksft_print_msg("PR_SET_TAGGED_ADDR_CTRL: failed %d %d (%s)\n",
26 			       ret, errno, strerror(errno));
27 	return ret;
28 }
29 
30 static int get_tagged_addr_ctrl(void)
31 {
32 	int ret;
33 
34 	ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
35 	if (ret < 0)
36 		ksft_print_msg("PR_GET_TAGGED_ADDR_CTRL failed: %d %d (%s)\n",
37 			       ret, errno, strerror(errno));
38 	return ret;
39 }
40 
41 /*
42  * Read the current mode without having done any configuration, should
43  * run first.
44  */
45 void check_basic_read(void)
46 {
47 	int ret;
48 
49 	ret = get_tagged_addr_ctrl();
50 	if (ret < 0) {
51 		ksft_test_result_fail("check_basic_read\n");
52 		return;
53 	}
54 
55 	if (ret & PR_MTE_TCF_SYNC)
56 		ksft_print_msg("SYNC enabled\n");
57 	if (ret & PR_MTE_TCF_ASYNC)
58 		ksft_print_msg("ASYNC enabled\n");
59 
60 	/* Any configuration is valid */
61 	ksft_test_result_pass("check_basic_read\n");
62 }
63 
64 /*
65  * Attempt to set a specified combination of modes.
66  */
67 void set_mode_test(const char *name, int hwcap2, int hwcap3, int mask)
68 {
69 	int ret;
70 
71 	if ((getauxval(AT_HWCAP2) & hwcap2) != hwcap2) {
72 		ksft_test_result_skip("%s\n", name);
73 		return;
74 	}
75 
76 	if ((getauxval(AT_HWCAP3) & hwcap3) != hwcap3) {
77 		ksft_test_result_skip("%s\n", name);
78 		return;
79 	}
80 
81 	ret = set_tagged_addr_ctrl(mask);
82 	if (ret < 0) {
83 		ksft_test_result_fail("%s\n", name);
84 		return;
85 	}
86 
87 	ret = get_tagged_addr_ctrl();
88 	if (ret < 0) {
89 		ksft_test_result_fail("%s\n", name);
90 		return;
91 	}
92 
93 	if ((ret & (PR_MTE_TCF_MASK | PR_MTE_STORE_ONLY)) == mask) {
94 		ksft_test_result_pass("%s\n", name);
95 	} else {
96 		ksft_print_msg("Got %x, expected %x\n",
97 			       (ret & (int)PR_MTE_TCF_MASK), mask);
98 		ksft_test_result_fail("%s\n", name);
99 	}
100 }
101 
102 struct mte_mode {
103 	int mask;
104 	int hwcap2;
105 	int hwcap3;
106 	const char *name;
107 } mte_modes[] = {
108 	{ PR_MTE_TCF_NONE,                                        0,          0,                     "NONE"  },
109 	{ PR_MTE_TCF_SYNC,                                        HWCAP2_MTE, 0,                     "SYNC"  },
110 	{ PR_MTE_TCF_ASYNC,                                       HWCAP2_MTE, 0,                     "ASYNC" },
111 	{ PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC,                     HWCAP2_MTE, 0,                     "SYNC+ASYNC"  },
112 	{ PR_MTE_TCF_SYNC | PR_MTE_STORE_ONLY,                    HWCAP2_MTE, HWCAP3_MTE_STORE_ONLY, "SYNC+STONLY" },
113 	{ PR_MTE_TCF_ASYNC | PR_MTE_STORE_ONLY,                   HWCAP2_MTE, HWCAP3_MTE_STORE_ONLY, "ASYNC+STONLY" },
114 	{ PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC | PR_MTE_STORE_ONLY, HWCAP2_MTE, HWCAP3_MTE_STORE_ONLY, "SYNC+ASYNC+STONLY" },
115 };
116 
117 int main(void)
118 {
119 	int i;
120 
121 	ksft_print_header();
122 	ksft_set_plan(ARRAY_SIZE(mte_modes));
123 
124 	check_basic_read();
125 	for (i = 0; i < ARRAY_SIZE(mte_modes); i++)
126 		set_mode_test(mte_modes[i].name, mte_modes[i].hwcap2, mte_modes[i].hwcap3,
127 			      mte_modes[i].mask);
128 
129 	ksft_print_cnts();
130 
131 	return 0;
132 }
133