[ARM] Fix alignment fault handling for ARMv6 and later CPUs

On ARMv6 and later CPUs, it is possible for userspace processes to
get stuck on a misaligned load or store due to the "ignore fault"
setting; unlike previous CPUs, retrying the instruction without
the 'A' bit set does not always cause the load to succeed.

We have no real option but to default to fixing up alignment faults
on these CPUs, and having the CPU fix up those misaligned accesses
which it can.

Reported-by: Wolfgang Grandegger <wg@grandegger.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by Russell King and committed by Russell King baa745a3 794baba6

+23 -3
+23 -3
arch/arm/mm/alignment.c
··· 70 70 static unsigned long ai_multi; 71 71 static int ai_usermode; 72 72 73 + #define UM_WARN (1 << 0) 74 + #define UM_FIXUP (1 << 1) 75 + #define UM_SIGNAL (1 << 2) 76 + 73 77 #ifdef CONFIG_PROC_FS 74 78 static const char *usermode_action[] = { 75 79 "ignored", ··· 758 754 user: 759 755 ai_user += 1; 760 756 761 - if (ai_usermode & 1) 757 + if (ai_usermode & UM_WARN) 762 758 printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%0*lx " 763 759 "Address=0x%08lx FSR 0x%03x\n", current->comm, 764 760 task_pid_nr(current), instrptr, ··· 766 762 thumb_mode(regs) ? tinstr : instr, 767 763 addr, fsr); 768 764 769 - if (ai_usermode & 2) 765 + if (ai_usermode & UM_FIXUP) 770 766 goto fixup; 771 767 772 - if (ai_usermode & 4) 768 + if (ai_usermode & UM_SIGNAL) 773 769 force_sig(SIGBUS, current); 774 770 else 775 771 set_cr(cr_no_alignment); ··· 799 795 res->read_proc = proc_alignment_read; 800 796 res->write_proc = proc_alignment_write; 801 797 #endif 798 + 799 + /* 800 + * ARMv6 and later CPUs can perform unaligned accesses for 801 + * most single load and store instructions up to word size. 802 + * LDM, STM, LDRD and STRD still need to be handled. 803 + * 804 + * Ignoring the alignment fault is not an option on these 805 + * CPUs since we spin re-faulting the instruction without 806 + * making any progress. 807 + */ 808 + if (cpu_architecture() >= CPU_ARCH_ARMv6 && (cr_alignment & CR_U)) { 809 + cr_alignment &= ~CR_A; 810 + cr_no_alignment &= ~CR_A; 811 + set_cr(cr_alignment); 812 + ai_usermode = UM_FIXUP; 813 + } 802 814 803 815 hook_fault_code(1, do_alignment, SIGILL, "alignment exception"); 804 816 hook_fault_code(3, do_alignment, SIGILL, "alignment exception");