Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/powerpc: Add subpage protection self test.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>

mpe: Fix compile errors and formatting. Add tempfile logic to Makefile.

Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Paul Mackerras and committed by
Michael Ellerman
3776c209 b64eedb8

+228 -3
+2
tools/testing/selftests/powerpc/mm/.gitignore
··· 1 1 hugetlb_vs_thp_test 2 + subpage_prot 3 + tempfile
+6 -3
tools/testing/selftests/powerpc/mm/Makefile
··· 1 1 noarg: 2 2 $(MAKE) -C ../ 3 3 4 - PROGS := hugetlb_vs_thp_test 4 + PROGS := hugetlb_vs_thp_test subpage_prot 5 5 6 - all: $(PROGS) 6 + all: $(PROGS) tempfile 7 7 8 8 $(PROGS): ../harness.c 9 9 ··· 12 12 ./$$PROG; \ 13 13 done; 14 14 15 + tempfile: 16 + dd if=/dev/zero of=tempfile bs=64k count=1 17 + 15 18 clean: 16 - rm -f $(PROGS) 19 + rm -f $(PROGS) tempfile 17 20 18 21 .PHONY: all run_tests clean
+220
tools/testing/selftests/powerpc/mm/subpage_prot.c
··· 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 0x%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 0x%p and happened at 0x%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 0x%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 0x%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 + test_harness(test_anon, "subpage_prot_anon"); 211 + 212 + if (argc > 1) 213 + file_name = argv[1]; 214 + else 215 + file_name = "tempfile"; 216 + 217 + test_harness(test_file, "subpage_prot_file"); 218 + 219 + return 0; 220 + }