1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2017, Anshuman Khandual, IBM Corp.
5 * Works on architectures which support 128TB virtual
6 * address range and beyond.
17 #include "../kselftest.h"
20 * Maximum address range mapped with a single mmap()
21 * call is little bit more than 1GB. Hence 1GB is
22 * chosen as the single chunk size for address space
26 #define SZ_1GB (1024 * 1024 * 1024UL)
27 #define SZ_1TB (1024 * 1024 * 1024 * 1024UL)
29 #define MAP_CHUNK_SIZE SZ_1GB
32 * Address space till 128TB is mapped without any hint
33 * and is enabled by default. Address space beyond 128TB
34 * till 512TB is obtained by passing hint address as the
35 * first argument into mmap() system call.
37 * The process heap address space is divided into two
38 * different areas one below 128TB and one above 128TB
39 * till it reaches 512TB. One with size 128TB and the
42 * On Arm64 the address space is 256TB and support for
43 * high mappings up to 4PB virtual address space has
47 #define NR_CHUNKS_128TB ((128 * SZ_1TB) / MAP_CHUNK_SIZE) /* Number of chunks for 128TB */
48 #define NR_CHUNKS_256TB (NR_CHUNKS_128TB * 2UL)
49 #define NR_CHUNKS_384TB (NR_CHUNKS_128TB * 3UL)
50 #define NR_CHUNKS_3840TB (NR_CHUNKS_128TB * 30UL)
52 #define ADDR_MARK_128TB (1UL << 47) /* First address beyond 128TB */
53 #define ADDR_MARK_256TB (1UL << 48) /* First address beyond 256TB */
56 #define HIGH_ADDR_MARK ADDR_MARK_256TB
57 #define HIGH_ADDR_SHIFT 49
58 #define NR_CHUNKS_LOW NR_CHUNKS_256TB
59 #define NR_CHUNKS_HIGH NR_CHUNKS_3840TB
61 #define HIGH_ADDR_MARK ADDR_MARK_128TB
62 #define HIGH_ADDR_SHIFT 48
63 #define NR_CHUNKS_LOW NR_CHUNKS_128TB
64 #define NR_CHUNKS_HIGH NR_CHUNKS_384TB
67 static char *hint_addr(void)
69 int bits = HIGH_ADDR_SHIFT + rand() % (63 - HIGH_ADDR_SHIFT);
71 return (char *) (1UL << bits);
74 static void validate_addr(char *ptr, int high_addr)
76 unsigned long addr = (unsigned long) ptr;
78 if (high_addr && addr < HIGH_ADDR_MARK)
79 ksft_exit_fail_msg("Bad address %lx\n", addr);
81 if (addr > HIGH_ADDR_MARK)
82 ksft_exit_fail_msg("Bad address %lx\n", addr);
85 static int validate_lower_address_hint(void)
89 ptr = mmap((void *) (1UL << 45), MAP_CHUNK_SIZE, PROT_READ |
90 PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
92 if (ptr == MAP_FAILED)
98 static int validate_complete_va_space(void)
100 unsigned long start_addr, end_addr, prev_end_addr;
106 fd = open("va_dump", O_CREAT | O_WRONLY, 0600);
109 ksft_test_result_skip("cannot create or open dump file\n");
113 file = fopen("/proc/self/maps", "r");
115 ksft_exit_fail_msg("cannot open /proc/self/maps\n");
118 while (fgets(line, sizeof(line), file)) {
121 if (sscanf(line, "%lx-%lx %s[rwxp-]",
122 &start_addr, &end_addr, prot) != 3)
123 ksft_exit_fail_msg("cannot parse /proc/self/maps\n");
125 /* end of userspace mappings; ignore vsyscall mapping */
126 if (start_addr & (1UL << 63))
129 /* /proc/self/maps must have gaps less than MAP_CHUNK_SIZE */
130 if (start_addr - prev_end_addr >= MAP_CHUNK_SIZE)
133 prev_end_addr = end_addr;
139 * Confirm whether MAP_CHUNK_SIZE chunk can be found or not.
140 * If write succeeds, no need to check MAP_CHUNK_SIZE - 1
141 * addresses after that. If the address was not held by this
142 * process, write would fail with errno set to EFAULT.
143 * Anyways, if write returns anything apart from 1, exit the
144 * program since that would mean a bug in /proc/self/maps.
147 while (start_addr + hop < end_addr) {
148 if (write(fd, (void *)(start_addr + hop), 1) != 1)
150 lseek(fd, 0, SEEK_SET);
152 hop += MAP_CHUNK_SIZE;
158 int main(int argc, char *argv[])
160 char *ptr[NR_CHUNKS_LOW];
163 unsigned long i, lchunks, hchunks;
168 for (i = 0; i < NR_CHUNKS_LOW; i++) {
169 ptr[i] = mmap(NULL, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE,
170 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
172 if (ptr[i] == MAP_FAILED) {
173 if (validate_lower_address_hint())
174 ksft_exit_fail_msg("mmap unexpectedly succeeded with hint\n");
178 validate_addr(ptr[i], 0);
181 hptr = (char **) calloc(NR_CHUNKS_HIGH, sizeof(char *));
183 ksft_test_result_skip("Memory constraint not fulfilled\n");
187 for (i = 0; i < NR_CHUNKS_HIGH; i++) {
189 hptr[i] = mmap(hint, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE,
190 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
192 if (hptr[i] == MAP_FAILED)
195 validate_addr(hptr[i], 1);
198 if (validate_complete_va_space()) {
199 ksft_test_result_fail("BUG in mmap() or /proc/self/maps\n");
203 for (i = 0; i < lchunks; i++)
204 munmap(ptr[i], MAP_CHUNK_SIZE);
206 for (i = 0; i < hchunks; i++)
207 munmap(hptr[i], MAP_CHUNK_SIZE);
211 ksft_test_result_pass("Test\n");