powerpc/64: vmlinux support building with PCREL addresing
authorNicholas Piggin <npiggin@gmail.com>
Sat, 8 Apr 2023 02:17:51 +0000 (12:17 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Thu, 20 Apr 2023 02:59:21 +0000 (12:59 +1000)
PC-Relative or PCREL addressing is an extension to the ELF ABI which
uses Power ISA v3.1 PC-relative instructions to calculate addresses,
rather than the traditional TOC scheme.

Add an option to build vmlinux using pcrel addressing. Modules continue
to use TOC addressing.

- TOC address helpers and r2 are poisoned with -1 when running vmlinux.
  r2 could be used for something useful once things are ironed out.

- Assembly must call C functions with @notoc annotation, or the linker
  complains aobut a missing nop after the call. This is done with the
  CFUNC macro introduced earlier.

- Boot: with the exception of prom_init, the execution branches to the
  kernel virtual address early in boot, before any addresses are
  generated, which ensures 34-bit pcrel addressing does not miss the
  high PAGE_OFFSET bits. TOC relative addressing has a similar
  requirement. prom_init does not go to the virtual address and its
  addresses should not carry over to the post-prom kernel.

- Ftrace trampolines are converted from TOC addressing to pcrel
  addressing, including module ftrace trampolines that currently use the
  kernel TOC to find ftrace target functions.

- BPF function prologue and function calling generation are converted
  from TOC to pcrel.

- copypage_64.S has an interesting problem, prefixed instructions have
  alignment restrictions so the linker can add padding, which makes the
  assembler treat the difference between two local labels as
  non-constant even if alignment is arranged so padding is not required.
  This may need toolchain help to solve nicely, for now move the prefix
  instruction out of the alternate patch section to work around it.

This reduces kernel text size by about 6%.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230408021752.862660-6-npiggin@gmail.com
19 files changed:
arch/powerpc/Kconfig
arch/powerpc/Makefile
arch/powerpc/include/asm/paca.h
arch/powerpc/include/asm/ppc-opcode.h
arch/powerpc/include/asm/ppc_asm.h
arch/powerpc/include/asm/sections.h
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/head_64.S
arch/powerpc/kernel/irq.c
arch/powerpc/kernel/module_64.c
arch/powerpc/kernel/paca.c
arch/powerpc/kernel/trace/ftrace.c
arch/powerpc/kernel/vector.S
arch/powerpc/kernel/vmlinux.lds.S
arch/powerpc/lib/copypage_64.S
arch/powerpc/net/bpf_jit.h
arch/powerpc/net/bpf_jit_comp64.c
arch/powerpc/platforms/Kconfig.cputype
arch/powerpc/xmon/xmon.c

index 109c00bd91db207bd448ff55fdb4aa523ac9e93c..873eaa888a706af486b2c2fb5f41fc445d54028b 100644 (file)
@@ -7,6 +7,9 @@ config CC_HAS_ELFV2
 config CC_HAS_PREFIXED
        def_bool PPC64 && $(cc-option, -mcpu=power10 -mprefixed)
 
+config CC_HAS_PCREL
+       def_bool PPC64 && $(cc-option, -mcpu=power10 -mpcrel)
+
 config 32BIT
        bool
        default y if PPC32
index 9fb770d3b40900f9434cbc8257809048f910a561..0297ad9a4ffb297ccba3a5c4ff0ed9c4d5dbb430 100644 (file)
@@ -107,6 +107,9 @@ LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) += -z notext
 LDFLAGS_vmlinux        := $(LDFLAGS_vmlinux-y)
 
 ifdef CONFIG_PPC64
+ifdef CONFIG_PPC_KERNEL_PCREL
+       KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-pcrel)
+endif
 ifeq ($(call cc-option-yn,-mcmodel=medium),y)
        # -mcmodel=medium breaks modules because it uses 32bit offsets from
        # the TOC pointer to create pointers where possible. Pointers into the
