Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions arch/powerpc/net/bpf_jit.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
#include <asm/ppc-opcode.h>
#include <linux/build_bug.h>

/*
* We need at least 2 passes for proper code generation, and may need
* additional passes if code size changes between passes.
*/
#define CODEGEN_MIN_PASSES 2
#define CODEGEN_MAX_PASSES 3

#ifdef CONFIG_PPC64_ELF_ABI_V1
#define FUNCTION_DESCR_SIZE 24
#else
Expand Down Expand Up @@ -188,6 +195,12 @@ struct codegen_context {

#define bpf_to_ppc(r) (ctx->b2p[r])

#ifdef CONFIG_PPC64
#define PPC_RAW_CMPLLI(a, i) PPC_RAW_CMPLDI(a, i)
#else
#define PPC_RAW_CMPLLI(a, i) PPC_RAW_CMPLWI(a, i)
#endif

#ifdef CONFIG_PPC32
#define BPF_FIXUP_LEN 3 /* Three instructions => 12 bytes */
#else
Expand All @@ -214,10 +227,11 @@ int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context *
int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct codegen_context *ctx,
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_build_epilogue(u32 *image, u32 *fimage, struct codegen_context *ctx);
void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, 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);
int bpf_jit_emit_exit_insn(u32 *image, u32 *fimage, struct codegen_context *ctx, int tmp_reg,
long exit_addr);
void prepare_for_fsession_fentry(u32 *image, struct codegen_context *ctx, int cookie_cnt,
int cookie_off, int retval_off);
void store_func_meta(u32 *image, struct codegen_context *ctx, u64 func_meta, int func_meta_off);
Expand Down
102 changes: 76 additions & 26 deletions arch/powerpc/net/bpf_jit_comp.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,37 @@ asm (
" .popsection ;"
);

void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx)
void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context *ctx)
{
int ool_stub_idx, long_branch_stub_idx;
int stub_sz;

/*
* The dummy_tramp_addr field is placed at bottom of Long branch stub.
* In the final pass, align the mis-aligned dummy_tramp_addr field
* in the fimage. The alignment NOP must appear before OOL stub,
* to make ool_stub_idx & long_branch_stub_idx constant from end.
*
* dummy_tramp_addr must be 8-byte aligned for load-register
* compatibility. The fimage can be non 8-byte aligned, so final
* alignment depends on start of fimage and the stub's instruction
* count. The stubs block has 11 instructions (with
* CONFIG_PPC_FTRACE_OUT_OF_LINE) or 10 instructions (without)
* before dummy_tramp_addr field. Emit a NOP if the address of
* dummy_tramp_addr is non aligned.
*
* In pass=0 when image==NULL, conservatively account for space
* required to accommodate alignment NOP. In case final pass skips
* emitting alignment NOP, the image buffer have 4 spare bytes and
* jited_len signifies correct program size.
*/

stub_sz = IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) ? 44 : 40;
if (!image || !IS_ALIGNED((unsigned long)fimage + ctx->idx*4 + stub_sz, SZL))
EMIT(PPC_RAW_NOP());

/*
* nop // optional, for alignment of dummy_tramp_addr
* Out-of-line stub:
* mflr r0
* [b|bl] tramp
Expand All @@ -70,44 +96,46 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx)

/*
* Long branch stub:
* .long <dummy_tramp_addr>
* mflr r11
* bcl 20,31,$+4
* mflr r12
* ld r12, -8-SZL(r12)
* mflr r12 // lr/r12 stores pc of current(this) inst.
* ld r12, 20(r12) // offset(dummy_tramp_addr) from prev inst. is 20
* mtctr r12
* mtlr r11 // needed to retain ftrace ABI
* mtlr r11 // needed to retain ftrace ABI
* bctr
* .long <dummy_tramp_addr> // SZL bytes aligned
*/
if (image)
*((unsigned long *)&image[ctx->idx]) = (unsigned long)dummy_tramp;
ctx->idx += SZL / 4;
long_branch_stub_idx = ctx->idx;
EMIT(PPC_RAW_MFLR(_R11));
EMIT(PPC_RAW_BCL4());
EMIT(PPC_RAW_MFLR(_R12));
EMIT(PPC_RAW_LL(_R12, _R12, -8-SZL));
EMIT(PPC_RAW_LL(_R12, _R12, 20));
EMIT(PPC_RAW_MTCTR(_R12));
EMIT(PPC_RAW_MTLR(_R11));
EMIT(PPC_RAW_BCTR());

if (image)
*((unsigned long *)&image[ctx->idx]) = (unsigned long)dummy_tramp;

ctx->idx += SZL / 4;

if (!bpf_jit_ool_stub) {
bpf_jit_ool_stub = (ctx->idx - ool_stub_idx) * 4;
bpf_jit_long_branch_stub = (ctx->idx - long_branch_stub_idx) * 4;
}
}

