Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 088c01f1 authored by Dave Martin's avatar Dave Martin Committed by Russell King
Browse files

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: default avatarDave Martin <dave.martin@linaro.org>
Acked-by: default avatarNicolas Pitre <nicolas.pitre@linaro.org>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent bf912d99
Loading
Loading
Loading
Loading
+30 −12
Original line number Original line Diff line number Diff line
@@ -95,6 +95,33 @@ static const char *usermode_action[] = {
	"signal+warn"
	"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)
static int alignment_proc_show(struct seq_file *m, void *v)
{
{
	seq_printf(m, "User:\t\t%lu\n", ai_user);
	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))
		if (get_user(mode, buffer))
			return -EFAULT;
			return -EFAULT;
		if (mode >= '0' && mode <= '5')
		if (mode >= '0' && mode <= '5')
			ai_usermode = mode - '0';
			ai_usermode = safe_usermode(mode - '0', true);
	}
	}
	return count;
	return count;
}
}
@@ -926,20 +953,11 @@ static int __init alignment_init(void)
		return -ENOMEM;
		return -ENOMEM;
#endif
#endif


	/*
	if (cpu_is_v6_unaligned()) {
	 * 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)) {
		cr_alignment &= ~CR_A;
		cr_alignment &= ~CR_A;
		cr_no_alignment &= ~CR_A;
		cr_no_alignment &= ~CR_A;
		set_cr(cr_alignment);
		set_cr(cr_alignment);
		ai_usermode = UM_FIXUP;
		ai_usermode = safe_usermode(ai_usermode, false);
	}
	}


	hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,
	hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,