diff options
Diffstat (limited to 'arch/powerpc/net/bpf_jit_comp.c')
| -rw-r--r-- | arch/powerpc/net/bpf_jit_comp.c | 68 | 
1 files changed, 62 insertions, 6 deletions
| diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 90ce75f0f1e2..d6ffdd0f2309 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -101,6 +101,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *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; @@ -131,7 +133,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)  		image = jit_data->image;  		bpf_hdr = jit_data->header;  		proglen = jit_data->proglen; -		alloclen = proglen + FUNCTION_DESCR_SIZE;  		extra_pass = true;  		goto skip_init_ctx;  	} @@ -149,7 +150,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)  	cgctx.stack_size = round_up(fp->aux->stack_depth, 16);  	/* Scouting faux-generate pass 0 */ -	if (bpf_jit_build_body(fp, 0, &cgctx, addrs, false)) { +	if (bpf_jit_build_body(fp, 0, &cgctx, addrs, 0)) {  		/* We hit something illegal or unsupported. */  		fp = org_fp;  		goto out_addrs; @@ -162,7 +163,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)  	 */  	if (cgctx.seen & SEEN_TAILCALL) {  		cgctx.idx = 0; -		if (bpf_jit_build_body(fp, 0, &cgctx, addrs, false)) { +		if (bpf_jit_build_body(fp, 0, &cgctx, addrs, 0)) {  			fp = org_fp;  			goto out_addrs;  		} @@ -177,8 +178,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)  	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; +	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) { @@ -186,6 +190,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *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); @@ -210,7 +217,7 @@ skip_init_ctx:  		/* 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, extra_pass)) { +		if (bpf_jit_build_body(fp, code_base, &cgctx, addrs, pass)) {  			bpf_jit_binary_free(bpf_hdr);  			fp = org_fp;  			goto out_addrs; @@ -238,7 +245,7 @@ skip_codegen_passes:  	fp->bpf_func = (void *)image;  	fp->jited = 1; -	fp->jited_len = alloclen; +	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) { @@ -262,3 +269,52 @@ out:  	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; +} |