Commit d243b62b authored by Naveen N Rao's avatar Naveen N Rao Committed by Michael Ellerman
Browse files

powerpc64/bpf: Add support for bpf trampolines



Add support for bpf_arch_text_poke() and arch_prepare_bpf_trampoline()
for 64-bit powerpc. While the code is generic, BPF trampolines are only
enabled on 64-bit powerpc. 32-bit powerpc will need testing and some
updates.

BPF Trampolines adhere to the existing ftrace ABI utilizing a
two-instruction profiling sequence, as well as the newer ABI utilizing a
three-instruction profiling sequence enabling return with a 'blr'. The
trampoline code itself closely follows x86 implementation.

BPF prog JIT is extended to mimic 64-bit powerpc approach for ftrace
having a single nop at function entry, followed by the function
profiling sequence out-of-line and a separate long branch stub for calls
to trampolines that are out of range. A dummy_tramp is provided to
simplify synchronization similar to arm64.

When attaching a bpf trampoline to a bpf prog, we can patch up to three
things:
- the nop at bpf prog entry to go to the out-of-line stub
- the instruction in the out-of-line stub to either call the bpf trampoline
directly, or to branch to the long_branch stub.
- the trampoline address before the long_branch stub.

We do not need any synchronization here since we always have a valid
branch target regardless of the order in which the above stores are
seen. dummy_tramp ensures that the long_branch stub goes to a valid
destination on other cpus, even when the branch to the long_branch stub
is seen before the updated trampoline address.

However, when detaching a bpf trampoline from a bpf prog, or if changing
the bpf trampoline address, we need synchronization to ensure that other
cpus can no longer branch into the older trampoline so that it can be
safely freed. bpf_tramp_image_put() uses rcu_tasks to ensure all cpus
make forward progress, but we still need to ensure that other cpus
execute isync (or some CSI) so that they don't go back into the
trampoline again. While here, update the stale comment that describes
the redzone usage in ppc64 BPF JIT.

Signed-off-by: default avatarNaveen N Rao <naveen@kernel.org>
Signed-off-by: default avatarHari Bathini <hbathini@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://patch.msgid.link/20241030070850.1361304-18-hbathini@linux.ibm.com
parent 71db948b
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -587,12 +587,26 @@
#define PPC_RAW_MTSPR(spr, d)		(0x7c0003a6 | ___PPC_RS(d) | __PPC_SPR(spr))
#define PPC_RAW_EIEIO()			(0x7c0006ac)

/* bcl 20,31,$+4 */
#define PPC_RAW_BCL4()			(0x429f0005)
#define PPC_RAW_BRANCH(offset)		(0x48000000 | PPC_LI(offset))
#define PPC_RAW_BL(offset)		(0x48000001 | PPC_LI(offset))
#define PPC_RAW_TW(t0, a, b)		(0x7c000008 | ___PPC_RS(t0) | ___PPC_RA(a) | ___PPC_RB(b))
#define PPC_RAW_TRAP()			PPC_RAW_TW(31, 0, 0)
#define PPC_RAW_SETB(t, bfa)		(0x7c000100 | ___PPC_RT(t) | ___PPC_RA((bfa) << 2))

#ifdef CONFIG_PPC32
#define PPC_RAW_STL		PPC_RAW_STW
#define PPC_RAW_STLU		PPC_RAW_STWU
#define PPC_RAW_LL		PPC_RAW_LWZ
#define PPC_RAW_CMPLI		PPC_RAW_CMPWI
#else
#define PPC_RAW_STL		PPC_RAW_STD
#define PPC_RAW_STLU		PPC_RAW_STDU
#define PPC_RAW_LL		PPC_RAW_LD
#define PPC_RAW_CMPLI		PPC_RAW_CMPDI
#endif

/* Deal with instructions that older assemblers aren't aware of */
#define	PPC_BCCTR_FLUSH		stringify_in_c(.long PPC_INST_BCCTR_FLUSH)
#define	PPC_CP_ABORT		stringify_in_c(.long PPC_RAW_CP_ABORT)
+17 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@

#include <asm/types.h>
#include <asm/ppc-opcode.h>
#include <linux/build_bug.h>

#ifdef CONFIG_PPC64_ELF_ABI_V1
#define FUNCTION_DESCR_SIZE	24
@@ -21,6 +22,9 @@

