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 | |
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')
-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 | } | ||