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
15static 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
26static 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 */
41void 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 */
63void 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
93struct 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
104int 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