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 int 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 
    173 int 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 
    208 int 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