162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (C) 2022 ARM Limited
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <stdbool.h>
562306a36Sopenharmony_ci#include <stdio.h>
662306a36Sopenharmony_ci#include <string.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <sys/auxv.h>
962306a36Sopenharmony_ci#include <sys/prctl.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <asm/hwcap.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "kselftest.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic int set_tagged_addr_ctrl(int val)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	int ret;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	ret = prctl(PR_SET_TAGGED_ADDR_CTRL, val, 0, 0, 0);
2062306a36Sopenharmony_ci	if (ret < 0)
2162306a36Sopenharmony_ci		ksft_print_msg("PR_SET_TAGGED_ADDR_CTRL: failed %d %d (%s)\n",
2262306a36Sopenharmony_ci			       ret, errno, strerror(errno));
2362306a36Sopenharmony_ci	return ret;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int get_tagged_addr_ctrl(void)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	int ret;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
3162306a36Sopenharmony_ci	if (ret < 0)
3262306a36Sopenharmony_ci		ksft_print_msg("PR_GET_TAGGED_ADDR_CTRL failed: %d %d (%s)\n",
3362306a36Sopenharmony_ci			       ret, errno, strerror(errno));
3462306a36Sopenharmony_ci	return ret;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * Read the current mode without having done any configuration, should
3962306a36Sopenharmony_ci * run first.
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_civoid check_basic_read(void)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	int ret;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	ret = get_tagged_addr_ctrl();
4662306a36Sopenharmony_ci	if (ret < 0) {
4762306a36Sopenharmony_ci		ksft_test_result_fail("check_basic_read\n");
4862306a36Sopenharmony_ci		return;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (ret & PR_MTE_TCF_SYNC)
5262306a36Sopenharmony_ci		ksft_print_msg("SYNC enabled\n");
5362306a36Sopenharmony_ci	if (ret & PR_MTE_TCF_ASYNC)
5462306a36Sopenharmony_ci		ksft_print_msg("ASYNC enabled\n");
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Any configuration is valid */
5762306a36Sopenharmony_ci	ksft_test_result_pass("check_basic_read\n");
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/*
6162306a36Sopenharmony_ci * Attempt to set a specified combination of modes.
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_civoid set_mode_test(const char *name, int hwcap2, int mask)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	int ret;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if ((getauxval(AT_HWCAP2) & hwcap2) != hwcap2) {
6862306a36Sopenharmony_ci		ksft_test_result_skip("%s\n", name);
6962306a36Sopenharmony_ci		return;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	ret = set_tagged_addr_ctrl(mask);
7362306a36Sopenharmony_ci	if (ret < 0) {
7462306a36Sopenharmony_ci		ksft_test_result_fail("%s\n", name);
7562306a36Sopenharmony_ci		return;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ret = get_tagged_addr_ctrl();
7962306a36Sopenharmony_ci	if (ret < 0) {
8062306a36Sopenharmony_ci		ksft_test_result_fail("%s\n", name);
8162306a36Sopenharmony_ci		return;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if ((ret & PR_MTE_TCF_MASK) == mask) {
8562306a36Sopenharmony_ci		ksft_test_result_pass("%s\n", name);
8662306a36Sopenharmony_ci	} else {
8762306a36Sopenharmony_ci		ksft_print_msg("Got %x, expected %x\n",
8862306a36Sopenharmony_ci			       (ret & PR_MTE_TCF_MASK), mask);
8962306a36Sopenharmony_ci		ksft_test_result_fail("%s\n", name);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct mte_mode {
9462306a36Sopenharmony_ci	int mask;
9562306a36Sopenharmony_ci	int hwcap2;
9662306a36Sopenharmony_ci	const char *name;
9762306a36Sopenharmony_ci} mte_modes[] = {
9862306a36Sopenharmony_ci	{ PR_MTE_TCF_NONE,  0,          "NONE"  },
9962306a36Sopenharmony_ci	{ PR_MTE_TCF_SYNC,  HWCAP2_MTE, "SYNC"  },
10062306a36Sopenharmony_ci	{ PR_MTE_TCF_ASYNC, HWCAP2_MTE, "ASYNC" },
10162306a36Sopenharmony_ci	{ PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC,  HWCAP2_MTE, "SYNC+ASYNC"  },
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ciint main(void)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int i;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	ksft_print_header();
10962306a36Sopenharmony_ci	ksft_set_plan(5);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	check_basic_read();
11262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mte_modes); i++)
11362306a36Sopenharmony_ci		set_mode_test(mte_modes[i].name, mte_modes[i].hwcap2,
11462306a36Sopenharmony_ci			      mte_modes[i].mask);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	ksft_print_cnts();
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return 0;
11962306a36Sopenharmony_ci}
120