@@ -185,7 +188,11 @@ KBUILD_CFLAGS += $(call cc-option,-mprefixed)
 else
 KBUILD_CFLAGS += $(call cc-option,-mno-prefixed)
 endif
+ifdef CONFIG_PPC_KERNEL_PCREL
+KBUILD_CFLAGS += $(call cc-option,-mpcrel)
+else
 KBUILD_CFLAGS += $(call cc-option,-mno-pcrel)
+endif
 
 # No AltiVec or VSX or MMA instructions when building kernel
 KBUILD_CFLAGS += $(call cc-option,-mno-altivec)
index 0ab3511a47d7762382656e71bb0ba76a84c10411..da0377f465973786ae9d955a857af9b0d482ec7b 100644 (file)
@@ -88,7 +88,9 @@ struct paca_struct {
        u16 lock_token;                 /* Constant 0x8000, used in locks */
 #endif
 
+#ifndef CONFIG_PPC_KERNEL_PCREL
        u64 kernel_toc;                 /* Kernel TOC address */
+#endif
        u64 kernelbase;                 /* Base address of kernel */
        u64 kernel_msr;                 /* MSR while running in kernel */
        void *emergency_sp;             /* pointer to emergency stack */
index 21e33e46f4b8cf8c90fb2a2ca7d10956ee314cf2..ca5a0da7df4e533318eae617f36b40d71735ed65 100644 (file)
  * 16-bit immediate helper macros: HA() is for use with sign-extending instrs
  * (e.g. LD, ADDI).  If the bottom 16 bits is "-ve", add another bit into the
  * top half to negate the effect (i.e. 0xffff + 1 = 0x(1)0000).
+ *
+ * XXX: should these mask out possible sign bits?
  */
 #define IMM_H(i)                ((uintptr_t)(i)>>16)
 #define IMM_HA(i)               (((uintptr_t)(i)>>16) +                       \
                                        (((uintptr_t)(i) & 0x8000) >> 15))
 
+/*
+ * 18-bit immediate helper for prefix 18-bit upper immediate si0 field.
+ */
+#define IMM_H18(i)              (((uintptr_t)(i)>>16) & 0x3ffff)
+
 
 /* opcode and xopcode for instructions */
 #define OP_PREFIX      1
 #define PPC_PREFIX_8LS                 0x04000000
 
 /* Prefixed instructions */
+#define PPC_INST_PADDI                 0x38000000
 #define PPC_INST_PLD                   0xe4000000
 #define PPC_INST_PSTD                  0xf4000000
 
index f08b7990a69dd806ee863a30664a1854eb2bd75b..4dfabf4ef9664d772abdd3de68d15cac54ad7da9 100644 (file)
 /*
  * Used to name C functions called from asm
  */
+#if defined(CONFIG_PPC_KERNEL_PCREL) && !defined(MODULE)
+#define CFUNC(name) name@notoc
+#else
 #define CFUNC(name) name
