aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
blob: a876db1f096abe29979cc6b859a4aaa3efce9ca2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 ARM Limited

#define _GNU_SOURCE

#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "kselftest.h"
#include "mte_common_util.h"

#define PR_SET_TAGGED_ADDR_CTRL 55
#define PR_GET_TAGGED_ADDR_CTRL 56
# define PR_TAGGED_ADDR_ENABLE  (1UL << 0)
# define PR_MTE_TCF_SHIFT	1
# define PR_MTE_TCF_NONE	(0UL << PR_MTE_TCF_SHIFT)
# define PR_MTE_TCF_SYNC	(1UL << PR_MTE_TCF_SHIFT)
# define PR_MTE_TCF_ASYNC	(2UL << PR_MTE_TCF_SHIFT)
# define PR_MTE_TCF_MASK	(3UL << PR_MTE_TCF_SHIFT)
# define PR_MTE_TAG_SHIFT	3
# define PR_MTE_TAG_MASK	(0xffffUL << PR_MTE_TAG_SHIFT)

#include "mte_def.h"

#define NUM_ITERATIONS		1024
#define MAX_THREADS		5
#define THREAD_ITERATIONS	1000

void *execute_thread(void *x)
{
	pid_t pid = *((pid_t *)x);
	pid_t tid = gettid();
	uint64_t prctl_tag_mask;
	uint64_t prctl_set;
	uint64_t prctl_get;
	uint64_t prctl_tcf;

	srand(time(NULL) ^ (pid << 16) ^ (tid << 16));

	prctl_tag_mask = rand() & 0xffff;

	if (prctl_tag_mask % 2)
		prctl_tcf = PR_MTE_TCF_SYNC;
	else
		prctl_tcf = PR_MTE_TCF_ASYNC;

	prctl_set = PR_TAGGED_ADDR_ENABLE | prctl_tcf | (prctl_tag_mask << PR_MTE_TAG_SHIFT);

	for (int j = 0; j < THREAD_ITERATIONS; j++) {
		if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_set, 0, 0, 0)) {
			perror("prctl() failed");
			goto fail;
		}

		prctl_get = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);

		if (prctl_set != prctl_get) {
			ksft_print_msg("Error: prctl_set: 0x%lx != prctl_get: 0x%lx\n",
						prctl_set, prctl_get);
			goto fail;
		}
	}

	return (void *)KSFT_PASS;

fail:
	return (void *)KSFT_FAIL;
}

int execute_test(pid_t pid)
{
	pthread_t thread_id[MAX_THREADS];
	int thread_data[MAX_THREADS];

	for (int i = 0; i < MAX_THREADS; i++)
		pthread_create(&thread_id[i], NULL,
			       execute_thread, (void *)&pid);

	for (int i = 0; i < MAX_THREADS; i++)
		pthread_join(thread_id[i], (void *)&thread_data[i]);

	for (int i = 0; i < MAX_THREADS; i++)
		if (thread_data[i] == KSFT_FAIL)
			return KSFT_FAIL;

	return KSFT_PASS;
}

int mte_gcr_fork_test(void)
{
	pid_t pid;
	int results[NUM_ITERATIONS];
	pid_t cpid;
	int res;

	for (int i = 0; i < NUM_ITERATIONS; i++) {
		pid = fork();

		if (pid < 0)
			return KSFT_FAIL;

		if (pid == 0) {
			cpid = getpid();

			res = execute_test(cpid);

			exit(res);
		}
	}

	for (int i = 0; i < NUM_ITERATIONS; i++) {
		wait(&res);

		if (WIFEXITED(res))
			results[i] = WEXITSTATUS(res);
		else
			--i;
	}

	for (int i = 0; i < NUM_ITERATIONS; i++)
		if (results[i] == KSFT_FAIL)
			return KSFT_FAIL;

	return KSFT_PASS;
}

int main(int argc, char *argv[])
{
	int err;

	err = mte_default_setup();
	if (err)
		return err;

	ksft_set_plan(1);

	evaluate_test(mte_gcr_fork_test(),
		"Verify that GCR_EL1 is set correctly on context switch\n");

	mte_restore_setup();
	ksft_print_cnts();

	return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
}