Skip to content

Commit e9ffcda

Browse files
committed
WIP bpf, arm32: support JIT exception handling
After: root@qemu-armhf:/usr/libexec/kselftests-bpf# ./test_progs -a exhandler torvalds#96 exhandler:OK Summary: 1/0 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Tony Ambardar <[email protected]>
1 parent 0837907 commit e9ffcda

File tree

1 file changed

+128
-17
lines changed

1 file changed

+128
-17
lines changed

arch/arm/net/bpf_jit_32.c

Lines changed: 128 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/if_vlan.h>
2020
#include <linux/math64.h>
2121

22+
#include <asm/asm-extable.h>
2223
#include <asm/cacheflush.h>
2324
#include <asm/hwcap.h>
2425
#include <asm/opcodes.h>
@@ -120,7 +121,8 @@ enum {
120121
#define TMP_REG_2 (MAX_BPF_JIT_REG + 1) /* TEMP Register 2 */
121122
#define TCALL_CNT (MAX_BPF_JIT_REG + 2) /* Tail Call Count */
122123

123-
#define FLAG_IMM_OVERFLOW (1 << 0)
124+
#define FLAG_IMM_OVERFLOW BIT(0)
125+
#define FLAG_EX_TABLE_ERR BIT(1)
124126

125127
/*
126128
* Map BPF registers to ARM 32-bit registers or stack register space.
@@ -198,6 +200,7 @@ struct jit_ctx {
198200
u32 *offsets;
199201
u32 *target;
200202
u32 stack_size;
203+
u32 exentry_idx;
201204
s32 prologue_tcc_skip; /* offset: skip BPF FP, TCC, R1 setup */
202205
s32 epilogue_main_skip; /* offset: skip restoring BPF callee-saved */
203206
#if __LINUX_ARM_ARCH__ < 7
@@ -447,6 +450,78 @@ static inline int bpf2a32_offset(int bpf_to, int bpf_from,
447450
return to - from - 1;
448451
}
449452

453+
#define EX_DONT_CLEAR (ARM_PC) /* ARM reg unused for BPF */
454+
455+
bool ex_handler_bpf(const struct exception_table_entry *ex,
456+
struct pt_regs *regs)
457+
{
458+
u32 ex_data = ex->data;
459+
u8 dst_reg = FIELD_GET(EX_DATA_REG, ex_data);
460+
u8 offset = FIELD_GET(EX_DATA_FIX_OFF, ex_data);
461+
bool is_arm_rd = FIELD_GET(EX_DATA_RD_FLAG, ex_data);
462+
463+
if (dst_reg != EX_DONT_CLEAR) {
464+
regs->uregs[dst_reg] = 0;
465+
if (is_arm_rd)
466+
regs->uregs[dst_reg + 1] = 0;
467+
}
468+
regs->ARM_pc += offset;
469+
return true;
470+
}
471+
472+
/* For faultable load/stores, add an entry to the exception table.
473+
* This must be called immediately after emitting the insn.
474+
*/
475+
static void add_exception_handler(u8 code, u8 dst_reg, bool clear_reg,
476+
struct jit_ctx *ctx)
477+
{
478+
struct exception_table_entry *ex;
479+
bool is_arm_rd;
480+
u8 offset = 4;
481+
u32 *pc;
482+
483+
if (BPF_MODE(code) != BPF_PROBE_MEM &&
484+
BPF_MODE(code) != BPF_PROBE_MEMSX &&
485+
BPF_MODE(code) != BPF_PROBE_MEM32 &&
486+
BPF_MODE(code) != BPF_PROBE_ATOMIC)
487+
return;
488+
489+
/*
490+
* A BPF_PROBE_xxx insn may be JITed to multiple faultable ARM
491+
* insns, so count these during first pass and later reconcile
492+
* with prog->aux->num_exentries.
493+
*/
494+
if (ctx->target == NULL) {
495+
ctx->exentry_idx++;
496+
return;
497+
}
498+
499+
if (WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries) ||
500+
!ctx->prog->aux->extable) {
501+
/* Signal error into flags since no return val */
502+
ctx->flags |= FLAG_EX_TABLE_ERR;
503+
return;
504+
}
505+
506+
ex = &ctx->prog->aux->extable[ctx->exentry_idx];
507+
pc = &ctx->target[ctx->idx - 1];
508+
509+
is_arm_rd = (*pc & ARM_INST_LDRD_I) == ARM_INST_LDRD_I;
510+
ex->insn = (unsigned long)pc;
511+
512+
if (!clear_reg)
513+
dst_reg = EX_DONT_CLEAR;
514+
515+
ex->data = FIELD_PREP(EX_DATA_FIX_OFF, offset) |
516+
FIELD_PREP(EX_DATA_REG, dst_reg) |
517+
FIELD_PREP(EX_DATA_RD_FLAG, is_arm_rd);
518+
519+
ex->type = EX_TYPE_BPF;
520+
ex->is_typed = true;
521+
522+
ctx->exentry_idx++;
523+
}
524+
450525
/*
451526
* Move an immediate, optionally using a fixed number of instructions
452527
* to avoid changes in JITed program length during later JIT passes.
@@ -1279,9 +1354,10 @@ static inline void emit_str_r(const s8 dst, const s8 src[],
12791354

12801355
/* dst = *(size*)(src + off) */
12811356
static inline void emit_ldx_r(const s8 dst[], const s8 src,
1282-
s16 off, struct jit_ctx *ctx, const u8 sz){
1357+
s16 off, struct jit_ctx *ctx, const u8 code){
12831358
const s8 *tmp = bpf2a32[TMP_REG_1];
12841359
const s8 *rd = is_stacked(dst_lo) ? tmp : dst;
1360+
const u8 sz = BPF_SIZE(code);
12851361
s8 rm = src;
12861362

12871363
if (!is_ldst_imm(off, sz)) {
@@ -1297,33 +1373,39 @@ static inline void emit_ldx_r(const s8 dst[], const s8 src,
12971373
case BPF_B:
12981374
/* Load a Byte */
12991375
emit(ARM_LDRB_I(rd[1], rm, off), ctx);
1376+
add_exception_handler(code, rd[1], true, ctx);
13001377
emit_cond_zext(rd, ctx);
13011378
break;
13021379
case BPF_H:
13031380
/* Load a HalfWord */
13041381
emit(ARM_LDRH_I(rd[1], rm, off), ctx);
1382+
add_exception_handler(code, rd[1], true, ctx);
13051383
emit_cond_zext(rd, ctx);
13061384
break;
13071385
case BPF_W:
13081386
/* Load a Word */
13091387
emit(ARM_LDR_I(rd[1], rm, off), ctx);
1388+
add_exception_handler(code, rd[1], true, ctx);
13101389
//FIXME emit_cond_zext(rd, ctx);
13111390
emit_a32_mov_i(rd[0], 0, false, ctx); // FORCE ZEXT
13121391
break;
13131392
case BPF_DW:
13141393
/* Load a Double Word */
13151394
emit(ARM_LDR_I(rd[1], rm, off), ctx);
1395+
add_exception_handler(code, rd[1], true, ctx);
13161396
emit(ARM_LDR_I(rd[0], rm, off + 4), ctx);
1397+
add_exception_handler(code, rd[0], true, ctx);
13171398
break;
13181399
}
13191400
arm_bpf_put_reg64(dst, rd, ctx);
13201401
}
13211402

13221403
/* dst = *(signed size*)(src + off) */
13231404
static inline void emit_ldsx_r(const s8 dst[], const s8 src,
1324-
s16 off, struct jit_ctx *ctx, const u8 sz){
1405+
s16 off, struct jit_ctx *ctx, const u8 code){
13251406
const s8 *tmp = bpf2a32[TMP_REG_1];
13261407
const s8 *rd = is_stacked(dst_lo) ? tmp : dst;
1408+
const u8 sz = BPF_SIZE(code);
13271409
s8 rm = src;
13281410
int add_off;
13291411

@@ -1358,6 +1440,9 @@ static inline void emit_ldsx_r(const s8 dst[], const s8 src,
13581440
emit(ARM_LDR_I(rd[1], rm, off), ctx);
13591441
break;
13601442
}
1443+
/* Trap ARM exceptions during BPF PROBE insns */
1444+
add_exception_handler(code, rd[1], true, ctx);
1445+
13611446
/* Carry the sign extension to upper 32 bits */
13621447
emit(ARM_ASR_I(rd[0], rd[1], 31), ctx);
13631448
arm_bpf_put_reg64(dst, rd, ctx);
@@ -2395,15 +2480,22 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
23952480
case BPF_LDX | BPF_MEM | BPF_H:
23962481
case BPF_LDX | BPF_MEM | BPF_B:
23972482
case BPF_LDX | BPF_MEM | BPF_DW:
2483+
case BPF_LDX | BPF_PROBE_MEM | BPF_W:
2484+
case BPF_LDX | BPF_PROBE_MEM | BPF_H:
2485+
case BPF_LDX | BPF_PROBE_MEM | BPF_B:
2486+
case BPF_LDX | BPF_PROBE_MEM | BPF_DW:
23982487
/* LDSX: dst = *(signed size *)(src + off) */
23992488
case BPF_LDX | BPF_MEMSX | BPF_B:
24002489
case BPF_LDX | BPF_MEMSX | BPF_H:
24012490
case BPF_LDX | BPF_MEMSX | BPF_W:
2491+
case BPF_LDX | BPF_PROBE_MEMSX | BPF_B:
2492+
case BPF_LDX | BPF_PROBE_MEMSX | BPF_H:
2493+
case BPF_LDX | BPF_PROBE_MEMSX | BPF_W:
24022494
rn = arm_bpf_get_reg32(src_lo, tmp2[1], ctx);
24032495
if (BPF_MODE(insn->code) == BPF_MEMSX)
2404-
emit_ldsx_r(dst, rn, off, ctx, BPF_SIZE(code));
2496+
emit_ldsx_r(dst, rn, off, ctx, code);
24052497
else
2406-
emit_ldx_r(dst, rn, off, ctx, BPF_SIZE(code));
2498+
emit_ldx_r(dst, rn, off, ctx, code);
24072499
break;
24082500
/* speculation barrier */
24092501
case BPF_ST | BPF_NOSPEC:
@@ -2706,6 +2798,14 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
27062798
pr_warn("load offset to literal pool out of range\n");
27072799
return -EFAULT;
27082800
}
2801+
if (ctx->flags & FLAG_EX_TABLE_ERR) {
2802+
/*
2803+
* This instruction required an exception handler
2804+
* but failed to build it.
2805+
*/
2806+
pr_warn("failure building exception table\n");
2807+
return -EINVAL;
2808+
}
27092809
return 0;
27102810
}
27112811