+#endif
 
 /*
  * We use __powerpc64__ here because we want the compat VDSO to use the 32-bit
        .globl name; \
 name:
 
+#if defined(CONFIG_PPC_KERNEL_PCREL) && !defined(MODULE)
+#define _GLOBAL_TOC _GLOBAL
+#else
 #define _GLOBAL_TOC(name) \
        .align 2 ; \
        .type name,@function; \
@@ -220,6 +227,7 @@ name: \
 0:     addis r2,r12,(.TOC.-0b)@ha; \
        addi r2,r2,(.TOC.-0b)@l; \
        .localentry name,.-name
+#endif
 
 #define DOTSYM(a)      a
 
@@ -351,8 +359,13 @@ n:
 
 #ifdef __powerpc64__
 
+#ifdef CONFIG_PPC_KERNEL_PCREL
+#define __LOAD_PACA_TOC(reg)                   \
+       li      reg,-1
+#else
 #define __LOAD_PACA_TOC(reg)                   \
        ld      reg,PACATOC(r13)
+#endif
 
 #define LOAD_PACA_TOC()                                \
        __LOAD_PACA_TOC(r2)
@@ -366,9 +379,15 @@ n:
        ori     reg, reg, (expr)@l;             \
        rldimi  reg, tmp, 32, 0
 
+#if defined(CONFIG_PPC_KERNEL_PCREL) && !defined(MODULE)
+#define LOAD_REG_ADDR(reg,name)                        \
+       pla     reg,name@pcrel
+
+#else
 #define LOAD_REG_ADDR(reg,name)                        \
        addis   reg,r2,name@toc@ha;             \
        addi    reg,reg,name@toc@l
+#endif
 
 #ifdef CONFIG_PPC_BOOK3E_64
 /*
index 9c00c9c0ca8f1724cf575c5f0301cfd852fefb2d..4e1f548c8d373db6d5ec3fb0dfcbe34e7b0fb7cb 100644 (file)
@@ -46,10 +46,15 @@ extern char end_virt_trampolines[];
  */
 static inline unsigned long kernel_toc_addr(void)
 {
+#ifdef CONFIG_PPC_KERNEL_PCREL
+       BUILD_BUG();
+       return -1UL;
+#else
        unsigned long toc_ptr;
 
        asm volatile("mr %0, 2" : "=r" (toc_ptr));
        return toc_ptr;
+#endif
 }
 
 static inline int overlaps_interrupt_vector_text(unsigned long start,
index d24a59a98c0c9e88e66814b143a7b8d1fafe4b11..9f14d95b8b32fd38ce180b9abe082a42b1f51cfd 100644 (file)
@@ -185,7 +185,9 @@ int main(void)
                                 offsetof(struct task_struct, thread_info));
        OFFSET(PACASAVEDMSR, paca_struct, saved_msr);
        OFFSET(PACAR1, paca_struct, saved_r1);
+#ifndef CONFIG_PPC_KERNEL_PCREL
        OFFSET(PACATOC, paca_struct, kernel_toc);
+#endif
        OFFSET(PACAKBASE, paca_struct, kernelbase);
        OFFSET(PACAKMSR, paca_struct, kernel_msr);
 #ifdef CONFIG_PPC_BOOK3S_64
index 6e04e30ae44c012f0d3e6d8a8a01b3e64f81e6fd..f132d8704263cd683f78eed26387aee0babef52c 100644 (file)
@@ -330,6 +330,12 @@ _GLOBAL(fsl_secondary_thread_init)
  */
 _GLOBAL(generic_secondary_smp_init)
        FIXUP_ENDIAN
+
+       li      r13,0
+
+       /* Poison TOC */
+       li      r2,-1
+
        mr      r24,r3
        mr      r25,r4
 
@@ -528,6 +534,9 @@ __start_initialization_multiplatform:
        /* Zero r13 (paca) so early program check / mce don't use it */
        li      r13,0
 
+       /* Poison TOC */
+       li      r2,-1
+
        /*
         * Are we booted from a PROM Of-type client-interface ?
         */
@@ -921,6 +930,10 @@ SYM_FUNC_END(enable_64b_mode)
  * this.
  */
 _GLOBAL(relative_toc)
+#ifdef CONFIG_PPC_KERNEL_PCREL
+       tdnei   r2,-1
+       blr
+#else
        mflr    r0
        bcl     20,31,$+4
 0:     mflr    r11
@@ -931,6 +944,7 @@ _GLOBAL(relative_toc)
 
 .balign 8
 p_toc: .8byte  .TOC. - 0b
