Linux
tools/testing/selftests/devmem/tests.c
Source file coverage
Path:
tools/testing/selftests/devmem/tests.c
Lines:
569
Non-empty lines:
488
Non-empty lines covered with requirements:
217 / 488 (44.5%)
Functions:
19
Functions covered by requirements:
13 / 19 (68.4%)
1
// SPDX-License-Identifier: GPL-2.0+
2
/* devmem test tests.c
3
 *
4
 * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
5
 * Written by Alessandro Carminati (acarmina@redhat.com)
6
 */
7
 
8
#define _FILE_OFFSET_BITS 64
9
#include <errno.h>
10
#include <fcntl.h>
11
#include <stddef.h>
12
#include <stdio.h>
13
#include <stdlib.h>
14
#include <string.h>
15
#include <sys/stat.h>
16
#include <sys/sysmacros.h>
17
#include <time.h>
18
#include <sys/wait.h>
19
#include <unistd.h>
20
 
21
#include "tests.h"
22
#include "debug.h"
23
#include "utils.h"
24
#include "ram_map.h"
25
#include "secret.h"
26
 
27
#define KPROBE_EVENTS_PATH "%s/kprobe_events"
28
#define KPROBE_EVENTS_ENABLE "%s/events/kprobes/enable"
29
#define TRACE_PIPE_PATH "%s/trace_pipe"
30
#define MAX_LINE_LENGTH 256
31
#define RETPROBE_NAME "open_retprobe"
32
 
33
struct open_res {
34
	int open_resv;
35
	bool test_res;
36
};
37
char *tracing_dir;
38
char *tracingdirs[3] = {
39
	NULL,
40
	"/sys/kernel/tracing",
41
	"/sys/kernel/debug/tracing"
42
};
43
 
44
int check_and_set_tracefs_mount(void)
45
{
46
	FILE *mounts_file;
47
	char line[256];
48
	char device[64], mount_point[128], fs_type[32];
49
	int retval = 0;
50
 
51
	mounts_file = fopen("/proc/mounts", "r");
52
	if (mounts_file == NULL) {
53
		perror("Failed to open /proc/mounts");
54
		return 0; // Cannot verify, assume not mounted
55
	}
56
 
57
	while (fgets(line, sizeof(line), mounts_file)) {
58
		if (sscanf(line, "%s %s %s", device, mount_point, fs_type) >= 3) {
59
			if (strcmp(mount_point, "/sys/kernel/tracing") == 0 &&
60
			    strcmp(fs_type, "tracefs") == 0) {
61
				retval = 1;
62
				break;
63
			}
64
			if (strcmp(mount_point, "/sys/kernel/debug/tracing") == 0 &&
65
			    strcmp(fs_type, "tracefs") == 0) {
66
				retval = 2;
67
				break;
68
			}
69
		}
70
	}
71
	tracing_dir = tracingdirs[retval];
72
	return retval;
73
}
74
 
75
int get_device_numbers(int fd, unsigned int *major_num,
76
			unsigned int *minor_num)
77
{
78
	struct stat file_stat;
79
 
80
	if (fstat(fd, &file_stat) == -1) {
81
		perror("fstat failed");
82
		return -1;
83
	}
84
 
85
	if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode)) {
86
		*major_num = major(file_stat.st_rdev);
87
		*minor_num = minor(file_stat.st_rdev);
88
		return 0;
89
	}
90
	fprintf(stderr, "File descriptor does not refer to a device file.\n");
91
	return -1;
92
}
93
 
94
static int write_file(const char *path, const char *data)
95
{
96
	int fd = open(path, O_WRONLY | O_TRUNC);
97
	ssize_t ret;
98
 
99
	if (fd < 0) {
100
		deb_printf("Error opening file %s: %s\n",
101
		    path, strerror(errno));
102
		return -1;
103
	}
104
	deb_printf("echo \"%s\" >%s\n", data, path);
105
	ret = write(fd, data, strlen(data));
106
	close(fd);
107
	if (ret < 0) {
108
		deb_printf("Error writing to file %s: %s\n",
109
		   path, strerror(errno));
110
		return -1;
111
	}
112
	return 0;
113
}
114
 
