162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright IBM Corp.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it
562306a36Sopenharmony_ci * under the terms of version 2.1 of the GNU Lesser General Public License
662306a36Sopenharmony_ci * as published by the Free Software Foundation.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This program is distributed in the hope that it would be useful, but
962306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
1062306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <assert.h>
1562306a36Sopenharmony_ci#include <errno.h>
1662306a36Sopenharmony_ci#include <fcntl.h>
1762306a36Sopenharmony_ci#include <signal.h>
1862306a36Sopenharmony_ci#include <stdarg.h>
1962306a36Sopenharmony_ci#include <stdio.h>
2062306a36Sopenharmony_ci#include <stdlib.h>
2162306a36Sopenharmony_ci#include <string.h>
2262306a36Sopenharmony_ci#include <sys/mman.h>
2362306a36Sopenharmony_ci#include <sys/ptrace.h>
2462306a36Sopenharmony_ci#include <sys/syscall.h>
2562306a36Sopenharmony_ci#include <ucontext.h>
2662306a36Sopenharmony_ci#include <unistd.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "utils.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cichar *file_name;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciint in_test;
3362306a36Sopenharmony_civolatile int faulted;
3462306a36Sopenharmony_civolatile void *dar;
3562306a36Sopenharmony_ciint errors;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void segv(int signum, siginfo_t *info, void *ctxt_v)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	ucontext_t *ctxt = (ucontext_t *)ctxt_v;
4062306a36Sopenharmony_ci	struct pt_regs *regs = ctxt->uc_mcontext.regs;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!in_test) {
4362306a36Sopenharmony_ci		fprintf(stderr, "Segfault outside of test !\n");
4462306a36Sopenharmony_ci		exit(1);
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	faulted = 1;
4862306a36Sopenharmony_ci	dar = (void *)regs->dar;
4962306a36Sopenharmony_ci	regs->nip += 4;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic inline void do_read(const volatile void *addr)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	int ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	asm volatile("lwz %0,0(%1); twi 0,%0,0; isync;\n"
5762306a36Sopenharmony_ci		     : "=r" (ret) : "r" (addr) : "memory");
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic inline void do_write(const volatile void *addr)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	int val = 0x1234567;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	asm volatile("stw %0,0(%1); sync; \n"
6562306a36Sopenharmony_ci		     : : "r" (val), "r" (addr) : "memory");
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic inline void check_faulted(void *addr, long page, long subpage, int write)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int want_fault = (subpage == ((page + 3) % 16));
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (write)
7362306a36Sopenharmony_ci		want_fault |= (subpage == ((page + 1) % 16));
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (faulted != want_fault) {
7662306a36Sopenharmony_ci		printf("Failed at %p (p=%ld,sp=%ld,w=%d), want=%s, got=%s !\n",
7762306a36Sopenharmony_ci		       addr, page, subpage, write,
7862306a36Sopenharmony_ci		       want_fault ? "fault" : "pass",
7962306a36Sopenharmony_ci		       faulted ? "fault" : "pass");
8062306a36Sopenharmony_ci		++errors;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (faulted) {
8462306a36Sopenharmony_ci		if (dar != addr) {
8562306a36Sopenharmony_ci			printf("Fault expected at %p and happened at %p !\n",
8662306a36Sopenharmony_ci			       addr, dar);
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci		faulted = 0;
8962306a36Sopenharmony_ci		asm volatile("sync" : : : "memory");
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int run_test(void *addr, unsigned long size)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	unsigned int *map;
9662306a36Sopenharmony_ci	long i, j, pages, err;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	pages = size / 0x10000;
9962306a36Sopenharmony_ci	map = malloc(pages * 4);
10062306a36Sopenharmony_ci	assert(map);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/*
10362306a36Sopenharmony_ci	 * for each page, mark subpage i % 16 read only and subpage
10462306a36Sopenharmony_ci	 * (i + 3) % 16 inaccessible
10562306a36Sopenharmony_ci	 */
10662306a36Sopenharmony_ci	for (i = 0; i < pages; i++) {
10762306a36Sopenharmony_ci		map[i] = (0x40000000 >> (((i + 1) * 2) % 32)) |
10862306a36Sopenharmony_ci			(0xc0000000 >> (((i + 3) * 2) % 32));
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	err = syscall(__NR_subpage_prot, addr, size, map);
11262306a36Sopenharmony_ci	if (err) {
11362306a36Sopenharmony_ci		perror("subpage_perm");
11462306a36Sopenharmony_ci		return 1;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci	free(map);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	in_test = 1;
11962306a36Sopenharmony_ci	errors = 0;
12062306a36Sopenharmony_ci	for (i = 0; i < pages; i++) {
12162306a36Sopenharmony_ci		for (j = 0; j < 16; j++, addr += 0x1000) {
12262306a36Sopenharmony_ci			do_read(addr);
12362306a36Sopenharmony_ci			check_faulted(addr, i, j, 0);
12462306a36Sopenharmony_ci			do_write(addr);
12562306a36Sopenharmony_ci			check_faulted(addr, i, j, 1);
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	in_test = 0;
13062306a36Sopenharmony_ci	if (errors) {
13162306a36Sopenharmony_ci		printf("%d errors detected\n", errors);
13262306a36Sopenharmony_ci		return 1;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return 0;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int syscall_available(void)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	int rc;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	errno = 0;
14362306a36Sopenharmony_ci	rc = syscall(__NR_subpage_prot, 0, 0, 0);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return rc == 0 || (errno != ENOENT && errno != ENOSYS);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ciint test_anon(void)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	unsigned long align;
15162306a36Sopenharmony_ci	struct sigaction act = {
15262306a36Sopenharmony_ci		.sa_sigaction = segv,
15362306a36Sopenharmony_ci		.sa_flags = SA_SIGINFO
15462306a36Sopenharmony_ci	};
15562306a36Sopenharmony_ci	void *mallocblock;
15662306a36Sopenharmony_ci	unsigned long mallocsize;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	SKIP_IF(!syscall_available());
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (getpagesize() != 0x10000) {
16162306a36Sopenharmony_ci		fprintf(stderr, "Kernel page size must be 64K!\n");
16262306a36Sopenharmony_ci		return 1;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	sigaction(SIGSEGV, &act, NULL);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	mallocsize = 4 * 16 * 1024 * 1024;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	FAIL_IF(posix_memalign(&mallocblock, 64 * 1024, mallocsize));
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	align = (unsigned long)mallocblock;
17262306a36Sopenharmony_ci	if (align & 0xffff)
17362306a36Sopenharmony_ci		align = (align | 0xffff) + 1;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	mallocblock = (void *)align;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	printf("allocated malloc block of 0x%lx bytes at %p\n",
17862306a36Sopenharmony_ci	       mallocsize, mallocblock);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	printf("testing malloc block...\n");
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return run_test(mallocblock, mallocsize);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ciint test_file(void)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct sigaction act = {
18862306a36Sopenharmony_ci		.sa_sigaction = segv,
18962306a36Sopenharmony_ci		.sa_flags = SA_SIGINFO
19062306a36Sopenharmony_ci	};
19162306a36Sopenharmony_ci	void *fileblock;
19262306a36Sopenharmony_ci	off_t filesize;
19362306a36Sopenharmony_ci	int fd;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	SKIP_IF(!syscall_available());
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	fd = open(file_name, O_RDWR);
19862306a36Sopenharmony_ci	if (fd == -1) {
19962306a36Sopenharmony_ci		perror("failed to open file");
20062306a36Sopenharmony_ci		return 1;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	sigaction(SIGSEGV, &act, NULL);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	filesize = lseek(fd, 0, SEEK_END);
20562306a36Sopenharmony_ci	if (filesize & 0xffff)
20662306a36Sopenharmony_ci		filesize &= ~0xfffful;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	fileblock = mmap(NULL, filesize, PROT_READ | PROT_WRITE,
20962306a36Sopenharmony_ci			 MAP_SHARED, fd, 0);
21062306a36Sopenharmony_ci	if (fileblock == MAP_FAILED) {
21162306a36Sopenharmony_ci		perror("failed to map file");
21262306a36Sopenharmony_ci		return 1;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	printf("allocated %s for 0x%lx bytes at %p\n",
21562306a36Sopenharmony_ci	       file_name, filesize, fileblock);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	printf("testing file map...\n");
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return run_test(fileblock, filesize);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ciint main(int argc, char *argv[])
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	int rc;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	rc = test_harness(test_anon, "subpage_prot_anon");
22762306a36Sopenharmony_ci	if (rc)
22862306a36Sopenharmony_ci		return rc;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (argc > 1)
23162306a36Sopenharmony_ci		file_name = argv[1];
23262306a36Sopenharmony_ci	else
23362306a36Sopenharmony_ci		file_name = "tempfile";
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return test_harness(test_file, "subpage_prot_file");
23662306a36Sopenharmony_ci}
237