1// SPDX-License-Identifier: LGPL-2.1-or-later
2/*
3 * Copyright (C) 2005-2006 David Gibson & Adam Litke, IBM Corporation.
4 * Author: David Gibson & Adam Litke
5 */
6
7/*\
8 * [Description]
9 *
10 * On some old ppc64 kernel, when hpage is mmaped on 32 bit boundary and
11 * normal page below it, it triggers the bug caused by off-by-one error.
12 *
13 * WARNING: The offsets and addresses used within are specifically
14 * calculated to trigger the bug as it existed. Don't mess with them
15 * unless you *really* know what you're doing.
16 */
17
18#define _GNU_SOURCE
19#include <stdio.h>
20#include <sys/mount.h>
21#include <limits.h>
22#include <sys/param.h>
23#include <sys/types.h>
24
25#include "hugetlb.h"
26
27#define FOURGB (1ULL << 32)
28#define MNTPOINT "hugetlbfs/"
29static int  fd = -1;
30static unsigned long hpage_size;
31static int page_size;
32
33static void run_test(void)
34{
35	void *p, *q = NULL;
36	unsigned long long lowaddr;
37	unsigned long long below_start;
38	unsigned long long above_end;
39
40	p = mmap((void *)FOURGB, hpage_size, PROT_READ|PROT_WRITE,
41		 MAP_SHARED | MAP_FIXED, fd, 0);
42	if (p == MAP_FAILED) {
43		/* slice 0 (high) spans from 4G-1T */
44		below_start = FOURGB;
45		above_end = 1024ULL*1024*1024*1024;
46
47		if (range_is_mapped(below_start, above_end) == 1) {
48			tst_res(TINFO|TERRNO, "region 4G-IT is not free & "
49					"mmap() failed expected");
50			tst_res(TPASS, "Successful but inconclusive");
51		} else
52			tst_res(TFAIL|TERRNO, "mmap() huge failed unexpected");
53		goto cleanup;
54	}
55	if (p != (void *)FOURGB) {
56		tst_res(TFAIL, "Wrong address with MAP_FIXED huge");
57		goto cleanup;
58	}
59
60	tst_res(TINFO, "Mapped hugetlb at %p", p);
61
62	memset(p, 0, hpage_size);
63
64	/* Test just below 4GB to check for off-by-one errors */
65	lowaddr = FOURGB - page_size;
66	q = mmap((void *)lowaddr, page_size, PROT_READ|PROT_WRITE,
67		 MAP_SHARED|MAP_FIXED|MAP_ANONYMOUS, 0, 0);
68	if (q == MAP_FAILED) {
69		below_start = FOURGB - page_size;
70		above_end = FOURGB;
71
72		if (range_is_mapped(below_start, above_end) == 1) {
73			tst_res(TINFO|TERRNO, "region (4G-page)-4G is not free & "
74					"mmap() failed expected");
75			tst_res(TPASS, "Successful but inconclusive");
76		} else
77			tst_res(TFAIL|TERRNO, "mmap() normal failed unexpected");
78		goto cleanup;
79	}
80	if (q != (void *)lowaddr) {
81		tst_res(TFAIL, "Wrong address with MAP_FIXED normal");
82		goto cleanup;
83	}
84
85	memset(q, 0, page_size);
86	tst_res(TPASS, "Successful");
87
88cleanup:
89	if (p && p != MAP_FAILED)
90		SAFE_MUNMAP(p, hpage_size);
91	if (q && q != MAP_FAILED)
92		SAFE_MUNMAP(q, page_size);
93}
94
95static void setup(void)
96{
97	page_size = getpagesize();
98	hpage_size = SAFE_READ_MEMINFO("Hugepagesize:")*1024;
99
100	if (sizeof(void *) <= 4)
101		tst_brk(TCONF, "Machine must be >32 bit");
102	if (hpage_size > FOURGB)
103		tst_brk(TCONF, "Huge page size is too large");
104	fd = tst_creat_unlinked(MNTPOINT, 0);
105}
106
107static void cleanup(void)
108{
109	if (fd > 0)
110		SAFE_CLOSE(fd);
111}
112
113static struct tst_test test = {
114	.tags = (struct tst_tag[]) {
115		{"linux-git", "9a94c5793a7b"},
116		{}
117	},
118	.needs_root = 1,
119	.mntpoint = MNTPOINT,
120	.needs_hugetlbfs = 1,
121	.needs_tmpdir = 1,
122	.setup = setup,
123	.cleanup = cleanup,
124	.test_all = run_test,
125	.hugepages = {2, TST_NEEDS},
126};
127