| From foo@baz Fri Mar 16 15:43:17 CET 2018 |
| From: Josh Poimboeuf <jpoimboe@redhat.com> |
| Date: Thu, 16 Nov 2017 11:45:37 -0600 |
| Subject: powerpc/modules: Don't try to restore r2 after a sibling call |
| |
| From: Josh Poimboeuf <jpoimboe@redhat.com> |
| |
| |
| [ Upstream commit b9eab08d012fa093947b230f9a87257c27fb829b ] |
| |
| When attempting to load a livepatch module, I got the following error: |
| |
| module_64: patch_module: Expect noop after relocate, got 3c820000 |
| |
| The error was triggered by the following code in |
| unregister_netdevice_queue(): |
| |
| 14c: 00 00 00 48 b 14c <unregister_netdevice_queue+0x14c> |
| 14c: R_PPC64_REL24 net_set_todo |
| 150: 00 00 82 3c addis r4,r2,0 |
| |
| GCC didn't insert a nop after the branch to net_set_todo() because it's |
| a sibling call, so it never returns. The nop isn't needed after the |
| branch in that case. |
| |
| Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> |
| Acked-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> |
| Reviewed-and-tested-by: Kamalesh Babulal <kamalesh@linux.vnet.ibm.com> |
| Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/powerpc/include/asm/code-patching.h | 1 + |
| arch/powerpc/kernel/module_64.c | 12 +++++++++++- |
| arch/powerpc/lib/code-patching.c | 5 +++++ |
| 3 files changed, 17 insertions(+), 1 deletion(-) |
| |
| --- a/arch/powerpc/include/asm/code-patching.h |
| +++ b/arch/powerpc/include/asm/code-patching.h |
| @@ -33,6 +33,7 @@ int patch_branch(unsigned int *addr, uns |
| int patch_instruction(unsigned int *addr, unsigned int instr); |
| |
| int instr_is_relative_branch(unsigned int instr); |
| +int instr_is_relative_link_branch(unsigned int instr); |
| int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr); |
| unsigned long branch_target(const unsigned int *instr); |
| unsigned int translate_branch(const unsigned int *dest, |
| --- a/arch/powerpc/kernel/module_64.c |
| +++ b/arch/powerpc/kernel/module_64.c |
| @@ -486,7 +486,17 @@ static bool is_early_mcount_callsite(u32 |
| restore r2. */ |
| static int restore_r2(u32 *instruction, struct module *me) |
| { |
| - if (is_early_mcount_callsite(instruction - 1)) |
| + u32 *prev_insn = instruction - 1; |
| + |
| + if (is_early_mcount_callsite(prev_insn)) |
| + return 1; |
| + |
| + /* |
| + * Make sure the branch isn't a sibling call. Sibling calls aren't |
| + * "link" branches and they don't return, so they don't need the r2 |
| + * restore afterwards. |
| + */ |
| + if (!instr_is_relative_link_branch(*prev_insn)) |
| return 1; |
| |
| if (*instruction != PPC_INST_NOP) { |
| --- a/arch/powerpc/lib/code-patching.c |
| +++ b/arch/powerpc/lib/code-patching.c |
| @@ -302,6 +302,11 @@ int instr_is_relative_branch(unsigned in |
| return instr_is_branch_iform(instr) || instr_is_branch_bform(instr); |
| } |
| |
| +int instr_is_relative_link_branch(unsigned int instr) |
| +{ |
| + return instr_is_relative_branch(instr) && (instr & BRANCH_SET_LINK); |
| +} |
| + |
| static unsigned long branch_iform_target(const unsigned int *instr) |
| { |
| signed long imm; |