1/*
2 * Copyright (C) 2012 Linux Test Project, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it
13 * is free of the rightful claim of any third person regarding
14 * infringement or the like.  Any license provided herein, whether
15 * implied or otherwise, applies only to this software file.  Patent
16 * licenses, if any, provided herein do not apply to combinations of
17 * this program with other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301, USA.
23 */
24/*
25 * Test Name: mremap05
26 *
27 * Test Description:
28 *  Verify that MREMAP_FIXED fails without MREMAP_MAYMOVE.
29 *  Verify that MREMAP_FIXED|MREMAP_MAYMOVE fails if target address
30 *    is not page aligned.
31 *  Verify that MREMAP_FIXED|MREMAP_MAYMOVE fails if old range
32 *    overlaps with new range.
33 *  Verify that MREMAP_FIXED|MREMAP_MAYMOVE can move mapping to new address.
34 *  Verify that MREMAP_FIXED|MREMAP_MAYMOVE unmaps previous mapping
35 *    at the address range specified by new_address and new_size.
36 */
37
38#define _GNU_SOURCE
39#include "config.h"
40#include <sys/mman.h>
41#include <errno.h>
42#include <unistd.h>
43#include "test.h"
44
45char *TCID = "mremap05";
46
47#ifdef HAVE_MREMAP_FIXED
48
49struct test_case_t {
50	char *old_address;
51	char *new_address;
52	size_t old_size;	/* in pages */
53	size_t new_size;	/* in pages */
54	int flags;
55	const const char *msg;
56	void *exp_ret;
57	int exp_errno;
58	char *ret;
59	void (*setup) (struct test_case_t *);
60	void (*cleanup) (struct test_case_t *);
61};
62
63static void setup(void);
64static void cleanup(void);
65static void setup0(struct test_case_t *);
66static void setup1(struct test_case_t *);
67static void setup2(struct test_case_t *);
68static void setup3(struct test_case_t *);
69static void setup4(struct test_case_t *);
70static void cleanup0(struct test_case_t *);
71static void cleanup1(struct test_case_t *);
72
73struct test_case_t tdat[] = {
74	{
75	 .old_size = 1,
76	 .new_size = 1,
77	 .flags = MREMAP_FIXED,
78	 .msg = "MREMAP_FIXED requires MREMAP_MAYMOVE",
79	 .exp_ret = MAP_FAILED,
80	 .exp_errno = EINVAL,
81	 .setup = setup0,
82	 .cleanup = cleanup0},
83	{
84	 .old_size = 1,
85	 .new_size = 1,
86	 .flags = MREMAP_FIXED | MREMAP_MAYMOVE,
87	 .msg = "new_addr has to be page aligned",
88	 .exp_ret = MAP_FAILED,
89	 .exp_errno = EINVAL,
90	 .setup = setup1,
91	 .cleanup = cleanup0},
92	{
93	 .old_size = 2,
94	 .new_size = 1,
95	 .flags = MREMAP_FIXED | MREMAP_MAYMOVE,
96	 .msg = "old/new area must not overlap",
97	 .exp_ret = MAP_FAILED,
98	 .exp_errno = EINVAL,
99	 .setup = setup2,
100	 .cleanup = cleanup0},
101	{
102	 .old_size = 1,
103	 .new_size = 1,
104	 .flags = MREMAP_FIXED | MREMAP_MAYMOVE,
105	 .msg = "mremap #1",
106	 .setup = setup3,
107	 .cleanup = cleanup0},
108	{
109	 .old_size = 1,
110	 .new_size = 1,
111	 .flags = MREMAP_FIXED | MREMAP_MAYMOVE,
112	 .msg = "mremap #2",
113	 .setup = setup4,
114	 .cleanup = cleanup1},
115};
116
117static int pagesize;
118static int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]);
119
120static void free_test_area(void *p, int size)
121{
122	if (munmap(p, size) < 0)
123		tst_brkm(TBROK | TERRNO, cleanup, "free_test_area munmap");
124}
125
126static void *get_test_area(int size, int free_area)
127{
128	void *p;
129	p = mmap(NULL, size, PROT_READ | PROT_WRITE,
130		 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
131	if (p == MAP_FAILED)
132		tst_brkm(TBROK | TERRNO, cleanup, "get_test_area mmap");
133	if (free_area)
134		free_test_area(p, size);
135	return p;
136}
137
138static void test_mremap(struct test_case_t *t)
139{
140	t->ret = mremap(t->old_address, t->old_size, t->new_size, t->flags,
141			t->new_address);
142
143	if (t->ret == t->exp_ret) {
144		if (t->ret != MAP_FAILED) {
145			tst_resm(TPASS, "%s", t->msg);
146			if (*(t->ret) == 0x1)
147				tst_resm(TPASS, "%s value OK", t->msg);
148			else
149				tst_resm(TPASS, "%s value failed", t->msg);
150		} else {
151			if (errno == t->exp_errno)
152				tst_resm(TPASS, "%s", t->msg);
153			else
154				tst_resm(TFAIL | TERRNO, "%s", t->msg);
155		}
156	} else {
157		tst_resm(TFAIL, "%s ret: %p, expected: %p", t->msg,
158			 t->ret, t->exp_ret);
159	}
160}
161
162static void setup0(struct test_case_t *t)
163{
164	t->old_address = get_test_area(t->old_size * pagesize, 0);
165	t->new_address = get_test_area(t->new_size * pagesize, 1);
166}
167
168static void setup1(struct test_case_t *t)
169{
170	t->old_address = get_test_area(t->old_size * pagesize, 0);
171	t->new_address = get_test_area((t->new_size + 1) * pagesize, 1) + 1;
172}
173
174static void setup2(struct test_case_t *t)
175{
176	t->old_address = get_test_area(t->old_size * pagesize, 0);
177	t->new_address = t->old_address;
178}
179
180static void setup3(struct test_case_t *t)
181{
182	t->old_address = get_test_area(t->old_size * pagesize, 0);
183	t->new_address = get_test_area(t->new_size * pagesize, 1);
184	t->exp_ret = t->new_address;
185	*(t->old_address) = 0x1;
186}
187
188static void setup4(struct test_case_t *t)
189{
190	t->old_address = get_test_area(t->old_size * pagesize, 0);
191	t->new_address = get_test_area(t->new_size * pagesize, 0);
192	t->exp_ret = t->new_address;
193	*(t->old_address) = 0x1;
194	*(t->new_address) = 0x2;
195}
196
197static void cleanup0(struct test_case_t *t)
198{
199	if (t->ret == MAP_FAILED)
200		free_test_area(t->old_address, t->old_size * pagesize);
201	else
202		free_test_area(t->ret, t->new_size * pagesize);
203}
204
205static void cleanup1(struct test_case_t *t)
206{
207	if (t->ret == MAP_FAILED) {
208		free_test_area(t->old_address, t->old_size * pagesize);
209		free_test_area(t->new_address, t->new_size * pagesize);
210	} else {
211		free_test_area(t->ret, t->new_size * pagesize);
212	}
213}
214
215int main(int ac, char **av)
216{
217	int lc, testno;
218
219	tst_parse_opts(ac, av, NULL, NULL);
220
221	setup();
222	for (lc = 0; TEST_LOOPING(lc); lc++) {
223		tst_count = 0;
224		for (testno = 0; testno < TST_TOTAL; testno++) {
225			tdat[testno].setup(&tdat[testno]);
226			test_mremap(&tdat[testno]);
227			tdat[testno].cleanup(&tdat[testno]);
228		}
229	}
230	cleanup();
231	tst_exit();
232}
233
234static void setup(void)
235{
236	pagesize = getpagesize();
237}
238
239static void cleanup(void)
240{
241}
242
243#else
244
245int main(void)
246{
247	tst_brkm(TCONF, NULL, "MREMAP_FIXED not present in <sys/mman.h>");
248}
249
250#endif /* HAVE_MREMAP_FIXED */
251