int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr)
int bpf_jit_emit_exit_insn(u32 *image, u32 *fimage, struct codegen_context *ctx,
int tmp_reg, long exit_addr)
{
if (!exit_addr || is_offset_in_branch_range(exit_addr - (ctx->idx * 4))) {
if (exit_addr && is_offset_in_branch_range(exit_addr - (long)(ctx->idx * 4))) {
PPC_JMP(exit_addr);
} else if (ctx->alt_exit_addr) {
if (WARN_ON(!is_offset_in_branch_range((long)ctx->alt_exit_addr - (ctx->idx * 4))))
return -1;
} else if (ctx->alt_exit_addr && is_offset_in_branch_range(
(long)(ctx->alt_exit_addr) - (long)(ctx->idx * 4))) {
PPC_JMP(ctx->alt_exit_addr);
} else {
ctx->alt_exit_addr = ctx->idx * 4;
bpf_jit_build_epilogue(image, ctx);
bpf_jit_build_epilogue(image, fimage, ctx);
}

return 0;
Expand Down Expand Up @@ -274,6 +302,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
*/
if (cgctx.seen & SEEN_TAILCALL || !is_offset_in_branch_range((long)cgctx.idx * 4)) {
cgctx.idx = 0;
cgctx.alt_exit_addr = 0;
if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false))
goto out_err;
}
Expand All @@ -286,7 +315,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
*/
bpf_jit_build_prologue(NULL, &cgctx);
addrs[fp->len] = cgctx.idx * 4;
bpf_jit_build_epilogue(NULL, &cgctx);
bpf_jit_build_epilogue(NULL, NULL, &cgctx);

fixup_len = fp->aux->num_exentries * BPF_FIXUP_LEN * 4;
extable_len = fp->aux->num_exentries * sizeof(struct exception_table_entry);
Expand All @@ -306,10 +335,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
code_base = (u32 *)(image + FUNCTION_DESCR_SIZE);
fcode_base = (u32 *)(fimage + FUNCTION_DESCR_SIZE);

/* Code generation passes 1-2 */
for (pass = 1; pass < 3; pass++) {
/* Code generation passes 1-2+, loop until program size converges. */
for (pass = 1; pass <= CODEGEN_MAX_PASSES; pass++) {
u32 prev_proglen = proglen;

/* Now build the prologue, body code & epilogue for real. */
cgctx.idx = 0;
cgctx.exentry_idx = 0;
cgctx.alt_exit_addr = 0;
bpf_jit_build_prologue(code_base, &cgctx);
if (bpf_jit_build_body(fp, code_base, fcode_base, &cgctx, addrs, pass,
Expand All @@ -318,11 +350,26 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
bpf_jit_binary_pack_free(fhdr, hdr);
goto out_err;
}
bpf_jit_build_epilogue(code_base, &cgctx);
addrs[fp->len] = cgctx.idx * 4;
bpf_jit_build_epilogue(code_base, fcode_base, &cgctx);

proglen = cgctx.idx * 4;

if (bpf_jit_enable > 1)
pr_info("Pass %d: shrink = %d, seen = 0x%x\n", pass,
proglen - (cgctx.idx * 4), cgctx.seen);
prev_proglen - proglen, cgctx.seen);

/* Check if program size has converged, but ensure minimum passes */
if (pass >= CODEGEN_MIN_PASSES && proglen == prev_proglen)
break;

if (pass == CODEGEN_MAX_PASSES && proglen != prev_proglen) {
pr_err("BPF JIT: Program did not converge after %d passes\n",
CODEGEN_MAX_PASSES);
bpf_arch_text_copy(&fhdr->size, &hdr->size, sizeof(hdr->size));
bpf_jit_binary_pack_free(fhdr, hdr);
goto out_err;
}
}

if (bpf_jit_enable > 1)
Expand Down Expand Up @@ -357,7 +404,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
(void *)fimage + FUNCTION_DESCR_SIZE);