115
static void cleanup_probes(void)
116
{
117
	deb_printf("Cleaning up kprobes and tracing...\n");
118
	char buf[100];
119
 
120
	sprintf(buf, KPROBE_EVENTS_PATH, tracing_dir);
121
	if (write_file(buf, "\n") != 0)
122
		deb_printf("Failed to clear retprobes. Manual cleanup may be required.\n");
123
 
124
	sprintf(buf, KPROBE_EVENTS_ENABLE, tracing_dir);
125
	if (write_file(buf, "0") != 0)
126
		deb_printf("Failed to clear retprobes. Manual cleanup may be required.\n");
127
 
128
}
129
 
130
static void traced_open(const char *filename, const char *expected_func_name,
131
			struct open_res *r)
132
{
133
	pid_t child_pid, parent_pid, traced_pid, result;
134
	char retprobe_setup_cmd[MAX_LINE_LENGTH];
135
	char tmp_path[MAX_LINE_LENGTH];
136
	char line[MAX_LINE_LENGTH];
137
	int open_resv, retval = -1;
138
	struct open_res res;
139
	int status, timeout;
140
	FILE *trace_file;
141
	time_t start;
142
	int pfd[2];
143
	int sn;
144
 
145
	r->open_resv = -1;
146
	r->test_res = false;
147
 
148
	parent_pid = getpid();
149
 
150
	if (pipe(pfd) == -1) {
151
		perror("pipe failed");
152
		return;
153
	}
154
 
155
	deb_printf("Configuring kprobes on '%s'...\n", expected_func_name);
156
	snprintf(tmp_path, sizeof(tmp_path), KPROBE_EVENTS_PATH, tracing_dir);
157
	snprintf(retprobe_setup_cmd, sizeof(retprobe_setup_cmd),
158
		 "r2:kprobes/%s_ret %s retval=$retval ", RETPROBE_NAME,
159
		 expected_func_name);
160
	if (write_file(tmp_path, retprobe_setup_cmd) != 0) {
161
		cleanup_probes();
162
		return;
163
	}
164
	snprintf(tmp_path, sizeof(tmp_path), KPROBE_EVENTS_ENABLE,
165
		 tracing_dir);
166
	if (write_file(tmp_path, "1") != 0) {
167
		cleanup_probes();
168
		return;
169
	}
170
 
171
	child_pid = fork();
172
	if (child_pid == -1) {
173
		deb_printf("fork failed\n");
174
		cleanup_probes();
175
		return;
176
	}
177
 
178
	if (child_pid == 0) {
179
		close(pfd[0]);
180
		snprintf(line, sizeof(line), TRACE_PIPE_PATH, tracing_dir);
181
		trace_file = fopen(line, "r");
182
		if (!trace_file) {
183
			deb_printf("fopen trace_pipe failed in child\n");
184
			exit(EXIT_FAILURE);
185
		}
186
 
187
		open_resv = -1;
188
 
189
		sleep(2);
190
		while (fgets(line, sizeof(line), trace_file) != NULL) {
191
			traced_pid = -1;
192
			deb_printf("Received =>%s\n", line);
193
			deb_printf("matching against: RETPROBE_NAME=\"%s\" and expected_func_name=\"%s\"\n",
194
				   RETPROBE_NAME, expected_func_name);
195
			deb_printf("matching against: RETPROBE_NAME=\"%s\" => %p\n",
196
				   RETPROBE_NAME, strstr(line, RETPROBE_NAME));
197
			deb_printf("matching against: expected_func_name=\"%s\" =>%p\n",
198
			   expected_func_name, strstr(line, expected_func_name));
199
 
200
			if (strstr(line, RETPROBE_NAME) &&
201
			    strstr(line, expected_func_name)) {
202
				sn = sscanf(line, " %*[^-]-%d%*[^=]=%x", &traced_pid, &open_resv);
203
				deb_printf("scanned (%d)traced_pid=%d, open_resv=%d parent_pid=%d\n",
204
				    sn, traced_pid, open_resv, parent_pid);
205
				if (traced_pid == parent_pid && open_resv == 0) {
206
					deb_printf("found!\n");
207
					res.open_resv = open_resv;
208
					res.test_res = true;
209
					write(pfd[1], &res, sizeof(res));
210
					fclose(trace_file);
211
					exit(EXIT_SUCCESS);
212
				}
213
			}
214
		}
215
		fclose(trace_file);
216
		res.open_resv = -1;
217
		res.test_res = false;
218
		write(pfd[1], &res, sizeof(res));
219
		exit(EXIT_FAILURE);
220
	} else {
221
		close(pfd[1]);
222
		sleep(1);
223
		deb_printf("Parent process (PID %d) is calling open()...\n",
224
		    parent_pid);
225
		retval = open(filename, O_RDONLY);
226
		if (retval == -1) {
227
			deb_printf("open failed\n");
228
			kill(child_pid, SIGTERM);
229
			waitpid(child_pid, NULL, 0);
230
			cleanup_probes();
231
			return;
232
		}
233
 
234
		start = time(NULL);
235
		timeout = 15;
236
 
237
		while (1) {
238
			result = waitpid(-1, &status, WNOHANG);
239
			if (result == -1) {
240
				perror("waitpid");
241
				break;
242
			} else if (result > 0) {
243
				deb_printf("Child exited normally\n");
244
				break;
245
			}
246
 
247
			if (time(NULL) - start >= timeout) {
248
				printf("Timeout reached! Killing child...\n");
249
				kill(child_pid, SIGKILL);
250
				waitpid(child_pid, NULL, 0);
251
				break;
252
			}
253
			usleep(100000);
254
		}
255
 
256
		if (read(pfd[0], r, sizeof(struct open_res)) !=
257
		   sizeof(struct open_res)) {
258
			deb_printf("Failed to read data from child process.\n");
259
			r->test_res = false;
260
		}
261
 
262
		close(pfd[0]);
263
 
264
		cleanup_probes();
265
 
266
		r->open_resv = retval;
267
		if (r->open_resv >= 0 && r->test_res)
268
			r->test_res = true;
269
		else
270
			r->test_res = false;
271
	}
272
}
273
 
