1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: LGPL-2.1-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (C) 2005-2006 David Gibson & Adam Litke, IBM Corporation.
4f08c3bdfSopenharmony_ci * Author: David Gibson & Adam Litke
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci/*\
8f08c3bdfSopenharmony_ci * [Description]
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci * Certain kernels have a bug where brk() does not perform the same
11f08c3bdfSopenharmony_ci * checks that a MAP_FIXED mmap() will, allowing brk() to create a
12f08c3bdfSopenharmony_ci * normal page VMA in a hugepage only address region. This can lead
13f08c3bdfSopenharmony_ci * to oopses or other badness.
14f08c3bdfSopenharmony_ci */
15f08c3bdfSopenharmony_ci
16f08c3bdfSopenharmony_ci#define _GNU_SOURCE
17f08c3bdfSopenharmony_ci#include <stdio.h>
18f08c3bdfSopenharmony_ci#include <sys/mount.h>
19f08c3bdfSopenharmony_ci#include <limits.h>
20f08c3bdfSopenharmony_ci#include <sys/param.h>
21f08c3bdfSopenharmony_ci#include <sys/types.h>
22f08c3bdfSopenharmony_ci
23f08c3bdfSopenharmony_ci#include "hugetlb.h"
24f08c3bdfSopenharmony_ci#include "tst_safe_stdio.h"
25f08c3bdfSopenharmony_ci
26f08c3bdfSopenharmony_ci#define MNTPOINT "hugetlbfs/"
27f08c3bdfSopenharmony_cistatic long hpage_size;
28f08c3bdfSopenharmony_cistatic int huge_fd = -1;
29f08c3bdfSopenharmony_ci
30f08c3bdfSopenharmony_ci#ifdef __powerpc64__
31f08c3bdfSopenharmony_cistatic int arch_has_slice_support(void)
32f08c3bdfSopenharmony_ci{
33f08c3bdfSopenharmony_ci	char mmu_type[16];
34f08c3bdfSopenharmony_ci
35f08c3bdfSopenharmony_ci	SAFE_FILE_LINES_SCANF("/proc/cpuinfo", "MMU : %16s", mmu_type);
36f08c3bdfSopenharmony_ci	return strcmp(mmu_type, "Hash") == 0;
37f08c3bdfSopenharmony_ci}
38f08c3bdfSopenharmony_ci
39f08c3bdfSopenharmony_cistatic void *next_chunk(void *addr)
40f08c3bdfSopenharmony_ci{
41f08c3bdfSopenharmony_ci	if (!arch_has_slice_support())
42f08c3bdfSopenharmony_ci		return PALIGN(addr, hpage_size);
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_ci	if ((unsigned long)addr < 0x100000000UL)
45f08c3bdfSopenharmony_ci		/* 256M segments below 4G */
46f08c3bdfSopenharmony_ci		return PALIGN(addr, 0x10000000UL);
47f08c3bdfSopenharmony_ci	/* 1TB segments above */
48f08c3bdfSopenharmony_ci	return PALIGN(addr, 0x10000000000UL);
49f08c3bdfSopenharmony_ci}
50f08c3bdfSopenharmony_ci#elif defined(__powerpc__)
51f08c3bdfSopenharmony_cistatic void *next_chunk(void *addr)
52f08c3bdfSopenharmony_ci{
53f08c3bdfSopenharmony_ci	if (tst_kernel_bits() == 32)
54f08c3bdfSopenharmony_ci		return PALIGN(addr, hpage_size);
55f08c3bdfSopenharmony_ci	else
56f08c3bdfSopenharmony_ci		return PALIGN(addr, 0x10000000UL);
57f08c3bdfSopenharmony_ci}
58f08c3bdfSopenharmony_ci#elif defined(__ia64__)
59f08c3bdfSopenharmony_cistatic void *next_chunk(void *addr)
60f08c3bdfSopenharmony_ci{
61f08c3bdfSopenharmony_ci	return PALIGN(addr, 0x8000000000000000UL);
62f08c3bdfSopenharmony_ci}
63f08c3bdfSopenharmony_ci#else
64f08c3bdfSopenharmony_cistatic void *next_chunk(void *addr)
65f08c3bdfSopenharmony_ci{
66f08c3bdfSopenharmony_ci	return PALIGN(addr, hpage_size);
67f08c3bdfSopenharmony_ci}
68f08c3bdfSopenharmony_ci#endif
69f08c3bdfSopenharmony_ci
70f08c3bdfSopenharmony_cistatic void run_test(void)
71f08c3bdfSopenharmony_ci{
72f08c3bdfSopenharmony_ci	void *brk0, *hugemap_addr, *newbrk;
73f08c3bdfSopenharmony_ci	char *p;
74f08c3bdfSopenharmony_ci	int err;
75f08c3bdfSopenharmony_ci
76f08c3bdfSopenharmony_ci	brk0 = sbrk(0);
77f08c3bdfSopenharmony_ci	tst_res(TINFO, "Initial break at %p", brk0);
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ci	hugemap_addr = next_chunk(brk0) + hpage_size;
80f08c3bdfSopenharmony_ci
81f08c3bdfSopenharmony_ci	p = SAFE_MMAP(hugemap_addr, hpage_size, PROT_READ|PROT_WRITE,
82f08c3bdfSopenharmony_ci			MAP_PRIVATE|MAP_FIXED, huge_fd, 0);
83f08c3bdfSopenharmony_ci	if (p != hugemap_addr) {
84f08c3bdfSopenharmony_ci		tst_res(TFAIL, "mmap() at unexpected address %p instead of %p\n", p,
85f08c3bdfSopenharmony_ci		     hugemap_addr);
86f08c3bdfSopenharmony_ci		goto cleanup;
87f08c3bdfSopenharmony_ci	}
88f08c3bdfSopenharmony_ci
89f08c3bdfSopenharmony_ci	newbrk = next_chunk(brk0) + getpagesize();
90f08c3bdfSopenharmony_ci	err = brk((void *)newbrk);
91f08c3bdfSopenharmony_ci	if (err == -1) {
92f08c3bdfSopenharmony_ci		/* Failing the brk() is an acceptable kernel response */
93f08c3bdfSopenharmony_ci		tst_res(TPASS, "Failing the brk at %p is an acceptable response",
94f08c3bdfSopenharmony_ci				newbrk);
95f08c3bdfSopenharmony_ci	} else {
96f08c3bdfSopenharmony_ci		/* Suceeding the brk() is acceptable if the new memory is
97f08c3bdfSopenharmony_ci		 * properly accesible and we don't have a kernel blow up when
98f08c3bdfSopenharmony_ci		 * we touch it.
99f08c3bdfSopenharmony_ci		 */
100f08c3bdfSopenharmony_ci		tst_res(TINFO, "New break at %p", newbrk);
101f08c3bdfSopenharmony_ci		memset(brk0, 0, newbrk-brk0);
102f08c3bdfSopenharmony_ci		tst_res(TPASS, "memory is accessible, hence successful brk() is "
103f08c3bdfSopenharmony_ci				"an acceptable response");
104f08c3bdfSopenharmony_ci	}
105f08c3bdfSopenharmony_cicleanup:
106f08c3bdfSopenharmony_ci	SAFE_MUNMAP(p, hpage_size);
107f08c3bdfSopenharmony_ci	err = brk(brk0);
108f08c3bdfSopenharmony_ci	if (err == -1)
109f08c3bdfSopenharmony_ci		tst_brk(TBROK, "Failed to set break at the original position");
110f08c3bdfSopenharmony_ci}
111f08c3bdfSopenharmony_ci
112f08c3bdfSopenharmony_cistatic void setup(void)
113f08c3bdfSopenharmony_ci{
114f08c3bdfSopenharmony_ci	hpage_size = SAFE_READ_MEMINFO(MEMINFO_HPAGE_SIZE)*1024;
115f08c3bdfSopenharmony_ci	huge_fd = tst_creat_unlinked(MNTPOINT, 0);
116f08c3bdfSopenharmony_ci}
117f08c3bdfSopenharmony_ci
118f08c3bdfSopenharmony_cistatic void cleanup(void)
119f08c3bdfSopenharmony_ci{
120f08c3bdfSopenharmony_ci	SAFE_CLOSE(huge_fd);
121f08c3bdfSopenharmony_ci}
122f08c3bdfSopenharmony_ci
123f08c3bdfSopenharmony_cistatic struct tst_test test = {
124f08c3bdfSopenharmony_ci	.needs_root = 1,
125f08c3bdfSopenharmony_ci	.mntpoint = MNTPOINT,
126f08c3bdfSopenharmony_ci	.needs_hugetlbfs = 1,
127f08c3bdfSopenharmony_ci	.taint_check = TST_TAINT_D | TST_TAINT_W,
128f08c3bdfSopenharmony_ci	.setup = setup,
129f08c3bdfSopenharmony_ci	.cleanup = cleanup,
130f08c3bdfSopenharmony_ci	.test_all = run_test,
131f08c3bdfSopenharmony_ci	.hugepages = {1, TST_NEEDS},
132f08c3bdfSopenharmony_ci};
133