xref: /linux/tools/testing/selftests/arm64/mte/check_hugetlb_options.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2024 Ampere Computing LLC
3 
4 #define _GNU_SOURCE
5 
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ucontext.h>
13 #include <sys/mman.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 
18 #include "kselftest.h"
19 #include "mte_common_util.h"
20 #include "mte_def.h"
21 
22 #define TAG_CHECK_ON		0
23 #define TAG_CHECK_OFF		1
24 
25 static unsigned long default_huge_page_size(void)
26 {
27 	unsigned long hps = 0;
28 	char *line = NULL;
29 	size_t linelen = 0;
30 	FILE *f = fopen("/proc/meminfo", "r");
31 
32 	if (!f)
33 		return 0;
34 	while (getline(&line, &linelen, f) > 0) {
35 		if (sscanf(line, "Hugepagesize:       %lu kB", &hps) == 1) {
36 			hps <<= 10;
37 			break;
38 		}
39 	}
40 
41 	free(line);
42 	fclose(f);
43 	return hps;
44 }
45 
46 static bool is_hugetlb_allocated(void)
47 {
48 	unsigned long hps = 0;
49 	char *line = NULL;
50 	size_t linelen = 0;
51 	FILE *f = fopen("/proc/meminfo", "r");
52 
53 	if (!f)
54 		return false;
55 	while (getline(&line, &linelen, f) > 0) {
56 		if (sscanf(line, "Hugetlb:       %lu kB", &hps) == 1) {
57 			hps <<= 10;
58 			break;
59 		}
60 	}
61 
62 	free(line);
63 	fclose(f);
64 
65 	if (hps > 0)
66 		return true;
67 
68 	return false;
69 }
70 
71 static void write_sysfs(char *str, unsigned long val)
72 {
73 	FILE *f;
74 
75 	f = fopen(str, "w");
76 	if (!f) {
77 		ksft_print_msg("ERR: missing %s\n", str);
78 		return;
79 	}
80 	fprintf(f, "%lu", val);
81 	fclose(f);
82 }
83 
84 static void allocate_hugetlb()
85 {
86 	write_sysfs("/proc/sys/vm/nr_hugepages", 2);
87 }
88 
89 static void free_hugetlb()
90 {
91 	write_sysfs("/proc/sys/vm/nr_hugepages", 0);
92 }
93 
94 static int check_child_tag_inheritance(char *ptr, int size, int mode)
95 {
96 	int i, parent_tag, child_tag, fault, child_status;
97 	pid_t child;
98 
99 	parent_tag = MT_FETCH_TAG((uintptr_t)ptr);
100 	fault = 0;
101 
102 	child = fork();
103 	if (child == -1) {
104 		ksft_print_msg("FAIL: child process creation\n");
105 		return KSFT_FAIL;
106 	} else if (child == 0) {
107 		mte_initialize_current_context(mode, (uintptr_t)ptr, size);
108 		/* Do copy on write */
109 		memset(ptr, '1', size);
110 		mte_wait_after_trig();
111 		if (cur_mte_cxt.fault_valid == true) {
112 			fault = 1;
113 			goto check_child_tag_inheritance_err;
114 		}
115 		for (i = 0; i < size; i += MT_GRANULE_SIZE) {
116 			child_tag = MT_FETCH_TAG((uintptr_t)(mte_get_tag_address(ptr + i)));
117 			if (parent_tag != child_tag) {
118 				ksft_print_msg("FAIL: child mte tag (%d) mismatch\n", i);
119 				fault = 1;
120 				goto check_child_tag_inheritance_err;
121 			}
122 		}
123 check_child_tag_inheritance_err:
124 		_exit(fault);
125 	}
126 	/* Wait for child process to terminate */
127 	wait(&child_status);
128 	if (WIFEXITED(child_status))
129 		fault = WEXITSTATUS(child_status);
130 	else
131 		fault = 1;
132 	return (fault) ? KSFT_FAIL : KSFT_PASS;
133 }
134 
135 static int check_mte_memory(char *ptr, int size, int mode, int tag_check)
136 {
137 	mte_initialize_current_context(mode, (uintptr_t)ptr, size);
138 	memset(ptr, '1', size);
139 	mte_wait_after_trig();
140 	if (cur_mte_cxt.fault_valid == true)
141 		return KSFT_FAIL;
142 
143 	return KSFT_PASS;
144 }
145 
146 static int check_hugetlb_memory_mapping(int mem_type, int mode, int mapping, int tag_check)
147 {
148 	char *ptr, *map_ptr;
149 	int result;
150 	unsigned long map_size;
151 
152 	map_size = default_huge_page_size();
153 
154 	mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
155 	map_ptr = (char *)mte_allocate_memory(map_size, mem_type, mapping, false);
156 	if (check_allocated_memory(map_ptr, map_size, mem_type, false) != KSFT_PASS)
157 		return KSFT_FAIL;
158 
159 	mte_initialize_current_context(mode, (uintptr_t)map_ptr, map_size);
160 	/* Only mte enabled memory will allow tag insertion */
161 	ptr = mte_insert_tags((void *)map_ptr, map_size);
162 	if (!ptr || cur_mte_cxt.fault_valid == true) {
163 		ksft_print_msg("FAIL: Insert tags on anonymous mmap memory\n");
164 		munmap((void *)map_ptr, map_size);
165 		return KSFT_FAIL;
166 	}
167 	result = check_mte_memory(ptr, map_size, mode, tag_check);
168 	mte_clear_tags((void *)ptr, map_size);
169 	mte_free_memory((void *)map_ptr, map_size, mem_type, false);
170 	if (result == KSFT_FAIL)
171 		return KSFT_FAIL;
172 
173 	return KSFT_PASS;
174 }
175 
176 static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping)
177 {
178 	char *map_ptr;
179 	int prot_flag, result;
180 	unsigned long map_size;
181 
182 	prot_flag = PROT_READ | PROT_WRITE;
183 	mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
184 	map_size = default_huge_page_size();
185 	map_ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping,
186 							0, 0);
187 	if (check_allocated_memory_range(map_ptr, map_size, mem_type,
188 					 0, 0) != KSFT_PASS)
189 		return KSFT_FAIL;
190 	/* Try to clear PROT_MTE property and verify it by tag checking */
191 	if (mprotect(map_ptr, map_size, prot_flag)) {
192 		mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type,
193 					  0, 0);
194 		ksft_print_msg("FAIL: mprotect not ignoring clear PROT_MTE property\n");
195 		return KSFT_FAIL;
196 	}
197 	result = check_mte_memory(map_ptr, map_size, mode, TAG_CHECK_ON);
198 	mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type, 0, 0);
199 	if (result != KSFT_PASS)
200 		return KSFT_FAIL;
201 
202 	return KSFT_PASS;
203 }
204 
205 static int check_child_hugetlb_memory_mapping(int mem_type, int mode, int mapping)
206 {
207 	char *ptr;
208 	int result;
209 	unsigned long map_size;
210 
211 	map_size = default_huge_page_size();
212 
213 	mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
214 	ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping,
215 						    0, 0);
216 	if (check_allocated_memory_range(ptr, map_size, mem_type,
217 					 0, 0) != KSFT_PASS)
218 		return KSFT_FAIL;
219 	result = check_child_tag_inheritance(ptr, map_size, mode);
220 	mte_free_memory_tag_range((void *)ptr, map_size, mem_type, 0, 0);
221 	if (result == KSFT_FAIL)
222 		return result;
223 
224 	return KSFT_PASS;
225 }
226 
227 int main(int argc, char *argv[])
228 {
229 	int err;
230 
231 	err = mte_default_setup();
232 	if (err)
233 		return err;
234 
235 	/* Register signal handlers */
236 	mte_register_signal(SIGBUS, mte_default_handler);
237 	mte_register_signal(SIGSEGV, mte_default_handler);
238 
239 	allocate_hugetlb();
240 
241 	if (!is_hugetlb_allocated()) {
242 		ksft_print_msg("ERR: Unable allocate hugetlb pages\n");
243 		return KSFT_FAIL;
244 	}
245 
246 	/* Set test plan */
247 	ksft_set_plan(12);
248 
249 	mte_enable_pstate_tco();
250 
251 	evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF),
252 	"Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check off\n");
253 
254 	mte_disable_pstate_tco();
255 	evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_NONE_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF),
256 	"Check hugetlb memory with private mapping, no error mode, mmap memory and tag check off\n");
257 
258 	evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
259 	"Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check on\n");
260 	evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
261 	"Check hugetlb memory with private mapping, sync error mode, mmap/mprotect memory and tag check on\n");
262 	evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
263 	"Check hugetlb memory with private mapping, async error mode, mmap memory and tag check on\n");
264 	evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
265 	"Check hugetlb memory with private mapping, async error mode, mmap/mprotect memory and tag check on\n");
266 
267 	evaluate_test(check_clear_prot_mte_flag(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
268 	"Check clear PROT_MTE flags with private mapping, sync error mode and mmap memory\n");
269 	evaluate_test(check_clear_prot_mte_flag(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
270 	"Check clear PROT_MTE flags with private mapping and sync error mode and mmap/mprotect memory\n");
271 
272 	evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
273 		"Check child hugetlb memory with private mapping, precise mode and mmap memory\n");
274 	evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
275 		"Check child hugetlb memory with private mapping, precise mode and mmap memory\n");
276 	evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
277 		"Check child hugetlb memory with private mapping, precise mode and mmap/mprotect memory\n");
278 	evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
279 		"Check child hugetlb memory with private mapping, precise mode and mmap/mprotect memory\n");
280 
281 	mte_restore_setup();
282 	free_hugetlb();
283 	ksft_print_cnts();
284 	return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
285 }
286