274
int test_read_at_addr_32bit_ge(struct test_context *t)
275
{
276
	if (is_64bit_arch()) {
277
		deb_printf("Skipped (64-bit architecture)\n");
278
		return SKIPPED;
279
	}
280
 
281
	uint64_t target_addr = 0x100000000ULL;
282
	int ret = try_read_dev_mem(t->fd, target_addr, 0, NULL);
283
 
284
	if (ret == 0) {
285
		deb_printf("PASS: Read beyond 4 GiB at 0x%llx returned 0 bytes\n",
286
		    target_addr);
287
		return PASS;
288
	}
289
	deb_printf("FAIL: Expected 0 bytes at 0x%llx, got %d (errno=%d)\n",
290
		    target_addr, ret, -ret);
291
	return FAIL;
292
}
293
 
294
int test_read_outside_linear_map(struct test_context *t)
295
{
296
	uint64_t tolerance, start_addr, max_addr, last_linear;
297
 
298
	if (sizeof(void *) == 8) {
299
		deb_printf("Skipped: 64-bit architecture\n");
300
		return SKIPPED;
301
	}
302
 
303
	if (!t->map || t->map->count == 0) {
304
		deb_printf("No memory map provided!\n");
305
		return SKIPPED;
306
	}
307
 
308
	start_addr = t->map->regions[0].start;
309
	max_addr = t->map->regions[t->map->count - 1].end;
310
 
311
	deb_printf("Scanning between 0x%llx and 0x%llx\n",
312
		   (unsigned long long)start_addr, (unsigned long long)max_addr);
313
 
314
	last_linear = find_last_linear_byte(t->fd, start_addr, max_addr);
315
 
316
	deb_printf("Last readable linear address: 0x%llx\n",
317
		   (unsigned long long)last_linear);
318
 
319
	tolerance = 16 * 1024 * 1024;
320
	if (last_linear + 1 >= EXPECTED_LINEAR_LIMIT - tolerance &&
321
		last_linear + 1 <= EXPECTED_LINEAR_LIMIT + tolerance) {
322
		deb_printf("PASS: Linear map ends near 1 GiB boundary.\n");
323
		return PASS;
324
	}
325
	deb_printf("FAIL: Linear map ends unexpectedly (expected ~890MB).\n");
326
	return FAIL;
327
}
328
 
