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 static int set_tagged_addr_ctrl(int val)
16 {
17 	int ret;
18 
19 	ret = prctl(PR_SET_TAGGED_ADDR_CTRL, val, 0, 0, 0);
20 	if (ret < 0)
21 		ksft_print_msg("PR_SET_TAGGED_ADDR_CTRL: failed %d %d (%s)\n",
22 			       ret, errno, strerror(errno));
23 	return ret;
24 }
25 
26 static int get_tagged_addr_ctrl(void)
27 {
28 	int ret;
29 
30 	ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
31 	if (ret < 0)
32 		ksft_print_msg("PR_GET_TAGGED_ADDR_CTRL failed: %d %d (%s)\n",
33 			       ret, errno, strerror(errno));
34 	return ret;
35 }
36 
37 /*
38  * Read the current mode without having done any configuration, should
39  * run first.
40  */
41 void check_basic_read(void)
42 {
43 	int ret;
44 
45 	ret = get_tagged_addr_ctrl();
46 	if (ret < 0) {
47 		ksft_test_result_fail("check_basic_read\n");
48 		return;
49 	}
50 
51 	if (ret & PR_MTE_TCF_SYNC)
52 		ksft_print_msg("SYNC enabled\n");
53 	if (ret & PR_MTE_TCF_ASYNC)
54 		ksft_print_msg("ASYNC enabled\n");
55 
56 	/* Any configuration is valid */
57 	ksft_test_result_pass("check_basic_read\n");
58 }
59 
60 /*
61  * Attempt to set a specified combination of modes.
62  */
63 void set_mode_test(const char *name, int hwcap2, int mask)
64 {
65 	int ret;
66 
67 	if ((getauxval(AT_HWCAP2) & hwcap2) != hwcap2) {
68 		ksft_test_result_skip("%s\n", name);
69 		return;
70 	}
71 
72 	ret = set_tagged_addr_ctrl(mask);
73 	if (ret < 0) {
74 		ksft_test_result_fail("%s\n", name);
75 		return;
76 	}
77 
78 	ret = get_tagged_addr_ctrl();
79 	if (ret < 0) {
80 		ksft_test_result_fail("%s\n", name);
81 		return;
82 	}
83 
84 	if ((ret & PR_MTE_TCF_MASK) == mask) {
85 		ksft_test_result_pass("%s\n", name);
86 	} else {
87 		ksft_print_msg("Got %x, expected %x\n",
88 			       (ret & PR_MTE_TCF_MASK), mask);
89 		ksft_test_result_fail("%s\n", name);
90 	}
91 }
92 
93 struct mte_mode {
94 	int mask;
95 	int hwcap2;
96 	const char *name;
97 } mte_modes[] = {
98 	{ PR_MTE_TCF_NONE,  0,          "NONE"  },
99 	{ PR_MTE_TCF_SYNC,  HWCAP2_MTE, "SYNC"  },
100 	{ PR_MTE_TCF_ASYNC, HWCAP2_MTE, "ASYNC" },
101 	{ PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC,  HWCAP2_MTE, "SYNC+ASYNC"  },
102 };
103 
104 int main(void)
105 {
106 	int i;
107 
108 	ksft_print_header();
109 	ksft_set_plan(5);
110 
111 	check_basic_read();
112 	for (i = 0; i < ARRAY_SIZE(mte_modes); i++)
113 		set_mode_test(mte_modes[i].name, mte_modes[i].hwcap2,
114 			      mte_modes[i].mask);
115 
116 	ksft_print_cnts();
117 
118 	return 0;
119 }
120