+#endif
 
 /*
  * This is where the main kernel code starts.
index c9535f2760b5b73fafa49147e4633e8372b98f7f..6f7d4edaa0bc1518c432eb2bfd98064fb37e0ecb 100644 (file)
@@ -206,7 +206,11 @@ static __always_inline void call_do_softirq(const void *sp)
        asm volatile (
                 PPC_STLU "     %%r1, %[offset](%[sp])  ;"
                "mr             %%r1, %[sp]             ;"
+#ifdef CONFIG_PPC_KERNEL_PCREL
+               "bl             %[callee]@notoc         ;"
+#else
                "bl             %[callee]               ;"
+#endif
                 PPC_LL "       %%r1, 0(%%r1)           ;"
                 : // Outputs
                 : // Inputs
@@ -259,7 +263,11 @@ static __always_inline void call_do_irq(struct pt_regs *regs, void *sp)
                 PPC_STLU "     %%r1, %[offset](%[sp])  ;"
                "mr             %%r4, %%r1              ;"
                "mr             %%r1, %[sp]             ;"
+#ifdef CONFIG_PPC_KERNEL_PCREL
+               "bl             %[callee]@notoc         ;"
+#else
                "bl             %[callee]               ;"
+#endif
                 PPC_LL "       %%r1, 0(%%r1)           ;"
                 : // Outputs
                   "+r" (r3)
index 2ac78d207f7742d39deefaf117f3c8da66f38dcd..0fc04abac3bd7283cc753942018c4cd1e7e800b5 100644 (file)
@@ -101,17 +101,18 @@ static unsigned long stub_func_addr(func_desc_t func)
 /* Like PPC32, we need little trampolines to do > 24-bit jumps (into
    the kernel itself).  But on PPC64, these need to be used for every
    jump, actually, to reset r2 (TOC+0x8000). */
-struct ppc64_stub_entry
-{
-       /* 28 byte jump instruction sequence (7 instructions). We only
-        * need 6 instructions on ABIv2 but we always allocate 7 so
-        * so we don't have to modify the trampoline load instruction. */
+struct ppc64_stub_entry {
+       /*
+        * 28 byte jump instruction sequence (7 instructions) that can
+        * hold ppc64_stub_insns or stub_insns. Must be 8-byte aligned
+        * with PCREL kernels that use prefix instructions in the stub.
+        */
        u32 jump[7];
        /* Used by ftrace to identify stubs */
        u32 magic;
        /* Data for the above code */
        func_desc_t funcdata;
-};
+} __aligned(8);
 
 /*
  * PPC64 uses 24 bit jumps, but we need to jump into other modules or
@@ -333,11 +334,21 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
 #ifdef CONFIG_MPROFILE_KERNEL
 
 static u32 stub_insns[] = {
+#ifdef CONFIG_PPC_KERNEL_PCREL
+       PPC_RAW_LD(_R12, _R13, offsetof(struct paca_struct, kernelbase)),
+       PPC_RAW_NOP(), /* align the prefix insn */
+       /* paddi r12,r12,addr */
+       PPC_PREFIX_MLS | __PPC_PRFX_R(0),
+       PPC_INST_PADDI | ___PPC_RT(_R12) | ___PPC_RA(_R12),
+       PPC_RAW_MTCTR(_R12),
+       PPC_RAW_BCTR(),
+#else
        PPC_RAW_LD(_R12, _R13, offsetof(struct paca_struct, kernel_toc)),
        PPC_RAW_ADDIS(_R12, _R12, 0),
        PPC_RAW_ADDI(_R12, _R12, 0),
        PPC_RAW_MTCTR(_R12),
        PPC_RAW_BCTR(),
+#endif
 };
 
 /*
@@ -358,18 +369,37 @@ static inline int create_ftrace_stub(struct ppc64_stub_entry *entry,
 {
        long reladdr;
 
-       memcpy(entry->jump, stub_insns, sizeof(stub_insns));
-
-       /* Stub uses address relative to kernel toc (from the paca) */
-       reladdr = addr - kernel_toc_addr();
-       if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
-               pr_err("%s: Address of %ps out of range of kernel_toc.\n",
-                                                       me->name, (void *)addr);
+       if ((unsigned long)entry->jump % 8 != 0) {
+               pr_err("%s: Address of stub entry is not 8-byte aligned\n", me->name);
                return 0;
        }
 
