root/tools/testing/selftests/powerpc/mm/subpage_prot.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. segv
  2. do_read
  3. do_write
  4. check_faulted
  5. run_test
  6. syscall_available
  7. test_anon
  8. test_file
  9. main

   1 /*
   2  * Copyright IBM Corp.
   3  *
   4  * This program is free software; you can redistribute it and/or modify it
   5  * under the terms of version 2.1 of the GNU Lesser General Public License
   6  * as published by the Free Software Foundation.
   7  *
   8  * This program is distributed in the hope that it would be useful, but
   9  * WITHOUT ANY WARRANTY; without even the implied warranty of
  10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11  *
  12  */
  13 
  14 #include <assert.h>
  15 #include <errno.h>
  16 #include <fcntl.h>
  17 #include <signal.h>
  18 #include <stdarg.h>
  19 #include <stdio.h>
  20 #include <stdlib.h>
  21 #include <string.h>
  22 #include <sys/mman.h>
  23 #include <sys/ptrace.h>
  24 #include <sys/syscall.h>
  25 #include <ucontext.h>
  26 #include <unistd.h>
  27 
  28 #include "utils.h"
  29 
  30 char *file_name;
  31 
  32 int in_test;
  33 volatile int faulted;
  34 volatile void *dar;
  35 int errors;
  36 
  37 static void segv(int signum, siginfo_t *info, void *ctxt_v)
  38 {
  39         ucontext_t *ctxt = (ucontext_t *)ctxt_v;
  40         struct pt_regs *regs = ctxt->uc_mcontext.regs;
  41 
  42         if (!in_test) {
  43                 fprintf(stderr, "Segfault outside of test !\n");
  44                 exit(1);
  45         }
  46 
  47         faulted = 1;
  48         dar = (void *)regs->dar;
  49         regs->nip += 4;
  50 }
  51 
  52 static inline void do_read(const volatile void *addr)
  53 {
  54         int ret;
  55 
  56         asm volatile("lwz %0,0(%1); twi 0,%0,0; isync;\n"
  57                      : "=r" (ret) : "r" (addr) : "memory");
  58 }
  59 
  60 static inline void do_write(const volatile void *addr)
  61 {
  62         int val = 0x1234567;
  63 
  64         asm volatile("stw %0,0(%1); sync; \n"
  65                      : : "r" (val), "r" (addr) : "memory");
  66 }
  67 
  68 static inline void check_faulted(void *addr, long page, long subpage, int write)
  69 {
  70         int want_fault = (subpage == ((page + 3) % 16));
  71 
  72         if (write)
  73                 want_fault |= (subpage == ((page + 1) % 16));
  74 
  75         if (faulted != want_fault) {
  76                 printf("Failed at %p (p=%ld,sp=%ld,w=%d), want=%s, got=%s !\n",
  77                        addr, page, subpage, write,
  78                        want_fault ? "fault" : "pass",
  79                        faulted ? "fault" : "pass");
  80                 ++errors;
  81         }
  82 
  83         if (faulted) {
  84                 if (dar != addr) {
  85                         printf("Fault expected at %p and happened at %p !\n",
  86                                addr, dar);
  87                 }
  88                 faulted = 0;
  89                 asm volatile("sync" : : : "memory");
  90         }
  91 }
  92 
  93 static int run_test(void *addr, unsigned long size)
  94 {
  95         unsigned int *map;
  96         long i, j, pages, err;
  97 
  98         pages = size / 0x10000;
  99         map = malloc(pages * 4);
 100         assert(map);
 101 
 102         /*
 103          * for each page, mark subpage i % 16 read only and subpage
 104          * (i + 3) % 16 inaccessible
 105          */
 106         for (i = 0; i < pages; i++) {
 107                 map[i] = (0x40000000 >> (((i + 1) * 2) % 32)) |
 108                         (0xc0000000 >> (((i + 3) * 2) % 32));
 109         }
 110 
 111         err = syscall(__NR_subpage_prot, addr, size, map);
 112         if (err) {
 113                 perror("subpage_perm");
 114                 return 1;
 115         }
 116         free(map);
 117 
 118         in_test = 1;
 119         errors = 0;
 120         for (i = 0; i < pages; i++) {
 121                 for (j = 0; j < 16; j++, addr += 0x1000) {
 122                         do_read(addr);
 123                         check_faulted(addr, i, j, 0);
 124                         do_write(addr);
 125                         check_faulted(addr, i, j, 1);
 126                 }
 127         }
 128 
 129         in_test = 0;
 130         if (errors) {
 131                 printf("%d errors detected\n", errors);
 132                 return 1;
 133         }
 134 
 135         return 0;
 136 }
 137 
 138 static int syscall_available(void)
 139 {
 140         int rc;
 141 
 142         errno = 0;
 143         rc = syscall(__NR_subpage_prot, 0, 0, 0);
 144 
 145         return rc == 0 || (errno != ENOENT && errno != ENOSYS);
 146 }
 147 
 148 int test_anon(void)
 149 {
 150         unsigned long align;
 151         struct sigaction act = {
 152                 .sa_sigaction = segv,
 153                 .sa_flags = SA_SIGINFO
 154         };
 155         void *mallocblock;
 156         unsigned long mallocsize;
 157 
 158         SKIP_IF(!syscall_available());
 159 
 160         if (getpagesize() != 0x10000) {
 161                 fprintf(stderr, "Kernel page size must be 64K!\n");
 162                 return 1;
 163         }
 164 
 165         sigaction(SIGSEGV, &act, NULL);
 166 
 167         mallocsize = 4 * 16 * 1024 * 1024;
 168 
 169         FAIL_IF(posix_memalign(&mallocblock, 64 * 1024, mallocsize));
 170 
 171         align = (unsigned long)mallocblock;
 172         if (align & 0xffff)
 173                 align = (align | 0xffff) + 1;
 174 
 175         mallocblock = (void *)align;
 176 
 177         printf("allocated malloc block of 0x%lx bytes at %p\n",
 178                mallocsize, mallocblock);
 179 
 180         printf("testing malloc block...\n");
 181 
 182         return run_test(mallocblock, mallocsize);
 183 }
 184 
 185 int test_file(void)
 186 {
 187         struct sigaction act = {
 188                 .sa_sigaction = segv,
 189                 .sa_flags = SA_SIGINFO
 190         };
 191         void *fileblock;
 192         off_t filesize;
 193         int fd;
 194 
 195         SKIP_IF(!syscall_available());
 196 
 197         fd = open(file_name, O_RDWR);
 198         if (fd == -1) {
 199                 perror("failed to open file");
 200                 return 1;
 201         }
 202         sigaction(SIGSEGV, &act, NULL);
 203 
 204         filesize = lseek(fd, 0, SEEK_END);
 205         if (filesize & 0xffff)
 206                 filesize &= ~0xfffful;
 207 
 208         fileblock = mmap(NULL, filesize, PROT_READ | PROT_WRITE,
 209                          MAP_SHARED, fd, 0);
 210         if (fileblock == MAP_FAILED) {
 211                 perror("failed to map file");
 212                 return 1;
 213         }
 214         printf("allocated %s for 0x%lx bytes at %p\n",
 215                file_name, filesize, fileblock);
 216 
 217         printf("testing file map...\n");
 218 
 219         return run_test(fileblock, filesize);
 220 }
 221 
 222 int main(int argc, char *argv[])
 223 {
 224         int rc;
 225 
 226         rc = test_harness(test_anon, "subpage_prot_anon");
 227         if (rc)
 228                 return rc;
 229 
 230         if (argc > 1)
 231                 file_name = argv[1];
 232         else
 233                 file_name = "tempfile";
 234 
 235         return test_harness(test_file, "subpage_prot_file");
 236 }

/* [<][>][^][v][top][bottom][index][help] */