1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: LGPL-2.1-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (C) 2005-2006 IBM Corporation.
4f08c3bdfSopenharmony_ci * Author: David Gibson & Adam Litke
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci/*\
8f08c3bdfSopenharmony_ci * [Description]
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci * This test uses mprotect to change protection of hugepage mapping and
11f08c3bdfSopenharmony_ci * perform read/write operation. It checks if the operation results in
12f08c3bdfSopenharmony_ci * expected behaviour as per the protection.
13f08c3bdfSopenharmony_ci */
14f08c3bdfSopenharmony_ci#include <setjmp.h>
15f08c3bdfSopenharmony_ci#include "hugetlb.h"
16f08c3bdfSopenharmony_ci
17f08c3bdfSopenharmony_ci#define MNTPOINT "hugetlbfs/"
18f08c3bdfSopenharmony_ci#define RANDOM_CONSTANT 0x1234ABCD
19f08c3bdfSopenharmony_ci
20f08c3bdfSopenharmony_cistatic int fd = -1;
21f08c3bdfSopenharmony_cistatic sigjmp_buf sig_escape;
22f08c3bdfSopenharmony_cistatic void *sig_expected = MAP_FAILED;
23f08c3bdfSopenharmony_cistatic long hpage_size;
24f08c3bdfSopenharmony_cistatic void *addr;
25f08c3bdfSopenharmony_ci
26f08c3bdfSopenharmony_cistatic struct tcase {
27f08c3bdfSopenharmony_ci	char *tname;
28f08c3bdfSopenharmony_ci	unsigned long len1;
29f08c3bdfSopenharmony_ci	int prot1;
30f08c3bdfSopenharmony_ci	char *prot1_str;
31f08c3bdfSopenharmony_ci	unsigned long len2;
32f08c3bdfSopenharmony_ci	int prot2;
33f08c3bdfSopenharmony_ci	char *prot2_str;
34f08c3bdfSopenharmony_ci} tcases[] = {
35f08c3bdfSopenharmony_ci	{"R->RW", 1, PROT_READ, "PROT_READ",
36f08c3bdfSopenharmony_ci		1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"},
37f08c3bdfSopenharmony_ci
38f08c3bdfSopenharmony_ci	{"RW->R", 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE",
39f08c3bdfSopenharmony_ci		1, PROT_READ, "PROT_READ"},
40f08c3bdfSopenharmony_ci
41f08c3bdfSopenharmony_ci	{"R->RW 1/2", 2, PROT_READ, "PROT_READ",
42f08c3bdfSopenharmony_ci		1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"},
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_ci	{"RW->R 1/2", 2, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE",
45f08c3bdfSopenharmony_ci		1, PROT_READ, "PROT_READ"},
46f08c3bdfSopenharmony_ci
47f08c3bdfSopenharmony_ci	{"NONE->R", 1, PROT_NONE, "PROT_NONE",
48f08c3bdfSopenharmony_ci		1, PROT_READ, "PROT_READ"},
49f08c3bdfSopenharmony_ci
50f08c3bdfSopenharmony_ci	{"NONE->RW", 1, PROT_NONE, "PROT_NONE",
51f08c3bdfSopenharmony_ci		1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"},
52f08c3bdfSopenharmony_ci};
53f08c3bdfSopenharmony_ci
54f08c3bdfSopenharmony_cistatic void sig_handler(int signum, siginfo_t *si, void *uc)
55f08c3bdfSopenharmony_ci{
56f08c3bdfSopenharmony_ci	(void)uc;
57f08c3bdfSopenharmony_ci
58f08c3bdfSopenharmony_ci	if (signum == SIGSEGV) {
59f08c3bdfSopenharmony_ci		tst_res(TINFO, "SIGSEGV at %p (sig_expected=%p)", si->si_addr,
60f08c3bdfSopenharmony_ci		       sig_expected);
61f08c3bdfSopenharmony_ci		if (si->si_addr == sig_expected)
62f08c3bdfSopenharmony_ci			siglongjmp(sig_escape, 1);
63f08c3bdfSopenharmony_ci		tst_res(TFAIL, "SIGSEGV somewhere unexpected");
64f08c3bdfSopenharmony_ci	} else {
65f08c3bdfSopenharmony_ci		tst_res(TFAIL, "Unexpected signal %s", strsignal(signum));
66f08c3bdfSopenharmony_ci	}
67f08c3bdfSopenharmony_ci}
68f08c3bdfSopenharmony_ci
69f08c3bdfSopenharmony_cistatic int test_read(void *p)
70f08c3bdfSopenharmony_ci{
71f08c3bdfSopenharmony_ci	volatile unsigned long *pl = p;
72f08c3bdfSopenharmony_ci	unsigned long x;
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_ci	if (sigsetjmp(sig_escape, 1)) {
75f08c3bdfSopenharmony_ci		/* We got a SEGV */
76f08c3bdfSopenharmony_ci		sig_expected = MAP_FAILED;
77f08c3bdfSopenharmony_ci		return -1;
78f08c3bdfSopenharmony_ci	}
79f08c3bdfSopenharmony_ci
80f08c3bdfSopenharmony_ci	sig_expected = p;
81f08c3bdfSopenharmony_ci	barrier();
82f08c3bdfSopenharmony_ci	x = *pl;
83f08c3bdfSopenharmony_ci	tst_res(TINFO, "Read back %lu", x);
84f08c3bdfSopenharmony_ci	barrier();
85f08c3bdfSopenharmony_ci	sig_expected = MAP_FAILED;
86f08c3bdfSopenharmony_ci	return 0;
87f08c3bdfSopenharmony_ci}
88f08c3bdfSopenharmony_ci
89f08c3bdfSopenharmony_cistatic int test_write(void *p, unsigned long val)
90f08c3bdfSopenharmony_ci{
91f08c3bdfSopenharmony_ci	volatile unsigned long *pl = p;
92f08c3bdfSopenharmony_ci	unsigned long x;
93f08c3bdfSopenharmony_ci
94f08c3bdfSopenharmony_ci	if (sigsetjmp(sig_escape, 1)) {
95f08c3bdfSopenharmony_ci		/* We got a SEGV */
96f08c3bdfSopenharmony_ci		sig_expected = MAP_FAILED;
97f08c3bdfSopenharmony_ci		return -1;
98f08c3bdfSopenharmony_ci	}
99f08c3bdfSopenharmony_ci
100f08c3bdfSopenharmony_ci	sig_expected = p;
101f08c3bdfSopenharmony_ci	barrier();
102f08c3bdfSopenharmony_ci	*pl = val;
103f08c3bdfSopenharmony_ci	x = *pl;
104f08c3bdfSopenharmony_ci	barrier();
105f08c3bdfSopenharmony_ci	sig_expected = MAP_FAILED;
106f08c3bdfSopenharmony_ci
107f08c3bdfSopenharmony_ci	return (x != val);
108f08c3bdfSopenharmony_ci}
109f08c3bdfSopenharmony_ci
110f08c3bdfSopenharmony_cistatic int test_prot(void *p, int prot, char *prot_str)
111f08c3bdfSopenharmony_ci{
112f08c3bdfSopenharmony_ci	int r, w;
113f08c3bdfSopenharmony_ci
114f08c3bdfSopenharmony_ci	r = test_read(p);
115f08c3bdfSopenharmony_ci	tst_res(TINFO, "On Read: %d", r);
116f08c3bdfSopenharmony_ci	w = test_write(p, RANDOM_CONSTANT);
117f08c3bdfSopenharmony_ci	tst_res(TINFO, "On Write: %d", w);
118f08c3bdfSopenharmony_ci
119f08c3bdfSopenharmony_ci	if (prot & PROT_READ) {
120f08c3bdfSopenharmony_ci		if (r != 0) {
121f08c3bdfSopenharmony_ci			tst_res(TFAIL, "read failed on mmap(prot %s)", prot_str);
122f08c3bdfSopenharmony_ci			return -1;
123f08c3bdfSopenharmony_ci		}
124f08c3bdfSopenharmony_ci	} else {
125f08c3bdfSopenharmony_ci		if (r != -1) {
126f08c3bdfSopenharmony_ci			tst_res(TFAIL, "read succeeded on mmap(prot %s)", prot_str);
127f08c3bdfSopenharmony_ci			return -1;
128f08c3bdfSopenharmony_ci		}
129f08c3bdfSopenharmony_ci	}
130f08c3bdfSopenharmony_ci
131f08c3bdfSopenharmony_ci	if (prot & PROT_WRITE) {
132f08c3bdfSopenharmony_ci		switch (w) {
133f08c3bdfSopenharmony_ci		case -1:
134f08c3bdfSopenharmony_ci			tst_res(TFAIL, "write failed on mmap(prot %s)", prot_str);
135f08c3bdfSopenharmony_ci			return -1;
136f08c3bdfSopenharmony_ci		case 0:
137f08c3bdfSopenharmony_ci			break;
138f08c3bdfSopenharmony_ci		case 1:
139f08c3bdfSopenharmony_ci			tst_res(TFAIL, "write mismatch on mmap(prot %s)", prot_str);
140f08c3bdfSopenharmony_ci			return -1;
141f08c3bdfSopenharmony_ci		default:
142f08c3bdfSopenharmony_ci			tst_res(TWARN, "Bug in test");
143f08c3bdfSopenharmony_ci			return -1;
144f08c3bdfSopenharmony_ci		}
145f08c3bdfSopenharmony_ci	} else {
146f08c3bdfSopenharmony_ci		switch (w) {
147f08c3bdfSopenharmony_ci		case -1:
148f08c3bdfSopenharmony_ci			break;
149f08c3bdfSopenharmony_ci		case 0:
150f08c3bdfSopenharmony_ci			tst_res(TFAIL, "write succeeded on mmap(prot %s)", prot_str);
151f08c3bdfSopenharmony_ci			return -1;
152f08c3bdfSopenharmony_ci		case 1:
153f08c3bdfSopenharmony_ci			tst_res(TFAIL, "write mismatch on mmap(prot %s)", prot_str);
154f08c3bdfSopenharmony_ci			return -1;
155f08c3bdfSopenharmony_ci		default:
156f08c3bdfSopenharmony_ci			tst_res(TWARN, "Bug in test");
157f08c3bdfSopenharmony_ci			break;
158f08c3bdfSopenharmony_ci		}
159f08c3bdfSopenharmony_ci	}
160f08c3bdfSopenharmony_ci
161f08c3bdfSopenharmony_ci	return 0;
162f08c3bdfSopenharmony_ci}
163f08c3bdfSopenharmony_ci
164f08c3bdfSopenharmony_cistatic void run_test(unsigned int i)
165f08c3bdfSopenharmony_ci{
166f08c3bdfSopenharmony_ci	void *p;
167f08c3bdfSopenharmony_ci	int ret;
168f08c3bdfSopenharmony_ci	struct tcase *tc = &tcases[i];
169f08c3bdfSopenharmony_ci
170f08c3bdfSopenharmony_ci	tst_res(TINFO, "Test Name: %s", tc->tname);
171f08c3bdfSopenharmony_ci
172f08c3bdfSopenharmony_ci	p = SAFE_MMAP(NULL, tc->len1*hpage_size, tc->prot1, MAP_SHARED, fd, 0);
173f08c3bdfSopenharmony_ci
174f08c3bdfSopenharmony_ci	ret = test_prot(p, tc->prot1, tc->prot1_str);
175f08c3bdfSopenharmony_ci	if (ret)
176f08c3bdfSopenharmony_ci		goto cleanup;
177f08c3bdfSopenharmony_ci
178f08c3bdfSopenharmony_ci	ret = mprotect(p, tc->len2*hpage_size, tc->prot2);
179f08c3bdfSopenharmony_ci	if (ret != 0) {
180f08c3bdfSopenharmony_ci		tst_res(TFAIL|TERRNO, "%s: mprotect(prot %s)",
181f08c3bdfSopenharmony_ci				tc->tname, tc->prot2_str);
182f08c3bdfSopenharmony_ci		goto cleanup;
183f08c3bdfSopenharmony_ci	}
184f08c3bdfSopenharmony_ci
185f08c3bdfSopenharmony_ci	ret = test_prot(p, tc->prot2, tc->prot2_str);
186f08c3bdfSopenharmony_ci	if (ret)
187f08c3bdfSopenharmony_ci		goto cleanup;
188f08c3bdfSopenharmony_ci
189f08c3bdfSopenharmony_ci	if (tc->len2 < tc->len1)
190f08c3bdfSopenharmony_ci		ret = test_prot(p + tc->len2*hpage_size, tc->prot1, tc->prot1_str);
191f08c3bdfSopenharmony_ci
192f08c3bdfSopenharmony_ci	tst_res(TPASS, "Successfully tested mprotect %s", tc->tname);
193f08c3bdfSopenharmony_ci
194f08c3bdfSopenharmony_cicleanup:
195f08c3bdfSopenharmony_ci	SAFE_MUNMAP(p, tc->len1*hpage_size);
196f08c3bdfSopenharmony_ci}
197f08c3bdfSopenharmony_ci
198f08c3bdfSopenharmony_cistatic void setup(void)
199f08c3bdfSopenharmony_ci{
200f08c3bdfSopenharmony_ci	struct sigaction sa = {
201f08c3bdfSopenharmony_ci		.sa_sigaction = sig_handler,
202f08c3bdfSopenharmony_ci		.sa_flags = SA_SIGINFO,
203f08c3bdfSopenharmony_ci	};
204f08c3bdfSopenharmony_ci
205f08c3bdfSopenharmony_ci	hpage_size = tst_get_hugepage_size();
206f08c3bdfSopenharmony_ci	SAFE_SIGACTION(SIGSEGV, &sa, NULL);
207f08c3bdfSopenharmony_ci
208f08c3bdfSopenharmony_ci	fd = tst_creat_unlinked(MNTPOINT, 0);
209f08c3bdfSopenharmony_ci	addr = SAFE_MMAP(NULL, 2*hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
210f08c3bdfSopenharmony_ci	memset(addr, 0, hpage_size);
211f08c3bdfSopenharmony_ci	SAFE_MUNMAP(addr, hpage_size);
212f08c3bdfSopenharmony_ci}
213f08c3bdfSopenharmony_ci
214f08c3bdfSopenharmony_cistatic void cleanup(void)
215f08c3bdfSopenharmony_ci{
216f08c3bdfSopenharmony_ci	SAFE_MUNMAP(addr+hpage_size, hpage_size);
217f08c3bdfSopenharmony_ci	if (fd >= 0)
218f08c3bdfSopenharmony_ci		SAFE_CLOSE(fd);
219f08c3bdfSopenharmony_ci}
220f08c3bdfSopenharmony_ci
221f08c3bdfSopenharmony_cistatic struct tst_test test = {
222f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(tcases),
223f08c3bdfSopenharmony_ci	.needs_root = 1,
224f08c3bdfSopenharmony_ci	.mntpoint = MNTPOINT,
225f08c3bdfSopenharmony_ci	.needs_hugetlbfs = 1,
226f08c3bdfSopenharmony_ci	.needs_tmpdir = 1,
227f08c3bdfSopenharmony_ci	.setup = setup,
228f08c3bdfSopenharmony_ci	.cleanup = cleanup,
229f08c3bdfSopenharmony_ci	.test = run_test,
230f08c3bdfSopenharmony_ci	.hugepages = {2, TST_NEEDS},
231f08c3bdfSopenharmony_ci};
232