#define CTX_NIA(ctx) ((unsigned long)ctx->idx * 4)

#define SZL			sizeof(unsigned long)
#define BPF_INSN_SAFETY		64

#define PLANT_INSTR(d, idx, instr)					      \
	do { if (d) { (d)[idx] = instr; } idx++; } while (0)
#define EMIT(instr)		PLANT_INSTR(image, ctx->idx, instr)
@@ -81,6 +85,18 @@
				EMIT(PPC_RAW_ORI(d, d, (uintptr_t)(i) &       \
							0xffff));             \
		} } while (0)
#define PPC_LI_ADDR	PPC_LI64

#ifndef CONFIG_PPC_KERNEL_PCREL
#define PPC64_LOAD_PACA()						      \
	EMIT(PPC_RAW_LD(_R2, _R13, offsetof(struct paca_struct, kernel_toc)))
#else
#define PPC64_LOAD_PACA()	do {} while (0)
#endif
#else
#define PPC_LI64(d, i)	BUILD_BUG()
#define PPC_LI_ADDR	PPC_LI32
#define PPC64_LOAD_PACA() BUILD_BUG()
#endif

/*
@@ -165,6 +181,7 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
		       u32 *addrs, int pass, bool extra_pass);
void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx);
void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx);
void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx);
void bpf_jit_realloc_regs(struct codegen_context *ctx);
int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr);

+846 −1

File changed.

Preview size limit exceeded, changes collapsed.

+6 −1
Original line number Diff line number Diff line
@@ -127,13 +127,16 @@ void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
{
	int i;

	/* Instruction for trampoline attach */
	EMIT(PPC_RAW_NOP());

	/* Initialize tail_call_cnt, to be skipped if we do tail calls. */
	if (ctx->seen & SEEN_TAILCALL)
		EMIT(PPC_RAW_LI(_R4, 0));
	else
		EMIT(PPC_RAW_NOP());

#define BPF_TAILCALL_PROLOGUE_SIZE	4
#define BPF_TAILCALL_PROLOGUE_SIZE	8

	if (bpf_has_stack_frame(ctx))
		EMIT(PPC_RAW_STWU(_R1, _R1, -BPF_PPC_STACKFRAME(ctx)));
@@ -198,6 +201,8 @@ void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
	bpf_jit_emit_common_epilogue(image, ctx);

	EMIT(PPC_RAW_BLR());

	bpf_jit_build_fentry_stubs(image, ctx);
}

/* Relative offset needs to be calculated based on final image location */
+8 −3
Original line number Diff line number Diff line
@@ -84,7 +84,7 @@ static inline bool bpf_has_stack_frame(struct codegen_context *ctx)
}

/*
 * When not setting up our own stackframe, the redzone usage is:
 * When not setting up our own stackframe, the redzone (288 bytes) usage is:
 *
 *		[	prev sp		] <-------------
 *		[	  ...       	] 		|
@@ -92,7 +92,7 @@ static inline bool bpf_has_stack_frame(struct codegen_context *ctx)
 *		[   nv gpr save area	] 5*8
 *		[    tail_call_cnt	] 8
 *		[    local_tmp_var	] 16
 *		[   unused red zone	] 208 bytes protected
 *		[   unused red zone	] 224
 */
static int bpf_jit_stack_local(struct codegen_context *ctx)
{
@@ -126,6 +126,9 @@ void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
{
	int i;

	/* Instruction for trampoline attach */
	EMIT(PPC_RAW_NOP());

#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)));
@@ -200,6 +203,8 @@ void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
	EMIT(PPC_RAW_MR(_R3, bpf_to_ppc(BPF_REG_0)));

	EMIT(PPC_RAW_BLR());

	bpf_jit_build_fentry_stubs(image, ctx);
}

int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context *ctx, u64 func)
@@ -303,7 +308,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
	 */
	int b2p_bpf_array = bpf_to_ppc(BPF_REG_2);
	int b2p_index = bpf_to_ppc(BPF_REG_3);
	int bpf_tailcall_prologue_size = 8;
	int bpf_tailcall_prologue_size = 12;

	if (!IS_ENABLED(CONFIG_PPC_KERNEL_PCREL) && IS_ENABLED(CONFIG_PPC64_ELF_ABI_V2))
		bpf_tailcall_prologue_size += 4; /* skip past the toc load */