Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/bpf: enable kfunc call

Currently, bpf jit code on powerpc assumes all the bpf functions and
helpers to be part of core kernel text. This is false for kfunc case,
as function addresses may not be part of core kernel text area. So,
add support for addresses that are not within core kernel text area
too, to enable kfunc support. Emit instructions based on whether the
function address is within core kernel text address or not, to retain
optimized instruction sequence where possible.

In case of PCREL, as a bpf function that is not within core kernel
text area is likely to go out of range with relative addressing on
kernel base, use PC relative addressing. If that goes out of range,
load the full address with PPC_LI64().

With addresses that are not within core kernel text area supported,
override bpf_jit_supports_kfunc_call() to enable kfunc support. Also,
override bpf_jit_supports_far_kfunc_call() to enable 64-bit pointers,
as an address offset can be more than 32-bit long on PPC64.

Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20240502173205.142794-2-hbathini@linux.ibm.com

authored by

Hari Bathini and committed by
Michael Ellerman
61688a82 2ecfe59c

+61 -10
+10
arch/powerpc/net/bpf_jit_comp.c
··· 359 359 360 360 bpf_prog_unlock_free(fp); 361 361 } 362 + 363 + bool bpf_jit_supports_kfunc_call(void) 364 + { 365 + return true; 366 + } 367 + 368 + bool bpf_jit_supports_far_kfunc_call(void) 369 + { 370 + return IS_ENABLED(CONFIG_PPC64); 371 + }
+51 -10
arch/powerpc/net/bpf_jit_comp64.c
··· 208 208 unsigned long func_addr = func ? ppc_function_entry((void *)func) : 0; 209 209 long reladdr; 210 210 211 - if (WARN_ON_ONCE(!core_kernel_text(func_addr))) 211 + if (WARN_ON_ONCE(!kernel_text_address(func_addr))) 212 212 return -EINVAL; 213 213 214 - if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) { 215 - reladdr = func_addr - local_paca->kernelbase; 214 + #ifdef CONFIG_PPC_KERNEL_PCREL 215 + reladdr = func_addr - local_paca->kernelbase; 216 216 217 - if (reladdr >= (long)SZ_8G || reladdr < -(long)SZ_8G) { 218 - pr_err("eBPF: address of %ps out of range of 34-bit relative address.\n", 219 - (void *)func); 220 - return -ERANGE; 221 - } 217 + if (reladdr < (long)SZ_8G && reladdr >= -(long)SZ_8G) { 222 218 EMIT(PPC_RAW_LD(_R12, _R13, offsetof(struct paca_struct, kernelbase))); 223 219 /* Align for subsequent prefix instruction */ 224 220 if (!IS_ALIGNED((unsigned long)fimage + CTX_NIA(ctx), 8)) ··· 223 227 EMIT(PPC_PREFIX_MLS | __PPC_PRFX_R(0) | IMM_H18(reladdr)); 224 228 EMIT(PPC_INST_PADDI | ___PPC_RT(_R12) | ___PPC_RA(_R12) | IMM_L(reladdr)); 225 229 } else { 230 + unsigned long pc = (unsigned long)fimage + CTX_NIA(ctx); 231 + bool alignment_needed = !IS_ALIGNED(pc, 8); 232 + 233 + reladdr = func_addr - (alignment_needed ? pc + 4 : pc); 234 + 235 + if (reladdr < (long)SZ_8G && reladdr >= -(long)SZ_8G) { 236 + if (alignment_needed) 237 + EMIT(PPC_RAW_NOP()); 238 + /* pla r12,addr */ 239 + EMIT(PPC_PREFIX_MLS | __PPC_PRFX_R(1) | IMM_H18(reladdr)); 240 + EMIT(PPC_INST_PADDI | ___PPC_RT(_R12) | IMM_L(reladdr)); 241 + } else { 242 + /* We can clobber r12 */ 243 + PPC_LI64(_R12, func); 244 + } 245 + } 246 + EMIT(PPC_RAW_MTCTR(_R12)); 247 + EMIT(PPC_RAW_BCTRL()); 248 + #else 249 + if (core_kernel_text(func_addr)) { 226 250 reladdr = func_addr - kernel_toc_addr(); 227 251 if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { 228 252 pr_err("eBPF: address of %ps out of range of kernel_toc.\n", (void *)func); ··· 251 235 252 236 EMIT(PPC_RAW_ADDIS(_R12, _R2, PPC_HA(reladdr))); 253 237 EMIT(PPC_RAW_ADDI(_R12, _R12, PPC_LO(reladdr))); 238 + EMIT(PPC_RAW_MTCTR(_R12)); 239 + EMIT(PPC_RAW_BCTRL()); 240 + } else { 241 + if (IS_ENABLED(CONFIG_PPC64_ELF_ABI_V1)) { 242 + /* func points to the function descriptor */ 243 + PPC_LI64(bpf_to_ppc(TMP_REG_2), func); 244 + /* Load actual entry point from function descriptor */ 245 + EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_1), bpf_to_ppc(TMP_REG_2), 0)); 246 + /* ... and move it to CTR */ 247 + EMIT(PPC_RAW_MTCTR(bpf_to_ppc(TMP_REG_1))); 248 + /* 249 + * Load TOC from function descriptor at offset 8. 250 + * We can clobber r2 since we get called through a 251 + * function pointer (so caller will save/restore r2). 252 + */ 253 + EMIT(PPC_RAW_LD(_R2, bpf_to_ppc(TMP_REG_2), 8)); 254 + } else { 255 + PPC_LI64(_R12, func); 256 + EMIT(PPC_RAW_MTCTR(_R12)); 257 + } 258 + EMIT(PPC_RAW_BCTRL()); 259 + /* 260 + * Load r2 with kernel TOC as kernel TOC is used if function address falls 261 + * within core kernel text. 262 + */ 263 + EMIT(PPC_RAW_LD(_R2, _R13, offsetof(struct paca_struct, kernel_toc))); 254 264 } 255 - EMIT(PPC_RAW_MTCTR(_R12)); 256 - EMIT(PPC_RAW_BCTRL()); 265 + #endif 257 266 258 267 return 0; 259 268 }