diff options
Diffstat (limited to 'arm-fix-idiv.patch')
-rw-r--r-- | arm-fix-idiv.patch | 52 |
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 + |