}
}
+static int bpf_jit_emit_func_call(u32 *image, struct codegen_context *ctx, u64 func_addr, int reg)
+{
+ long 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_addr);
+ return -ERANGE;
+ }
+
+ EMIT(PPC_RAW_ADDIS(reg, _R2, PPC_HA(reladdr)));
+ EMIT(PPC_RAW_ADDI(reg, reg, PPC_LO(reladdr)));
+ EMIT(PPC_RAW_MTCTR(reg));
+ EMIT(PPC_RAW_BCTRL());
+
+ return 0;
+}
+
int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context *ctx, u64 func)
{
unsigned long func_addr = func ? ppc_function_entry((void *)func) : 0;
- long reladdr;
+ long __maybe_unused reladdr;
+ int ret;
/* bpf to bpf call, func is not known in the initial pass. Emit 5 nops as a placeholder */
if (!func) {
EMIT(PPC_RAW_BCTRL());
#else
if (core_kernel_text(func_addr)) {
- 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());
+ ret = bpf_jit_emit_func_call(image, ctx, func_addr, _R12);
+ if (ret)
+ return ret;
} else {
if (IS_ENABLED(CONFIG_PPC64_ELF_ABI_V1)) {
/* func points to the function descriptor */
if (ret < 0)
return ret;
+ /*
+ * Call to arch_bpf_timed_may_goto() is emitted by the
+ * verifier and called with custom calling convention with
+ * first argument and return value in BPF_REG_AX (_R12).
+ *
+ * The generic helper or bpf function call emission path
+ * may use the same scratch register as BPF_REG_AX to
+ * materialize the target address. This would clobber AX
+ * and break timed may_goto semantics.
+ *
+ * Emit a minimal indirect call sequence here using a temp
+ * register and skip the normal post-call return-value move.
+ */
+
+ if (func_addr == (u64)arch_bpf_timed_may_goto) {
+ ret = 0;
+ if (!IS_ENABLED(CONFIG_PPC_KERNEL_PCREL))
+ ret = bpf_jit_emit_func_call(image, ctx, func_addr,
+ tmp1_reg);
+
+ if (ret || IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) {
+ PPC_LI_ADDR(tmp1_reg, func_addr);
+ EMIT(PPC_RAW_MTCTR(tmp1_reg));
+ EMIT(PPC_RAW_BCTRL());
+ }
+
+ break;
+ }
+
/* Take care of powerpc ABI requirements before kfunc call */
if (insn[i].src_reg == BPF_PSEUDO_KFUNC_CALL) {
if (prepare_for_kfunc_call(fp, image, ctx, &insn[i]))
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2025 IBM Corporation, Saket Kumar Bhaskar <skb99@linux.ibm.com> */
+
+#include <linux/linkage.h>
+#include <asm/ppc_asm.h>
+
+/*
+ * arch_bpf_timed_may_goto() trampoline for powerpc64
+ *
+ * Custom BPF convention (verifier/JIT):
+ * - input: stack offset in BPF_REG_AX (r12)
+ * - output: updated count in BPF_REG_AX (r12)
+ *
+ * Call bpf_check_timed_may_goto(ptr) with normal powerpc64 ABI:
+ * - r3 = ptr, return in r3
+ *
+ * Preserve BPF regs R0-R5 (mapping: r8, r3-r7).
+ */
+
+SYM_FUNC_START(arch_bpf_timed_may_goto)
+ /* Prologue: save LR, allocate frame */
+ mflr r0
+ std r0, 16(r1)
+ stdu r1, -112(r1)
+
+ /* Save BPF registers R0 - R5 (r8, r3-r7) */
+ std r3, 32(r1)
+ std r4, 40(r1)
+ std r5, 48(r1)
+ std r6, 56(r1)
+ std r7, 64(r1)
+ std r8, 72(r1)
+
+ /*
+ * r3 = BPF_REG_FP + BPF_REG_AX
+ * BPF_REG_FP is r31; BPF_REG_AX is r12 (stack offset in bytes).
+ */
+ add r3, r31, r12
+ bl bpf_check_timed_may_goto
+
+ /* Put return value back into AX */
+ mr r12, r3
+
+ /* Restore BPF registers R0 - R5 (r8, r3-r7) */
+ ld r3, 32(r1)
+ ld r4, 40(r1)
+ ld r5, 48(r1)
+ ld r6, 56(r1)
+ ld r7, 64(r1)
+ ld r8, 72(r1)
+
+ /* Epilogue: pop frame, restore LR, return */
+ addi r1, r1, 112
+ ld r0, 16(r1)
+ mtlr r0
+ blr
+SYM_FUNC_END(arch_bpf_timed_may_goto)