329
int test_write_outside_linear_map(struct test_context *t)
330
{
331
	uint64_t tolerance, start_addr, max_addr, last_linear;
332
 
333
	if (sizeof(void *) == 8) {
334
		deb_printf("Skipped: 64-bit architecture\n");
335
		return SKIPPED;
336
	}
337
 
338
	if (!t->map || t->map->count == 0) {
339
		deb_printf("No memory map provided!\n");
340
		return SKIPPED;
341
	}
342
 
343
	start_addr = t->map->regions[0].start;
344
	max_addr = t->map->regions[t->map->count - 1].end;
345
 
346
	deb_printf("Scanning between 0x%llx and 0x%llx\n", (unsigned long long)start_addr,
347
		   (unsigned long long)max_addr);
348
 
349
	last_linear = find_last_linear_byte(t->fd, start_addr, max_addr);
350
 
351
	deb_printf("Last readable linear address: 0x%llx\n",
352
		   (unsigned long long)last_linear);
353
 
354
	tolerance = 16 * 1024 * 1024;
355
	if (last_linear + 1 >= EXPECTED_LINEAR_LIMIT - tolerance &&
356
	    last_linear + 1 <= EXPECTED_LINEAR_LIMIT + tolerance) {
357
		deb_printf("PASS: Linear map ends near 1 GiB boundary.\n");
358
		fill_random_chars(t->srcbuf, BOUNCE_BUF_SIZE);
359
		if (try_write_dev_mem(t->fd, last_linear + 0x1000,
360
		    BOUNCE_BUF_SIZE, t->srcbuf) < 0) {
361
			return FAIL;
362
		}
363
		return PASS;
364
	}
365
	deb_printf("FAIL: Linear map ends unexpectedly (expected ~890MB).\n");
366
	return FAIL;
367
}
368
 
369
int test_strict_devmem(struct test_context *t)
370
{
371
	int res = FAIL;
372
	uint64_t addr;
373
	ssize_t ret;
374
	uint8_t buf;
375
 
376
	addr = find_high_system_ram_addr(t->map);
377
	if (addr == 0) {
378
		deb_printf("No high System RAM region found.\n");
379
		res = SKIPPED;
380
		return res;
381
	}
382
 
383
	deb_printf("Testing physical address: 0x%llx\n", addr);
384
 
385
	ret = pread(t->fd, &buf, 1, addr);
386
	if (ret < 0) {
387
		if (errno == EPERM) {
388
			deb_printf("CONFIG_STRICT_DEVMEM is ENABLED\n");
389
		} else if (errno == EFAULT || errno == ENXIO) {
390
			deb_printf("Invalid address (errno=%d). Try another region.\n", errno);
391
			res = SKIPPED;
392
		} else if (errno == EACCES) {
393
			deb_printf("Access blocked by LSM or lockdown (errno=EACCES).\n");
394
			res = SKIPPED;
395
		} else {
396
			perror("pread");
397
		}
398
	} else {
399
		deb_printf("CONFIG_STRICT_DEVMEM is DISABLED\n");
400
		res = PASS;
401
	}
402
 
403
	if (res != PASS)
404
		t->strict_devmem_state = true;
405
 
406
	return res;
407
}
408
 
409
int test_devmem_access(struct test_context *t)
410
{
411
	struct open_res res;
412
 
413
	if (!check_and_set_tracefs_mount()) {
414
		deb_printf("Tracing directory not found. This test requires debugfs mounted.\n");
415
		return FAIL;
416
	}
417
 
418
	traced_open("/dev/mem", "memory_open", &res);
419
	if ((res.test_res) && (res.open_resv >= 0)) {
420
		deb_printf("test_res=%d, open_resv=%d\n",
421
		    res.test_res, res.open_resv);
422
		t->fd = res.open_resv;
423
		t->devmem_init_state = true;
424
		return PASS;
425
	}
426
	return FAIL;
427
}
428
 
429
int test_read_secret_area(struct test_context *t)
430
{
431
	void *tmp_ptr;
432
 
433
	deb_printf("\ntest_read_secret_area - start\n");
434
	tmp_ptr = secret_alloc(BOUNCE_BUF_SIZE);
435
 
436
	if (tmp_ptr) {
437
		deb_printf("secret_alloc [ok] tmp_ptr va addr = 0x%lx\n",
438
		    tmp_ptr);
439
		fill_random_chars(tmp_ptr, BOUNCE_BUF_SIZE); // lazy alloc
440
		if (t->verbose)
441
			print_hex(tmp_ptr, 32);
442
		t->tst_addr = virt_to_phys(tmp_ptr);
443
		if (t->tst_addr) {
444
			deb_printf("filled with things -> tst_addr phy addr = 0x%lx\n",
445
				   t->tst_addr);
446
			if (try_read_dev_mem(t->fd, t->tst_addr,
447
			    BOUNCE_BUF_SIZE, t->dstbuf) < 0)
448
				return PASS;
449
		}
450
	}
451
	return FAIL;
452
}
453
 
