diff options
author | Dave Martin <dave.martin@linaro.org> | 2011-07-28 09:28:52 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-08-09 03:42:39 -0400 |
commit | 088c01f1e39dbe93a13e0b00f4532ed8b79d35f4 (patch) | |
tree | beaf9200bfd86e7250413a1c5490457bab90c8d3 /arch/arm/mm | |
parent | bf912d99e94cd1f43a7decce2e9b79a3ca7f2418 (diff) |
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 be7c638b648b..1df38e833570 100644 --- a/arch/arm/mm/alignment.c +++ b/arch/arm/mm/alignment.c | |||
@@ -95,6 +95,33 @@ static const char *usermode_action[] = { | |||
95 | "signal+warn" | 95 | "signal+warn" |
96 | }; | 96 | }; |
97 | 97 | ||
98 | /* Return true if and only if the ARMv6 unaligned access model is in use. */ | ||
99 | static bool cpu_is_v6_unaligned(void) | ||
100 | { | ||
101 | return cpu_architecture() >= CPU_ARCH_ARMv6 && (cr_alignment & CR_U); | ||
102 | } | ||
103 | |||
104 | static int safe_usermode(int new_usermode, bool warn) | ||
105 | { | ||
106 | /* | ||
107 | * ARMv6 and later CPUs can perform unaligned accesses for | ||
108 | * most single load and store instructions up to word size. | ||
109 | * LDM, STM, LDRD and STRD still need to be handled. | ||
110 | * | ||
111 | * Ignoring the alignment fault is not an option on these | ||
112 | * CPUs since we spin re-faulting the instruction without | ||
113 | * making any progress. | ||
114 | */ | ||
115 | if (cpu_is_v6_unaligned() && !(new_usermode & (UM_FIXUP | UM_SIGNAL))) { | ||
116 | new_usermode |= UM_FIXUP; | ||
117 | |||
118 | if (warn) | ||
119 | printk(KERN_WARNING "alignment: ignoring faults is unsafe on this CPU. Defaulting to fixup mode.\n"); | ||
120 | } | ||
121 | |||
122 | return new_usermode; | ||
123 | } | ||
124 | |||
98 | static int alignment_proc_show(struct seq_file *m, void *v) | 125 | static int alignment_proc_show(struct seq_file *m, void *v) |
99 | { | 126 | { |
100 | seq_printf(m, "User:\t\t%lu\n", ai_user); | 127 | 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 | |||
125 | if (get_user(mode, buffer)) | 152 | if (get_user(mode, buffer)) |
126 | return -EFAULT; | 153 | return -EFAULT; |
127 | if (mode >= '0' && mode <= '5') | 154 | if (mode >= '0' && mode <= '5') |
128 | ai_usermode = mode - '0'; | 155 | ai_usermode = safe_usermode(mode - '0', true); |
129 | } | 156 | } |
130 | return count; | 157 | return count; |
131 | } | 158 | } |
@@ -926,20 +953,11 @@ static int __init alignment_init(void) | |||
926 | return -ENOMEM; | 953 | return -ENOMEM; |
927 | #endif | 954 | #endif |
928 | 955 | ||
929 | /* | 956 | if (cpu_is_v6_unaligned()) { |
930 | * ARMv6 and later CPUs can perform unaligned accesses for | ||
931 | * most single load and store instructions up to word size. | ||
932 | * LDM, STM, LDRD and STRD still need to be handled. | ||
933 | * | ||
934 | * Ignoring the alignment fault is not an option on these | ||
935 | * CPUs since we spin re-faulting the instruction without | ||
936 | * making any progress. | ||
937 | */ | ||
938 | if (cpu_architecture() >= CPU_ARCH_ARMv6 && (cr_alignment & CR_U)) { | ||
939 | cr_alignment &= ~CR_A; | 957 | cr_alignment &= ~CR_A; |
940 | cr_no_alignment &= ~CR_A; | 958 | cr_no_alignment &= ~CR_A; |
941 | set_cr(cr_alignment); | 959 | set_cr(cr_alignment); |
942 | ai_usermode = UM_FIXUP; | 960 | ai_usermode = safe_usermode(ai_usermode, false); |
943 | } | 961 | } |
944 | 962 | ||
945 | hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN, | 963 | hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN, |