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