/* Make sure that the stack is quadword aligned. */
cgctx.stack_size = round_up(fp->aux->stack_depth, 16);
/* Scouting faux-generate pass 0 */ if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) { /* We hit something illegal or unsupported. */
fp = org_fp; goto out_addrs;
}
/* * If we have seen a tail call, we need a second pass. * This is because bpf_jit_emit_common_epilogue() is called * from bpf_jit_emit_tail_call() with a not yet stable ctx->seen. * We also need a second pass if we ended up with too large * a program so as to ensure BPF_EXIT branches are in range.
*/ if (cgctx.seen & SEEN_TAILCALL || !is_offset_in_branch_range((long)cgctx.idx * 4)) {
cgctx.idx = 0; if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) {
fp = org_fp; goto out_addrs;
}
}
bpf_jit_realloc_regs(&cgctx); /* * Pretend to build prologue, given the features we've seen. This will * update ctgtx.idx as it pretends to output instructions, then we can * calculate total size from idx.
*/
bpf_jit_build_prologue(NULL, &cgctx);
addrs[fp->len] = cgctx.idx * 4;
bpf_jit_build_epilogue(NULL, &cgctx);
/* Code generation passes 1-2 */ for (pass = 1; pass < 3; pass++) { /* Now build the prologue, body code & epilogue for real. */
cgctx.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,
extra_pass)) {
bpf_arch_text_copy(&fhdr->size, &hdr->size, sizeof(hdr->size));
bpf_jit_binary_pack_free(fhdr, hdr);
fp = org_fp; goto out_addrs;
}
bpf_jit_build_epilogue(code_base, &cgctx);
if (bpf_jit_enable > 1)
pr_info("Pass %d: shrink = %d, seen = 0x%x\n", pass,
proglen - (cgctx.idx * 4), cgctx.seen);
}
if (bpf_jit_enable > 1) /* * Note that we output the base address of the code_base * rather than image, since opcodes are in code_base.
*/
bpf_jit_dump(flen, proglen, pass, code_base);
/* * The caller should check for (BPF_MODE(code) == BPF_PROBE_MEM) before calling * this function, as this only applies to BPF_PROBE_MEM, for now.
*/ int bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, u32 *fimage, int pass, struct codegen_context *ctx, int insn_idx, int jmp_off, int dst_reg)
{
off_t offset; unsignedlong pc; struct exception_table_entry *ex, *ex_entry;
u32 *fixup;
/* Populate extable entries only in the last pass */ if (pass != 2) return 0;
if (!fp->aux->extable ||
WARN_ON_ONCE(ctx->exentry_idx >= fp->aux->num_exentries)) return -EINVAL;
/* * Program is first written to image before copying to the * final location (fimage). Accordingly, update in the image first. * As all offsets used are relative, copying as is to the * final location should be alright.
*/
pc = (unsignedlong)&image[insn_idx];
ex = (void *)fp->aux->extable - (void *)fimage + (void *)image;
/* * If we fail the final pass of JIT (from jit_subprogs), * the program may not be finalized yet. Call finalize here * before freeing it.
*/ if (jit_data) {
bpf_jit_binary_pack_finalize(jit_data->fhdr, jit_data->hdr);
kvfree(jit_data->addrs);
kfree(jit_data);
}
hdr = bpf_jit_binary_pack_hdr(fp);
bpf_jit_binary_pack_free(hdr, NULL);
WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(fp));
}
/* Remember prog start time returned by __bpf_prog_enter */
EMIT(PPC_RAW_MR(_R26, _R3));
/* * if (__bpf_prog_enter(p) == 0) * goto skip_exec_of_prog; * * Emit a nop to be later patched with conditional branch, once offset is known
*/
EMIT(PPC_RAW_CMPLI(_R3, 0));
jmp_idx = ctx->idx;
EMIT(PPC_RAW_NOP());
/* p->bpf_func(ctx) */
EMIT(PPC_RAW_ADDI(_R3, _R1, regs_off)); if (!p->jited)
PPC_LI_ADDR(_R4, (unsignedlong)p->insnsi); /* Account for max possible instructions during dummy pass for size calculation */ if (image && !create_branch(&branch_insn, (u32 *)&ro_image[ctx->idx],
(unsignedlong)p->bpf_func,
BRANCH_SET_LINK)) {
image[ctx->idx] = ppc_inst_val(branch_insn);
ctx->idx++;
} else {
EMIT(PPC_RAW_LL(_R12, _R25, offsetof(struct bpf_prog, bpf_func)));
EMIT(PPC_RAW_MTCTR(_R12));
EMIT(PPC_RAW_BCTRL());
}
if (save_ret)
EMIT(PPC_RAW_STL(_R3, _R1, retval_off));
/* Fix up branch */ if (image) { if (create_cond_branch(&branch_insn, &image[jmp_idx],
(unsignedlong)&image[ctx->idx], COND_EQ << 16)) return -EINVAL;
image[jmp_idx] = ppc_inst_val(branch_insn);
}
staticint invoke_bpf_mod_ret(u32 *image, u32 *ro_image, struct codegen_context *ctx, struct bpf_tramp_links *tl, int regs_off, int retval_off, int run_ctx_off, u32 *branches)
{ int i;
/* * The first fmod_ret program will receive a garbage return value. * Set this to 0 to avoid confusing the program.
*/
EMIT(PPC_RAW_LI(_R3, 0));
EMIT(PPC_RAW_STL(_R3, _R1, retval_off)); for (i = 0; i < tl->nr_links; i++) { if (invoke_bpf_prog(image, ro_image, ctx, tl->links[i], regs_off, retval_off,
run_ctx_off, true)) return -EINVAL;
/* * mod_ret prog stored return value after prog ctx. Emit: * if (*(u64 *)(ret_val) != 0) * goto do_fexit;
*/
EMIT(PPC_RAW_LL(_R3, _R1, retval_off));
EMIT(PPC_RAW_CMPLI(_R3, 0));
/* * Save the location of the branch and generate a nop, which is * replaced with a conditional jump once do_fexit (i.e. the * start of the fexit invocation) is finalized.
*/
branches[i] = ctx->idx;
EMIT(PPC_RAW_NOP());
}
return 0;
}
staticvoid bpf_trampoline_setup_tail_call_cnt(u32 *image, struct codegen_context *ctx, int func_frame_offset, int r4_off)
{ if (IS_ENABLED(CONFIG_PPC64)) { /* See bpf_jit_stack_tailcallcnt() */ int tailcallcnt_offset = 6 * 8;
staticvoid bpf_trampoline_restore_tail_call_cnt(u32 *image, struct codegen_context *ctx, int func_frame_offset, int r4_off)
{ if (IS_ENABLED(CONFIG_PPC64)) { /* See bpf_jit_stack_tailcallcnt() */ int tailcallcnt_offset = 6 * 8;
staticvoid bpf_trampoline_save_args(u32 *image, struct codegen_context *ctx, int func_frame_offset, int nr_regs, int regs_off)
{ int param_save_area_offset;
param_save_area_offset = func_frame_offset; /* the two frames we alloted */
param_save_area_offset += STACK_FRAME_MIN_SIZE; /* param save area is past frame header */
for (int i = 0; i < nr_regs; i++) { if (i < 8) {
EMIT(PPC_RAW_STL(_R3 + i, _R1, regs_off + i * SZL));
} else {
EMIT(PPC_RAW_LL(_R3, _R1, param_save_area_offset + i * SZL));
EMIT(PPC_RAW_STL(_R3, _R1, regs_off + i * SZL));
}
}
}
/* Used when restoring just the register parameters when returning back */ staticvoid bpf_trampoline_restore_args_regs(u32 *image, struct codegen_context *ctx, int nr_regs, int regs_off)
{ for (int i = 0; i < nr_regs && i < 8; i++)
EMIT(PPC_RAW_LL(_R3 + i, _R1, regs_off + i * SZL));
}
/* Used when we call into the traced function. Replicate parameter save area */ staticvoid bpf_trampoline_restore_args_stack(u32 *image, struct codegen_context *ctx, int func_frame_offset, int nr_regs, int regs_off)
{ int param_save_area_offset;
param_save_area_offset = func_frame_offset; /* the two frames we alloted */
param_save_area_offset += STACK_FRAME_MIN_SIZE; /* param save area is past frame header */
for (int i = 8; i < nr_regs; i++) {
EMIT(PPC_RAW_LL(_R3, _R1, param_save_area_offset + i * SZL));
EMIT(PPC_RAW_STL(_R3, _R1, STACK_FRAME_MIN_SIZE + i * SZL));
}
bpf_trampoline_restore_args_regs(image, ctx, nr_regs, regs_off);
}
nr_regs = m->nr_args; /* Extra registers for struct arguments */ for (i = 0; i < m->nr_args; i++) if (m->arg_size[i] > SZL)
nr_regs += round_up(m->arg_size[i], SZL) / SZL - 1;
if (nr_regs > MAX_BPF_FUNC_ARGS) return -EOPNOTSUPP;
ctx = &codegen_ctx;
memset(ctx, 0, sizeof(*ctx));
/* * Generated stack layout: * * func prev back chain [ back chain ] * [ ] * bpf prog redzone/tailcallcnt [ ... ] 64 bytes (64-bit powerpc) * [ ] -- * LR save area [ r0 save (64-bit) ] | header * [ r0 save (32-bit) ] | * dummy frame for unwind [ back chain 1 ] -- * [ padding ] align stack frame * r4_off [ r4 (tailcallcnt) ] optional - 32-bit powerpc * alt_lr_off [ real lr (ool stub)] optional - actual lr * [ r26 ] * nvr_off [ r25 ] nvr save area * retval_off [ return value ] * [ reg argN ] * [ ... ] * regs_off [ reg_arg1 ] prog ctx context * nregs_off [ args count ] * ip_off [ traced function ] * [ ... ] * run_ctx_off [ bpf_tramp_run_ctx ] * [ reg argN ] * [ ... ] * param_save_area [ reg_arg1 ] min 8 doublewords, per ABI * [ TOC save (64-bit) ] -- * [ LR save (64-bit) ] | header * [ LR save (32-bit) ] | * bpf trampoline frame [ back chain 2 ] -- *
*/
/* * Room for parameter save area. * * As per the ABI, this is required if we call into the traced * function (BPF_TRAMP_F_CALL_ORIG): * - if the function takes more than 8 arguments for the rest to spill onto the stack * - or, if the function has variadic arguments * - or, if this functions's prototype was not available to the caller * * Reserve space for at least 8 registers for now. This can be optimized later.
*/
bpf_frame_size += (nr_regs > 8 ? nr_regs : 8) * SZL;
/* Room for return value of func_addr or fentry prog */
retval_off = bpf_frame_size;
save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET); if (save_ret)
bpf_frame_size += SZL;
/* Room for nvr save area */
nvr_off = bpf_frame_size;
bpf_frame_size += 2 * SZL;
/* Optional save area for actual LR in case of ool ftrace */ if (IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) {
alt_lr_off = bpf_frame_size;
bpf_frame_size += SZL;
}
/* Padding to align stack frame, if any */
bpf_frame_size = round_up(bpf_frame_size, SZL * 2);
/* Dummy frame size for proper unwind - includes 64-bytes red zone for 64-bit powerpc */
bpf_dummy_frame_size = STACK_FRAME_MIN_SIZE + 64;
/* Offset to the traced function's stack frame */
func_frame_offset = bpf_dummy_frame_size + bpf_frame_size;
/* Create dummy frame for unwind, store original return value */
EMIT(PPC_RAW_STL(_R0, _R1, PPC_LR_STKOFF)); /* Protect red zone where tail call count goes */
EMIT(PPC_RAW_STLU(_R1, _R1, -bpf_dummy_frame_size));
/* Save our return address */
EMIT(PPC_RAW_MFLR(_R3)); if (IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE))
EMIT(PPC_RAW_STL(_R3, _R1, alt_lr_off)); else
EMIT(PPC_RAW_STL(_R3, _R1, bpf_frame_size + PPC_LR_STKOFF));
/* * Save ip address of the traced function. * We could recover this from LR, but we will need to address for OOL trampoline, * and optional GEP area.
*/ if (IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) || flags & BPF_TRAMP_F_IP_ARG) {
EMIT(PPC_RAW_LWZ(_R4, _R3, 4));
EMIT(PPC_RAW_SLWI(_R4, _R4, 6));
EMIT(PPC_RAW_SRAWI(_R4, _R4, 6));
EMIT(PPC_RAW_ADD(_R3, _R3, _R4));
EMIT(PPC_RAW_ADDI(_R3, _R3, 4));
}
if (flags & BPF_TRAMP_F_IP_ARG)
EMIT(PPC_RAW_STL(_R3, _R1, ip_off));
if (IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) /* Fake our LR for unwind */
EMIT(PPC_RAW_STL(_R3, _R1, bpf_frame_size + PPC_LR_STKOFF));
/* Save function arg count -- see bpf_get_func_arg_cnt() */
EMIT(PPC_RAW_LI(_R3, nr_regs));
EMIT(PPC_RAW_STL(_R3, _R1, nregs_off));
if (flags & BPF_TRAMP_F_CALL_ORIG) {
PPC_LI_ADDR(_R3, (unsignedlong)im);
ret = bpf_jit_emit_func_call_rel(image, ro_image, ctx,
(unsignedlong)__bpf_tramp_enter); if (ret) return ret;
}
for (i = 0; i < fentry->nr_links; i++) if (invoke_bpf_prog(image, ro_image, ctx, fentry->links[i], regs_off, retval_off,
run_ctx_off, flags & BPF_TRAMP_F_RET_FENTRY_RET)) return -EINVAL;
if (fmod_ret->nr_links) {
branches = kcalloc(fmod_ret->nr_links, sizeof(u32), GFP_KERNEL); if (!branches) return -ENOMEM;
if (invoke_bpf_mod_ret(image, ro_image, ctx, fmod_ret, regs_off, retval_off,
run_ctx_off, branches)) {
ret = -EINVAL; goto cleanup;
}
}
/* Call the traced function */ if (flags & BPF_TRAMP_F_CALL_ORIG) { /* * The address in LR save area points to the correct point in the original function * with both PPC_FTRACE_OUT_OF_LINE as well as with traditional ftrace instruction * sequence
*/
EMIT(PPC_RAW_LL(_R3, _R1, bpf_frame_size + PPC_LR_STKOFF));
EMIT(PPC_RAW_MTCTR(_R3));
/* Replicate tail_call_cnt before calling the original BPF prog */ if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
bpf_trampoline_setup_tail_call_cnt(image, ctx, func_frame_offset, r4_off);
/* Reserve space to patch branch instruction to skip fexit progs */ if (ro_image) /* image is NULL for dummy pass */
im->ip_after_call = &((u32 *)ro_image)[ctx->idx];
EMIT(PPC_RAW_NOP());
}
/* Update branches saved in invoke_bpf_mod_ret with address of do_fexit */ for (i = 0; i < fmod_ret->nr_links && image; i++) { if (create_cond_branch(&branch_insn, &image[branches[i]],
(unsignedlong)&image[ctx->idx], COND_NE << 16)) {
ret = -EINVAL; goto cleanup;
}
image[branches[i]] = ppc_inst_val(branch_insn);
}
for (i = 0; i < fexit->nr_links; i++) if (invoke_bpf_prog(image, ro_image, ctx, fexit->links[i], regs_off, retval_off,
run_ctx_off, false)) {
ret = -EINVAL; goto cleanup;
}
if (flags & BPF_TRAMP_F_CALL_ORIG) { if (ro_image) /* image is NULL for dummy pass */
im->ip_epilogue = &((u32 *)ro_image)[ctx->idx];
PPC_LI_ADDR(_R3, im);
ret = bpf_jit_emit_func_call_rel(image, ro_image, ctx,
(unsignedlong)__bpf_tramp_exit); if (ret) goto cleanup;
}
if (flags & BPF_TRAMP_F_RESTORE_REGS)
bpf_trampoline_restore_args_regs(image, ctx, nr_regs, regs_off);
/* Restore return value of func_addr or fentry prog */ if (save_ret)
EMIT(PPC_RAW_LL(_R3, _R1, retval_off));
/* * rw_image doesn't need to be in module memory range, so we can * use kvmalloc.
*/
rw_image = kvmalloc(size, GFP_KERNEL); if (!rw_image) return -ENOMEM;
ret = __arch_prepare_bpf_trampoline(im, rw_image, rw_image + size, image, m,
flags, tlinks, func_addr); if (ret < 0) goto out;
if (bpf_jit_enable > 1)
bpf_jit_dump(1, ret - BPF_INSN_SAFETY * 4, 1, rw_image);
tmp = bpf_arch_text_copy(image, rw_image, size); if (IS_ERR(tmp))
ret = PTR_ERR(tmp);
/* * A 3-step process for bpf prog entry: * 1. At bpf prog entry, a single nop/b: * bpf_func: * [nop|b] ool_stub * 2. Out-of-line stub: * 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) * mtctr r12 * mtlr r11 // needed to retain ftrace ABI * bctr * * dummy_tramp is used to reduce synchronization requirements. * * When attaching a bpf trampoline to a bpf prog, 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.
*/ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, void *old_addr, void *new_addr)
{ unsignedlong bpf_func, bpf_func_end, size, offset;
ppc_inst_t old_inst, new_inst; int ret = 0, branch_flags; char name[KSYM_NAME_LEN];
/* We currently only support poking bpf programs */ if (!__bpf_address_lookup(bpf_func, &size, &offset, name)) {
pr_err("%s (0x%lx): kernel/modules are not supported\n", __func__, bpf_func); return -EOPNOTSUPP;
}
/* * If we are not poking at bpf prog entry, then we are simply patching in/out * an unconditional branch instruction at im->ip_after_call
*/ if (offset) { if (poke_type != BPF_MOD_JUMP) {
pr_err("%s (0x%lx): calls are not supported in bpf prog body\n", __func__,
bpf_func); return -EOPNOTSUPP;
}
old_inst = ppc_inst(PPC_RAW_NOP()); if (old_addr) if (create_branch(&old_inst, ip, (unsignedlong)old_addr, 0)) return -ERANGE;
new_inst = ppc_inst(PPC_RAW_NOP()); if (new_addr) if (create_branch(&new_inst, ip, (unsignedlong)new_addr, 0)) return -ERANGE;
mutex_lock(&text_mutex);
ret = bpf_modify_inst(ip, old_inst, new_inst);
mutex_unlock(&text_mutex);
/* Make sure all cpus see the new instruction */
smp_call_function(do_isync, NULL, 1); return ret;
}
bpf_func_end = bpf_func + size;
/* Address of the jmp/call instruction in the out-of-line stub */
ip = (void *)(bpf_func_end - bpf_jit_ool_stub + 4);
if (!is_offset_in_branch_range((long)ip - 4 - bpf_func)) {
pr_err("%s (0x%lx): bpf prog too large, ool stub out of branch range\n", __func__,
bpf_func); return -ERANGE;
}
/* * 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.
*/ 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),
(new_addr && !is_offset_in_branch_range(new_addr - ip)) ?
(unsignedlong)new_addr : (unsignedlong)dummy_tramp); if (ret) goto out;
/* 2. Update the branch/call in the out-of-line stub */
ret = bpf_modify_inst(ip, old_inst, new_inst); if (ret) goto out;
/* 3. Update instruction at bpf prog entry */
ip = (void *)bpf_func; if (!old_addr || !new_addr) { if (!old_addr) {
old_inst = ppc_inst(PPC_RAW_NOP());
create_branch(&new_inst, ip, bpf_func_end - bpf_jit_ool_stub, 0);
} else {
new_inst = ppc_inst(PPC_RAW_NOP());
create_branch(&old_inst, ip, bpf_func_end - bpf_jit_ool_stub, 0);
}
ret = bpf_modify_inst(ip, old_inst, new_inst);
}
out:
mutex_unlock(&text_mutex);
/* * Sync only if we are not attaching a trampoline to a bpf prog so the older * trampoline can be freed safely.
*/ if (old_addr)
smp_call_function(do_isync, NULL, 1);
return ret;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.