diff options
Diffstat (limited to 'arch/sparc64/kernel/unaligned.c')
| -rw-r--r-- | arch/sparc64/kernel/unaligned.c | 261 |
1 files changed, 59 insertions, 202 deletions
diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c index 4372bf32ecf6..11c3e88732e4 100644 --- a/arch/sparc64/kernel/unaligned.c +++ b/arch/sparc64/kernel/unaligned.c | |||
| @@ -180,169 +180,28 @@ static void __attribute_used__ unaligned_panic(char *str, struct pt_regs *regs) | |||
| 180 | die_if_kernel(str, regs); | 180 | die_if_kernel(str, regs); |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | #define do_integer_load(dest_reg, size, saddr, is_signed, asi, errh) ({ \ | 183 | extern void do_int_load(unsigned long *dest_reg, int size, |
| 184 | __asm__ __volatile__ ( \ | 184 | unsigned long *saddr, int is_signed, int asi); |
| 185 | "wr %4, 0, %%asi\n\t" \ | ||
| 186 | "cmp %1, 8\n\t" \ | ||
| 187 | "bge,pn %%icc, 9f\n\t" \ | ||
| 188 | " cmp %1, 4\n\t" \ | ||
| 189 | "be,pt %%icc, 6f\n" \ | ||
| 190 | "4:\t" " lduba [%2] %%asi, %%l1\n" \ | ||
| 191 | "5:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \ | ||
| 192 | "sll %%l1, 8, %%l1\n\t" \ | ||
| 193 | "brz,pt %3, 3f\n\t" \ | ||
| 194 | " add %%l1, %%l2, %%l1\n\t" \ | ||
| 195 | "sllx %%l1, 48, %%l1\n\t" \ | ||
| 196 | "srax %%l1, 48, %%l1\n" \ | ||
| 197 | "3:\t" "ba,pt %%xcc, 0f\n\t" \ | ||
| 198 | " stx %%l1, [%0]\n" \ | ||
| 199 | "6:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \ | ||
| 200 | "sll %%l1, 24, %%l1\n" \ | ||
| 201 | "7:\t" "lduba [%2 + 2] %%asi, %%g7\n\t" \ | ||
| 202 | "sll %%l2, 16, %%l2\n" \ | ||
| 203 | "8:\t" "lduba [%2 + 3] %%asi, %%g1\n\t" \ | ||
| 204 | "sll %%g7, 8, %%g7\n\t" \ | ||
| 205 | "or %%l1, %%l2, %%l1\n\t" \ | ||
| 206 | "or %%g7, %%g1, %%g7\n\t" \ | ||
| 207 | "or %%l1, %%g7, %%l1\n\t" \ | ||
| 208 | "brnz,a,pt %3, 3f\n\t" \ | ||
| 209 | " sra %%l1, 0, %%l1\n" \ | ||
| 210 | "3:\t" "ba,pt %%xcc, 0f\n\t" \ | ||
| 211 | " stx %%l1, [%0]\n" \ | ||
| 212 | "9:\t" "lduba [%2] %%asi, %%l1\n" \ | ||
| 213 | "10:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \ | ||
| 214 | "sllx %%l1, 56, %%l1\n" \ | ||
| 215 | "11:\t" "lduba [%2 + 2] %%asi, %%g7\n\t" \ | ||
| 216 | "sllx %%l2, 48, %%l2\n" \ | ||
| 217 | "12:\t" "lduba [%2 + 3] %%asi, %%g1\n\t" \ | ||
| 218 | "sllx %%g7, 40, %%g7\n\t" \ | ||
| 219 | "sllx %%g1, 32, %%g1\n\t" \ | ||
| 220 | "or %%l1, %%l2, %%l1\n\t" \ | ||
| 221 | "or %%g7, %%g1, %%g7\n" \ | ||
| 222 | "13:\t" "lduba [%2 + 4] %%asi, %%l2\n\t" \ | ||
| 223 | "or %%l1, %%g7, %%g7\n" \ | ||
| 224 | "14:\t" "lduba [%2 + 5] %%asi, %%g1\n\t" \ | ||
| 225 | "sllx %%l2, 24, %%l2\n" \ | ||
| 226 | "15:\t" "lduba [%2 + 6] %%asi, %%l1\n\t" \ | ||
| 227 | "sllx %%g1, 16, %%g1\n\t" \ | ||
| 228 | "or %%g7, %%l2, %%g7\n" \ | ||
| 229 | "16:\t" "lduba [%2 + 7] %%asi, %%l2\n\t" \ | ||
| 230 | "sllx %%l1, 8, %%l1\n\t" \ | ||
| 231 | "or %%g7, %%g1, %%g7\n\t" \ | ||
| 232 | "or %%l1, %%l2, %%l1\n\t" \ | ||
| 233 | "or %%g7, %%l1, %%g7\n\t" \ | ||
| 234 | "cmp %1, 8\n\t" \ | ||
| 235 | "be,a,pt %%icc, 0f\n\t" \ | ||
| 236 | " stx %%g7, [%0]\n\t" \ | ||
| 237 | "srlx %%g7, 32, %%l1\n\t" \ | ||
| 238 | "sra %%g7, 0, %%g7\n\t" \ | ||
| 239 | "stx %%l1, [%0]\n\t" \ | ||
| 240 | "stx %%g7, [%0 + 8]\n" \ | ||
| 241 | "0:\n\t" \ | ||
| 242 | "wr %%g0, %5, %%asi\n\n\t" \ | ||
| 243 | ".section __ex_table\n\t" \ | ||
| 244 | ".word 4b, " #errh "\n\t" \ | ||
| 245 | ".word 5b, " #errh "\n\t" \ | ||
| 246 | ".word 6b, " #errh "\n\t" \ | ||
| 247 | ".word 7b, " #errh "\n\t" \ | ||
| 248 | ".word 8b, " #errh "\n\t" \ | ||
| 249 | ".word 9b, " #errh "\n\t" \ | ||
| 250 | ".word 10b, " #errh "\n\t" \ | ||
| 251 | ".word 11b, " #errh "\n\t" \ | ||
| 252 | ".word 12b, " #errh "\n\t" \ | ||
| 253 | ".word 13b, " #errh "\n\t" \ | ||
| 254 | ".word 14b, " #errh "\n\t" \ | ||
| 255 | ".word 15b, " #errh "\n\t" \ | ||
| 256 | ".word 16b, " #errh "\n\n\t" \ | ||
| 257 | ".previous\n\t" \ | ||
| 258 | : : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed), \ | ||
| 259 | "r" (asi), "i" (ASI_AIUS) \ | ||
| 260 | : "l1", "l2", "g7", "g1", "cc"); \ | ||
| 261 | }) | ||
| 262 | 185 | ||
| 263 | #define store_common(dst_addr, size, src_val, asi, errh) ({ \ | 186 | extern void __do_int_store(unsigned long *dst_addr, int size, |
| 264 | __asm__ __volatile__ ( \ | 187 | unsigned long *src_val, int asi); |
| 265 | "wr %3, 0, %%asi\n\t" \ | 188 | |
| 266 | "ldx [%2], %%l1\n" \ | 189 | static inline void do_int_store(int reg_num, int size, unsigned long *dst_addr, |
| 267 | "cmp %1, 2\n\t" \ | 190 | struct pt_regs *regs, int asi) |
| 268 | "be,pn %%icc, 2f\n\t" \ | 191 | { |
| 269 | " cmp %1, 4\n\t" \ | 192 | unsigned long zero = 0; |
| 270 | "be,pt %%icc, 1f\n\t" \ | 193 | unsigned long *src_val = &zero; |
| 271 | " srlx %%l1, 24, %%l2\n\t" \ | 194 | |
| 272 | "srlx %%l1, 56, %%g1\n\t" \ | 195 | if (size == 16) { |
| 273 | "srlx %%l1, 48, %%g7\n" \ | 196 | size = 8; |
| 274 | "4:\t" "stba %%g1, [%0] %%asi\n\t" \ | 197 | zero = (((long)(reg_num ? |
| 275 | "srlx %%l1, 40, %%g1\n" \ | 198 | (unsigned)fetch_reg(reg_num, regs) : 0)) << 32) | |
| 276 | "5:\t" "stba %%g7, [%0 + 1] %%asi\n\t" \ | 199 | (unsigned)fetch_reg(reg_num + 1, regs); |
| 277 | "srlx %%l1, 32, %%g7\n" \ | 200 | } else if (reg_num) { |
| 278 | "6:\t" "stba %%g1, [%0 + 2] %%asi\n" \ | 201 | src_val = fetch_reg_addr(reg_num, regs); |
| 279 | "7:\t" "stba %%g7, [%0 + 3] %%asi\n\t" \ | 202 | } |
| 280 | "srlx %%l1, 16, %%g1\n" \ | 203 | __do_int_store(dst_addr, size, src_val, asi); |
| 281 | "8:\t" "stba %%l2, [%0 + 4] %%asi\n\t" \ | 204 | } |
| 282 | "srlx %%l1, 8, %%g7\n" \ | ||
| 283 | "9:\t" "stba %%g1, [%0 + 5] %%asi\n" \ | ||
| 284 | "10:\t" "stba %%g7, [%0 + 6] %%asi\n\t" \ | ||
| 285 | "ba,pt %%xcc, 0f\n" \ | ||
| 286 | "11:\t" " stba %%l1, [%0 + 7] %%asi\n" \ | ||
| 287 | "1:\t" "srl %%l1, 16, %%g7\n" \ | ||
| 288 | "12:\t" "stba %%l2, [%0] %%asi\n\t" \ | ||
| 289 | "srl %%l1, 8, %%l2\n" \ | ||
| 290 | "13:\t" "stba %%g7, [%0 + 1] %%asi\n" \ | ||
| 291 | "14:\t" "stba %%l2, [%0 + 2] %%asi\n\t" \ | ||
| 292 | "ba,pt %%xcc, 0f\n" \ | ||
| 293 | "15:\t" " stba %%l1, [%0 + 3] %%asi\n" \ | ||
| 294 | "2:\t" "srl %%l1, 8, %%l2\n" \ | ||
| 295 | "16:\t" "stba %%l2, [%0] %%asi\n" \ | ||
| 296 | "17:\t" "stba %%l1, [%0 + 1] %%asi\n" \ | ||
| 297 | "0:\n\t" \ | ||
| 298 | "wr %%g0, %4, %%asi\n\n\t" \ | ||
| 299 | ".section __ex_table\n\t" \ | ||
| 300 | ".word 4b, " #errh "\n\t" \ | ||
| 301 | ".word 5b, " #errh "\n\t" \ | ||
| 302 | ".word 6b, " #errh "\n\t" \ | ||
| 303 | ".word 7b, " #errh "\n\t" \ | ||
| 304 | ".word 8b, " #errh "\n\t" \ | ||
| 305 | ".word 9b, " #errh "\n\t" \ | ||
| 306 | ".word 10b, " #errh "\n\t" \ | ||
| 307 | ".word 11b, " #errh "\n\t" \ | ||
| 308 | ".word 12b, " #errh "\n\t" \ | ||
| 309 | ".word 13b, " #errh "\n\t" \ | ||
| 310 | ".word 14b, " #errh "\n\t" \ | ||
| 311 | ".word 15b, " #errh "\n\t" \ | ||
| 312 | ".word 16b, " #errh "\n\t" \ | ||
| 313 | ".word 17b, " #errh "\n\n\t" \ | ||
| 314 | ".previous\n\t" \ | ||
| 315 | : : "r" (dst_addr), "r" (size), "r" (src_val), "r" (asi), "i" (ASI_AIUS)\ | ||
| 316 | : "l1", "l2", "g7", "g1", "cc"); \ | ||
| 317 | }) | ||
| 318 | |||
| 319 | #define do_integer_store(reg_num, size, dst_addr, regs, asi, errh) ({ \ | ||
| 320 | unsigned long zero = 0; \ | ||
| 321 | unsigned long *src_val = &zero; \ | ||
| 322 | \ | ||
| 323 | if (size == 16) { \ | ||
| 324 | size = 8; \ | ||
| 325 | zero = (((long)(reg_num ? \ | ||
| 326 | (unsigned)fetch_reg(reg_num, regs) : 0)) << 32) | \ | ||
| 327 | (unsigned)fetch_reg(reg_num + 1, regs); \ | ||
| 328 | } else if (reg_num) src_val = fetch_reg_addr(reg_num, regs); \ | ||
| 329 | store_common(dst_addr, size, src_val, asi, errh); \ | ||
| 330 | }) | ||
| 331 | |||
| 332 | extern void smp_capture(void); | ||
| 333 | extern void smp_release(void); | ||
| 334 | |||
| 335 | #define do_atomic(srcdest_reg, mem, errh) ({ \ | ||
| 336 | unsigned long flags, tmp; \ | ||
| 337 | \ | ||
| 338 | smp_capture(); \ | ||
| 339 | local_irq_save(flags); \ | ||
| 340 | tmp = *srcdest_reg; \ | ||
| 341 | do_integer_load(srcdest_reg, 4, mem, 0, errh); \ | ||
| 342 | store_common(mem, 4, &tmp, errh); \ | ||
| 343 | local_irq_restore(flags); \ | ||
| 344 | smp_release(); \ | ||
| 345 | }) | ||
| 346 | 205 | ||
| 347 | static inline void advance(struct pt_regs *regs) | 206 | static inline void advance(struct pt_regs *regs) |
| 348 | { | 207 | { |
| @@ -364,24 +223,29 @@ static inline int ok_for_kernel(unsigned int insn) | |||
| 364 | return !floating_point_load_or_store_p(insn); | 223 | return !floating_point_load_or_store_p(insn); |
| 365 | } | 224 | } |
| 366 | 225 | ||
| 367 | void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("kernel_mna_trap_fault"); | 226 | void kernel_mna_trap_fault(void) |
| 368 | |||
| 369 | void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) | ||
| 370 | { | 227 | { |
| 371 | unsigned long g2 = regs->u_regs [UREG_G2]; | 228 | struct pt_regs *regs = current_thread_info()->kern_una_regs; |
| 229 | unsigned int insn = current_thread_info()->kern_una_insn; | ||
| 230 | unsigned long g2 = regs->u_regs[UREG_G2]; | ||
| 372 | unsigned long fixup = search_extables_range(regs->tpc, &g2); | 231 | unsigned long fixup = search_extables_range(regs->tpc, &g2); |
| 373 | 232 | ||
| 374 | if (!fixup) { | 233 | if (!fixup) { |
| 375 | unsigned long address = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f)); | 234 | unsigned long address; |
| 235 | |||
| 236 | address = compute_effective_address(regs, insn, | ||
| 237 | ((insn >> 25) & 0x1f)); | ||
| 376 | if (address < PAGE_SIZE) { | 238 | if (address < PAGE_SIZE) { |
| 377 | printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler"); | 239 | printk(KERN_ALERT "Unable to handle kernel NULL " |
| 240 | "pointer dereference in mna handler"); | ||
| 378 | } else | 241 | } else |
| 379 | printk(KERN_ALERT "Unable to handle kernel paging request in mna handler"); | 242 | printk(KERN_ALERT "Unable to handle kernel paging " |
| 243 | "request in mna handler"); | ||
| 380 | printk(KERN_ALERT " at virtual address %016lx\n",address); | 244 | printk(KERN_ALERT " at virtual address %016lx\n",address); |
| 381 | printk(KERN_ALERT "current->{mm,active_mm}->context = %016lx\n", | 245 | printk(KERN_ALERT "current->{active_,}mm->context = %016lx\n", |
| 382 | (current->mm ? CTX_HWBITS(current->mm->context) : | 246 | (current->mm ? CTX_HWBITS(current->mm->context) : |
| 383 | CTX_HWBITS(current->active_mm->context))); | 247 | CTX_HWBITS(current->active_mm->context))); |
| 384 | printk(KERN_ALERT "current->{mm,active_mm}->pgd = %016lx\n", | 248 | printk(KERN_ALERT "current->{active_,}mm->pgd = %016lx\n", |
| 385 | (current->mm ? (unsigned long) current->mm->pgd : | 249 | (current->mm ? (unsigned long) current->mm->pgd : |
| 386 | (unsigned long) current->active_mm->pgd)); | 250 | (unsigned long) current->active_mm->pgd)); |
| 387 | die_if_kernel("Oops", regs); | 251 | die_if_kernel("Oops", regs); |
| @@ -400,48 +264,41 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u | |||
| 400 | enum direction dir = decode_direction(insn); | 264 | enum direction dir = decode_direction(insn); |
| 401 | int size = decode_access_size(insn); | 265 | int size = decode_access_size(insn); |
| 402 | 266 | ||
| 267 | current_thread_info()->kern_una_regs = regs; | ||
| 268 | current_thread_info()->kern_una_insn = insn; | ||
| 269 | |||
| 403 | if (!ok_for_kernel(insn) || dir == both) { | 270 | if (!ok_for_kernel(insn) || dir == both) { |
| 404 | printk("Unsupported unaligned load/store trap for kernel at <%016lx>.\n", | 271 | printk("Unsupported unaligned load/store trap for kernel " |
| 405 | regs->tpc); | 272 | "at <%016lx>.\n", regs->tpc); |
| 406 | unaligned_panic("Kernel does fpu/atomic unaligned load/store.", regs); | 273 | unaligned_panic("Kernel does fpu/atomic " |
| 407 | 274 | "unaligned load/store.", regs); | |
| 408 | __asm__ __volatile__ ("\n" | 275 | |
| 409 | "kernel_unaligned_trap_fault:\n\t" | 276 | kernel_mna_trap_fault(); |
| 410 | "mov %0, %%o0\n\t" | ||
| 411 | "call kernel_mna_trap_fault\n\t" | ||
| 412 | " mov %1, %%o1\n\t" | ||
| 413 | : | ||
| 414 | : "r" (regs), "r" (insn) | ||
| 415 | : "o0", "o1", "o2", "o3", "o4", "o5", "o7", | ||
| 416 | "g1", "g2", "g3", "g4", "g7", "cc"); | ||
| 417 | } else { | 277 | } else { |
| 418 | unsigned long addr = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f)); | 278 | unsigned long addr; |
| 419 | 279 | ||
| 280 | addr = compute_effective_address(regs, insn, | ||
| 281 | ((insn >> 25) & 0x1f)); | ||
| 420 | #ifdef DEBUG_MNA | 282 | #ifdef DEBUG_MNA |
| 421 | printk("KMNA: pc=%016lx [dir=%s addr=%016lx size=%d] retpc[%016lx]\n", | 283 | printk("KMNA: pc=%016lx [dir=%s addr=%016lx size=%d] " |
| 422 | regs->tpc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]); | 284 | "retpc[%016lx]\n", |
| 285 | regs->tpc, dirstrings[dir], addr, size, | ||
| 286 | regs->u_regs[UREG_RETPC]); | ||
| 423 | #endif | 287 | #endif |
| 424 | switch (dir) { | 288 | switch (dir) { |
| 425 | case load: | 289 | case load: |
| 426 | do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs), | 290 | do_int_load(fetch_reg_addr(((insn>>25)&0x1f), regs), |
| 427 | size, (unsigned long *) addr, | 291 | size, (unsigned long *) addr, |
| 428 | decode_signedness(insn), decode_asi(insn, regs), | 292 | decode_signedness(insn), |
| 429 | kernel_unaligned_trap_fault); | 293 | decode_asi(insn, regs)); |
| 430 | break; | 294 | break; |
| 431 | 295 | ||
| 432 | case store: | 296 | case store: |
| 433 | do_integer_store(((insn>>25)&0x1f), size, | 297 | do_int_store(((insn>>25)&0x1f), size, |
| 434 | (unsigned long *) addr, regs, | 298 | (unsigned long *) addr, regs, |
| 435 | decode_asi(insn, regs), | 299 | decode_asi(insn, regs)); |
| 436 | kernel_unaligned_trap_fault); | ||
| 437 | break; | ||
| 438 | #if 0 /* unsupported */ | ||
| 439 | case both: | ||
| 440 | do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs), | ||
| 441 | (unsigned long *) addr, | ||
| 442 | kernel_unaligned_trap_fault); | ||
| 443 | break; | 300 | break; |
| 444 | #endif | 301 | |
| 445 | default: | 302 | default: |
| 446 | panic("Impossible kernel unaligned trap."); | 303 | panic("Impossible kernel unaligned trap."); |
| 447 | /* Not reached... */ | 304 | /* Not reached... */ |
