/* * linux/arch/arm/mm/alignment.c * * Copyright (C) 1995 Linus Torvalds * Modifications for ARM processor (c) 1995-2001 Russell King * Thumb alignment fault fixups (c) 2004 MontaVista Software, Inc. * - Adapted from gdb/sim/arm/thumbemu.c -- Thumb instruction emulation. * Copyright (C) 1996, Cygnus Software Technologies Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fault.h" /* * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 * /proc/sys/debug/alignment, modified and integrated into * Linux 2.1 by Russell King * * Speed optimisations and better fault handling by Russell King. * * *** NOTE *** * This code is not portable to processors with late data abort handling. */ #define CODING_BITS(i) (i & 0x0e000000) #define LDST_I_BIT(i) (i & (1 << 26)) /* Immediate constant */ #define LDST_P_BIT(i) (i & (1 << 24)) /* Preindex */ #define LDST_U_BIT(i) (i & (1 << 23)) /* Add offset */ #define LDST_W_BIT(i) (i & (1 << 21)) /* Writeback */ #define LDST_L_BIT(i) (i & (1 << 20)) /* Load */ #define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0) #define LDSTHD_I_BIT(i) (i & (1 << 22)) /* double/half-word immed */ #define LDM_S_BIT(i) (i & (1 << 22)) /* write CPSR from SPSR */ #define RN_BITS(i) ((i >> 16) & 15) /* Rn */ #define RD_BITS(i) ((i >> 12) & 15) /* Rd */ #define RM_BITS(i) (i & 15) /* Rm */ #define REGMASK_BITS(i) (i & 0xffff) #define OFFSET_BITS(i) (i & 0x0fff) #define IS_SHIFT(i) (i & 0x0ff0) #define SHIFT_BITS(i) ((i >> 7) & 0x1f) #define SHIFT_TYPE(i) (i & 0x60) #define SHIFT_LSL 0x00 #define SHIFT_LSR 0x20 #define SHIFT_ASR 0x40 #define SHIFT_RORRRX 0x60 #define BAD_INSTR 0xdeadc0de /* Thumb-2 32 bit format per ARMv7 DDI0406A A6.3, either f800h,e800h,f800h */ #define IS_T32(hi16) \ (((hi16) & 0xe000) == 0xe000 && ((hi16) & 0x1800)) static unsigned long ai_user; static unsigned long ai_sys; static unsigned long ai_skipped; static unsigned long ai_half; static unsigned long ai_word; static unsigned long ai_dword; static unsigned long ai_multi; static int ai_usermode; core_param(alignment, ai_usermode, int, 0600); #define UM_WARN (1 << 0) #define UM_FIXUP (1 << 1) #define UM_SIGNAL (1 << 2) /* Return true if and only if the ARMv6 unaligned access model is in use. */ static bool cpu_is_v6_unaligned(void) { return cpu_architecture() >= CPU_ARCH_ARMv6 && (cr_alignment & CR_U); } static int safe_usermode(int new_usermode, bool warn) { /* * ARMv6 and later CPUs can perform unaligned accesses for * most single load and store instructions up to word size. * LDM, STM, LDRD and STRD still need to be handled. * * Ignoring the alignment fault is not an option on these * CPUs since we spin re-faulting the instruction without * making any progress. */ if (cpu_is_v6_unaligned() && !(new_usermode & (UM_FIXUP | UM_SIGNAL))) { new_usermode |= UM_FIXUP; if (warn) printk(KERN_WARNING "alignment: ignoring faults is unsafe on this CPU. Defaulting to fixup mode.\n"); } return new_usermode; } #ifdef CONFIG_PROC_FS static const char *usermode_action[] = { "ignored", "warn", "fixup", "fixup+warn", "signal", "signal+warn" }; static int alignment_proc_show(struct seq_file *m, void *v) { seq_printf(m, "User:\t\t%lu\n", ai_user); seq_printf(m, "System:\t\t%lu\n", ai_sys);