diff options
author | Anton Blanchard <anton@samba.org> | 2014-02-04 06:09:15 +0100 |
---|---|---|
committer | Anton Blanchard <anton@samba.org> | 2014-04-23 02:05:21 +0200 |
commit | c71b7eff426fa7d8fd33e0964a7f79a3b41faff9 (patch) | |
tree | 19035b32883d0b554d3b318a80f11f1acc29a485 /arch/powerpc/include/asm/code-patching.h | |
parent | powerpc: Ignore .TOC. relocations (diff) | |
download | linux-c71b7eff426fa7d8fd33e0964a7f79a3b41faff9.tar.xz linux-c71b7eff426fa7d8fd33e0964a7f79a3b41faff9.zip |
powerpc: Add ABIv2 support to ppc_function_entry
Skip over the well known global entry point code for ABIv2.
Signed-off-by: Anton Blanchard <anton@samba.org>
Diffstat (limited to 'arch/powerpc/include/asm/code-patching.h')
-rw-r--r-- | arch/powerpc/include/asm/code-patching.h | 40 |
1 files changed, 36 insertions, 4 deletions
diff --git a/arch/powerpc/include/asm/code-patching.h b/arch/powerpc/include/asm/code-patching.h index 97e02f985df8..37991e154ef8 100644 --- a/arch/powerpc/include/asm/code-patching.h +++ b/arch/powerpc/include/asm/code-patching.h @@ -42,15 +42,47 @@ void __patch_exception(int exc, unsigned long addr); } while (0) #endif +#define OP_RT_RA_MASK 0xffff0000UL +#define LIS_R2 0x3c020000UL +#define ADDIS_R2_R12 0x3c4c0000UL +#define ADDI_R2_R2 0x38420000UL + static inline unsigned long ppc_function_entry(void *func) { -#ifdef CONFIG_PPC64 +#if defined(CONFIG_PPC64) +#if defined(_CALL_ELF) && _CALL_ELF == 2 + u32 *insn = func; + + /* + * A PPC64 ABIv2 function may have a local and a global entry + * point. We need to use the local entry point when patching + * functions, so identify and step over the global entry point + * sequence. + * + * The global entry point sequence is always of the form: + * + * addis r2,r12,XXXX + * addi r2,r2,XXXX + * + * A linker optimisation may convert the addis to lis: + * + * lis r2,XXXX + * addi r2,r2,XXXX + */ + if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) || + ((*insn & OP_RT_RA_MASK) == LIS_R2)) && + ((*(insn+1) & OP_RT_RA_MASK) == ADDI_R2_R2)) + return (unsigned long)(insn + 2); + else + return (unsigned long)func; +#else /* - * On PPC64 the function pointer actually points to the function's - * descriptor. The first entry in the descriptor is the address - * of the function text. + * On PPC64 ABIv1 the function pointer actually points to the + * function's descriptor. The first entry in the descriptor is the + * address of the function text. */ return ((func_descr_t *)func)->entry; +#endif #else return (unsigned long)func; #endif |