@@ -2750,6 +2850,9 @@ static int validate_code(struct jit_ctx *ctx)
27502850
return -1;
27512851
}
27522852

2853+
if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries))
2854+
return -1;
2855+
27532856
return 0;
27542857
}
27552858

@@ -2771,13 +2874,13 @@ if (bpf_jit_enable > 1) \
27712874

27722875
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
27732876
{
2877+
unsigned int image_size, prog_size, extable_size;
27742878
struct bpf_prog *tmp, *orig_prog = prog;
27752879
struct bpf_binary_header *header;
27762880
struct arm32_jit_data *jit_data;
27772881
bool tmp_blinded = false;
27782882
bool extra_pass = false;
27792883
struct jit_ctx ctx;
2780-
unsigned int image_size;
27812884
u8 *image_ptr;
27822885

27832886
/* If BPF JIT was not enabled then we must fall back to
@@ -2812,7 +2915,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
28122915
ctx = jit_data->ctx;
28132916
image_ptr = jit_data->image;
28142917
header = jit_data->header;
2815-
image_size = sizeof(u32) * ctx.idx;
2918+
prog_size = sizeof(u32) * ctx.idx;
28162919
extra_pass = true;
28172920
goto skip_init_ctx;
28182921
}
@@ -2863,15 +2966,20 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
28632966
}
28642967
}
28652968
#endif
2866-
/* Now we can get the actual image size of the JITed arm code.
2867-
* Currently, we are not considering the THUMB-2 instructions
2868-
* for jit, although it can decrease the size of the image.
2869-
*
2870-
* As each arm instruction is of length 32bit, we are translating
2871-
* number of JITed instructions into the size required to store these
2872-
* JITed code.
2969+
/* Now we determine sizes of the JITed ARM code and any literal
2970+
* pool, and the exception table if needed. ARM insns are always
2971+
* 32-bit since the JIT doesn't support THUMB-2.
28732972
*/
2874-
image_size = sizeof(u32) * ctx.idx;
2973+
prog_size = sizeof(u32) * ctx.idx;
2974+
2975+
/* Handle multiple JITed insns output per BPF_PROBE_xxx insn. */
2976+
if (ctx.exentry_idx > prog->aux->num_exentries)
2977+
prog->aux->num_exentries = ctx.exentry_idx;
2978+
2979+
extable_size = prog->aux->num_exentries *
2980+
sizeof(struct exception_table_entry);
2981+
2982+
image_size = prog_size + extable_size;
28752983

28762984
/* Now we know the size of the structure to make */
28772985
header = bpf_jit_binary_alloc(image_size, &image_ptr,
@@ -2889,8 +2997,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
28892997
* pass to finalize JMP offsets if using bpf2bpf calls.
28902998
*/
28912999
ctx.target = (u32 *) image_ptr;
3000+
if (extable_size)
3001+
prog->aux->extable = (void *)image_ptr + prog_size;
28923002
skip_init_ctx:
28933003
ctx.idx = 0;
3004+
ctx.exentry_idx = 0;
28943005

28953006
build_prologue(&ctx);
28963007

@@ -2911,7 +3022,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
29113022

29123023
if (bpf_jit_enable > 1)
29133024
/* there are 2 passes here */
2914-
bpf_jit_dump(prog->len, image_size, 2, ctx.target);
3025+
bpf_jit_dump(prog->len, prog_size, 2, ctx.target);
29153026

29163027
if (!prog->is_func || extra_pass) {
29173028
if (extra_pass && ctx.idx != jit_data->ctx.idx) {
@@ -2931,7 +3042,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
29313042
}
29323043
prog->bpf_func = (void *)ctx.target;
29333044
prog->jited = 1;
2934-
prog->jited_len = image_size;
3045+
prog->jited_len = prog_size;
29353046

29363047
DEBUG_PASS(3);
29373048

0 commit comments

Comments
 (0)