diff options
| author | Dave Martin <dave.martin@linaro.org> | 2011-07-28 14:28:52 +0100 | 
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-08-09 08:42:39 +0100 | 
| commit | 088c01f1e39dbe93a13e0b00f4532ed8b79d35f4 (patch) | |
| tree | beaf9200bfd86e7250413a1c5490457bab90c8d3 /arch/arm/mm | |
| parent | bf912d99e94cd1f43a7decce2e9b79a3ca7f2418 (diff) | |
| download | olio-linux-3.10-088c01f1e39dbe93a13e0b00f4532ed8b79d35f4.tar.xz olio-linux-3.10-088c01f1e39dbe93a13e0b00f4532ed8b79d35f4.zip  | |
ARM: 7007/1: alignment: Prevent ignoring of faults with ARMv6 unaligned access model
Currently, it's possible to set the kernel to ignore alignment
faults when changing the alignment fault handling mode at runtime
via /proc/sys/alignment, even though this is undesirable on ARMv6
and above, where it can result in infinite spins where an un-fixed-
up instruction repeatedly faults.
In addition, the kernel clobbers any alignment mode specified on
the command-line if running on ARMv6 or above.
This patch factors out the necessary safety check into a couple of
new helper functions, and checks and modifies the fault handling
mode as appropriate on boot and on writes to /proc/cpu/alignment.
Prior to ARMv6, the behaviour is unchanged.
For ARMv6 and above, the behaviour changes as follows:
  * Attempting to ignore faults on ARMv6 results in the mode being
    forced to UM_FIXUP instead.  A warning is printed if this
    happened as a result of a write to /proc/cpu/alignment.  The
    user's UM_WARN bit (if present) is still honoured.
  * An alignment= argument from the kernel command-line is now
    honoured, except that the kernel will modify the specified mode
    as described above.  This is allows modes such as UM_SIGNAL and
    UM_WARN to be active immediately from boot, which is useful for
    debugging purposes.
Signed-off-by: Dave Martin <dave.martin@linaro.org>
Acked-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mm')
| -rw-r--r-- | arch/arm/mm/alignment.c | 42 | 
1 files changed, 30 insertions, 12 deletions
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c index be7c638b648..1df38e83357 100644 --- a/arch/arm/mm/alignment.c +++ b/arch/arm/mm/alignment.c @@ -95,6 +95,33 @@ static const char *usermode_action[] = {  	"signal+warn"  }; +/* 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; +} +  static int alignment_proc_show(struct seq_file *m, void *v)  {  	seq_printf(m, "User:\t\t%lu\n", ai_user); @@ -125,7 +152,7 @@ static ssize_t alignment_proc_write(struct file *file, const char __user *buffer  		if (get_user(mode, buffer))  			return -EFAULT;  		if (mode >= '0' && mode <= '5') -			ai_usermode = mode - '0'; +			ai_usermode = safe_usermode(mode - '0', true);  	}  	return count;  } @@ -926,20 +953,11 @@ static int __init alignment_init(void)  		return -ENOMEM;  #endif -	/* -	 * 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_architecture() >= CPU_ARCH_ARMv6 && (cr_alignment & CR_U)) { +	if (cpu_is_v6_unaligned()) {  		cr_alignment &= ~CR_A;  		cr_no_alignment &= ~CR_A;  		set_cr(cr_alignment); -		ai_usermode = UM_FIXUP; +		ai_usermode = safe_usermode(ai_usermode, false);  	}  	hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,  |