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#include "safe_macros.h"
45
46char *TCID = "mremap05";
47
48struct test_case_t {
49	char *old_address;
50	char *new_address;
51	size_t old_size;	/* in pages */
52	size_t new_size;	/* in pages */
53	int flags;
54	const char *msg;
55	void *exp_ret;
56	int exp_errno;
57	char *ret;
58	void (*setup) (struct test_case_t *);
59	void (*cleanup) (struct test_case_t *);
60};
61
62static void setup(void);
63static void cleanup(void);
64static void setup0(struct test_case_t *);
65static void setup1(struct test_case_t *);
66static void setup2(struct test_case_t *);
67static void setup3(struct test_case_t *);
68static void setup4(struct test_case_t *);
69static void cleanup0(struct test_case_t *);
70static void cleanup1(struct test_case_t *);
71
72struct test_case_t tdat[] = {
73	{
74	 .old_size = 1,
75	 .new_size = 1,
76	 .flags = MREMAP_FIXED,
77	 .msg = "MREMAP_FIXED requires MREMAP_MAYMOVE",
78	 .exp_ret = MAP_FAILED,
79	 .exp_errno = EINVAL,
80	 .setup = setup0,
81	 .cleanup = cleanup0},
82	{
83	 .old_size = 1,
84	 .new_size = 1,
85	 .flags = MREMAP_FIXED | MREMAP_MAYMOVE,
86	 .msg = "new_addr has to be page aligned",
87	 .exp_ret = MAP_FAILED,
88	 .exp_errno = EINVAL,
89	 .setup = setup1,
90	 .cleanup = cleanup0},
91	{
92	 .old_size = 2,
93	 .new_size = 1,
94	 .flags = MREMAP_FIXED | MREMAP_MAYMOVE,
95	 .msg = "old/new area must not overlap",
96	 .exp_ret = MAP_FAILED,
97	 .exp_errno = EINVAL,
98	 .setup = setup2,
99	 .cleanup = cleanup0},
100	{
101	 .old_size = 1,
102	 .new_size = 1,
103	 .flags = MREMAP_FIXED | MREMAP_MAYMOVE,
104	 .msg = "mremap #1",
105	 .setup = setup3,
106	 .cleanup = cleanup0},
107	{
108	 .old_size = 1,
109	 .new_size = 1,
110	 .flags = MREMAP_FIXED | MREMAP_MAYMOVE,
111	 .msg = "mremap #2",
112	 .setup = setup4,
113	 .cleanup = cleanup1},
114};
115
116static int pagesize;
117static int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]);
118
119static void free_test_area(void *p, int size)
120{
121	SAFE_MUNMAP(cleanup, p, size);
122}
123
124static void *get_test_area(int size, int free_area)
125{
126	void *p;
127	p = mmap(NULL, size, PROT_READ | PROT_WRITE,
128		 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
129	if (p == MAP_FAILED)
130		tst_brkm(TBROK | TERRNO, cleanup, "get_test_area mmap");
131	if (free_area)
132		free_test_area(p, size);
133	return p;
134}
135
136static void test_mremap(struct test_case_t *t)
137{
138	t->ret = mremap(t->old_address, t->old_size, t->new_size, t->flags,
139			t->new_address);
140
141	if (t->ret == t->exp_ret) {
142		if (t->ret != MAP_FAILED) {
143			tst_resm(TPASS, "%s", t->msg);
144			if (*(t->ret) == 0x1)
145				tst_resm(TPASS, "%s value OK", t->msg);
146			else
147				tst_resm(TPASS, "%s value failed", t->msg);
148		} else {
149			if (errno == t->exp_errno)
150				tst_resm(TPASS, "%s", t->msg);
151			else
152				tst_resm(TFAIL | TERRNO, "%s", t->msg);
153		}
154	} else {
155		tst_resm(TFAIL, "%s ret: %p, expected: %p", t->msg,
156			 t->ret, t->exp_ret);
157	}
158}
159
160static void setup0(struct test_case_t *t)
161{
162	t->old_address = get_test_area(t->old_size * pagesize, 0);
163	t->new_address = get_test_area(t->new_size * pagesize, 1);
164}
165
166static void setup1(struct test_case_t *t)
167{
168	t->old_address = get_test_area(t->old_size * pagesize, 0);
169	t->new_address = get_test_area((t->new_size + 1) * pagesize, 1) + 1;
170}
171
172static void setup2(struct test_case_t *t)
173{
174	t->old_address = get_test_area(t->old_size * pagesize, 0);
175	t->new_address = t->old_address;
176}
177
178static void setup3(struct test_case_t *t)
179{
180	t->old_address = get_test_area(t->old_size * pagesize, 0);
181	t->new_address = get_test_area(t->new_size * pagesize, 1);
182	t->exp_ret = t->new_address;
183	*(t->old_address) = 0x1;
184}
185
186static void setup4(struct test_case_t *t)
187{
188	t->old_address = get_test_area(t->old_size * pagesize, 0);
189	t->new_address = get_test_area(t->new_size * pagesize, 0);
190	t->exp_ret = t->new_address;
191	*(t->old_address) = 0x1;
192	*(t->new_address) = 0x2;
193}
194
195static void cleanup0(struct test_case_t *t)
196{
197	if (t->ret == MAP_FAILED)
198		free_test_area(t->old_address, t->old_size * pagesize);
199	else
200		free_test_area(t->ret, t->new_size * pagesize);
201}
202
203static void cleanup1(struct test_case_t *t)
204{
205	if (t->ret == MAP_FAILED) {
206		free_test_area(t->old_address, t->old_size * pagesize);
207		free_test_area(t->new_address, t->new_size * pagesize);
208	} else {
209		free_test_area(t->ret, t->new_size * pagesize);
210	}
211}
212
213int main(int ac, char **av)
214{
215	int lc, testno;
216
217	tst_parse_opts(ac, av, NULL, NULL);
218
219	setup();
220	for (lc = 0; TEST_LOOPING(lc); lc++) {
221		tst_count = 0;
222		for (testno = 0; testno < TST_TOTAL; testno++) {
223			tdat[testno].setup(&tdat[testno]);
224			test_mremap(&tdat[testno]);
225			tdat[testno].cleanup(&tdat[testno]);
226		}
227	}
228	cleanup();
229	tst_exit();
230}
231
232static void setup(void)
233{
234	pagesize = getpagesize();
235}
236
237static void cleanup(void)
238{
239}
240