forked from Minki/linux
23b51916ee
BPF load instruction with BPF_PROBE_MEM mode can cause a fault inside kernel. Append exception table for such instructions within BPF program. Unlike other archs which uses extable 'fixup' field to pass dest_reg and nip, BPF exception table on PowerPC follows the generic PowerPC exception table design, where it populates both fixup and extable sections within BPF program. fixup section contains 3 instructions, first 2 instructions clear dest_reg (lower & higher 32-bit registers) and last instruction jumps to next instruction in the BPF code. extable 'insn' field contains relative offset of the instruction and 'fixup' field contains relative offset of the fixup entry. Example layout of BPF program with extable present: +------------------+ | | | | 0x4020 -->| lwz r28,4(r4) | | | | | 0x40ac -->| lwz r3,0(r24) | | lwz r4,4(r24) | | | | | |------------------| 0x4278 -->| li r28,0 | \ | li r27,0 | | fixup entry | b 0x4024 | / 0x4284 -->| li r4,0 | | li r3,0 | | b 0x40b4 | |------------------| 0x4290 -->| insn=0xfffffd90 | \ extable entry | fixup=0xffffffe4 | / 0x4298 -->| insn=0xfffffe14 | | fixup=0xffffffe8 | +------------------+ (Addresses shown here are chosen random, not real) Signed-off-by: Hari Bathini <hbathini@linux.ibm.com> Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20211012123056.485795-8-hbathini@linux.ibm.com
321 lines
7.9 KiB
C
321 lines
7.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* eBPF JIT compiler
|
|
*
|
|
* Copyright 2016 Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
|
|
* IBM Corporation
|
|
*
|
|
* Based on the powerpc classic BPF JIT compiler by Matt Evans
|
|
*/
|
|
#include <linux/moduleloader.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/asm-compat.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/filter.h>
|
|
#include <linux/if_vlan.h>
|
|
#include <asm/kprobes.h>
|
|
#include <linux/bpf.h>
|
|
|
|
#include "bpf_jit.h"
|
|
|
|
static void bpf_jit_fill_ill_insns(void *area, unsigned int size)
|
|
{
|
|
memset32(area, BREAKPOINT_INSTRUCTION, size / 4);
|
|
}
|
|
|
|
/* Fix the branch target addresses for subprog calls */
|
|
static int bpf_jit_fixup_subprog_calls(struct bpf_prog *fp, u32 *image,
|
|
struct codegen_context *ctx, u32 *addrs)
|
|
{
|
|
const struct bpf_insn *insn = fp->insnsi;
|
|
bool func_addr_fixed;
|
|
u64 func_addr;
|
|
u32 tmp_idx;
|
|
int i, ret;
|
|
|
|
for (i = 0; i < fp->len; i++) {
|
|
/*
|
|
* During the extra pass, only the branch target addresses for
|
|
* the subprog calls need to be fixed. All other instructions
|
|
* can left untouched.
|
|
*
|
|
* The JITed image length does not change because we already
|
|
* ensure that the JITed instruction sequence for these calls
|
|
* are of fixed length by padding them with NOPs.
|
|
*/
|
|
if (insn[i].code == (BPF_JMP | BPF_CALL) &&
|
|
insn[i].src_reg == BPF_PSEUDO_CALL) {
|
|
ret = bpf_jit_get_func_addr(fp, &insn[i], true,
|
|
&func_addr,
|
|
&func_addr_fixed);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/*
|
|
* Save ctx->idx as this would currently point to the
|
|
* end of the JITed image and set it to the offset of
|
|
* the instruction sequence corresponding to the
|
|
* subprog call temporarily.
|
|
*/
|
|
tmp_idx = ctx->idx;
|
|
ctx->idx = addrs[i] / 4;
|
|
bpf_jit_emit_func_call_rel(image, ctx, func_addr);
|
|
|
|
/*
|
|
* Restore ctx->idx here. This is safe as the length
|
|
* of the JITed sequence remains unchanged.
|
|
*/
|
|
ctx->idx = tmp_idx;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct powerpc64_jit_data {
|
|
struct bpf_binary_header *header;
|
|
u32 *addrs;
|
|
u8 *image;
|
|
u32 proglen;
|
|
struct codegen_context ctx;
|
|
};
|
|
|
|
bool bpf_jit_needs_zext(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
|
|
{
|
|
u32 proglen;
|
|
u32 alloclen;
|
|
u8 *image = NULL;
|
|
u32 *code_base;
|
|
u32 *addrs;
|
|
struct powerpc64_jit_data *jit_data;
|
|
struct codegen_context cgctx;
|
|
int pass;
|
|
int flen;
|
|
struct bpf_binary_header *bpf_hdr;
|
|
struct bpf_prog *org_fp = fp;
|
|
struct bpf_prog *tmp_fp;
|
|
bool bpf_blinded = false;
|
|
bool extra_pass = false;
|
|
u32 extable_len;
|
|
u32 fixup_len;
|
|
|
|
if (!fp->jit_requested)
|
|
return org_fp;
|
|
|
|
tmp_fp = bpf_jit_blind_constants(org_fp);
|
|
if (IS_ERR(tmp_fp))
|
|
return org_fp;
|
|
|
|
if (tmp_fp != org_fp) {
|
|
bpf_blinded = true;
|
|
fp = tmp_fp;
|
|
}
|
|
|
|
jit_data = fp->aux->jit_data;
|
|
if (!jit_data) {
|
|
jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL);
|
|
if (!jit_data) {
|
|
fp = org_fp;
|
|
goto out;
|
|
}
|
|
fp->aux->jit_data = jit_data;
|
|
}
|
|
|
|
flen = fp->len;
|
|
addrs = jit_data->addrs;
|
|
if (addrs) {
|
|
cgctx = jit_data->ctx;
|
|
image = jit_data->image;
|
|
bpf_hdr = jit_data->header;
|
|
proglen = jit_data->proglen;
|
|
extra_pass = true;
|
|
goto skip_init_ctx;
|
|
}
|
|
|
|
addrs = kcalloc(flen + 1, sizeof(*addrs), GFP_KERNEL);
|
|
if (addrs == NULL) {
|
|
fp = org_fp;
|
|
goto out_addrs;
|
|
}
|
|
|
|
memset(&cgctx, 0, sizeof(struct codegen_context));
|
|
memcpy(cgctx.b2p, b2p, sizeof(cgctx.b2p));
|
|
|
|
/* 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, 0, &cgctx, addrs, 0)) {
|
|
/* 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.
|
|
*/
|
|
if (cgctx.seen & SEEN_TAILCALL) {
|
|
cgctx.idx = 0;
|
|
if (bpf_jit_build_body(fp, 0, &cgctx, addrs, 0)) {
|
|
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(0, &cgctx);
|
|
bpf_jit_build_epilogue(0, &cgctx);
|
|
|
|
fixup_len = fp->aux->num_exentries * BPF_FIXUP_LEN * 4;
|
|
extable_len = fp->aux->num_exentries * sizeof(struct exception_table_entry);
|
|
|
|
proglen = cgctx.idx * 4;
|
|
alloclen = proglen + FUNCTION_DESCR_SIZE + fixup_len + extable_len;
|
|
|
|
bpf_hdr = bpf_jit_binary_alloc(alloclen, &image, 4, bpf_jit_fill_ill_insns);
|
|
if (!bpf_hdr) {
|
|
fp = org_fp;
|
|
goto out_addrs;
|
|
}
|
|
|
|
if (extable_len)
|
|
fp->aux->extable = (void *)image + FUNCTION_DESCR_SIZE + proglen + fixup_len;
|
|
|
|
skip_init_ctx:
|
|
code_base = (u32 *)(image + FUNCTION_DESCR_SIZE);
|
|
|
|
if (extra_pass) {
|
|
/*
|
|
* Do not touch the prologue and epilogue as they will remain
|
|
* unchanged. Only fix the branch target address for subprog
|
|
* calls in the body.
|
|
*
|
|
* This does not change the offsets and lengths of the subprog
|
|
* call instruction sequences and hence, the size of the JITed
|
|
* image as well.
|
|
*/
|
|
bpf_jit_fixup_subprog_calls(fp, code_base, &cgctx, addrs);
|
|
|
|
/* There is no need to perform the usual passes. */
|
|
goto skip_codegen_passes;
|
|
}
|
|
|
|
/* Code generation passes 1-2 */
|
|
for (pass = 1; pass < 3; pass++) {
|
|
/* Now build the prologue, body code & epilogue for real. */
|
|
cgctx.idx = 0;
|
|
bpf_jit_build_prologue(code_base, &cgctx);
|
|
if (bpf_jit_build_body(fp, code_base, &cgctx, addrs, pass)) {
|
|
bpf_jit_binary_free(bpf_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);
|
|
}
|
|
|
|
skip_codegen_passes:
|
|
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);
|
|
|
|
#ifdef PPC64_ELF_ABI_v1
|
|
/* Function descriptor nastiness: Address + TOC */
|
|
((u64 *)image)[0] = (u64)code_base;
|
|
((u64 *)image)[1] = local_paca->kernel_toc;
|
|
#endif
|
|
|
|
fp->bpf_func = (void *)image;
|
|
fp->jited = 1;
|
|
fp->jited_len = proglen + FUNCTION_DESCR_SIZE;
|
|
|
|
bpf_flush_icache(bpf_hdr, (u8 *)bpf_hdr + (bpf_hdr->pages * PAGE_SIZE));
|
|
if (!fp->is_func || extra_pass) {
|
|
bpf_jit_binary_lock_ro(bpf_hdr);
|
|
bpf_prog_fill_jited_linfo(fp, addrs);
|
|
out_addrs:
|
|
kfree(addrs);
|
|
kfree(jit_data);
|
|
fp->aux->jit_data = NULL;
|
|
} else {
|
|
jit_data->addrs = addrs;
|
|
jit_data->ctx = cgctx;
|
|
jit_data->proglen = proglen;
|
|
jit_data->image = image;
|
|
jit_data->header = bpf_hdr;
|
|
}
|
|
|
|
out:
|
|
if (bpf_blinded)
|
|
bpf_jit_prog_release_other(fp, fp == org_fp ? tmp_fp : org_fp);
|
|
|
|
return fp;
|
|
}
|
|
|
|
/*
|
|
* 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, int pass, struct codegen_context *ctx,
|
|
int insn_idx, int jmp_off, int dst_reg)
|
|
{
|
|
off_t offset;
|
|
unsigned long pc;
|
|
struct exception_table_entry *ex;
|
|
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;
|
|
|
|
pc = (unsigned long)&image[insn_idx];
|
|
|
|
fixup = (void *)fp->aux->extable -
|
|
(fp->aux->num_exentries * BPF_FIXUP_LEN * 4) +
|
|
(ctx->exentry_idx * BPF_FIXUP_LEN * 4);
|
|
|
|
fixup[0] = PPC_RAW_LI(dst_reg, 0);
|
|
if (IS_ENABLED(CONFIG_PPC32))
|
|
fixup[1] = PPC_RAW_LI(dst_reg - 1, 0); /* clear higher 32-bit register too */
|
|
|
|
fixup[BPF_FIXUP_LEN - 1] =
|
|
PPC_RAW_BRANCH((long)(pc + jmp_off) - (long)&fixup[BPF_FIXUP_LEN - 1]);
|
|
|
|
ex = &fp->aux->extable[ctx->exentry_idx];
|
|
|
|
offset = pc - (long)&ex->insn;
|
|
if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN))
|
|
return -ERANGE;
|
|
ex->insn = offset;
|
|
|
|
offset = (long)fixup - (long)&ex->fixup;
|
|
if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN))
|
|
return -ERANGE;
|
|
ex->fixup = offset;
|
|
|
|
ctx->exentry_idx++;
|
|
return 0;
|
|
}
|