brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · 4e4c1e3 Raw
214 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2017, Anshuman Khandual, IBM Corp.4 *5 * Works on architectures which support 128TB virtual6 * address range and beyond.7 */8#include <stdio.h>9#include <stdlib.h>10#include <string.h>11#include <unistd.h>12#include <errno.h>13#include <sys/mman.h>14#include <sys/time.h>15#include <fcntl.h>16 17#include "../kselftest.h"18 19/*20 * Maximum address range mapped with a single mmap()21 * call is little bit more than 1GB. Hence 1GB is22 * chosen as the single chunk size for address space23 * mapping.24 */25 26#define SZ_1GB	(1024 * 1024 * 1024UL)27#define SZ_1TB	(1024 * 1024 * 1024 * 1024UL)28 29#define MAP_CHUNK_SIZE	SZ_1GB30 31/*32 * Address space till 128TB is mapped without any hint33 * and is enabled by default. Address space beyond 128TB34 * till 512TB is obtained by passing hint address as the35 * first argument into mmap() system call.36 *37 * The process heap address space is divided into two38 * different areas one below 128TB and one above 128TB39 * till it reaches 512TB. One with size 128TB and the40 * other being 384TB.41 *42 * On Arm64 the address space is 256TB and support for43 * high mappings up to 4PB virtual address space has44 * been added.45 */46 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)51 52#define ADDR_MARK_128TB  (1UL << 47) /* First address beyond 128TB */53#define ADDR_MARK_256TB  (1UL << 48) /* First address beyond 256TB */54 55#ifdef __aarch64__56#define HIGH_ADDR_MARK  ADDR_MARK_256TB57#define HIGH_ADDR_SHIFT 4958#define NR_CHUNKS_LOW   NR_CHUNKS_256TB59#define NR_CHUNKS_HIGH  NR_CHUNKS_3840TB60#else61#define HIGH_ADDR_MARK  ADDR_MARK_128TB62#define HIGH_ADDR_SHIFT 4863#define NR_CHUNKS_LOW   NR_CHUNKS_128TB64#define NR_CHUNKS_HIGH  NR_CHUNKS_384TB65#endif66 67static char *hind_addr(void)68{69	int bits = HIGH_ADDR_SHIFT + rand() % (63 - HIGH_ADDR_SHIFT);70 71	return (char *) (1UL << bits);72}73 74static void validate_addr(char *ptr, int high_addr)75{76	unsigned long addr = (unsigned long) ptr;77 78	if (high_addr && addr < HIGH_ADDR_MARK)79		ksft_exit_fail_msg("Bad address %lx\n", addr);80 81	if (addr > HIGH_ADDR_MARK)82		ksft_exit_fail_msg("Bad address %lx\n", addr);83}84 85static int validate_lower_address_hint(void)86{87	char *ptr;88 89	ptr = mmap((void *) (1UL << 45), MAP_CHUNK_SIZE, PROT_READ |90		   PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);91 92	if (ptr == MAP_FAILED)93		return 0;94 95	return 1;96}97 98static int validate_complete_va_space(void)99{100	unsigned long start_addr, end_addr, prev_end_addr;101	char line[400];102	char prot[6];103	FILE *file;104	int fd;105 106	fd = open("va_dump", O_CREAT | O_WRONLY, 0600);107	unlink("va_dump");108	if (fd < 0) {109		ksft_test_result_skip("cannot create or open dump file\n");110		ksft_finished();111	}112 113	file = fopen("/proc/self/maps", "r");114	if (file == NULL)115		ksft_exit_fail_msg("cannot open /proc/self/maps\n");116 117	prev_end_addr = 0;118	while (fgets(line, sizeof(line), file)) {119		unsigned long hop;120 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");124 125		/* end of userspace mappings; ignore vsyscall mapping */126		if (start_addr & (1UL << 63))127			return 0;128 129		/* /proc/self/maps must have gaps less than MAP_CHUNK_SIZE */130		if (start_addr - prev_end_addr >= MAP_CHUNK_SIZE)131			return 1;132 133		prev_end_addr = end_addr;134 135		if (prot[0] != 'r')136			continue;137 138		/*139		 * Confirm whether MAP_CHUNK_SIZE chunk can be found or not.140		 * If write succeeds, no need to check MAP_CHUNK_SIZE - 1141		 * addresses after that. If the address was not held by this142		 * process, write would fail with errno set to EFAULT.143		 * Anyways, if write returns anything apart from 1, exit the144		 * program since that would mean a bug in /proc/self/maps.145		 */146		hop = 0;147		while (start_addr + hop < end_addr) {148			if (write(fd, (void *)(start_addr + hop), 1) != 1)149				return 1;150			lseek(fd, 0, SEEK_SET);151 152			hop += MAP_CHUNK_SIZE;153		}154	}155	return 0;156}157 158int main(int argc, char *argv[])159{160	char *ptr[NR_CHUNKS_LOW];161	char **hptr;162	char *hint;163	unsigned long i, lchunks, hchunks;164 165	ksft_print_header();166	ksft_set_plan(1);167 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);171 172		if (ptr[i] == MAP_FAILED) {173			if (validate_lower_address_hint())174				ksft_exit_fail_msg("mmap unexpectedly succeeded with hint\n");175			break;176		}177 178		validate_addr(ptr[i], 0);179	}180	lchunks = i;181	hptr = (char **) calloc(NR_CHUNKS_HIGH, sizeof(char *));182	if (hptr == NULL) {183		ksft_test_result_skip("Memory constraint not fulfilled\n");184		ksft_finished();185	}186 187	for (i = 0; i < NR_CHUNKS_HIGH; i++) {188		hint = hind_addr();189		hptr[i] = mmap(hint, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE,190			       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);191 192		if (hptr[i] == MAP_FAILED)193			break;194 195		validate_addr(hptr[i], 1);196	}197	hchunks = i;198	if (validate_complete_va_space()) {199		ksft_test_result_fail("BUG in mmap() or /proc/self/maps\n");200		ksft_finished();201	}202 203	for (i = 0; i < lchunks; i++)204		munmap(ptr[i], MAP_CHUNK_SIZE);205 206	for (i = 0; i < hchunks; i++)207		munmap(hptr[i], MAP_CHUNK_SIZE);208 209	free(hptr);210 211	ksft_test_result_pass("Test\n");212	ksft_finished();213}214