summaryrefslogtreecommitdiffstats
path: root/arm-fix-idiv.patch
diff options
context:
space:
mode:
Diffstat (limited to 'arm-fix-idiv.patch')
-rw-r--r--arm-fix-idiv.patch52
1 files changed, 52 insertions, 0 deletions
diff --git a/arm-fix-idiv.patch b/arm-fix-idiv.patch
new file mode 100644
index 000000000..26eac32d8
--- /dev/null
+++ b/arm-fix-idiv.patch
@@ -0,0 +1,52 @@
+From 208fae5c3b9431013ad7bcea07cbcee114e7d163 Mon Sep 17 00:00:00 2001
+From: Nicolas Pitre <nicolas.pitre@linaro.org>
+Date: Mon, 14 Mar 2016 02:55:45 +0100
+Subject: ARM: 8550/1: protect idiv patching against undefined gcc behavior
+
+It was reported that a kernel with CONFIG_ARM_PATCH_IDIV=y stopped
+booting when compiled with the upcoming gcc 6. Turns out that turning
+a function address into a writable array is undefined and gcc 6 decided
+it was OK to omit the store to the first word of the function while
+still preserving the store to the second word.
+
+Even though gcc 6 is now fixed to behave more coherently, it is a
+mystery that gcc 4 and gcc 5 actually produce wanted code in the kernel.
+And in fact the reduced test case to illustrate the issue does indeed
+break with gcc < 6 as well.
+
+In any case, let's guard the kernel against undefined compiler behavior
+by hiding the nature of the array location as suggested by gcc
+developers.
+
+Reference: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70128
+
+Signed-off-by: Nicolas Pitre <nico@linaro.org>
+Reported-by: Marcin Juszkiewicz <mjuszkiewicz@redhat.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: stable@vger.kernel.org # v4.5
+Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
+---
+ arch/arm/kernel/setup.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
+index 139791e..a28fce0 100644
+--- a/arch/arm/kernel/setup.c
++++ b/arch/arm/kernel/setup.c
+@@ -430,11 +430,13 @@ static void __init patch_aeabi_idiv(void)
+ pr_info("CPU: div instructions available: patching division code\n");
+
+ fn_addr = ((uintptr_t)&__aeabi_uidiv) & ~1;
++ asm ("" : "+g" (fn_addr));
+ ((u32 *)fn_addr)[0] = udiv_instruction();
+ ((u32 *)fn_addr)[1] = bx_lr_instruction();
+ flush_icache_range(fn_addr, fn_addr + 8);
+
+ fn_addr = ((uintptr_t)&__aeabi_idiv) & ~1;
++ asm ("" : "+g" (fn_addr));
+ ((u32 *)fn_addr)[0] = sdiv_instruction();
+ ((u32 *)fn_addr)[1] = bx_lr_instruction();
+ flush_icache_range(fn_addr, fn_addr + 8);
+--
+cgit v0.12
+