Le 26/09/2022 à 08:43, Benjamin Gray a écrit :
> Implement static call support for 64 bit V2 ABI. This requires
> making sure the TOC is kept correct across kernel-module
> boundaries. As a secondary concern, it tries to use the local
> entry point of a target wherever possible. It does so by
> checking if both tramp & target are kernel code, and falls
> back to detecting the common global entry point patterns
> if modules are involved. Detecting the global entry point is
> also required for setting the local entry point as the trampoline
> target: if we cannot detect the local entry point, then we need to
> convservatively initialise r12 and use the global entry point.
> 
> The trampolines are marked with `.localentry NAME, 1` to make the
> linker save and restore the TOC on each call to the trampoline. This
> allows the trampoline to safely target functions with different TOC
> values.
> 
> However this directive also implies the TOC is not initialised on entry
> to the trampoline. The kernel TOC is easily found in the PACA, but not
> an arbitrary module TOC. Therefore the trampoline implementation depends
> on whether it's in the kernel or not. If in the kernel, we initialise
> the TOC using the PACA. If in a module, we have to initialise the TOC
> with zero context, so it's quite expensive.
> 
> Signed-off-by: Benjamin Gray <bg...@linux.ibm.com>
> ---
>   arch/powerpc/Kconfig                     |  2 +-
>   arch/powerpc/include/asm/code-patching.h |  1 +
>   arch/powerpc/include/asm/static_call.h   | 80 +++++++++++++++++++--
>   arch/powerpc/kernel/Makefile             |  3 +-
>   arch/powerpc/kernel/static_call.c        | 90 ++++++++++++++++++++++--
>   5 files changed, 164 insertions(+), 12 deletions(-)
> 
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 4c466acdc70d..e7a66635eade 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -248,7 +248,7 @@ config PPC
>       select HAVE_SOFTIRQ_ON_OWN_STACK
>       select HAVE_STACKPROTECTOR              if PPC32 && 
> $(cc-option,-mstack-protector-guard=tls -mstack-protector-guard-reg=r2)
>       select HAVE_STACKPROTECTOR              if PPC64 && 
> $(cc-option,-mstack-protector-guard=tls -mstack-protector-guard-reg=r13)
> -     select HAVE_STATIC_CALL                 if PPC32
> +     select HAVE_STATIC_CALL                 if PPC32 || PPC64_ELF_ABI_V2
>       select HAVE_SYSCALL_TRACEPOINTS
>       select HAVE_VIRT_CPU_ACCOUNTING
>       select HUGETLB_PAGE_SIZE_VARIABLE       if PPC_BOOK3S_64 && HUGETLB_PAGE
> diff --git a/arch/powerpc/include/asm/code-patching.h 
> b/arch/powerpc/include/asm/code-patching.h
> index 15efd8ab22da..8d1850080af8 100644
> --- a/arch/powerpc/include/asm/code-patching.h
> +++ b/arch/powerpc/include/asm/code-patching.h
> @@ -132,6 +132,7 @@ int translate_branch(ppc_inst_t *instr, const u32 *dest, 
> const u32 *src);
>   bool is_conditional_branch(ppc_inst_t instr);
>   
>   #define OP_RT_RA_MASK       0xffff0000UL
> +#define OP_SI_MASK   0x0000ffffUL
>   #define LIS_R2              (PPC_RAW_LIS(_R2, 0))
>   #define ADDIS_R2_R12        (PPC_RAW_ADDIS(_R2, _R12, 0))
>   #define ADDI_R2_R2  (PPC_RAW_ADDI(_R2, _R2, 0))
> diff --git a/arch/powerpc/include/asm/static_call.h 
> b/arch/powerpc/include/asm/static_call.h
> index de1018cc522b..3d6e82200cb7 100644
> --- a/arch/powerpc/include/asm/static_call.h
> +++ b/arch/powerpc/include/asm/static_call.h
> @@ -2,12 +2,75 @@
>   #ifndef _ASM_POWERPC_STATIC_CALL_H
>   #define _ASM_POWERPC_STATIC_CALL_H
>   
> +#ifdef CONFIG_PPC64_ELF_ABI_V2
> +
> +#ifdef MODULE
> +
> +#define __PPC_SCT(name, inst)                                        \
> +     asm(".pushsection .text, \"ax\"                         \n"     \
> +         ".align 6                                           \n"     \
> +         ".globl " STATIC_CALL_TRAMP_STR(name) "             \n"     \
> +         ".localentry " STATIC_CALL_TRAMP_STR(name) ", 1     \n"     \
> +         STATIC_CALL_TRAMP_STR(name) ":                      \n"     \
> +         "   mflr    11                                      \n"     \
> +         "   bcl     20, 31, $+4                             \n"     \
> +         "0: mflr    12                                      \n"     \
> +         "   mtlr    11                                      \n"     \
> +         "   addi    12, 12, (" STATIC_CALL_TRAMP_STR(name) " - 0b)  \n"     
> \
> +         "   addis 2, 12, (.TOC.-" STATIC_CALL_TRAMP_STR(name) ")@ha \n"     
> \
> +         "   addi 2, 2, (.TOC.-" STATIC_CALL_TRAMP_STR(name) ")@l    \n"     
> \
> +         "   " inst "                                        \n"     \
> +         "   ld      12, (2f - " STATIC_CALL_TRAMP_STR(name) ")(12)  \n"     
> \
> +         "   mtctr   12                                      \n"     \
> +         "   bctr                                            \n"     \
> +         "1: li      3, 0                                    \n"     \
> +         "   blr                                             \n"     \
> +         ".balign 8                                          \n"     \
> +         "2: .8byte 0                                        \n"     \
> +         ".type " STATIC_CALL_TRAMP_STR(name) ", @function   \n"     \
> +         ".size " STATIC_CALL_TRAMP_STR(name) ", . - " 
> STATIC_CALL_TRAMP_STR(name) " \n" \
> +         ".popsection                                        \n")
> +
> +#else /* KERNEL */
> +
> +#define __PPC_SCT(name, inst)                                        \
> +     asm(".pushsection .text, \"ax\"                         \n"     \
> +         ".align 5                                           \n"     \
> +         ".globl " STATIC_CALL_TRAMP_STR(name) "             \n"     \
> +         ".localentry " STATIC_CALL_TRAMP_STR(name) ", 1     \n"     \
> +         STATIC_CALL_TRAMP_STR(name) ":                      \n"     \
> +         "   ld      2, 16(13)                               \n"     \
> +         "   " inst "                                        \n"     \
> +         "   addis   12, 2, 2f@toc@ha                        \n"     \
> +         "   ld      12, 2f@toc@l(12)                        \n"     \
> +         "   mtctr   12                                      \n"     \
> +         "   bctr                                            \n"     \
> +         "1: li      3, 0                                    \n"     \
> +         "   blr                                             \n"     \
> +         ".balign 8                                          \n"     \
> +         "2: .8byte 0                                        \n"     \
> +         ".type " STATIC_CALL_TRAMP_STR(name) ", @function   \n"     \
> +         ".size " STATIC_CALL_TRAMP_STR(name) ", . - " 
> STATIC_CALL_TRAMP_STR(name) " \n" \
> +         ".popsection                                        \n")
> +
> +#endif /* MODULE */
> +
> +#define PPC_SCT_INST_MODULE          28              /* Offset of 
> instruction to update */
> +#define PPC_SCT_RET0_MODULE          44              /* Offset of label 1 */
> +#define PPC_SCT_DATA_MODULE          56              /* Offset of label 2 
> (aligned) */
> +
> +#define PPC_SCT_INST_KERNEL          4               /* Offset of 
> instruction to update */
> +#define PPC_SCT_RET0_KERNEL          24              /* Offset of label 1 */
> +#define PPC_SCT_DATA_KERNEL          32              /* Offset of label 2 
> (aligned) */
> +
> +#elif defined(CONFIG_PPC32)
> +
>   #define __PPC_SCT(name, inst)                                       \
>       asm(".pushsection .text, \"ax\"                         \n"     \
>           ".align 5                                           \n"     \
>           ".globl " STATIC_CALL_TRAMP_STR(name) "             \n"     \
>           STATIC_CALL_TRAMP_STR(name) ":                      \n"     \
> -         inst "                                              \n"     \
> +         "   " inst "                                        \n"     \
>           "   lis     12,2f@ha                                \n"     \
>           "   lwz     12,2f@l(12)                             \n"     \
>           "   mtctr   12                                      \n"     \
> @@ -19,11 +82,20 @@
>           ".size " STATIC_CALL_TRAMP_STR(name) ", . - " 
> STATIC_CALL_TRAMP_STR(name) " \n" \
>           ".popsection                                        \n")
>   
> -#define PPC_SCT_RET0         20              /* Offset of label 1 */
> -#define PPC_SCT_DATA         28              /* Offset of label 2 */
> +#define PPC_SCT_INST_MODULE          0               /* Offset of 
> instruction to update */
> +#define PPC_SCT_RET0_MODULE          20              /* Offset of label 1 */
> +#define PPC_SCT_DATA_MODULE          28              /* Offset of label 2 */
> +
> +#define PPC_SCT_INST_KERNEL          PPC_SCT_INST_MODULE
> +#define PPC_SCT_RET0_KERNEL          PPC_SCT_RET0_MODULE
> +#define PPC_SCT_DATA_KERNEL          PPC_SCT_DATA_MODULE
> +
> +#else /* !CONFIG_PPC64_ELF_ABI_V2 && !CONFIG_PPC32 */
> +#error "Unsupported ABI"
> +#endif /* CONFIG_PPC64_ELF_ABI_V2 */
>   
>   #define ARCH_DEFINE_STATIC_CALL_TRAMP(name, func)   __PPC_SCT(name, "b " 
> #func)
>   #define ARCH_DEFINE_STATIC_CALL_NULL_TRAMP(name)    __PPC_SCT(name, "blr")
> -#define ARCH_DEFINE_STATIC_CALL_RET0_TRAMP(name)     __PPC_SCT(name, "b 
> .+20")
> +#define ARCH_DEFINE_STATIC_CALL_RET0_TRAMP(name)     __PPC_SCT(name, "b 1f")
>   
>   #endif /* _ASM_POWERPC_STATIC_CALL_H */
> diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
> index 06d2d1f78f71..a30d0d0f5499 100644
> --- a/arch/powerpc/kernel/Makefile
> +++ b/arch/powerpc/kernel/Makefile
> @@ -128,8 +128,9 @@ extra-y                           += vmlinux.lds
>   
>   obj-$(CONFIG_RELOCATABLE)   += reloc_$(BITS).o
>   
> -obj-$(CONFIG_PPC32)          += entry_32.o setup_32.o early_32.o 
> static_call.o
> +obj-$(CONFIG_PPC32)          += entry_32.o setup_32.o early_32.o
>   obj-$(CONFIG_PPC64)         += dma-iommu.o iommu.o
> +obj-$(CONFIG_HAVE_STATIC_CALL)       += static_call.o
>   obj-$(CONFIG_KGDB)          += kgdb.o
>   obj-$(CONFIG_BOOTX_TEXT)    += btext.o
>   obj-$(CONFIG_SMP)           += smp.o
> diff --git a/arch/powerpc/kernel/static_call.c 
> b/arch/powerpc/kernel/static_call.c
> index 863a7aa24650..ecbb74e1b4d3 100644
> --- a/arch/powerpc/kernel/static_call.c
> +++ b/arch/powerpc/kernel/static_call.c
> @@ -4,30 +4,108 @@
>   
>   #include <asm/code-patching.h>
>   
> +static void* ppc_function_toc(u32 *func)
> +{
> +#ifdef CONFIG_PPC64_ELF_ABI_V2

Can you use IS_ENABLED(CONFIG_PPC64_ELF_ABI_V2) instead ?


> +     u32 insn1 = *func;
> +     u32 insn2 = *(func+1);
> +     u64 si1 = sign_extend64((insn1 & OP_SI_MASK) << 16, 31);
> +     u64 si2 = sign_extend64(insn2 & OP_SI_MASK, 15);
> +     u64 addr = ((u64) func + si1) + si2;
> +
> +     if ((((insn1 & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
> +          ((insn1 & OP_RT_RA_MASK) == LIS_R2)) &&
> +         ((insn2 & OP_RT_RA_MASK) == ADDI_R2_R2))
> +             return (void*)addr;
> +#endif
> +     return NULL;
> +}
> +
> +static bool shares_toc(void *func1, void *func2)
> +{
> +     if (IS_ENABLED(CONFIG_PPC64_ELF_ABI_V2)) {
> +             void* func1_toc;
> +             void* func2_toc;
> +
> +             if (func1 == NULL || func2 == NULL)
> +                     return false;
> +
> +             /* Assume the kernel only uses a single TOC */
> +             if (core_kernel_text((unsigned long)func1) &&
> +                 core_kernel_text((unsigned long)func2))
> +                     return true;
> +
> +             /* Fall back to calculating the TOC from common patterns
> +              * if modules are involved
> +              */
> +             func1_toc = ppc_function_toc(func1);
> +             func2_toc = ppc_function_toc(func2);
> +             return func1_toc != NULL && func2_toc != NULL && (func1_toc == 
> func2_toc);

I don't think the parenthesis are required for the third leg.

> +     }
> +
> +     return true;
> +}
> +
> +static void* get_inst_addr(void *tramp)
> +{
> +     return tramp + (core_kernel_text((unsigned long)tramp)
> +                             ? PPC_SCT_INST_KERNEL
> +                             : PPC_SCT_INST_MODULE);
> +}
> +
> +static void* get_ret0_addr(void* tramp)
> +{
> +     return tramp + (core_kernel_text((unsigned long)tramp)
> +                             ? PPC_SCT_RET0_KERNEL
> +                             : PPC_SCT_RET0_MODULE);
> +}
> +
> +static void* get_data_addr(void *tramp)
> +{
> +     return tramp + (core_kernel_text((unsigned long) tramp)
> +                             ? PPC_SCT_DATA_KERNEL
> +                             : PPC_SCT_DATA_MODULE);
> +}
> +
>   void arch_static_call_transform(void *site, void *tramp, void *func, bool 
> tail)
>   {
>       int err;
>       bool is_ret0 = (func == __static_call_return0);
> -     unsigned long target = (unsigned long)(is_ret0 ? tramp + PPC_SCT_RET0 : 
> func);
> -     bool is_short = is_offset_in_branch_range((long)target - (long)tramp);
> +     bool is_short;
> +     void* target = is_ret0 ? get_ret0_addr(tramp) : func;
> +     void* tramp_inst = get_inst_addr(tramp);
>   
>       if (!tramp)
>               return;
>   
> +     if (is_ret0)
> +             is_short = true;
> +     else if (shares_toc(tramp, target))
> +             is_short = is_offset_in_branch_range(
> +                     (long)ppc_function_entry(target) - (long)tramp_inst);
> +     else
> +             /* Combine out-of-range with not sharing a TOC. Though it's 
> possible an
> +              * out-of-range target shares a TOC, handling this separately 
> complicates
> +              * the trampoline. It's simpler to always use the global entry 
> point
> +              * in this case.
> +              */
> +             is_short = false;
> +
>       mutex_lock(&text_mutex);
>   
>       if (func && !is_short) {
> -             err = patch_instruction(tramp + PPC_SCT_DATA, ppc_inst(target));
> +             err = patch_memory(get_data_addr(tramp), target);
>               if (err)
>                       goto out;
>       }
>   
>       if (!func)
> -             err = patch_instruction(tramp, ppc_inst(PPC_RAW_BLR()));
> +             err = patch_instruction(tramp_inst, ppc_inst(PPC_RAW_BLR()));
>       else if (is_short)
> -             err = patch_branch(tramp, target, 0);
> +             err = patch_branch(tramp_inst, ppc_function_entry(target), 0);
>       else
> -             err = patch_instruction(tramp, ppc_inst(PPC_RAW_NOP()));
> +             err = patch_instruction(tramp_inst, ppc_inst(PPC_RAW_NOP()));
> +
>   out:
>       mutex_unlock(&text_mutex);
>   

Reply via email to