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.c3
*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 649
#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 25631
#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
- "1.1.2.9. read_mem FE_3.3, FE_3.3.1, FE_3.2.2" (TEST, Test)
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_set530
*/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
}