xref: /openbmc/linux/tools/testing/selftests/powerpc/mm/subpage_prot.c (revision c13aca79ff3c4af5fd31a5b2743a90eba6e36a26)
13776c209SPaul Mackerras /*
23776c209SPaul Mackerras  * Copyright IBM Corp.
33776c209SPaul Mackerras  *
43776c209SPaul Mackerras  * This program is free software; you can redistribute it and/or modify it
53776c209SPaul Mackerras  * under the terms of version 2.1 of the GNU Lesser General Public License
63776c209SPaul Mackerras  * as published by the Free Software Foundation.
73776c209SPaul Mackerras  *
83776c209SPaul Mackerras  * This program is distributed in the hope that it would be useful, but
93776c209SPaul Mackerras  * WITHOUT ANY WARRANTY; without even the implied warranty of
103776c209SPaul Mackerras  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
113776c209SPaul Mackerras  *
123776c209SPaul Mackerras  */
133776c209SPaul Mackerras 
143776c209SPaul Mackerras #include <assert.h>
153776c209SPaul Mackerras #include <errno.h>
163776c209SPaul Mackerras #include <fcntl.h>
173776c209SPaul Mackerras #include <signal.h>
183776c209SPaul Mackerras #include <stdarg.h>
193776c209SPaul Mackerras #include <stdio.h>
203776c209SPaul Mackerras #include <stdlib.h>
213776c209SPaul Mackerras #include <string.h>
223776c209SPaul Mackerras #include <sys/mman.h>
233776c209SPaul Mackerras #include <sys/ptrace.h>
243776c209SPaul Mackerras #include <sys/syscall.h>
253776c209SPaul Mackerras #include <ucontext.h>
263776c209SPaul Mackerras #include <unistd.h>
273776c209SPaul Mackerras 
283776c209SPaul Mackerras #include "utils.h"
293776c209SPaul Mackerras 
303776c209SPaul Mackerras char *file_name;
313776c209SPaul Mackerras 
323776c209SPaul Mackerras int in_test;
333776c209SPaul Mackerras volatile int faulted;
343776c209SPaul Mackerras volatile void *dar;
353776c209SPaul Mackerras int errors;
363776c209SPaul Mackerras 
segv(int signum,siginfo_t * info,void * ctxt_v)373776c209SPaul Mackerras static void segv(int signum, siginfo_t *info, void *ctxt_v)
383776c209SPaul Mackerras {
393776c209SPaul Mackerras 	ucontext_t *ctxt = (ucontext_t *)ctxt_v;
403776c209SPaul Mackerras 	struct pt_regs *regs = ctxt->uc_mcontext.regs;
413776c209SPaul Mackerras 
423776c209SPaul Mackerras 	if (!in_test) {
433776c209SPaul Mackerras 		fprintf(stderr, "Segfault outside of test !\n");
443776c209SPaul Mackerras 		exit(1);
453776c209SPaul Mackerras 	}
463776c209SPaul Mackerras 
473776c209SPaul Mackerras 	faulted = 1;
483776c209SPaul Mackerras 	dar = (void *)regs->dar;
493776c209SPaul Mackerras 	regs->nip += 4;
503776c209SPaul Mackerras }
513776c209SPaul Mackerras 
do_read(const volatile void * addr)523776c209SPaul Mackerras static inline void do_read(const volatile void *addr)
533776c209SPaul Mackerras {
543776c209SPaul Mackerras 	int ret;
553776c209SPaul Mackerras 
563776c209SPaul Mackerras 	asm volatile("lwz %0,0(%1); twi 0,%0,0; isync;\n"
573776c209SPaul Mackerras 		     : "=r" (ret) : "r" (addr) : "memory");
583776c209SPaul Mackerras }
593776c209SPaul Mackerras 
do_write(const volatile void * addr)603776c209SPaul Mackerras static inline void do_write(const volatile void *addr)
613776c209SPaul Mackerras {
623776c209SPaul Mackerras 	int val = 0x1234567;
633776c209SPaul Mackerras 
643776c209SPaul Mackerras 	asm volatile("stw %0,0(%1); sync; \n"
653776c209SPaul Mackerras 		     : : "r" (val), "r" (addr) : "memory");
663776c209SPaul Mackerras }
673776c209SPaul Mackerras 
check_faulted(void * addr,long page,long subpage,int write)683776c209SPaul Mackerras static inline void check_faulted(void *addr, long page, long subpage, int write)
693776c209SPaul Mackerras {
703776c209SPaul Mackerras 	int want_fault = (subpage == ((page + 3) % 16));
713776c209SPaul Mackerras 
723776c209SPaul Mackerras 	if (write)
733776c209SPaul Mackerras 		want_fault |= (subpage == ((page + 1) % 16));
743776c209SPaul Mackerras 
753776c209SPaul Mackerras 	if (faulted != want_fault) {
762f67798cSMichael Ellerman 		printf("Failed at %p (p=%ld,sp=%ld,w=%d), want=%s, got=%s !\n",
773776c209SPaul Mackerras 		       addr, page, subpage, write,
783776c209SPaul Mackerras 		       want_fault ? "fault" : "pass",
793776c209SPaul Mackerras 		       faulted ? "fault" : "pass");
803776c209SPaul Mackerras 		++errors;
813776c209SPaul Mackerras 	}
823776c209SPaul Mackerras 
833776c209SPaul Mackerras 	if (faulted) {
843776c209SPaul Mackerras 		if (dar != addr) {
852f67798cSMichael Ellerman 			printf("Fault expected at %p and happened at %p !\n",
863776c209SPaul Mackerras 			       addr, dar);
873776c209SPaul Mackerras 		}
883776c209SPaul Mackerras 		faulted = 0;
893776c209SPaul Mackerras 		asm volatile("sync" : : : "memory");
903776c209SPaul Mackerras 	}
913776c209SPaul Mackerras }
923776c209SPaul Mackerras 
run_test(void * addr,unsigned long size)933776c209SPaul Mackerras static int run_test(void *addr, unsigned long size)
943776c209SPaul Mackerras {
953776c209SPaul Mackerras 	unsigned int *map;
963776c209SPaul Mackerras 	long i, j, pages, err;
973776c209SPaul Mackerras 
983776c209SPaul Mackerras 	pages = size / 0x10000;
993776c209SPaul Mackerras 	map = malloc(pages * 4);
1003776c209SPaul Mackerras 	assert(map);
1013776c209SPaul Mackerras 
1023776c209SPaul Mackerras 	/*
1033776c209SPaul Mackerras 	 * for each page, mark subpage i % 16 read only and subpage
1043776c209SPaul Mackerras 	 * (i + 3) % 16 inaccessible
1053776c209SPaul Mackerras 	 */
1063776c209SPaul Mackerras 	for (i = 0; i < pages; i++) {
1073776c209SPaul Mackerras 		map[i] = (0x40000000 >> (((i + 1) * 2) % 32)) |
1083776c209SPaul Mackerras 			(0xc0000000 >> (((i + 3) * 2) % 32));
1093776c209SPaul Mackerras 	}
1103776c209SPaul Mackerras 
1113776c209SPaul Mackerras 	err = syscall(__NR_subpage_prot, addr, size, map);
1123776c209SPaul Mackerras 	if (err) {
1133776c209SPaul Mackerras 		perror("subpage_perm");
1143776c209SPaul Mackerras 		return 1;
1153776c209SPaul Mackerras 	}
1163776c209SPaul Mackerras 	free(map);
1173776c209SPaul Mackerras 
1183776c209SPaul Mackerras 	in_test = 1;
1193776c209SPaul Mackerras 	errors = 0;
1203776c209SPaul Mackerras 	for (i = 0; i < pages; i++) {
1213776c209SPaul Mackerras 		for (j = 0; j < 16; j++, addr += 0x1000) {
1223776c209SPaul Mackerras 			do_read(addr);
1233776c209SPaul Mackerras 			check_faulted(addr, i, j, 0);
1243776c209SPaul Mackerras 			do_write(addr);
1253776c209SPaul Mackerras 			check_faulted(addr, i, j, 1);
1263776c209SPaul Mackerras 		}
1273776c209SPaul Mackerras 	}
1283776c209SPaul Mackerras 
1293776c209SPaul Mackerras 	in_test = 0;
1303776c209SPaul Mackerras 	if (errors) {
1313776c209SPaul Mackerras 		printf("%d errors detected\n", errors);
1323776c209SPaul Mackerras 		return 1;
1333776c209SPaul Mackerras 	}
1343776c209SPaul Mackerras 
1353776c209SPaul Mackerras 	return 0;
1363776c209SPaul Mackerras }
1373776c209SPaul Mackerras 
syscall_available(void)138*cd4a6f3aSMichael Ellerman static int syscall_available(void)
139*cd4a6f3aSMichael Ellerman {
140*cd4a6f3aSMichael Ellerman 	int rc;
141*cd4a6f3aSMichael Ellerman 
142*cd4a6f3aSMichael Ellerman 	errno = 0;
143*cd4a6f3aSMichael Ellerman 	rc = syscall(__NR_subpage_prot, 0, 0, 0);
144*cd4a6f3aSMichael Ellerman 
145*cd4a6f3aSMichael Ellerman 	return rc == 0 || (errno != ENOENT && errno != ENOSYS);
146*cd4a6f3aSMichael Ellerman }
147*cd4a6f3aSMichael Ellerman 
test_anon(void)1483776c209SPaul Mackerras int test_anon(void)
1493776c209SPaul Mackerras {
1503776c209SPaul Mackerras 	unsigned long align;
1513776c209SPaul Mackerras 	struct sigaction act = {
1523776c209SPaul Mackerras 		.sa_sigaction = segv,
1533776c209SPaul Mackerras 		.sa_flags = SA_SIGINFO
1543776c209SPaul Mackerras 	};
1553776c209SPaul Mackerras 	void *mallocblock;
1563776c209SPaul Mackerras 	unsigned long mallocsize;
1573776c209SPaul Mackerras 
158*cd4a6f3aSMichael Ellerman 	SKIP_IF(!syscall_available());
159*cd4a6f3aSMichael Ellerman 
1603776c209SPaul Mackerras 	if (getpagesize() != 0x10000) {
1613776c209SPaul Mackerras 		fprintf(stderr, "Kernel page size must be 64K!\n");
1623776c209SPaul Mackerras 		return 1;
1633776c209SPaul Mackerras 	}
1643776c209SPaul Mackerras 
1653776c209SPaul Mackerras 	sigaction(SIGSEGV, &act, NULL);
1663776c209SPaul Mackerras 
1673776c209SPaul Mackerras 	mallocsize = 4 * 16 * 1024 * 1024;
1683776c209SPaul Mackerras 
1693776c209SPaul Mackerras 	FAIL_IF(posix_memalign(&mallocblock, 64 * 1024, mallocsize));
1703776c209SPaul Mackerras 
1713776c209SPaul Mackerras 	align = (unsigned long)mallocblock;
1723776c209SPaul Mackerras 	if (align & 0xffff)
1733776c209SPaul Mackerras 		align = (align | 0xffff) + 1;
1743776c209SPaul Mackerras 
1753776c209SPaul Mackerras 	mallocblock = (void *)align;
1763776c209SPaul Mackerras 
1772f67798cSMichael Ellerman 	printf("allocated malloc block of 0x%lx bytes at %p\n",
1783776c209SPaul Mackerras 	       mallocsize, mallocblock);
1793776c209SPaul Mackerras 
1803776c209SPaul Mackerras 	printf("testing malloc block...\n");
1813776c209SPaul Mackerras 
1823776c209SPaul Mackerras 	return run_test(mallocblock, mallocsize);
1833776c209SPaul Mackerras }
1843776c209SPaul Mackerras 
test_file(void)1853776c209SPaul Mackerras int test_file(void)
1863776c209SPaul Mackerras {
1873776c209SPaul Mackerras 	struct sigaction act = {
1883776c209SPaul Mackerras 		.sa_sigaction = segv,
1893776c209SPaul Mackerras 		.sa_flags = SA_SIGINFO
1903776c209SPaul Mackerras 	};
1913776c209SPaul Mackerras 	void *fileblock;
1923776c209SPaul Mackerras 	off_t filesize;
1933776c209SPaul Mackerras 	int fd;
1943776c209SPaul Mackerras 
195*cd4a6f3aSMichael Ellerman 	SKIP_IF(!syscall_available());
196*cd4a6f3aSMichael Ellerman 
1973776c209SPaul Mackerras 	fd = open(file_name, O_RDWR);
1983776c209SPaul Mackerras 	if (fd == -1) {
1993776c209SPaul Mackerras 		perror("failed to open file");
2003776c209SPaul Mackerras 		return 1;
2013776c209SPaul Mackerras 	}
2023776c209SPaul Mackerras 	sigaction(SIGSEGV, &act, NULL);
2033776c209SPaul Mackerras 
2043776c209SPaul Mackerras 	filesize = lseek(fd, 0, SEEK_END);
2053776c209SPaul Mackerras 	if (filesize & 0xffff)
2063776c209SPaul Mackerras 		filesize &= ~0xfffful;
2073776c209SPaul Mackerras 
2083776c209SPaul Mackerras 	fileblock = mmap(NULL, filesize, PROT_READ | PROT_WRITE,
2093776c209SPaul Mackerras 			 MAP_SHARED, fd, 0);
2103776c209SPaul Mackerras 	if (fileblock == MAP_FAILED) {
2113776c209SPaul Mackerras 		perror("failed to map file");
2123776c209SPaul Mackerras 		return 1;
2133776c209SPaul Mackerras 	}
2142f67798cSMichael Ellerman 	printf("allocated %s for 0x%lx bytes at %p\n",
2153776c209SPaul Mackerras 	       file_name, filesize, fileblock);
2163776c209SPaul Mackerras 
2173776c209SPaul Mackerras 	printf("testing file map...\n");
2183776c209SPaul Mackerras 
2193776c209SPaul Mackerras 	return run_test(fileblock, filesize);
2203776c209SPaul Mackerras }
2213776c209SPaul Mackerras 
main(int argc,char * argv[])2223776c209SPaul Mackerras int main(int argc, char *argv[])
2233776c209SPaul Mackerras {
2242f67798cSMichael Ellerman 	int rc;
2252f67798cSMichael Ellerman 
2262f67798cSMichael Ellerman 	rc = test_harness(test_anon, "subpage_prot_anon");
2272f67798cSMichael Ellerman 	if (rc)
2282f67798cSMichael Ellerman 		return rc;
2293776c209SPaul Mackerras 
2303776c209SPaul Mackerras 	if (argc > 1)
2313776c209SPaul Mackerras 		file_name = argv[1];
2323776c209SPaul Mackerras 	else
2333776c209SPaul Mackerras 		file_name = "tempfile";
2343776c209SPaul Mackerras 
2352f67798cSMichael Ellerman 	return test_harness(test_file, "subpage_prot_file");
2363776c209SPaul Mackerras }
237