Home | History | Annotate | Download | only in mm
      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 }
    237