diff options
| author | Paul Mackerras <paulus@samba.org> | 2014-12-09 01:14:07 -0500 |
|---|---|---|
| committer | Michael Ellerman <mpe@ellerman.id.au> | 2015-01-22 22:02:49 -0500 |
| commit | 3776c2096790233b06c8b4e82046daa77c63fced (patch) | |
| tree | 8c066ed2b434ac7533755dcf7d606f5605daf1c4 /tools/testing | |
| parent | b64eedb88b43da9e1ecfe4c197892e6562f4e1df (diff) | |
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>
Diffstat (limited to 'tools/testing')
| -rw-r--r-- | tools/testing/selftests/powerpc/mm/.gitignore | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/mm/Makefile | 9 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/mm/subpage_prot.c | 220 |
3 files changed, 228 insertions, 3 deletions
diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index 156f4e89c2f1..b43ade0ec861 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore | |||
| @@ -1 +1,3 @@ | |||
| 1 | hugetlb_vs_thp_test | 1 | hugetlb_vs_thp_test |
| 2 | subpage_prot | ||
| 3 | tempfile | ||
diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index 357ccbd6bad9..a14c538dd7f8 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | noarg: | 1 | noarg: |
| 2 | $(MAKE) -C ../ | 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 | $(PROGS): ../harness.c | 8 | $(PROGS): ../harness.c |
| 9 | 9 | ||
| @@ -12,7 +12,10 @@ run_tests: all | |||
| 12 | ./$$PROG; \ | 12 | ./$$PROG; \ |
| 13 | done; | 13 | done; |
| 14 | 14 | ||
| 15 | tempfile: | ||
| 16 | dd if=/dev/zero of=tempfile bs=64k count=1 | ||
| 17 | |||
| 15 | clean: | 18 | clean: |
| 16 | rm -f $(PROGS) | 19 | rm -f $(PROGS) tempfile |
| 17 | 20 | ||
| 18 | .PHONY: all run_tests clean | 21 | .PHONY: all run_tests clean |
diff --git a/tools/testing/selftests/powerpc/mm/subpage_prot.c b/tools/testing/selftests/powerpc/mm/subpage_prot.c new file mode 100644 index 000000000000..440180ff8089 --- /dev/null +++ b/tools/testing/selftests/powerpc/mm/subpage_prot.c | |||
| @@ -0,0 +1,220 @@ | |||
| 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 | } | ||