454
int test_read_restricted_area(struct test_context *t)
455
{
456
	fill_random_chars(t->dstbuf, BOUNCE_BUF_SIZE);
457
	if (t->verbose)
458
		print_hex(t->dstbuf, 32);
459
	t->tst_addr = pick_restricted_address(t->map);
460
	if (t->tst_addr) {
461
		if (try_read_dev_mem(t->fd, t->tst_addr, BOUNCE_BUF_SIZE,
462
		    t->dstbuf) >= 0) {
463
			if (t->verbose)
464
				print_hex(t->dstbuf, 32);
465
 
466
			if (is_zero(t->dstbuf, BOUNCE_BUF_SIZE))
467
				return PASS;
468
 
469
		}
470
	}
471
	return FAIL;
472
}
473
 
474
int test_read_allowed_area(struct test_context *t)
475
{
476
	fill_random_chars(t->srcbuf, BOUNCE_BUF_SIZE);
477
	t->tst_addr = virt_to_phys(t->srcbuf);
478
	if (t->tst_addr) {
479
		if (try_read_dev_mem(t->fd, t->tst_addr, BOUNCE_BUF_SIZE,
480
		    t->dstbuf) >= 0) {
481
			deb_printf("Read OK  compare twos\n", t->tst_addr);
482
			if (t->verbose) {
483
				print_hex(t->srcbuf, BOUNCE_BUF_SIZE);
484
				print_hex(t->dstbuf, BOUNCE_BUF_SIZE);
485
			}
486
			if (!memcmp(t->srcbuf, t->dstbuf, BOUNCE_BUF_SIZE))
487
				return PASS;
488
		}
489
	}
490
	return FAIL;
491
}
492
 
493
int test_read_allowed_area_ppos_advance(struct test_context *t)
494
{
495
	fill_random_chars(t->srcbuf, BOUNCE_BUF_SIZE);
496
	memset(t->dstbuf, 0, BOUNCE_BUF_SIZE);
497
	if (t->verbose)
498
		print_hex(t->srcbuf, 32);
499
	t->tst_addr = virt_to_phys(t->srcbuf);
500
	if (t->tst_addr) {
501
		if ((try_read_dev_mem(t->fd, t->tst_addr,
502
		    BOUNCE_BUF_SIZE / 2, t->dstbuf) >= 0) &&
503
			(try_read_inplace(t->fd, BOUNCE_BUF_SIZE / 2,
504
			    t->dstbuf) >= 0)){
505
			if (t->verbose)
506
				print_hex(t->dstbuf, 32);
507
 
508
			if (!memcmp(t->srcbuf + BOUNCE_BUF_SIZE / 2,
509
			    t->dstbuf, BOUNCE_BUF_SIZE / 2)) {
510
				return PASS;
511
			}
512
		}
513
	}
514
	return FAIL;
515
}
516
 
517
int test_write_outside_area(struct test_context *t)
518
{
519
	fill_random_chars(t->srcbuf, BOUNCE_BUF_SIZE);
520
	t->tst_addr = pick_outside_address(t->map);
521
	if (try_write_dev_mem(t->fd, t->tst_addr, BOUNCE_BUF_SIZE,
522
	    t->srcbuf) < 0)
523
		return PASS;
524
 
525
	return FAIL;
526
}
527
 
528
/*
529
 * this test needs to follow test_seek_seek_set
530
 */
531
int test_seek_seek_cur(struct test_context *t)
532
{
533
	t->tst_addr = pick_valid_ram_address(t->map);
534
	if (lseek(t->fd, 0, SEEK_SET) == (off_t)-1)
535
		return FAIL;
536
 
537
	if (lseek(t->fd, t->tst_addr, SEEK_CUR) == (off_t)-1)
538
		return FAIL;
539
 
540
	return PASS;
541
}
542
 
543
int test_seek_seek_set(struct test_context *t)
544
{
545
	t->tst_addr = pick_valid_ram_address(t->map);
546
	if (lseek(t->fd, t->tst_addr, SEEK_SET) == (off_t)-1)
547
		return FAIL;
548
 
549
	return PASS;
550
}
551
 
552
int test_seek_seek_other(struct test_context *t)
553
{
554
	if (lseek(t->fd, 0, SEEK_END) == (off_t)-1)
555
		return PASS;
556
 
557
	return FAIL;
558
}
559
 
560
int test_open_devnum(struct test_context *t)
561
{
562
	unsigned int major_num, minor_num;
563
 
564
	if (get_device_numbers(t->fd, &major_num, &minor_num) == 0) {
565
		if ((major_num == 1) && (minor_num == 1))
566
			return PASS;
567
	}
568
	return FAIL;
569
}