diff options
Diffstat (limited to 'arch/powerpc/lib/test_emulate_step.c')
-rw-r--r-- | arch/powerpc/lib/test_emulate_step.c | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c new file mode 100644 index 000000000000..2534c1447554 --- /dev/null +++ b/arch/powerpc/lib/test_emulate_step.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * Simple sanity test for emulate_step load/store instructions. | ||
3 | * | ||
4 | * Copyright IBM Corp. 2016 | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "emulate_step_test: " fmt | ||
13 | |||
14 | #include <linux/ptrace.h> | ||
15 | #include <asm/sstep.h> | ||
16 | #include <asm/ppc-opcode.h> | ||
17 | |||
18 | #define IMM_L(i) ((uintptr_t)(i) & 0xffff) | ||
19 | |||
20 | /* | ||
21 | * Defined with TEST_ prefix so it does not conflict with other | ||
22 | * definitions. | ||
23 | */ | ||
24 | #define TEST_LD(r, base, i) (PPC_INST_LD | ___PPC_RT(r) | \ | ||
25 | ___PPC_RA(base) | IMM_L(i)) | ||
26 | #define TEST_LWZ(r, base, i) (PPC_INST_LWZ | ___PPC_RT(r) | \ | ||
27 | ___PPC_RA(base) | IMM_L(i)) | ||
28 | #define TEST_LWZX(t, a, b) (PPC_INST_LWZX | ___PPC_RT(t) | \ | ||
29 | ___PPC_RA(a) | ___PPC_RB(b)) | ||
30 | #define TEST_STD(r, base, i) (PPC_INST_STD | ___PPC_RS(r) | \ | ||
31 | ___PPC_RA(base) | ((i) & 0xfffc)) | ||
32 | #define TEST_LDARX(t, a, b, eh) (PPC_INST_LDARX | ___PPC_RT(t) | \ | ||
33 | ___PPC_RA(a) | ___PPC_RB(b) | \ | ||
34 | __PPC_EH(eh)) | ||
35 | #define TEST_STDCX(s, a, b) (PPC_INST_STDCX | ___PPC_RS(s) | \ | ||
36 | ___PPC_RA(a) | ___PPC_RB(b)) | ||
37 | #define TEST_LFSX(t, a, b) (PPC_INST_LFSX | ___PPC_RT(t) | \ | ||
38 | ___PPC_RA(a) | ___PPC_RB(b)) | ||
39 | #define TEST_STFSX(s, a, b) (PPC_INST_STFSX | ___PPC_RS(s) | \ | ||
40 | ___PPC_RA(a) | ___PPC_RB(b)) | ||
41 | #define TEST_LFDX(t, a, b) (PPC_INST_LFDX | ___PPC_RT(t) | \ | ||
42 | ___PPC_RA(a) | ___PPC_RB(b)) | ||
43 | #define TEST_STFDX(s, a, b) (PPC_INST_STFDX | ___PPC_RS(s) | \ | ||
44 | ___PPC_RA(a) | ___PPC_RB(b)) | ||
45 | #define TEST_LVX(t, a, b) (PPC_INST_LVX | ___PPC_RT(t) | \ | ||
46 | ___PPC_RA(a) | ___PPC_RB(b)) | ||
47 | #define TEST_STVX(s, a, b) (PPC_INST_STVX | ___PPC_RS(s) | \ | ||
48 | ___PPC_RA(a) | ___PPC_RB(b)) | ||
49 | #define TEST_LXVD2X(s, a, b) (PPC_INST_LXVD2X | VSX_XX1((s), R##a, R##b)) | ||
50 | #define TEST_STXVD2X(s, a, b) (PPC_INST_STXVD2X | VSX_XX1((s), R##a, R##b)) | ||
51 | |||
52 | |||
53 | static void __init init_pt_regs(struct pt_regs *regs) | ||
54 | { | ||
55 | static unsigned long msr; | ||
56 | static bool msr_cached; | ||
57 | |||
58 | memset(regs, 0, sizeof(struct pt_regs)); | ||
59 | |||
60 | if (likely(msr_cached)) { | ||
61 | regs->msr = msr; | ||
62 | return; | ||
63 | } | ||
64 | |||
65 | asm volatile("mfmsr %0" : "=r"(regs->msr)); | ||
66 | |||
67 | regs->msr |= MSR_FP; | ||
68 | regs->msr |= MSR_VEC; | ||
69 | regs->msr |= MSR_VSX; | ||
70 | |||
71 | msr = regs->msr; | ||
72 | msr_cached = true; | ||
73 | } | ||
74 | |||
75 | static void __init show_result(char *ins, char *result) | ||
76 | { | ||
77 | pr_info("%-14s : %s\n", ins, result); | ||
78 | } | ||
79 | |||
80 | static void __init test_ld(void) | ||
81 | { | ||
82 | struct pt_regs regs; | ||
83 | unsigned long a = 0x23; | ||
84 | int stepped = -1; | ||
85 | |||
86 | init_pt_regs(®s); | ||
87 | regs.gpr[3] = (unsigned long) &a; | ||
88 | |||
89 | /* ld r5, 0(r3) */ | ||
90 | stepped = emulate_step(®s, TEST_LD(5, 3, 0)); | ||
91 | |||
92 | if (stepped == 1 && regs.gpr[5] == a) | ||
93 | show_result("ld", "PASS"); | ||
94 | else | ||
95 | show_result("ld", "FAIL"); | ||
96 | } | ||
97 | |||
98 | static void __init test_lwz(void) | ||
99 | { | ||
100 | struct pt_regs regs; | ||
101 | unsigned int a = 0x4545; | ||
102 | int stepped = -1; | ||
103 | |||
104 | init_pt_regs(®s); | ||
105 | regs.gpr[3] = (unsigned long) &a; | ||
106 | |||
107 | /* lwz r5, 0(r3) */ | ||
108 | stepped = emulate_step(®s, TEST_LWZ(5, 3, 0)); | ||
109 | |||
110 | if (stepped == 1 && regs.gpr[5] == a) | ||
111 | show_result("lwz", "PASS"); | ||
112 | else | ||
113 | show_result("lwz", "FAIL"); | ||
114 | } | ||
115 | |||
116 | static void __init test_lwzx(void) | ||
117 | { | ||
118 | struct pt_regs regs; | ||
119 | unsigned int a[3] = {0x0, 0x0, 0x1234}; | ||
120 | int stepped = -1; | ||
121 | |||
122 | init_pt_regs(®s); | ||
123 | regs.gpr[3] = (unsigned long) a; | ||
124 | regs.gpr[4] = 8; | ||
125 | regs.gpr[5] = 0x8765; | ||
126 | |||
127 | /* lwzx r5, r3, r4 */ | ||
128 | stepped = emulate_step(®s, TEST_LWZX(5, 3, 4)); | ||
129 | if (stepped == 1 && regs.gpr[5] == a[2]) | ||
130 | show_result("lwzx", "PASS"); | ||
131 | else | ||
132 | show_result("lwzx", "FAIL"); | ||
133 | } | ||
134 | |||
135 | static void __init test_std(void) | ||
136 | { | ||
137 | struct pt_regs regs; | ||
138 | unsigned long a = 0x1234; | ||
139 | int stepped = -1; | ||
140 | |||
141 | init_pt_regs(®s); | ||
142 | regs.gpr[3] = (unsigned long) &a; | ||
143 | regs.gpr[5] = 0x5678; | ||
144 | |||
145 | /* std r5, 0(r3) */ | ||
146 | stepped = emulate_step(®s, TEST_STD(5, 3, 0)); | ||
147 | if (stepped == 1 || regs.gpr[5] == a) | ||
148 | show_result("std", "PASS"); | ||
149 | else | ||
150 | show_result("std", "FAIL"); | ||
151 | } | ||
152 | |||
153 | static void __init test_ldarx_stdcx(void) | ||
154 | { | ||
155 | struct pt_regs regs; | ||
156 | unsigned long a = 0x1234; | ||
157 | int stepped = -1; | ||
158 | unsigned long cr0_eq = 0x1 << 29; /* eq bit of CR0 */ | ||
159 | |||
160 | init_pt_regs(®s); | ||
161 | asm volatile("mfcr %0" : "=r"(regs.ccr)); | ||
162 | |||
163 | |||
164 | /*** ldarx ***/ | ||
165 | |||
166 | regs.gpr[3] = (unsigned long) &a; | ||
167 | regs.gpr[4] = 0; | ||
168 | regs.gpr[5] = 0x5678; | ||
169 | |||
170 | /* ldarx r5, r3, r4, 0 */ | ||
171 | stepped = emulate_step(®s, TEST_LDARX(5, 3, 4, 0)); | ||
172 | |||
173 | /* | ||
174 | * Don't touch 'a' here. Touching 'a' can do Load/store | ||
175 | * of 'a' which result in failure of subsequent stdcx. | ||
176 | * Instead, use hardcoded value for comparison. | ||
177 | */ | ||
178 | if (stepped <= 0 || regs.gpr[5] != 0x1234) { | ||
179 | show_result("ldarx / stdcx.", "FAIL (ldarx)"); | ||
180 | return; | ||
181 | } | ||
182 | |||
183 | |||
184 | /*** stdcx. ***/ | ||
185 | |||
186 | regs.gpr[5] = 0x9ABC; | ||
187 | |||
188 | /* stdcx. r5, r3, r4 */ | ||
189 | stepped = emulate_step(®s, TEST_STDCX(5, 3, 4)); | ||
190 | |||
191 | /* | ||
192 | * Two possible scenarios that indicates successful emulation | ||
193 | * of stdcx. : | ||
194 | * 1. Reservation is active and store is performed. In this | ||
195 | * case cr0.eq bit will be set to 1. | ||
196 | * 2. Reservation is not active and store is not performed. | ||
197 | * In this case cr0.eq bit will be set to 0. | ||
198 | */ | ||
199 | if (stepped == 1 && ((regs.gpr[5] == a && (regs.ccr & cr0_eq)) | ||
200 | || (regs.gpr[5] != a && !(regs.ccr & cr0_eq)))) | ||
201 | show_result("ldarx / stdcx.", "PASS"); | ||
202 | else | ||
203 | show_result("ldarx / stdcx.", "FAIL (stdcx.)"); | ||
204 | } | ||
205 | |||
206 | #ifdef CONFIG_PPC_FPU | ||
207 | static void __init test_lfsx_stfsx(void) | ||
208 | { | ||
209 | struct pt_regs regs; | ||
210 | union { | ||
211 | float a; | ||
212 | int b; | ||
213 | } c; | ||
214 | int cached_b; | ||
215 | int stepped = -1; | ||
216 | |||
217 | init_pt_regs(®s); | ||
218 | |||
219 | |||
220 | /*** lfsx ***/ | ||
221 | |||
222 | c.a = 123.45; | ||
223 | cached_b = c.b; | ||
224 | |||
225 | regs.gpr[3] = (unsigned long) &c.a; | ||
226 | regs.gpr[4] = 0; | ||
227 | |||
228 | /* lfsx frt10, r3, r4 */ | ||
229 | stepped = emulate_step(®s, TEST_LFSX(10, 3, 4)); | ||
230 | |||
231 | if (stepped == 1) | ||
232 | show_result("lfsx", "PASS"); | ||
233 | else | ||
234 | show_result("lfsx", "FAIL"); | ||
235 | |||
236 | |||
237 | /*** stfsx ***/ | ||
238 | |||
239 | c.a = 678.91; | ||
240 | |||
241 | /* stfsx frs10, r3, r4 */ | ||
242 | stepped = emulate_step(®s, TEST_STFSX(10, 3, 4)); | ||
243 | |||
244 | if (stepped == 1 && c.b == cached_b) | ||
245 | show_result("stfsx", "PASS"); | ||
246 | else | ||
247 | show_result("stfsx", "FAIL"); | ||
248 | } | ||
249 | |||
250 | static void __init test_lfdx_stfdx(void) | ||
251 | { | ||
252 | struct pt_regs regs; | ||
253 | union { | ||
254 | double a; | ||
255 | long b; | ||
256 | } c; | ||
257 | long cached_b; | ||
258 | int stepped = -1; | ||
259 | |||
260 | init_pt_regs(®s); | ||
261 | |||
262 | |||
263 | /*** lfdx ***/ | ||
264 | |||
265 | c.a = 123456.78; | ||
266 | cached_b = c.b; | ||
267 | |||
268 | regs.gpr[3] = (unsigned long) &c.a; | ||
269 | regs.gpr[4] = 0; | ||
270 | |||
271 | /* lfdx frt10, r3, r4 */ | ||
272 | stepped = emulate_step(®s, TEST_LFDX(10, 3, 4)); | ||
273 | |||
274 | if (stepped == 1) | ||
275 | show_result("lfdx", "PASS"); | ||
276 | else | ||
277 | show_result("lfdx", "FAIL"); | ||
278 | |||
279 | |||
280 | /*** stfdx ***/ | ||
281 | |||
282 | c.a = 987654.32; | ||
283 | |||
284 | /* stfdx frs10, r3, r4 */ | ||
285 | stepped = emulate_step(®s, TEST_STFDX(10, 3, 4)); | ||
286 | |||
287 | if (stepped == 1 && c.b == cached_b) | ||
288 | show_result("stfdx", "PASS"); | ||
289 | else | ||
290 | show_result("stfdx", "FAIL"); | ||
291 | } | ||
292 | #else | ||
293 | static void __init test_lfsx_stfsx(void) | ||
294 | { | ||
295 | show_result("lfsx", "SKIP (CONFIG_PPC_FPU is not set)"); | ||
296 | show_result("stfsx", "SKIP (CONFIG_PPC_FPU is not set)"); | ||
297 | } | ||
298 | |||
299 | static void __init test_lfdx_stfdx(void) | ||
300 | { | ||
301 | show_result("lfdx", "SKIP (CONFIG_PPC_FPU is not set)"); | ||
302 | show_result("stfdx", "SKIP (CONFIG_PPC_FPU is not set)"); | ||
303 | } | ||
304 | #endif /* CONFIG_PPC_FPU */ | ||
305 | |||
306 | #ifdef CONFIG_ALTIVEC | ||
307 | static void __init test_lvx_stvx(void) | ||
308 | { | ||
309 | struct pt_regs regs; | ||
310 | union { | ||
311 | vector128 a; | ||
312 | u32 b[4]; | ||
313 | } c; | ||
314 | u32 cached_b[4]; | ||
315 | int stepped = -1; | ||
316 | |||
317 | init_pt_regs(®s); | ||
318 | |||
319 | |||
320 | /*** lvx ***/ | ||
321 | |||
322 | cached_b[0] = c.b[0] = 923745; | ||
323 | cached_b[1] = c.b[1] = 2139478; | ||
324 | cached_b[2] = c.b[2] = 9012; | ||
325 | cached_b[3] = c.b[3] = 982134; | ||
326 | |||
327 | regs.gpr[3] = (unsigned long) &c.a; | ||
328 | regs.gpr[4] = 0; | ||
329 | |||
330 | /* lvx vrt10, r3, r4 */ | ||
331 | stepped = emulate_step(®s, TEST_LVX(10, 3, 4)); | ||
332 | |||
333 | if (stepped == 1) | ||
334 | show_result("lvx", "PASS"); | ||
335 | else | ||
336 | show_result("lvx", "FAIL"); | ||
337 | |||
338 | |||
339 | /*** stvx ***/ | ||
340 | |||
341 | c.b[0] = 4987513; | ||
342 | c.b[1] = 84313948; | ||
343 | c.b[2] = 71; | ||
344 | c.b[3] = 498532; | ||
345 | |||
346 | /* stvx vrs10, r3, r4 */ | ||
347 | stepped = emulate_step(®s, TEST_STVX(10, 3, 4)); | ||
348 | |||
349 | if (stepped == 1 && cached_b[0] == c.b[0] && cached_b[1] == c.b[1] && | ||
350 | cached_b[2] == c.b[2] && cached_b[3] == c.b[3]) | ||
351 | show_result("stvx", "PASS"); | ||
352 | else | ||
353 | show_result("stvx", "FAIL"); | ||
354 | } | ||
355 | #else | ||
356 | static void __init test_lvx_stvx(void) | ||
357 | { | ||
358 | show_result("lvx", "SKIP (CONFIG_ALTIVEC is not set)"); | ||
359 | show_result("stvx", "SKIP (CONFIG_ALTIVEC is not set)"); | ||
360 | } | ||
361 | #endif /* CONFIG_ALTIVEC */ | ||
362 | |||
363 | #ifdef CONFIG_VSX | ||
364 | static void __init test_lxvd2x_stxvd2x(void) | ||
365 | { | ||
366 | struct pt_regs regs; | ||
367 | union { | ||
368 | vector128 a; | ||
369 | u32 b[4]; | ||
370 | } c; | ||
371 | u32 cached_b[4]; | ||
372 | int stepped = -1; | ||
373 | |||
374 | init_pt_regs(®s); | ||
375 | |||
376 | |||
377 | /*** lxvd2x ***/ | ||
378 | |||
379 | cached_b[0] = c.b[0] = 18233; | ||
380 | cached_b[1] = c.b[1] = 34863571; | ||
381 | cached_b[2] = c.b[2] = 834; | ||
382 | cached_b[3] = c.b[3] = 6138911; | ||
383 | |||
384 | regs.gpr[3] = (unsigned long) &c.a; | ||
385 | regs.gpr[4] = 0; | ||
386 | |||
387 | /* lxvd2x vsr39, r3, r4 */ | ||
388 | stepped = emulate_step(®s, TEST_LXVD2X(39, 3, 4)); | ||
389 | |||
390 | if (stepped == 1) | ||
391 | show_result("lxvd2x", "PASS"); | ||
392 | else | ||
393 | show_result("lxvd2x", "FAIL"); | ||
394 | |||
395 | |||
396 | /*** stxvd2x ***/ | ||
397 | |||
398 | c.b[0] = 21379463; | ||
399 | c.b[1] = 87; | ||
400 | c.b[2] = 374234; | ||
401 | c.b[3] = 4; | ||
402 | |||
403 | /* stxvd2x vsr39, r3, r4 */ | ||
404 | stepped = emulate_step(®s, TEST_STXVD2X(39, 3, 4)); | ||
405 | |||
406 | if (stepped == 1 && cached_b[0] == c.b[0] && cached_b[1] == c.b[1] && | ||
407 | cached_b[2] == c.b[2] && cached_b[3] == c.b[3]) | ||
408 | show_result("stxvd2x", "PASS"); | ||
409 | else | ||
410 | show_result("stxvd2x", "FAIL"); | ||
411 | } | ||
412 | #else | ||
413 | static void __init test_lxvd2x_stxvd2x(void) | ||
414 | { | ||
415 | show_result("lxvd2x", "SKIP (CONFIG_VSX is not set)"); | ||
416 | show_result("stxvd2x", "SKIP (CONFIG_VSX is not set)"); | ||
417 | } | ||
418 | #endif /* CONFIG_VSX */ | ||
419 | |||
420 | static int __init test_emulate_step(void) | ||
421 | { | ||
422 | test_ld(); | ||
423 | test_lwz(); | ||
424 | test_lwzx(); | ||
425 | test_std(); | ||
426 | test_ldarx_stdcx(); | ||
427 | test_lfsx_stfsx(); | ||
428 | test_lfdx_stfdx(); | ||
429 | test_lvx_stvx(); | ||
430 | test_lxvd2x_stxvd2x(); | ||
431 | |||
432 | return 0; | ||
433 | } | ||
434 | late_initcall(test_emulate_step); | ||