-       entry->jump[1] |= PPC_HA(reladdr);
-       entry->jump[2] |= PPC_LO(reladdr);
+       BUILD_BUG_ON(sizeof(stub_insns) > sizeof(entry->jump));
+       memcpy(entry->jump, stub_insns, sizeof(stub_insns));
+
+       if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) {
+               /* Stub uses address relative to kernel base (from the paca) */
+               reladdr = addr - local_paca->kernelbase;
+               if (reladdr > 0x1FFFFFFFFL || reladdr < -0x200000000L) {
+                       pr_err("%s: Address of %ps out of range of 34-bit relative address.\n",
+                               me->name, (void *)addr);
+                       return 0;
+               }
+
+               entry->jump[2] |= IMM_H18(reladdr);
+               entry->jump[3] |= IMM_L(reladdr);
+       } else {
+               /* Stub uses address relative to kernel toc (from the paca) */
+               reladdr = addr - kernel_toc_addr();
+               if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
+                       pr_err("%s: Address of %ps out of range of kernel_toc.\n",
+                               me->name, (void *)addr);
+                       return 0;
+               }
+
+               entry->jump[1] |= PPC_HA(reladdr);
+               entry->jump[2] |= PPC_LO(reladdr);
+       }
 
        /* Even though we don't use funcdata in the stub, it's needed elsewhere. */
        entry->funcdata = func_desc(addr);
index be8db402e96390e964d72022be9cf83dfd7a1e4f..cda4e00b67c1b8d84bb355d4cbb6cab4065afbd7 100644 (file)
@@ -191,7 +191,9 @@ void __init initialise_paca(struct paca_struct *new_paca, int cpu)
 #endif
        new_paca->lock_token = 0x8000;
        new_paca->paca_index = cpu;
+#ifndef CONFIG_PPC_KERNEL_PCREL
        new_paca->kernel_toc = kernel_toc_addr();
+#endif
        new_paca->kernelbase = (unsigned long) _stext;
        /* Only set MSR:IR/DR when MMU is initialized */
        new_paca->kernel_msr = MSR_KERNEL & ~(MSR_IR | MSR_DR);
