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) die_if_kernel(str, regs); } -#define do_integer_load(dest_reg, size, saddr, is_signed, asi, errh) ({ \ -__asm__ __volatile__ ( \ - "wr %4, 0, %%asi\n\t" \ - "cmp %1, 8\n\t" \ - "bge,pn %%icc, 9f\n\t" \ - " cmp %1, 4\n\t" \ - "be,pt %%icc, 6f\n" \ -"4:\t" " lduba [%2] %%asi, %%l1\n" \ -"5:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \ - "sll %%l1, 8, %%l1\n\t" \ - "brz,pt %3, 3f\n\t" \ - " add %%l1, %%l2, %%l1\n\t" \ - "sllx %%l1, 48, %%l1\n\t" \ - "srax %%l1, 48, %%l1\n" \ -"3:\t" "ba,pt %%xcc, 0f\n\t" \ - " stx %%l1, [%0]\n" \ -"6:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \ - "sll %%l1, 24, %%l1\n" \ -"7:\t" "lduba [%2 + 2] %%asi, %%g7\n\t" \ - "sll %%l2, 16, %%l2\n" \ -"8:\t" "lduba [%2 + 3] %%asi, %%g1\n\t" \ - "sll %%g7, 8, %%g7\n\t" \ - "or %%l1, %%l2, %%l1\n\t" \ - "or %%g7, %%g1, %%g7\n\t" \ - "or %%l1, %%g7, %%l1\n\t" \ - "brnz,a,pt %3, 3f\n\t" \ - " sra %%l1, 0, %%l1\n" \ -"3:\t" "ba,pt %%xcc, 0f\n\t" \ - " stx %%l1, [%0]\n" \ -"9:\t" "lduba [%2] %%asi, %%l1\n" \ -"10:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \ - "sllx %%l1, 56, %%l1\n" \ -"11:\t" "lduba [%2 + 2] %%asi, %%g7\n\t" \ - "sllx %%l2, 48, %%l2\n" \ -"12:\t" "lduba [%2 + 3] %%asi, %%g1\n\t" \ - "sllx %%g7, 40, %%g7\n\t" \ - "sllx %%g1, 32, %%g1\n\t" \ - "or %%l1, %%l2, %%l1\n\t" \ - "or %%g7, %%g1, %%g7\n" \ -"13:\t" "lduba [%2 + 4] %%asi, %%l2\n\t" \ - "or %%l1, %%g7, %%g7\n" \ -"14:\t" "lduba [%2 + 5] %%asi, %%g1\n\t" \ - "sllx %%l2, 24, %%l2\n" \ -"15:\t" "lduba [%2 + 6] %%asi, %%l1\n\t" \ - "sllx %%g1, 16, %%g1\n\t" \ - "or %%g7, %%l2, %%g7\n" \ -"16:\t" "lduba [%2 + 7] %%asi, %%l2\n\t" \ - "sllx %%l1, 8, %%l1\n\t" \ - "or %%g7, %%g1, %%g7\n\t" \ - "or %%l1, %%l2, %%l1\n\t" \ - "or %%g7, %%l1, %%g7\n\t" \ - "cmp %1, 8\n\t" \ - "be,a,pt %%icc, 0f\n\t" \ - " stx %%g7, [%0]\n\t" \ - "srlx %%g7, 32, %%l1\n\t" \ - "sra %%g7, 0, %%g7\n\t" \ - "stx %%l1, [%0]\n\t" \ - "stx %%g7, [%0 + 8]\n" \ -"0:\n\t" \ - "wr %%g0, %5, %%asi\n\n\t" \ - ".section __ex_table\n\t" \ - ".word 4b, " #errh "\n\t" \ - ".word 5b, " #errh "\n\t" \ - ".word 6b, " #errh "\n\t" \ - ".word 7b, " #errh "\n\t" \ - ".word 8b, " #errh "\n\t" \ - ".word 9b, " #errh "\n\t" \ - ".word 10b, " #errh "\n\t" \ - ".word 11b, " #errh "\n\t" \ - ".word 12b, " #errh "\n\t" \ - ".word 13b, " #errh "\n\t" \ - ".word 14b, " #errh "\n\t" \ - ".word 15b, " #errh "\n\t" \ - ".word 16b, " #errh "\n\n\t" \ - ".previous\n\t" \ - : : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed), \ - "r" (asi), "i" (ASI_AIUS) \ - : "l1", "l2", "g7", "g1", "cc"); \ -}) +extern void do_int_load(unsigned long *dest_reg, int size, + unsigned long *saddr, int is_signed, int asi); -#define store_common(dst_addr, size, src_val, asi, errh) ({ \ -__asm__ __volatile__ ( \ - "wr %3, 0, %%asi\n\t" \ - "ldx [%2], %%l1\n" \ - "cmp %1, 2\n\t" \ - "be,pn %%icc, 2f\n\t" \ - " cmp %1, 4\n\t" \ - "be,pt %%icc, 1f\n\t" \ - " srlx %%l1, 24, %%l2\n\t" \ - "srlx %%l1, 56, %%g1\n\t" \ - "srlx %%l1, 48, %%g7\n" \ -"4:\t" "stba %%g1, [%0] %%asi\n\t" \ - "srlx %%l1, 40, %%g1\n" \ -"5:\t" "stba %%g7, [%0 + 1] %%asi\n\t" \ - "srlx %%l1, 32, %%g7\n" \ -"6:\t" "stba %%g1, [%0 + 2] %%asi\n" \ -"7:\t" "stba %%g7, [%0 + 3] %%asi\n\t" \ - "srlx %%l1, 16, %%g1\n" \ -"8:\t" "stba %%l2, [%0 + 4] %%asi\n\t" \ - "srlx %%l1, 8, %%g7\n" \ -"9:\t" "stba %%g1, [%0 + 5] %%asi\n" \ -"10:\t" "stba %%g7, [%0 + 6] %%asi\n\t" \ - "ba,pt %%xcc, 0f\n" \ -"11:\t" " stba %%l1, [%0 + 7] %%asi\n" \ -"1:\t" "srl %%l1, 16, %%g7\n" \ -"12:\t" "stba %%l2, [%0] %%asi\n\t" \ - "srl %%l1, 8, %%l2\n" \ -"13:\t" "stba %%g7, [%0 + 1] %%asi\n" \ -"14:\t" "stba %%l2, [%0 + 2] %%asi\n\t" \ - "ba,pt %%xcc, 0f\n" \ -"15:\t" " stba %%l1, [%0 + 3] %%asi\n" \ -"2:\t" "srl %%l1, 8, %%l2\n" \ -"16:\t" "stba %%l2, [%0] %%asi\n" \ -"17:\t" "stba %%l1, [%0 + 1] %%asi\n" \ -"0:\n\t" \ - "wr %%g0, %4, %%asi\n\n\t" \ - ".section __ex_table\n\t" \ - ".word 4b, " #errh "\n\t" \ - ".word 5b, " #errh "\n\t" \ - ".word 6b, " #errh "\n\t" \ - ".word 7b, " #errh "\n\t" \ - ".word 8b, " #errh "\n\t" \ - ".word 9b, " #errh "\n\t" \ - ".word 10b, " #errh "\n\t" \ - ".word 11b, " #errh "\n\t" \ - ".word 12b, " #errh "\n\t" \ - ".word 13b, " #errh "\n\t" \ - ".word 14b, " #errh "\n\t" \ - ".word 15b, " #errh "\n\t" \ - ".word 16b, " #errh "\n\t" \ - ".word 17b, " #errh "\n\n\t" \ - ".previous\n\t" \ - : : "r" (dst_addr), "r" (size), "r" (src_val), "r" (asi), "i" (ASI_AIUS)\ - : "l1", "l2", "g7", "g1", "cc"); \ -}) - -#define do_integer_store(reg_num, size, dst_addr, regs, asi, errh) ({ \ - unsigned long zero = 0; \ - unsigned long *src_val = &zero; \ - \ - if (size == 16) { \ - size = 8; \ - zero = (((long)(reg_num ? \ - (unsigned)fetch_reg(reg_num, regs) : 0)) << 32) | \ - (unsigned)fetch_reg(reg_num + 1, regs); \ - } else if (reg_num) src_val = fetch_reg_addr(reg_num, regs); \ - store_common(dst_addr, size, src_val, asi, errh); \ -}) - -extern void smp_capture(void); -extern void smp_release(void); - -#define do_atomic(srcdest_reg, mem, errh) ({ \ - unsigned long flags, tmp; \ - \ - smp_capture(); \ - local_irq_save(flags); \ - tmp = *srcdest_reg; \ - do_integer_load(srcdest_reg, 4, mem, 0, errh); \ - store_common(mem, 4, &tmp, errh); \ - local_irq_restore(flags); \ - smp_release(); \ -}) +extern void __do_int_store(unsigned long *dst_addr, int size, + unsigned long *src_val, int asi); + +static inline void do_int_store(int reg_num, int size, unsigned long *dst_addr, + struct pt_regs *regs, int asi) +{ + unsigned long zero = 0; + unsigned long *src_val = &zero; + + if (size == 16) { + size = 8; + zero = (((long)(reg_num ? + (unsigned)fetch_reg(reg_num, regs) : 0)) << 32) | + (unsigned)fetch_reg(reg_num + 1, regs); + } else if (reg_num) { + src_val = fetch_reg_addr(reg_num, regs); + } + __do_int_store(dst_addr, size, src_val, asi); +} static inline void advance(struct pt_regs *regs) { @@ -364,24 +223,29 @@ static inline int ok_for_kernel(unsigned int insn) return !floating_point_load_or_store_p(insn); } -void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("kernel_mna_trap_fault"); - -void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) +void kernel_mna_trap_fault(void) { - unsigned long g2 = regs->u_regs [UREG_G2]; + struct pt_regs *regs = current_thread_info()->kern_una_regs; + unsigned int insn = current_thread_info()->kern_una_insn; + unsigned long g2 = regs->u_regs[UREG_G2]; unsigned long fixup = search_extables_range(regs->tpc, &g2); if (!fixup) { - unsigned long address = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f)); + unsigned long address; + + address = compute_effective_address(regs, insn, + ((insn >> 25) & 0x1f)); if (address < PAGE_SIZE) { - printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler"); + printk(KERN_ALERT "Unable to handle kernel NULL " + "pointer dereference in mna handler"); } else - printk(KERN_ALERT "Unable to handle kernel paging request in mna handler"); + printk(KERN_ALERT "Unable to handle kernel paging " + "request in mna handler"); printk(KERN_ALERT " at virtual address %016lx\n",address); - printk(KERN_ALERT "current->{mm,active_mm}->context = %016lx\n", + printk(KERN_ALERT "current->{active_,}mm->context = %016lx\n", (current->mm ? CTX_HWBITS(current->mm->context) : CTX_HWBITS(current->active_mm->context))); - printk(KERN_ALERT "current->{mm,active_mm}->pgd = %016lx\n", + printk(KERN_ALERT "current->{active_,}mm->pgd = %016lx\n", (current->mm ? (unsigned long) current->mm->pgd : (unsigned long) current->active_mm->pgd)); die_if_kernel("Oops", regs); @@ -400,48 +264,41 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u enum direction dir = decode_direction(insn); int size = decode_access_size(insn); + current_thread_info()->kern_una_regs = regs; + current_thread_info()->kern_una_insn = insn; + if (!ok_for_kernel(insn) || dir == both) { - printk("Unsupported unaligned load/store trap for kernel at <%016lx>.\n", - regs->tpc); - unaligned_panic("Kernel does fpu/atomic unaligned load/store.", regs); - - __asm__ __volatile__ ("\n" -"kernel_unaligned_trap_fault:\n\t" - "mov %0, %%o0\n\t" - "call kernel_mna_trap_fault\n\t" - " mov %1, %%o1\n\t" - : - : "r" (regs), "r" (insn) - : "o0", "o1", "o2", "o3", "o4", "o5", "o7", - "g1", "g2", "g3", "g4", "g7", "cc"); + printk("Unsupported unaligned load/store trap for kernel " + "at <%016lx>.\n", regs->tpc); + unaligned_panic("Kernel does fpu/atomic " + "unaligned load/store.", regs); + + kernel_mna_trap_fault(); } else { - unsigned long addr = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f)); + unsigned long addr; + addr = compute_effective_address(regs, insn, + ((insn >> 25) & 0x1f)); #ifdef DEBUG_MNA - printk("KMNA: pc=%016lx [dir=%s addr=%016lx size=%d] retpc[%016lx]\n", - regs->tpc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]); + printk("KMNA: pc=%016lx [dir=%s addr=%016lx size=%d] " + "retpc[%016lx]\n", + regs->tpc, dirstrings[dir], addr, size, + regs->u_regs[UREG_RETPC]); #endif switch (dir) { case load: - do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs), - size, (unsigned long *) addr, - decode_signedness(insn), decode_asi(insn, regs), - kernel_unaligned_trap_fault); + do_int_load(fetch_reg_addr(((insn>>25)&0x1f), regs), + size, (unsigned long *) addr, + decode_signedness(insn), + decode_asi(insn, regs)); break; case store: - do_integer_store(((insn>>25)&0x1f), size, - (unsigned long *) addr, regs, - decode_asi(insn, regs), - kernel_unaligned_trap_fault); - break; -#if 0 /* unsupported */ - case both: - do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs), - (unsigned long *) addr, - kernel_unaligned_trap_fault); + do_int_store(((insn>>25)&0x1f), size, + (unsigned long *) addr, regs, + decode_asi(insn, regs)); break; -#endif + default: panic("Impossible kernel unaligned trap."); /* Not reached... */ |