]>
Commit | Line | Data |
---|---|---|
4ceae137 RB |
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); |