index 72864fb7a6ccdca7ada5d904a38d76c1dabbebb7..a47f303734233b62038637b5527eeea5d3097cae 100644 (file)
@@ -727,6 +727,15 @@ int __init ftrace_dyn_arch_init(void)
 {
        int i;
        unsigned int *tramp[] = { ftrace_tramp_text, ftrace_tramp_init };
+#ifdef CONFIG_PPC_KERNEL_PCREL
+       u32 stub_insns[] = {
+               /* pla r12,addr */
+               PPC_PREFIX_MLS | __PPC_PRFX_R(1),
+               PPC_INST_PADDI | ___PPC_RT(_R12),
+               PPC_RAW_MTCTR(_R12),
+               PPC_RAW_BCTR()
+       };
+#else
        u32 stub_insns[] = {
                PPC_RAW_LD(_R12, _R13, PACATOC),
                PPC_RAW_ADDIS(_R12, _R12, 0),
@@ -734,6 +743,8 @@ int __init ftrace_dyn_arch_init(void)
                PPC_RAW_MTCTR(_R12),
                PPC_RAW_BCTR()
        };
+#endif
+
        unsigned long addr;
        long reladdr;
 
@@ -742,19 +753,36 @@ int __init ftrace_dyn_arch_init(void)
        else
                addr = ppc_global_function_entry((void *)ftrace_caller);
 
-       reladdr = addr - kernel_toc_addr();
+       if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) {
+               for (i = 0; i < 2; i++) {
+                       reladdr = addr - (unsigned long)tramp[i];
 
-       if (reladdr >= SZ_2G || reladdr < -(long)SZ_2G) {
-               pr_err("Address of %ps out of range of kernel_toc.\n",
+                       if (reladdr >= (long)SZ_8G || reladdr < -(long)SZ_8G) {
+                               pr_err("Address of %ps out of range of pcrel address.\n",
+                                       (void *)addr);
+                               return -1;
+                       }
+
+                       memcpy(tramp[i], stub_insns, sizeof(stub_insns));
+                       tramp[i][0] |= IMM_H18(reladdr);
+                       tramp[i][1] |= IMM_L(reladdr);
+                       add_ftrace_tramp((unsigned long)tramp[i]);
+               }
+       } else {
+               reladdr = addr - kernel_toc_addr();
+
+               if (reladdr >= (long)SZ_2G || reladdr < -(long)SZ_2G) {
+                       pr_err("Address of %ps out of range of kernel_toc.\n",
                                (void *)addr);
-               return -1;
-       }
+                       return -1;
+               }
 
-       for (i = 0; i < 2; i++) {
-               memcpy(tramp[i], stub_insns, sizeof(stub_insns));
-               tramp[i][1] |= PPC_HA(reladdr);
-               tramp[i][2] |= PPC_LO(reladdr);
-               add_ftrace_tramp((unsigned long)tramp[i]);
+               for (i = 0; i < 2; i++) {
+                       memcpy(tramp[i], stub_insns, sizeof(stub_insns));
+                       tramp[i][1] |= PPC_HA(reladdr);
+                       tramp[i][2] |= PPC_LO(reladdr);
+                       add_ftrace_tramp((unsigned long)tramp[i]);
+               }
        }
 
        return 0;
index ffe5d90abe17998ab8052eb36d05c2babbf9c013..fcc0ad6d9c7b0d42effda2df4d870ee2e59f451b 100644 (file)
@@ -177,9 +177,15 @@ fpone:
 fphalf:
        .quad   0x3fe0000000000000      /* 0.5 */
 
+#ifdef CONFIG_PPC_KERNEL_PCREL
+#define LDCONST(fr, name)              \
+       pla     r11,name@pcrel;         \
+       lfd     fr,0(r11)
+#else
 #define LDCONST(fr, name)              \
        addis   r11,r2,name@toc@ha;     \
        lfd     fr,name@toc@l(r11)
+#endif
 #endif
        .text
 /*
index ee86753e444ea41edd24a32581e538a91d3f442f..13614f0b269cf45d71ceaabfdf40c94dd6528f8f 100644 (file)
@@ -169,12 +169,18 @@ SECTIONS
        }
 
 #else /* CONFIG_PPC32 */
+#ifndef CONFIG_PPC_KERNEL_PCREL
        .toc1 : AT(ADDR(.toc1) - LOAD_OFFSET) {
                *(.toc1)
        }
+#endif
 
        .got : AT(ADDR(.got) - LOAD_OFFSET) ALIGN(256) {
+#ifdef CONFIG_PPC_KERNEL_PCREL
+               *(.got)
+#else
                *(.got .toc)
+#endif
        }
 
        SOFT_MASK_TABLE(8)
index 6812cb19d04aff99e6413927e2f3358baef173c2..5d09a029b55677813e4b898a52961df4e7b65648 100644 (file)
@@ -18,8 +18,18 @@ FTR_SECTION_ELSE
 #endif
 ALT_FTR_SECTION_END_IFCLR(CPU_FTR_VMX_COPY)
        ori     r5,r5,PAGE_SIZE@l
+#ifdef CONFIG_PPC_KERNEL_PCREL
+       /*
+        * Hack for toolchain - prefixed instructions cause label difference to
+        * be non-constant even if 8 byte alignment is known, so they can not
+        * be put in FTR sections.
+        */
+       LOAD_REG_ADDR(r10, ppc64_caches)
+BEGIN_FTR_SECTION
+#else
 BEGIN_FTR_SECTION
        LOAD_REG_ADDR(r10, ppc64_caches)
+#endif
        lwz     r11,DCACHEL1LOGBLOCKSIZE(r10)   /* log2 of cache block size */
        lwz     r12,DCACHEL1BLOCKSIZE(r10)      /* get cache block size */
        li      r9,0
index d767e39d564598598a0089a41fd470089ef04a4a..72b7bb34fadea9e608664b235d996a4eb49ae6c3 100644 (file)
@@ -19,6 +19,8 @@
 #define FUNCTION_DESCR_SIZE    0
 #endif
 
+#define CTX_NIA(ctx) ((unsigned long)ctx->idx * 4)
+
 #define PLANT_INSTR(d, idx, instr)                                           \
        do { if (d) { (d)[idx] = instr; } idx++; } while (0)
 #define EMIT(instr)            PLANT_INSTR(image, ctx->idx, instr)
@@ -26,7 +28,7 @@
 /* Long jump; (unconditional 'branch') */
 #define PPC_JMP(dest)                                                        \
        do {                                                                  \
-               long offset = (long)(dest) - (ctx->idx * 4);                  \
+               long offset = (long)(dest) - CTX_NIA(ctx);                    \
                if ((dest) != 0 && !is_offset_in_branch_range(offset)) {                      \
                        pr_err_ratelimited("Branch offset 0x%lx (@%u) out of range\n", offset, ctx->idx);                       \
                        return -ERANGE;                                       \
@@ -40,7 +42,7 @@
 /* "cond" here covers BO:BI fields. */
 #define PPC_BCC_SHORT(cond, dest)                                            \
        do {                                                                  \
-               long offset = (long)(dest) - (ctx->idx * 4);                  \
+               long offset = (long)(dest) - CTX_NIA(ctx);                    \
                if ((dest) != 0 && !is_offset_in_cond_branch_range(offset)) {                 \
                        pr_err_ratelimited("Conditional branch offset 0x%lx (@%u) out of range\n", offset, ctx->idx);           \
                        return -ERANGE;                                       \
  * state.
  */
 #define PPC_BCC(cond, dest)    do {                                          \
-               if (is_offset_in_cond_branch_range((long)(dest) - (ctx->idx * 4))) {    \
+               if (is_offset_in_cond_branch_range((long)(dest) - CTX_NIA(ctx))) {      \
                        PPC_BCC_SHORT(cond, dest);                            \
                        EMIT(PPC_RAW_NOP());                                  \
                } else {                                                      \
                        /* Flip the 'T or F' bit to invert comparison */      \
-                       PPC_BCC_SHORT(cond ^ COND_CMP_TRUE, (ctx->idx+2)*4);  \
+                       PPC_BCC_SHORT(cond ^ COND_CMP_TRUE, CTX_NIA(ctx) + 2*4);  \
                        PPC_JMP(dest);                                        \
                } } while(0)
 
index 8dd3cabaa83ad3ff04e4cdae25b79761fb7e0298..0f8048f6dad630b1547cfa9662d569a8a903c693 100644 (file)
@@ -126,8 +126,10 @@ void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
 {
        int i;
 
+#ifndef CONFIG_PPC_KERNEL_PCREL
        if (IS_ENABLED(CONFIG_PPC64_ELF_ABI_V2))
                EMIT(PPC_RAW_LD(_R2, _R13, offsetof(struct paca_struct, kernel_toc)));
+#endif
 
        /*
         * Initialize tail_call_cnt if we do tail calls.
@@ -208,16 +210,32 @@ static int bpf_jit_emit_func_call_hlp(u32 *image, struct codegen_context *ctx, u
        if (WARN_ON_ONCE(!core_kernel_text(func_addr)))
                return -EINVAL;
 
-       reladdr = func_addr - kernel_toc_addr();
-       if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
-               pr_err("eBPF: address of %ps out of range of kernel_toc.\n", (void *)func);
-               return -ERANGE;
-       }
+       if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) {
+               reladdr = func_addr - CTX_NIA(ctx);
 
-       EMIT(PPC_RAW_ADDIS(_R12, _R2, PPC_HA(reladdr)));
-       EMIT(PPC_RAW_ADDI(_R12, _R12, PPC_LO(reladdr)));
-       EMIT(PPC_RAW_MTCTR(_R12));
-       EMIT(PPC_RAW_BCTRL());
+               if (reladdr >= (long)SZ_8G || reladdr < -(long)SZ_8G) {
+                       pr_err("eBPF: address of %ps out of range of pcrel address.\n",
+                               (void *)func);
+                       return -ERANGE;
+               }
+               /* pla r12,addr */
+               EMIT(PPC_PREFIX_MLS | __PPC_PRFX_R(1) | IMM_H18(reladdr));
+               EMIT(PPC_INST_PADDI | ___PPC_RT(_R12) | IMM_L(reladdr));
+               EMIT(PPC_RAW_MTCTR(_R12));
+               EMIT(PPC_RAW_BCTR());
+
+       } else {
+               reladdr = func_addr - kernel_toc_addr();
+               if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
+                       pr_err("eBPF: address of %ps out of range of kernel_toc.\n", (void *)func);
+                       return -ERANGE;
+               }
+
+               EMIT(PPC_RAW_ADDIS(_R12, _R2, PPC_HA(reladdr)));
+               EMIT(PPC_RAW_ADDI(_R12, _R12, PPC_LO(reladdr)));
+               EMIT(PPC_RAW_MTCTR(_R12));
+               EMIT(PPC_RAW_BCTRL());
+       }
 
        return 0;
 }
index 1ff0d2818da607697aae18e59ac5ca6131fc0833..45fd975ef5212a2f0e65bef984f546a322aea912 100644 (file)
@@ -181,6 +181,7 @@ config POWER10_CPU
        depends on PPC_BOOK3S_64
        select ARCH_HAS_FAST_MULTIPLIER
        select PPC_HAVE_PREFIXED_SUPPORT
+       select PPC_HAVE_PCREL_SUPPORT
 
 config E5500_CPU
        bool "Freescale e5500"
@@ -471,6 +472,20 @@ config PPC_KERNEL_PREFIXED
          Kernel support for prefixed instructions in applications and guests
          is not affected by this option.
 
+config PPC_KERNEL_PCREL
+       depends on PPC_HAVE_PCREL_SUPPORT
+       depends on PPC_HAVE_PREFIXED_SUPPORT
+       depends on CC_HAS_PCREL
+       default n
+       select PPC_KERNEL_PREFIXED
+       bool "Build Kernel with PC-Relative addressing model"
+       help
+         POWER10 and later CPUs support pc relative addressing. Recent
+         compilers have support for an ELF ABI extension for a pc relative
+         ABI.
+
+         This option builds the kernel with the pc relative ABI model.
+
 config PPC_KUEP
        bool "Kernel Userspace Execution Prevention" if !40x
        default y if !40x
@@ -510,6 +525,9 @@ config PPC_HAVE_PMU_SUPPORT
 config PPC_HAVE_PREFIXED_SUPPORT
        bool
 
+config PPC_HAVE_PCREL_SUPPORT
+       bool
+
 config PMU_SYSFS
        bool "Create PMU SPRs sysfs file"
        default n
index e753a6bd48881026339abd56fe92c3944a0eb124..728d3c257e4a33323bdca9716dd2658548fc693a 100644 (file)
@@ -2634,7 +2634,9 @@ static void dump_one_paca(int cpu)
 
        DUMP(p, lock_token, "%#-*x");
        DUMP(p, paca_index, "%#-*x");
+#ifndef CONFIG_PPC_KERNEL_PCREL
        DUMP(p, kernel_toc, "%#-*llx");
+#endif
        DUMP(p, kernelbase, "%#-*llx");
        DUMP(p, kernel_msr, "%#-*llx");
        DUMP(p, emergency_sp, "%-*px");