1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: LGPL-2.1-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (C) 2009 IBM Corporation.
4f08c3bdfSopenharmony_ci * Author: David Gibson
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci/*\
8f08c3bdfSopenharmony_ci * [Description]
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci * Kernel has bug in mremap for some architecture. mremap() can cause
11f08c3bdfSopenharmony_ci * crashes on architectures with holes in the address space (like ia64)
12f08c3bdfSopenharmony_ci * and on powerpc with it's distict page size slices.
13f08c3bdfSopenharmony_ci *
14f08c3bdfSopenharmony_ci * This test perform mremap() with normal and hugepages around powerpc
15f08c3bdfSopenharmony_ci * slice boundary.
16f08c3bdfSopenharmony_ci */
17f08c3bdfSopenharmony_ci
18f08c3bdfSopenharmony_ci#define _GNU_SOURCE
19f08c3bdfSopenharmony_ci#include "hugetlb.h"
20f08c3bdfSopenharmony_ci
21f08c3bdfSopenharmony_ci#define RANDOM_CONSTANT 0x1234ABCD
22f08c3bdfSopenharmony_ci#define MNTPOINT "hugetlbfs/"
23f08c3bdfSopenharmony_ci
24f08c3bdfSopenharmony_cistatic int  fd = -1;
25f08c3bdfSopenharmony_cistatic unsigned long slice_boundary;
26f08c3bdfSopenharmony_cistatic unsigned long hpage_size, page_size;
27f08c3bdfSopenharmony_ci
28f08c3bdfSopenharmony_cistatic int init_slice_boundary(int fd)
29f08c3bdfSopenharmony_ci{
30f08c3bdfSopenharmony_ci	unsigned long slice_size;
31f08c3bdfSopenharmony_ci	void *p, *heap;
32f08c3bdfSopenharmony_ci	int i;
33f08c3bdfSopenharmony_ci#if defined(__LP64__) && !defined(__aarch64__)
34f08c3bdfSopenharmony_ci	/* powerpc: 1TB slices starting at 1 TB */
35f08c3bdfSopenharmony_ci	slice_boundary = 0x10000000000;
36f08c3bdfSopenharmony_ci	slice_size = 0x10000000000;
37f08c3bdfSopenharmony_ci#else
38f08c3bdfSopenharmony_ci	/* powerpc: 256MB slices up to 4GB */
39f08c3bdfSopenharmony_ci	slice_boundary = 0x00000000;
40f08c3bdfSopenharmony_ci	slice_size = 0x10000000;
41f08c3bdfSopenharmony_ci#endif
42f08c3bdfSopenharmony_ci
43f08c3bdfSopenharmony_ci	/* dummy malloc so we know where is heap */
44f08c3bdfSopenharmony_ci	heap = malloc(1);
45f08c3bdfSopenharmony_ci	free(heap);
46f08c3bdfSopenharmony_ci
47f08c3bdfSopenharmony_ci	 /* Avoid underflow on systems with large huge pages.
48f08c3bdfSopenharmony_ci	  * The additionally plus heap address is to reduce the possibility
49f08c3bdfSopenharmony_ci	  * of MAP_FIXED stomp over existing mappings.
50f08c3bdfSopenharmony_ci	  */
51f08c3bdfSopenharmony_ci	while (slice_boundary + slice_size < (unsigned long)heap + 2*hpage_size)
52f08c3bdfSopenharmony_ci		slice_boundary += slice_size;
53f08c3bdfSopenharmony_ci
54f08c3bdfSopenharmony_ci	/* Find 2 neighbour slices with couple huge pages free
55f08c3bdfSopenharmony_ci	 * around slice boundary.
56f08c3bdfSopenharmony_ci	 * 16 is the maximum number of slices (low/high)
57f08c3bdfSopenharmony_ci	 */
58f08c3bdfSopenharmony_ci	for (i = 0; i < 16-1; i++) {
59f08c3bdfSopenharmony_ci		slice_boundary += slice_size;
60f08c3bdfSopenharmony_ci		p = mmap((void *)(slice_boundary-2*hpage_size), 4*hpage_size,
61f08c3bdfSopenharmony_ci			PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0);
62f08c3bdfSopenharmony_ci		if (p == MAP_FAILED) {
63f08c3bdfSopenharmony_ci			tst_res(TINFO|TERRNO, "can't use slice_boundary: 0x%lx",
64f08c3bdfSopenharmony_ci					slice_boundary);
65f08c3bdfSopenharmony_ci		} else {
66f08c3bdfSopenharmony_ci			SAFE_MUNMAP(p, 4*hpage_size);
67f08c3bdfSopenharmony_ci			break;
68f08c3bdfSopenharmony_ci		}
69f08c3bdfSopenharmony_ci	}
70f08c3bdfSopenharmony_ci
71f08c3bdfSopenharmony_ci	if (p == MAP_FAILED) {
72f08c3bdfSopenharmony_ci		tst_res(TFAIL|TERRNO, "couldn't find 2 free neighbour slices");
73f08c3bdfSopenharmony_ci		return -1;
74f08c3bdfSopenharmony_ci	}
75f08c3bdfSopenharmony_ci
76f08c3bdfSopenharmony_ci	tst_res(TINFO, "using slice_boundary: 0x%lx", slice_boundary);
77f08c3bdfSopenharmony_ci
78f08c3bdfSopenharmony_ci	return 0;
79f08c3bdfSopenharmony_ci}
80f08c3bdfSopenharmony_ci
81f08c3bdfSopenharmony_cistatic void run_test(void)
82f08c3bdfSopenharmony_ci{
83f08c3bdfSopenharmony_ci	void *p = NULL, *q = NULL, *r;
84f08c3bdfSopenharmony_ci	long p_size, q_size;
85f08c3bdfSopenharmony_ci	int ret;
86f08c3bdfSopenharmony_ci
87f08c3bdfSopenharmony_ci	fd = tst_creat_unlinked(MNTPOINT, 0);
88f08c3bdfSopenharmony_ci	ret = init_slice_boundary(fd);
89f08c3bdfSopenharmony_ci	if (ret)
90f08c3bdfSopenharmony_ci		goto cleanup;
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_ci	/* First, hugepages above, normal below */
93f08c3bdfSopenharmony_ci	tst_res(TINFO, "Testing with hpage above & normal below the slice_boundary");
94f08c3bdfSopenharmony_ci	p_size = hpage_size;
95f08c3bdfSopenharmony_ci	p = SAFE_MMAP((void *)(slice_boundary + hpage_size), p_size,
96f08c3bdfSopenharmony_ci		 PROT_READ | PROT_WRITE,
97f08c3bdfSopenharmony_ci		 MAP_SHARED | MAP_FIXED, fd, 0);
98f08c3bdfSopenharmony_ci
99f08c3bdfSopenharmony_ci	ret = do_readback(p, p_size, "huge above");
100f08c3bdfSopenharmony_ci	if (ret)
101f08c3bdfSopenharmony_ci		goto cleanup;
102f08c3bdfSopenharmony_ci
103f08c3bdfSopenharmony_ci	q_size = page_size;
104f08c3bdfSopenharmony_ci	q = SAFE_MMAP((void *)(slice_boundary - page_size), q_size,
105f08c3bdfSopenharmony_ci		 PROT_READ | PROT_WRITE,
106f08c3bdfSopenharmony_ci		 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
107f08c3bdfSopenharmony_ci
108f08c3bdfSopenharmony_ci	ret = do_readback(q, q_size, "normal below");
109f08c3bdfSopenharmony_ci	if (ret)
110f08c3bdfSopenharmony_ci		goto cleanup;
111f08c3bdfSopenharmony_ci
112f08c3bdfSopenharmony_ci	r = mremap(q, page_size, 2*page_size, 0);
113f08c3bdfSopenharmony_ci	if (r == MAP_FAILED) {
114f08c3bdfSopenharmony_ci		tst_res(TINFO, "mremap(%p, %lu, %lu, 0) disallowed",
115f08c3bdfSopenharmony_ci				q, page_size, 2*page_size);
116f08c3bdfSopenharmony_ci	} else {
117f08c3bdfSopenharmony_ci		q_size = 2*page_size;
118f08c3bdfSopenharmony_ci		if (r != q) {
119f08c3bdfSopenharmony_ci			tst_res(TFAIL, "mremap() moved without MREMAP_MAYMOVE!?");
120f08c3bdfSopenharmony_ci			ret = -1;
121f08c3bdfSopenharmony_ci		} else
122f08c3bdfSopenharmony_ci			ret = do_readback(q, 2*page_size, "normal below expanded");
123f08c3bdfSopenharmony_ci	}
124f08c3bdfSopenharmony_ci
125f08c3bdfSopenharmony_ci	SAFE_MUNMAP(p, p_size);
126f08c3bdfSopenharmony_ci	SAFE_MUNMAP(q, q_size);
127f08c3bdfSopenharmony_ci	if (ret)
128f08c3bdfSopenharmony_ci		goto cleanup_fd;
129f08c3bdfSopenharmony_ci
130f08c3bdfSopenharmony_ci	/* Next, normal pages above, huge below */
131f08c3bdfSopenharmony_ci	tst_res(TINFO, "Testing with normal above & hpage below the slice_boundary");
132f08c3bdfSopenharmony_ci	p_size = page_size;
133f08c3bdfSopenharmony_ci	p = SAFE_MMAP((void *)(slice_boundary + hpage_size), p_size,
134f08c3bdfSopenharmony_ci		 PROT_READ|PROT_WRITE,
135f08c3bdfSopenharmony_ci		 MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
136f08c3bdfSopenharmony_ci
137f08c3bdfSopenharmony_ci	ret = do_readback(p, p_size, "normal above");
138f08c3bdfSopenharmony_ci	if (ret)
139f08c3bdfSopenharmony_ci		goto cleanup;
140f08c3bdfSopenharmony_ci
141f08c3bdfSopenharmony_ci	q_size = hpage_size;
142f08c3bdfSopenharmony_ci	q = SAFE_MMAP((void *)(slice_boundary - hpage_size),
143f08c3bdfSopenharmony_ci		 q_size, PROT_READ | PROT_WRITE,
144f08c3bdfSopenharmony_ci		 MAP_SHARED | MAP_FIXED, fd, 0);
145f08c3bdfSopenharmony_ci
146f08c3bdfSopenharmony_ci	ret = do_readback(q, q_size, "huge below");
147f08c3bdfSopenharmony_ci	if (ret)
148f08c3bdfSopenharmony_ci		goto cleanup;
149f08c3bdfSopenharmony_ci
150f08c3bdfSopenharmony_ci	r = mremap(q, hpage_size, 2*hpage_size, 0);
151f08c3bdfSopenharmony_ci	if (r == MAP_FAILED) {
152f08c3bdfSopenharmony_ci		tst_res(TINFO, "mremap(%p, %lu, %lu, 0) disallowed",
153f08c3bdfSopenharmony_ci				q, hpage_size, 2*hpage_size);
154f08c3bdfSopenharmony_ci	} else {
155f08c3bdfSopenharmony_ci		q_size = 2*hpage_size;
156f08c3bdfSopenharmony_ci		if (r != q) {
157f08c3bdfSopenharmony_ci			tst_res(TFAIL, "mremap() moved without MREMAP_MAYMOVE!?");
158f08c3bdfSopenharmony_ci			ret = -1;
159f08c3bdfSopenharmony_ci		} else
160f08c3bdfSopenharmony_ci			ret = do_readback(q, 2*hpage_size, "huge below expanded");
161f08c3bdfSopenharmony_ci	}
162f08c3bdfSopenharmony_ci	if (ret)
163f08c3bdfSopenharmony_ci		goto cleanup;
164f08c3bdfSopenharmony_ci
165f08c3bdfSopenharmony_ci	tst_res(TPASS, "Successful");
166f08c3bdfSopenharmony_ci
167f08c3bdfSopenharmony_cicleanup:
168f08c3bdfSopenharmony_ci	if (p)
169f08c3bdfSopenharmony_ci		SAFE_MUNMAP(p, p_size);
170f08c3bdfSopenharmony_ci	if (q)
171f08c3bdfSopenharmony_ci		SAFE_MUNMAP(q, q_size);
172f08c3bdfSopenharmony_cicleanup_fd:
173f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
174f08c3bdfSopenharmony_ci}
175f08c3bdfSopenharmony_ci
176f08c3bdfSopenharmony_cistatic void setup(void)
177f08c3bdfSopenharmony_ci{
178f08c3bdfSopenharmony_ci	hpage_size = tst_get_hugepage_size();
179f08c3bdfSopenharmony_ci	page_size = getpagesize();
180f08c3bdfSopenharmony_ci}
181f08c3bdfSopenharmony_ci
182f08c3bdfSopenharmony_cistatic void cleanup(void)
183f08c3bdfSopenharmony_ci{
184f08c3bdfSopenharmony_ci	if (fd >= 0)
185f08c3bdfSopenharmony_ci		SAFE_CLOSE(fd);
186f08c3bdfSopenharmony_ci}
187f08c3bdfSopenharmony_ci
188f08c3bdfSopenharmony_cistatic struct tst_test test = {
189f08c3bdfSopenharmony_ci	.needs_root = 1,
190f08c3bdfSopenharmony_ci	.mntpoint = MNTPOINT,
191f08c3bdfSopenharmony_ci	.needs_hugetlbfs = 1,
192f08c3bdfSopenharmony_ci	.needs_tmpdir = 1,
193f08c3bdfSopenharmony_ci	.setup = setup,
194f08c3bdfSopenharmony_ci	.cleanup = cleanup,
195f08c3bdfSopenharmony_ci	.test_all = run_test,
196f08c3bdfSopenharmony_ci	.hugepages = {4, TST_NEEDS},
197f08c3bdfSopenharmony_ci};
198