1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2019 SUSE LLC
4f08c3bdfSopenharmony_ci * Author: Christian Amann <camann@suse.com>
5f08c3bdfSopenharmony_ci *
6f08c3bdfSopenharmony_ci * Test userfaultfd
7f08c3bdfSopenharmony_ci *
8f08c3bdfSopenharmony_ci * Force a pagefault event and handle it using userfaultfd
9f08c3bdfSopenharmony_ci * from a different thread
10f08c3bdfSopenharmony_ci */
11f08c3bdfSopenharmony_ci
12f08c3bdfSopenharmony_ci#include "config.h"
13f08c3bdfSopenharmony_ci#include "tst_test.h"
14f08c3bdfSopenharmony_ci
15f08c3bdfSopenharmony_ci#include <poll.h>
16f08c3bdfSopenharmony_ci
17f08c3bdfSopenharmony_ci#include "tst_safe_macros.h"
18f08c3bdfSopenharmony_ci#include "tst_safe_pthread.h"
19f08c3bdfSopenharmony_ci#include "lapi/userfaultfd.h"
20f08c3bdfSopenharmony_ci
21f08c3bdfSopenharmony_cistatic int page_size;
22f08c3bdfSopenharmony_cistatic char *page;
23f08c3bdfSopenharmony_cistatic void *copy_page;
24f08c3bdfSopenharmony_cistatic int uffd;
25f08c3bdfSopenharmony_ci
26f08c3bdfSopenharmony_cistatic int sys_userfaultfd(int flags)
27f08c3bdfSopenharmony_ci{
28f08c3bdfSopenharmony_ci	return tst_syscall(__NR_userfaultfd, flags);
29f08c3bdfSopenharmony_ci}
30f08c3bdfSopenharmony_ci
31f08c3bdfSopenharmony_cistatic void set_pages(void)
32f08c3bdfSopenharmony_ci{
33f08c3bdfSopenharmony_ci	page_size = sysconf(_SC_PAGE_SIZE);
34f08c3bdfSopenharmony_ci	page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
35f08c3bdfSopenharmony_ci			MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
36f08c3bdfSopenharmony_ci	copy_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
37f08c3bdfSopenharmony_ci			MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
38f08c3bdfSopenharmony_ci}
39f08c3bdfSopenharmony_ci
40f08c3bdfSopenharmony_cistatic void handle_thread(void)
41f08c3bdfSopenharmony_ci{
42f08c3bdfSopenharmony_ci	static struct uffd_msg msg;
43f08c3bdfSopenharmony_ci	struct uffdio_copy uffdio_copy;
44f08c3bdfSopenharmony_ci
45f08c3bdfSopenharmony_ci	struct pollfd pollfd;
46f08c3bdfSopenharmony_ci	int nready;
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci	pollfd.fd = uffd;
49f08c3bdfSopenharmony_ci	pollfd.events = POLLIN;
50f08c3bdfSopenharmony_ci	nready = poll(&pollfd, 1, -1);
51f08c3bdfSopenharmony_ci	if (nready == -1)
52f08c3bdfSopenharmony_ci		tst_brk(TBROK | TERRNO,
53f08c3bdfSopenharmony_ci				"Error on poll");
54f08c3bdfSopenharmony_ci
55f08c3bdfSopenharmony_ci	SAFE_READ(1, uffd, &msg, sizeof(msg));
56f08c3bdfSopenharmony_ci
57f08c3bdfSopenharmony_ci	if (msg.event != UFFD_EVENT_PAGEFAULT)
58f08c3bdfSopenharmony_ci		tst_brk(TBROK | TERRNO,
59f08c3bdfSopenharmony_ci				"Received unexpected UFFD_EVENT");
60f08c3bdfSopenharmony_ci
61f08c3bdfSopenharmony_ci	memset(copy_page, 'X', page_size);
62f08c3bdfSopenharmony_ci
63f08c3bdfSopenharmony_ci	uffdio_copy.src = (unsigned long) copy_page;
64f08c3bdfSopenharmony_ci
65f08c3bdfSopenharmony_ci	uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address
66f08c3bdfSopenharmony_ci			& ~(page_size - 1);
67f08c3bdfSopenharmony_ci	uffdio_copy.len = page_size;
68f08c3bdfSopenharmony_ci	uffdio_copy.mode = 0;
69f08c3bdfSopenharmony_ci	uffdio_copy.copy = 0;
70f08c3bdfSopenharmony_ci	SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
71f08c3bdfSopenharmony_ci
72f08c3bdfSopenharmony_ci	close(uffd);
73f08c3bdfSopenharmony_ci}
74f08c3bdfSopenharmony_ci
75f08c3bdfSopenharmony_cistatic void run(void)
76f08c3bdfSopenharmony_ci{
77f08c3bdfSopenharmony_ci	pthread_t thr;
78f08c3bdfSopenharmony_ci	struct uffdio_api uffdio_api;
79f08c3bdfSopenharmony_ci	struct uffdio_register uffdio_register;
80f08c3bdfSopenharmony_ci
81f08c3bdfSopenharmony_ci	set_pages();
82f08c3bdfSopenharmony_ci
83f08c3bdfSopenharmony_ci	TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK));
84f08c3bdfSopenharmony_ci
85f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
86f08c3bdfSopenharmony_ci		if (TST_ERR == EPERM) {
87f08c3bdfSopenharmony_ci			tst_res(TCONF, "Hint: check /proc/sys/vm/unprivileged_userfaultfd");
88f08c3bdfSopenharmony_ci			tst_brk(TCONF | TTERRNO,
89f08c3bdfSopenharmony_ci				"userfaultfd() requires CAP_SYS_PTRACE on this system");
90f08c3bdfSopenharmony_ci		} else
91f08c3bdfSopenharmony_ci			tst_brk(TBROK | TTERRNO,
92f08c3bdfSopenharmony_ci				"Could not create userfault file descriptor");
93f08c3bdfSopenharmony_ci	}
94f08c3bdfSopenharmony_ci
95f08c3bdfSopenharmony_ci	uffd = TST_RET;
96f08c3bdfSopenharmony_ci	uffdio_api.api = UFFD_API;
97f08c3bdfSopenharmony_ci	uffdio_api.features = 0;
98f08c3bdfSopenharmony_ci	SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api);
99f08c3bdfSopenharmony_ci
100f08c3bdfSopenharmony_ci	uffdio_register.range.start = (unsigned long) page;
101f08c3bdfSopenharmony_ci	uffdio_register.range.len = page_size;
102f08c3bdfSopenharmony_ci	uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
103f08c3bdfSopenharmony_ci
104f08c3bdfSopenharmony_ci	SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
105f08c3bdfSopenharmony_ci
106f08c3bdfSopenharmony_ci	SAFE_PTHREAD_CREATE(&thr, NULL,
107f08c3bdfSopenharmony_ci			(void * (*)(void *)) handle_thread, NULL);
108f08c3bdfSopenharmony_ci
109f08c3bdfSopenharmony_ci	char c = page[0xf];
110f08c3bdfSopenharmony_ci
111f08c3bdfSopenharmony_ci	if (c == 'X')
112f08c3bdfSopenharmony_ci		tst_res(TPASS, "Pagefault handled!");
113f08c3bdfSopenharmony_ci	else
114f08c3bdfSopenharmony_ci		tst_res(TFAIL, "Pagefault not handled!");
115f08c3bdfSopenharmony_ci
116f08c3bdfSopenharmony_ci	SAFE_PTHREAD_JOIN(thr, NULL);
117f08c3bdfSopenharmony_ci}
118f08c3bdfSopenharmony_ci
119f08c3bdfSopenharmony_cistatic struct tst_test test = {
120f08c3bdfSopenharmony_ci	.test_all = run,
121f08c3bdfSopenharmony_ci	.min_kver = "4.3",
122f08c3bdfSopenharmony_ci};
123