diff mbox

powerpc/64: Fix HMI exception on LE with CONFIG_RELOCATABLE=y

Message ID 1492491886-29233-1-git-send-email-mpe@ellerman.id.au (mailing list archive)
State Accepted
Commit be5c5e843c4afa1c8397cb740b6032bd4142f32d
Headers show

Commit Message

Michael Ellerman April 18, 2017, 5:04 a.m. UTC
Prior to commit 2337d207288f ("powerpc/64: CONFIG_RELOCATABLE support for hmi
interrupts"), the branch from hmi_exception_early() to hmi_exception_realmode()
was just a bl hmi_exception_realmode, which the linker would turn into a bl to
the local entry point of hmi_exception_realmode. This was broken when
CONFIG_RELOCATABLE=y because hmi_exception_realmode() is not in the low part of
the kernel text that is copied down to 0x0.

But in fixing that, we added a new bug on little endian kernels. Because the
branch is now a bctrl when CONFIG_RELOCATABLE=y, we branch to the global entry
point of hmi_exception_realmode(). The global entry point must be called with
r12 containing the address of hmi_exception_realmode(), because it uses that
value to calculate the TOC value (r2).

This may manifest as a checkstop, because we take a junk value from r12 which
came from HSRR1, add a small constant to it and then use that as the TOC
pointer. The HSRR1 value will have 0x9 as the top nibble, which puts it above
RAM and somewhere in MMIO space.

Fix it by changing the BRANCH_LINK_TO_FAR() macro to always use r12 to load the
label we're branching to. This means r12 will be setup correctly on LE, fixing
this bug, and r12 is also volatile across function calls on BE so it's a good
choice anyway.

Fixes: 2337d207288f ("powerpc/64: CONFIG_RELOCATABLE support for hmi interrupts")
Reported-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Acked-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
 arch/powerpc/include/asm/exception-64s.h | 8 ++++----
 arch/powerpc/kernel/exceptions-64s.S     | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

Comments

Michael Ellerman April 18, 2017, 11:51 a.m. UTC | #1
On Tue, 2017-04-18 at 05:04:46 UTC, Michael Ellerman wrote:
> Prior to commit 2337d207288f ("powerpc/64: CONFIG_RELOCATABLE support for hmi
> interrupts"), the branch from hmi_exception_early() to hmi_exception_realmode()
> was just a bl hmi_exception_realmode, which the linker would turn into a bl to
> the local entry point of hmi_exception_realmode. This was broken when
> CONFIG_RELOCATABLE=y because hmi_exception_realmode() is not in the low part of
> the kernel text that is copied down to 0x0.
> 
> But in fixing that, we added a new bug on little endian kernels. Because the
> branch is now a bctrl when CONFIG_RELOCATABLE=y, we branch to the global entry
> point of hmi_exception_realmode(). The global entry point must be called with
> r12 containing the address of hmi_exception_realmode(), because it uses that
> value to calculate the TOC value (r2).
> 
> This may manifest as a checkstop, because we take a junk value from r12 which
> came from HSRR1, add a small constant to it and then use that as the TOC
> pointer. The HSRR1 value will have 0x9 as the top nibble, which puts it above
> RAM and somewhere in MMIO space.
> 
> Fix it by changing the BRANCH_LINK_TO_FAR() macro to always use r12 to load the
> label we're branching to. This means r12 will be setup correctly on LE, fixing
> this bug, and r12 is also volatile across function calls on BE so it's a good
> choice anyway.
> 
> Fixes: 2337d207288f ("powerpc/64: CONFIG_RELOCATABLE support for hmi interrupts")
> Reported-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> Acked-by: Nicholas Piggin <npiggin@gmail.com>
> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

Applied to powerpc fixes.

https://git.kernel.org/powerpc/c/be5c5e843c4afa1c8397cb740b6032

cheers
diff mbox

Patch

diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h
index 14752eee3d0c..ed3beadd2cc5 100644
--- a/arch/powerpc/include/asm/exception-64s.h
+++ b/arch/powerpc/include/asm/exception-64s.h
@@ -236,9 +236,9 @@  END_FTR_SECTION_NESTED(ftr,ftr,943)
 	mtctr	reg;							\
 	bctr
 
-#define BRANCH_LINK_TO_FAR(reg, label)					\
-	__LOAD_FAR_HANDLER(reg, label);					\
-	mtctr	reg;							\
+#define BRANCH_LINK_TO_FAR(label)					\
+	__LOAD_FAR_HANDLER(r12, label);					\
+	mtctr	r12;							\
 	bctrl
 
 /*
@@ -265,7 +265,7 @@  END_FTR_SECTION_NESTED(ftr,ftr,943)
 #define BRANCH_TO_COMMON(reg, label)					\
 	b	label
 
-#define BRANCH_LINK_TO_FAR(reg, label)					\
+#define BRANCH_LINK_TO_FAR(label)					\
 	bl	label
 
 #define BRANCH_TO_KVM(reg, label)					\
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 857bf7c5b946..6353019966e6 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -982,7 +982,7 @@  TRAMP_REAL_BEGIN(hmi_exception_early)
 	EXCEPTION_PROLOG_COMMON_2(PACA_EXGEN)
 	EXCEPTION_PROLOG_COMMON_3(0xe60)
 	addi	r3,r1,STACK_FRAME_OVERHEAD
-	BRANCH_LINK_TO_FAR(r4, hmi_exception_realmode)
+	BRANCH_LINK_TO_FAR(hmi_exception_realmode) /* Function call ABI */
 	/* Windup the stack. */
 	/* Move original HSRR0 and HSRR1 into the respective regs */
 	ld	r9,_MSR(r1)