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
 138static 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
 148int 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
 185int 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
 222int 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}
 237