out_addrs:
if (!image && priv_stack_ptr) {
if (!fp->jited && priv_stack_ptr) {
fp->aux->priv_stack_ptr = NULL;
free_percpu(priv_stack_ptr);
}
Expand Down Expand Up @@ -399,7 +446,7 @@ int bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, u32 *fimage, int pass
u32 *fixup;

/* Populate extable entries only in the last pass */
if (pass != 2)
if (pass < CODEGEN_MIN_PASSES)
return 0;

if (!fp->aux->extable ||
Expand Down Expand Up @@ -739,7 +786,7 @@ static void bpf_trampoline_setup_tail_call_info(u32 *image, struct codegen_conte
* Setting the tail_call_info in trampoline's frame
* depending on if previous frame had value or reference.
*/
EMIT(PPC_RAW_CMPLWI(_R3, MAX_TAIL_CALL_CNT));
EMIT(PPC_RAW_CMPLLI(_R3, MAX_TAIL_CALL_CNT));
PPC_BCC_CONST_SHORT(COND_GT, 8);
EMIT(PPC_RAW_ADDI(_R3, _R4, -BPF_PPC_TAILCALL));

Expand Down Expand Up @@ -1265,21 +1312,22 @@ static void do_isync(void *info __maybe_unused)
* bpf_func:
* [nop|b] ool_stub
* 2. Out-of-line stub:
* nop // optional nop for alignment
* ool_stub:
* mflr r0
* [b|bl] <bpf_prog>/<long_branch_stub>
* mtlr r0 // CONFIG_PPC_FTRACE_OUT_OF_LINE only
* b bpf_func + 4
* 3. Long branch stub:
* long_branch_stub:
* .long <branch_addr>/<dummy_tramp>
* mflr r11
* bcl 20,31,$+4
* mflr r12
* ld r12, -16(r12)
* ld r12, 20(r12)
* mtctr r12
* mtlr r11 // needed to retain ftrace ABI
* bctr
* .long <branch_addr>/<dummy_tramp>
*
* dummy_tramp is used to reduce synchronization requirements.
*
Expand Down Expand Up @@ -1381,10 +1429,12 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
* 1. Update the address in the long branch stub:
* If new_addr is out of range, we will have to use the long branch stub, so patch new_addr
* here. Otherwise, revert to dummy_tramp, but only if we had patched old_addr here.
*
* dummy_tramp_addr moved to bottom of long branch stub.
*/
if ((new_addr && !is_offset_in_branch_range(new_addr - ip)) ||
(old_addr && !is_offset_in_branch_range(old_addr - ip)))
ret = patch_ulong((void *)(bpf_func_end - bpf_jit_long_branch_stub - SZL),
ret = patch_ulong((void *)(bpf_func_end - SZL), /* SZL: dummy_tramp_addr offset */
(new_addr && !is_offset_in_branch_range(new_addr - ip)) ?
(unsigned long)new_addr : (unsigned long)dummy_tramp);
if (ret)
Expand Down
6 changes: 4 additions & 2 deletions arch/powerpc/net/bpf_jit_comp32.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,15 @@ static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx

}

void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
void bpf_jit_build_epilogue(u32 *image, u32 *fimage, struct codegen_context *ctx)
{
EMIT(PPC_RAW_MR(_R3, bpf_to_ppc(BPF_REG_0)));

bpf_jit_emit_common_epilogue(image, ctx);

EMIT(PPC_RAW_BLR());

bpf_jit_build_fentry_stubs(image, ctx);
bpf_jit_build_fentry_stubs(image, fimage, ctx);
}

/* Relative offset needs to be calculated based on final image location */
Expand Down Expand Up @@ -1150,6 +1150,8 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
*/
if (i != flen - 1) {
ret = bpf_jit_emit_exit_insn(image, ctx, _R0, exit_addr);
ret = bpf_jit_emit_exit_insn(image, fimage,
ctx, _R0, exit_addr);
if (ret)
return ret;
}
Expand Down
15 changes: 8 additions & 7 deletions arch/powerpc/net/bpf_jit_comp64.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
*/
EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_2), _R1, 0));
EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_1), bpf_to_ppc(TMP_REG_2), -(BPF_PPC_TAILCALL)));
EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
PPC_BCC_CONST_SHORT(COND_GT, 8);
EMIT(PPC_RAW_ADDI(bpf_to_ppc(TMP_REG_1), bpf_to_ppc(TMP_REG_2),
-(BPF_PPC_TAILCALL)));
Expand Down Expand Up @@ -398,7 +398,7 @@ static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx
}
}

void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
void bpf_jit_build_epilogue(u32 *image, u32 *fimage, struct codegen_context *ctx)
{
bpf_jit_emit_common_epilogue(image, ctx);

Expand All @@ -407,7 +407,7 @@ void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)

EMIT(PPC_RAW_BLR());

bpf_jit_build_fentry_stubs(image, ctx);
bpf_jit_build_fentry_stubs(image, fimage, ctx);
}

/*
Expand Down Expand Up @@ -662,7 +662,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
PPC_BCC_SHORT(COND_GE, out);

EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_1), _R1, bpf_jit_stack_tailcallinfo_offset(ctx)));
EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
PPC_BCC_CONST_SHORT(COND_LE, 8);

/* dereference TMP_REG_1 */
Expand All @@ -672,7 +672,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
* if (tail_call_info == MAX_TAIL_CALL_CNT)
* goto out;
*/
EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
PPC_BCC_SHORT(COND_EQ, out);

/*
Expand Down Expand Up @@ -707,7 +707,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
* tail_call_info.
*/
EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_2), _R1, bpf_jit_stack_tailcallinfo_offset(ctx)));
EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_2), MAX_TAIL_CALL_CNT));
EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_2), MAX_TAIL_CALL_CNT));
PPC_BCC_CONST_SHORT(COND_GT, 8);

/* First get address of tail_call_info */
Expand Down Expand Up @@ -1737,7 +1737,8 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
* we'll just fall through to the epilogue.
*/
if (i != flen - 1) {
ret = bpf_jit_emit_exit_insn(image, ctx, tmp1_reg, exit_addr);
ret = bpf_jit_emit_exit_insn(image, fimage, ctx,
tmp1_reg, exit_addr);
if (ret)
return ret;
}
Expand Down
Loading
Loading