diff options
Diffstat (limited to 'arch/sparc/kernel/unaligned.c')
-rw-r--r-- | arch/sparc/kernel/unaligned.c | 252 |
1 files changed, 47 insertions, 205 deletions
diff --git a/arch/sparc/kernel/unaligned.c b/arch/sparc/kernel/unaligned.c index a6330fbc9dd9..33857be16661 100644 --- a/arch/sparc/kernel/unaligned.c +++ b/arch/sparc/kernel/unaligned.c | |||
@@ -175,157 +175,31 @@ static void unaligned_panic(char *str) | |||
175 | panic(str); | 175 | panic(str); |
176 | } | 176 | } |
177 | 177 | ||
178 | #define do_integer_load(dest_reg, size, saddr, is_signed, errh) ({ \ | 178 | /* una_asm.S */ |
179 | __asm__ __volatile__ ( \ | 179 | extern int do_int_load(unsigned long *dest_reg, int size, |
180 | "cmp %1, 8\n\t" \ | 180 | unsigned long *saddr, int is_signed); |
181 | "be 9f\n\t" \ | 181 | extern int __do_int_store(unsigned long *dst_addr, int size, |
182 | " cmp %1, 4\n\t" \ | 182 | unsigned long *src_val); |
183 | "be 6f\n" \ | 183 | |
184 | "4:\t" " ldub [%2], %%l1\n" \ | 184 | static int do_int_store(int reg_num, int size, unsigned long *dst_addr, |
185 | "5:\t" "ldub [%2 + 1], %%l2\n\t" \ | 185 | struct pt_regs *regs) |
186 | "sll %%l1, 8, %%l1\n\t" \ | 186 | { |
187 | "tst %3\n\t" \ | 187 | unsigned long zero[2] = { 0, 0 }; |
188 | "be 3f\n\t" \ | 188 | unsigned long *src_val; |
189 | " add %%l1, %%l2, %%l1\n\t" \ | 189 | |
190 | "sll %%l1, 16, %%l1\n\t" \ | 190 | if (reg_num) |
191 | "sra %%l1, 16, %%l1\n" \ | 191 | src_val = fetch_reg_addr(reg_num, regs); |
192 | "3:\t" "b 0f\n\t" \ | 192 | else { |
193 | " st %%l1, [%0]\n" \ | 193 | src_val = &zero[0]; |
194 | "6:\t" "ldub [%2 + 1], %%l2\n\t" \ | 194 | if (size == 8) |
195 | "sll %%l1, 24, %%l1\n" \ | 195 | zero[1] = fetch_reg(1, regs); |
196 | "7:\t" "ldub [%2 + 2], %%g7\n\t" \ | 196 | } |
197 | "sll %%l2, 16, %%l2\n" \ | 197 | return __do_int_store(dst_addr, size, src_val); |
198 | "8:\t" "ldub [%2 + 3], %%g1\n\t" \ | 198 | } |
199 | "sll %%g7, 8, %%g7\n\t" \ | ||
200 | "or %%l1, %%l2, %%l1\n\t" \ | ||
201 | "or %%g7, %%g1, %%g7\n\t" \ | ||
202 | "or %%l1, %%g7, %%l1\n\t" \ | ||
203 | "b 0f\n\t" \ | ||
204 | " st %%l1, [%0]\n" \ | ||
205 | "9:\t" "ldub [%2], %%l1\n" \ | ||
206 | "10:\t" "ldub [%2 + 1], %%l2\n\t" \ | ||
207 | "sll %%l1, 24, %%l1\n" \ | ||
208 | "11:\t" "ldub [%2 + 2], %%g7\n\t" \ | ||
209 | "sll %%l2, 16, %%l2\n" \ | ||
210 | "12:\t" "ldub [%2 + 3], %%g1\n\t" \ | ||
211 | "sll %%g7, 8, %%g7\n\t" \ | ||
212 | "or %%l1, %%l2, %%l1\n\t" \ | ||
213 | "or %%g7, %%g1, %%g7\n\t" \ | ||
214 | "or %%l1, %%g7, %%g7\n" \ | ||
215 | "13:\t" "ldub [%2 + 4], %%l1\n\t" \ | ||
216 | "st %%g7, [%0]\n" \ | ||
217 | "14:\t" "ldub [%2 + 5], %%l2\n\t" \ | ||
218 | "sll %%l1, 24, %%l1\n" \ | ||
219 | "15:\t" "ldub [%2 + 6], %%g7\n\t" \ | ||
220 | "sll %%l2, 16, %%l2\n" \ | ||
221 | "16:\t" "ldub [%2 + 7], %%g1\n\t" \ | ||
222 | "sll %%g7, 8, %%g7\n\t" \ | ||
223 | "or %%l1, %%l2, %%l1\n\t" \ | ||
224 | "or %%g7, %%g1, %%g7\n\t" \ | ||
225 | "or %%l1, %%g7, %%g7\n\t" \ | ||
226 | "st %%g7, [%0 + 4]\n" \ | ||
227 | "0:\n\n\t" \ | ||
228 | ".section __ex_table,#alloc\n\t" \ | ||
229 | ".word 4b, " #errh "\n\t" \ | ||
230 | ".word 5b, " #errh "\n\t" \ | ||
231 | ".word 6b, " #errh "\n\t" \ | ||
232 | ".word 7b, " #errh "\n\t" \ | ||
233 | ".word 8b, " #errh "\n\t" \ | ||
234 | ".word 9b, " #errh "\n\t" \ | ||
235 | ".word 10b, " #errh "\n\t" \ | ||
236 | ".word 11b, " #errh "\n\t" \ | ||
237 | ".word 12b, " #errh "\n\t" \ | ||
238 | ".word 13b, " #errh "\n\t" \ | ||
239 | ".word 14b, " #errh "\n\t" \ | ||
240 | ".word 15b, " #errh "\n\t" \ | ||
241 | ".word 16b, " #errh "\n\n\t" \ | ||
242 | ".previous\n\t" \ | ||
243 | : : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed) \ | ||
244 | : "l1", "l2", "g7", "g1", "cc"); \ | ||
245 | }) | ||
246 | |||
247 | #define store_common(dst_addr, size, src_val, errh) ({ \ | ||
248 | __asm__ __volatile__ ( \ | ||
249 | "ld [%2], %%l1\n" \ | ||
250 | "cmp %1, 2\n\t" \ | ||
251 | "be 2f\n\t" \ | ||
252 | " cmp %1, 4\n\t" \ | ||
253 | "be 1f\n\t" \ | ||
254 | " srl %%l1, 24, %%l2\n\t" \ | ||
255 | "srl %%l1, 16, %%g7\n" \ | ||
256 | "4:\t" "stb %%l2, [%0]\n\t" \ | ||
257 | "srl %%l1, 8, %%l2\n" \ | ||
258 | "5:\t" "stb %%g7, [%0 + 1]\n\t" \ | ||
259 | "ld [%2 + 4], %%g7\n" \ | ||
260 | "6:\t" "stb %%l2, [%0 + 2]\n\t" \ | ||
261 | "srl %%g7, 24, %%l2\n" \ | ||
262 | "7:\t" "stb %%l1, [%0 + 3]\n\t" \ | ||
263 | "srl %%g7, 16, %%l1\n" \ | ||
264 | "8:\t" "stb %%l2, [%0 + 4]\n\t" \ | ||
265 | "srl %%g7, 8, %%l2\n" \ | ||
266 | "9:\t" "stb %%l1, [%0 + 5]\n" \ | ||
267 | "10:\t" "stb %%l2, [%0 + 6]\n\t" \ | ||
268 | "b 0f\n" \ | ||
269 | "11:\t" " stb %%g7, [%0 + 7]\n" \ | ||
270 | "1:\t" "srl %%l1, 16, %%g7\n" \ | ||
271 | "12:\t" "stb %%l2, [%0]\n\t" \ | ||
272 | "srl %%l1, 8, %%l2\n" \ | ||
273 | "13:\t" "stb %%g7, [%0 + 1]\n" \ | ||
274 | "14:\t" "stb %%l2, [%0 + 2]\n\t" \ | ||
275 | "b 0f\n" \ | ||
276 | "15:\t" " stb %%l1, [%0 + 3]\n" \ | ||
277 | "2:\t" "srl %%l1, 8, %%l2\n" \ | ||
278 | "16:\t" "stb %%l2, [%0]\n" \ | ||
279 | "17:\t" "stb %%l1, [%0 + 1]\n" \ | ||
280 | "0:\n\n\t" \ | ||
281 | ".section __ex_table,#alloc\n\t" \ | ||
282 | ".word 4b, " #errh "\n\t" \ | ||
283 | ".word 5b, " #errh "\n\t" \ | ||
284 | ".word 6b, " #errh "\n\t" \ | ||
285 | ".word 7b, " #errh "\n\t" \ | ||
286 | ".word 8b, " #errh "\n\t" \ | ||
287 | ".word 9b, " #errh "\n\t" \ | ||
288 | ".word 10b, " #errh "\n\t" \ | ||
289 | ".word 11b, " #errh "\n\t" \ | ||
290 | ".word 12b, " #errh "\n\t" \ | ||
291 | ".word 13b, " #errh "\n\t" \ | ||
292 | ".word 14b, " #errh "\n\t" \ | ||
293 | ".word 15b, " #errh "\n\t" \ | ||
294 | ".word 16b, " #errh "\n\t" \ | ||
295 | ".word 17b, " #errh "\n\n\t" \ | ||
296 | ".previous\n\t" \ | ||
297 | : : "r" (dst_addr), "r" (size), "r" (src_val) \ | ||
298 | : "l1", "l2", "g7", "g1", "cc"); \ | ||
299 | }) | ||
300 | |||
301 | #define do_integer_store(reg_num, size, dst_addr, regs, errh) ({ \ | ||
302 | unsigned long *src_val; \ | ||
303 | static unsigned long zero[2] = { 0, }; \ | ||
304 | \ | ||
305 | if (reg_num) src_val = fetch_reg_addr(reg_num, regs); \ | ||
306 | else { \ | ||
307 | src_val = &zero[0]; \ | ||
308 | if (size == 8) \ | ||
309 | zero[1] = fetch_reg(1, regs); \ | ||
310 | } \ | ||
311 | store_common(dst_addr, size, src_val, errh); \ | ||
312 | }) | ||
313 | 199 | ||
314 | extern void smp_capture(void); | 200 | extern void smp_capture(void); |
315 | extern void smp_release(void); | 201 | extern void smp_release(void); |
316 | 202 | ||
317 | #define do_atomic(srcdest_reg, mem, errh) ({ \ | ||
318 | unsigned long flags, tmp; \ | ||
319 | \ | ||
320 | smp_capture(); \ | ||
321 | local_irq_save(flags); \ | ||
322 | tmp = *srcdest_reg; \ | ||
323 | do_integer_load(srcdest_reg, 4, mem, 0, errh); \ | ||
324 | store_common(mem, 4, &tmp, errh); \ | ||
325 | local_irq_restore(flags); \ | ||
326 | smp_release(); \ | ||
327 | }) | ||
328 | |||
329 | static inline void advance(struct pt_regs *regs) | 203 | static inline void advance(struct pt_regs *regs) |
330 | { | 204 | { |
331 | regs->pc = regs->npc; | 205 | regs->pc = regs->npc; |
@@ -342,9 +216,7 @@ static inline int ok_for_kernel(unsigned int insn) | |||
342 | return !floating_point_load_or_store_p(insn); | 216 | return !floating_point_load_or_store_p(insn); |
343 | } | 217 | } |
344 | 218 | ||
345 | void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("kernel_mna_trap_fault"); | 219 | static void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) |
346 | |||
347 | void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) | ||
348 | { | 220 | { |
349 | unsigned long g2 = regs->u_regs [UREG_G2]; | 221 | unsigned long g2 = regs->u_regs [UREG_G2]; |
350 | unsigned long fixup = search_extables_range(regs->pc, &g2); | 222 | unsigned long fixup = search_extables_range(regs->pc, &g2); |
@@ -379,48 +251,34 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn) | |||
379 | printk("Unsupported unaligned load/store trap for kernel at <%08lx>.\n", | 251 | printk("Unsupported unaligned load/store trap for kernel at <%08lx>.\n", |
380 | regs->pc); | 252 | regs->pc); |
381 | unaligned_panic("Wheee. Kernel does fpu/atomic unaligned load/store."); | 253 | unaligned_panic("Wheee. Kernel does fpu/atomic unaligned load/store."); |
382 | |||
383 | __asm__ __volatile__ ("\n" | ||
384 | "kernel_unaligned_trap_fault:\n\t" | ||
385 | "mov %0, %%o0\n\t" | ||
386 | "call kernel_mna_trap_fault\n\t" | ||
387 | " mov %1, %%o1\n\t" | ||
388 | : | ||
389 | : "r" (regs), "r" (insn) | ||
390 | : "o0", "o1", "o2", "o3", "o4", "o5", "o7", | ||
391 | "g1", "g2", "g3", "g4", "g5", "g7", "cc"); | ||
392 | } else { | 254 | } else { |
393 | unsigned long addr = compute_effective_address(regs, insn); | 255 | unsigned long addr = compute_effective_address(regs, insn); |
256 | int err; | ||
394 | 257 | ||
395 | #ifdef DEBUG_MNA | 258 | #ifdef DEBUG_MNA |
396 | printk("KMNA: pc=%08lx [dir=%s addr=%08lx size=%d] retpc[%08lx]\n", | 259 | printk("KMNA: pc=%08lx [dir=%s addr=%08lx size=%d] retpc[%08lx]\n", |
397 | regs->pc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]); | 260 | regs->pc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]); |
398 | #endif | 261 | #endif |
399 | switch(dir) { | 262 | switch (dir) { |
400 | case load: | 263 | case load: |
401 | do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs), | 264 | err = do_int_load(fetch_reg_addr(((insn>>25)&0x1f), |
402 | size, (unsigned long *) addr, | 265 | regs), |
403 | decode_signedness(insn), | 266 | size, (unsigned long *) addr, |
404 | kernel_unaligned_trap_fault); | 267 | decode_signedness(insn)); |
405 | break; | 268 | break; |
406 | 269 | ||
407 | case store: | 270 | case store: |
408 | do_integer_store(((insn>>25)&0x1f), size, | 271 | err = do_int_store(((insn>>25)&0x1f), size, |
409 | (unsigned long *) addr, regs, | 272 | (unsigned long *) addr, regs); |
410 | kernel_unaligned_trap_fault); | ||
411 | break; | 273 | break; |
412 | #if 0 /* unsupported */ | ||
413 | case both: | ||
414 | do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs), | ||
415 | (unsigned long *) addr, | ||
416 | kernel_unaligned_trap_fault); | ||
417 | break; | ||
418 | #endif | ||
419 | default: | 274 | default: |
420 | panic("Impossible kernel unaligned trap."); | 275 | panic("Impossible kernel unaligned trap."); |
421 | /* Not reached... */ | 276 | /* Not reached... */ |
422 | } | 277 | } |
423 | advance(regs); | 278 | if (err) |
279 | kernel_mna_trap_fault(regs, insn); | ||
280 | else | ||
281 | advance(regs); | ||
424 | } | 282 | } |
425 | } | 283 | } |
426 | 284 | ||
@@ -459,9 +317,7 @@ static inline int ok_for_user(struct pt_regs *regs, unsigned int insn, | |||
459 | return 0; | 317 | return 0; |
460 | } | 318 | } |
461 | 319 | ||
462 | void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("user_mna_trap_fault"); | 320 | static void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) |
463 | |||
464 | void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) | ||
465 | { | 321 | { |
466 | siginfo_t info; | 322 | siginfo_t info; |
467 | 323 | ||
@@ -485,7 +341,7 @@ asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn) | |||
485 | if(!ok_for_user(regs, insn, dir)) { | 341 | if(!ok_for_user(regs, insn, dir)) { |
486 | goto kill_user; | 342 | goto kill_user; |
487 | } else { | 343 | } else { |
488 | int size = decode_access_size(insn); | 344 | int err, size = decode_access_size(insn); |
489 | unsigned long addr; | 345 | unsigned long addr; |
490 | 346 | ||
491 | if(floating_point_load_or_store_p(insn)) { | 347 | if(floating_point_load_or_store_p(insn)) { |
@@ -496,48 +352,34 @@ asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn) | |||
496 | addr = compute_effective_address(regs, insn); | 352 | addr = compute_effective_address(regs, insn); |
497 | switch(dir) { | 353 | switch(dir) { |
498 | case load: | 354 | case load: |
499 | do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs), | 355 | err = do_int_load(fetch_reg_addr(((insn>>25)&0x1f), |
500 | size, (unsigned long *) addr, | 356 | regs), |
501 | decode_signedness(insn), | 357 | size, (unsigned long *) addr, |
502 | user_unaligned_trap_fault); | 358 | decode_signedness(insn)); |
503 | break; | 359 | break; |
504 | 360 | ||
505 | case store: | 361 | case store: |
506 | do_integer_store(((insn>>25)&0x1f), size, | 362 | err = do_int_store(((insn>>25)&0x1f), size, |
507 | (unsigned long *) addr, regs, | 363 | (unsigned long *) addr, regs); |
508 | user_unaligned_trap_fault); | ||
509 | break; | 364 | break; |
510 | 365 | ||
511 | case both: | 366 | case both: |
512 | #if 0 /* unsupported */ | ||
513 | do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs), | ||
514 | (unsigned long *) addr, | ||
515 | user_unaligned_trap_fault); | ||
516 | #else | ||
517 | /* | 367 | /* |
518 | * This was supported in 2.4. However, we question | 368 | * This was supported in 2.4. However, we question |
519 | * the value of SWAP instruction across word boundaries. | 369 | * the value of SWAP instruction across word boundaries. |
520 | */ | 370 | */ |
521 | printk("Unaligned SWAP unsupported.\n"); | 371 | printk("Unaligned SWAP unsupported.\n"); |
522 | goto kill_user; | 372 | err = -EFAULT; |
523 | #endif | ||
524 | break; | 373 | break; |
525 | 374 | ||
526 | default: | 375 | default: |
527 | unaligned_panic("Impossible user unaligned trap."); | 376 | unaligned_panic("Impossible user unaligned trap."); |
528 | |||
529 | __asm__ __volatile__ ("\n" | ||
530 | "user_unaligned_trap_fault:\n\t" | ||
531 | "mov %0, %%o0\n\t" | ||
532 | "call user_mna_trap_fault\n\t" | ||
533 | " mov %1, %%o1\n\t" | ||
534 | : | ||
535 | : "r" (regs), "r" (insn) | ||
536 | : "o0", "o1", "o2", "o3", "o4", "o5", "o7", | ||
537 | "g1", "g2", "g3", "g4", "g5", "g7", "cc"); | ||
538 | goto out; | 377 | goto out; |
539 | } | 378 | } |
540 | advance(regs); | 379 | if (err) |
380 | goto kill_user; | ||
381 | else | ||
382 | advance(regs); | ||
541 | goto out; | 383 | goto out; |
542 | } | 384 | } |
543 | 385 | ||