linux/tools/testing/selftests/powerpc/mm/subpage_prot.c
<<
>>
Prefs
   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
  30char *file_name;
  31
  32int in_test;
  33volatile int faulted;
  34volatile void *dar;
  35int errors;
  36
  37static 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
  52static 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
  60static 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
  68static 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
  93static 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
 138int test_anon(void)
 139{
 140        unsigned long align;
 141        struct sigaction act = {
 142                .sa_sigaction = segv,
 143                .sa_flags = SA_SIGINFO
 144        };
 145        void *mallocblock;
 146        unsigned long mallocsize;
 147
 148        if (getpagesize() != 0x10000) {
 149                fprintf(stderr, "Kernel page size must be 64K!\n");
 150                return 1;
 151        }
 152
 153        sigaction(SIGSEGV, &act, NULL);
 154
 155        mallocsize = 4 * 16 * 1024 * 1024;
 156
 157        FAIL_IF(posix_memalign(&mallocblock, 64 * 1024, mallocsize));
 158
 159        align = (unsigned long)mallocblock;
 160        if (align & 0xffff)
 161                align = (align | 0xffff) + 1;
 162
 163        mallocblock = (void *)align;
 164
 165        printf("allocated malloc block of 0x%lx bytes at %p\n",
 166               mallocsize, mallocblock);
 167
 168        printf("testing malloc block...\n");
 169
 170        return run_test(mallocblock, mallocsize);
 171}
 172
 173int test_file(void)
 174{
 175        struct sigaction act = {
 176                .sa_sigaction = segv,
 177                .sa_flags = SA_SIGINFO
 178        };
 179        void *fileblock;
 180        off_t filesize;
 181        int fd;
 182
 183        fd = open(file_name, O_RDWR);
 184        if (fd == -1) {
 185                perror("failed to open file");
 186                return 1;
 187        }
 188        sigaction(SIGSEGV, &act, NULL);
 189
 190        filesize = lseek(fd, 0, SEEK_END);
 191        if (filesize & 0xffff)
 192                filesize &= ~0xfffful;
 193
 194        fileblock = mmap(NULL, filesize, PROT_READ | PROT_WRITE,
 195                         MAP_SHARED, fd, 0);
 196        if (fileblock == MAP_FAILED) {
 197                perror("failed to map file");
 198                return 1;
 199        }
 200        printf("allocated %s for 0x%lx bytes at %p\n",
 201               file_name, filesize, fileblock);
 202
 203        printf("testing file map...\n");
 204
 205        return run_test(fileblock, filesize);
 206}
 207
 208int main(int argc, char *argv[])
 209{
 210        int rc;
 211
 212        rc = test_harness(test_anon, "subpage_prot_anon");
 213        if (rc)
 214                return rc;
 215
 216        if (argc > 1)
 217                file_name = argv[1];
 218        else
 219                file_name = "tempfile";
 220
 221        return test_harness(test_file, "subpage_prot_file");
 222}
 223