Skip to content

Commit 22fc0e8

Browse files
puranjaymohanAlexei Starovoitov
authored andcommitted
bpf, arm64: support exceptions
The prologue generation code has been modified to make the callback program use the stack of the program marked as exception boundary where callee-saved registers are already pushed. As the bpf_throw function never returns, if it clobbers any callee-saved registers, they would remain clobbered. So, the prologue of the exception-boundary program is modified to push R23 and R24 as well, which the callback will then recover in its epilogue. The Procedure Call Standard for the Arm 64-bit Architecture[1] states that registers r19 to r28 should be saved by the callee. BPF programs on ARM64 already save all callee-saved registers except r23 and r24. This patch adds an instruction in prologue of the program to save these two registers and another instruction in the epilogue to recover them. These extra instructions are only added if bpf_throw() is used. Otherwise the emitted prologue/epilogue remains unchanged. [1] https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst Signed-off-by: Puranjay Mohan <puranjay12@gmail.com> Link: https://lore.kernel.org/r/20240201125225.72796-3-puranjay12@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent e74cb1b commit 22fc0e8

2 files changed

Lines changed: 68 additions & 20 deletions

File tree

arch/arm64/net/bpf_jit_comp.c

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,8 @@ static bool is_lsi_offset(int offset, int scale)
285285
/* Tail call offset to jump into */
286286
#define PROLOGUE_OFFSET (BTI_INSNS + 2 + PAC_INSNS + 8)
287287

288-
static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
288+
static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf,
289+
bool is_exception_cb)
289290
{
290291
const struct bpf_prog *prog = ctx->prog;
291292
const bool is_main_prog = !bpf_is_subprog(prog);
@@ -333,19 +334,34 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
333334
emit(A64_MOV(1, A64_R(9), A64_LR), ctx);
334335
emit(A64_NOP, ctx);
335336

336-
/* Sign lr */
337-
if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL))
338-
emit(A64_PACIASP, ctx);
339-
340-
/* Save FP and LR registers to stay align with ARM64 AAPCS */
341-
emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
342-
emit(A64_MOV(1, A64_FP, A64_SP), ctx);
343-
344-
/* Save callee-saved registers */
345-
emit(A64_PUSH(r6, r7, A64_SP), ctx);
346-
emit(A64_PUSH(r8, r9, A64_SP), ctx);
347-
emit(A64_PUSH(fp, tcc, A64_SP), ctx);
348-
emit(A64_PUSH(fpb, A64_R(28), A64_SP), ctx);
337+
if (!is_exception_cb) {
338+
/* Sign lr */
339+
if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL))
340+
emit(A64_PACIASP, ctx);
341+
/* Save FP and LR registers to stay align with ARM64 AAPCS */
342+
emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
343+
emit(A64_MOV(1, A64_FP, A64_SP), ctx);
344+
345+
/* Save callee-saved registers */
346+
emit(A64_PUSH(r6, r7, A64_SP), ctx);
347+
emit(A64_PUSH(r8, r9, A64_SP), ctx);
348+
emit(A64_PUSH(fp, tcc, A64_SP), ctx);
349+
emit(A64_PUSH(fpb, A64_R(28), A64_SP), ctx);
350+
} else {
351+
/*
352+
* Exception callback receives FP of Main Program as third
353+
* parameter
354+
*/
355+
emit(A64_MOV(1, A64_FP, A64_R(2)), ctx);
356+
/*
357+
* Main Program already pushed the frame record and the
358+
* callee-saved registers. The exception callback will not push
359+
* anything and re-use the main program's stack.
360+
*
361+
* 10 registers are on the stack
362+
*/
363+
emit(A64_SUB_I(1, A64_SP, A64_FP, 80), ctx);
364+
}
349365

350366
/* Set up BPF prog stack base register */
351367
emit(A64_MOV(1, fp, A64_SP), ctx);
@@ -365,6 +381,20 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
365381
emit_bti(A64_BTI_J, ctx);
366382
}
367383

384+
/*
385+
* Program acting as exception boundary should save all ARM64
386+
* Callee-saved registers as the exception callback needs to recover
387+
* all ARM64 Callee-saved registers in its epilogue.
388+
*/
389+
if (prog->aux->exception_boundary) {
390+
/*
391+
* As we are pushing two more registers, BPF_FP should be moved
392+
* 16 bytes
393+
*/
394+
emit(A64_SUB_I(1, fp, fp, 16), ctx);
395+
emit(A64_PUSH(A64_R(23), A64_R(24), A64_SP), ctx);
396+
}
397+
368398
emit(A64_SUB_I(1, fpb, fp, ctx->fpb_offset), ctx);
369399

370400
/* Stack must be multiples of 16B */
@@ -653,7 +683,7 @@ static void build_plt(struct jit_ctx *ctx)
653683
plt->target = (u64)&dummy_tramp;
654684
}
655685

656-
static void build_epilogue(struct jit_ctx *ctx)
686+
static void build_epilogue(struct jit_ctx *ctx, bool is_exception_cb)
657687
{
658688
const u8 r0 = bpf2a64[BPF_REG_0];
659689
const u8 r6 = bpf2a64[BPF_REG_6];
@@ -666,6 +696,15 @@ static void build_epilogue(struct jit_ctx *ctx)
666696
/* We're done with BPF stack */
667697
emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx);
668698

699+
/*
700+
* Program acting as exception boundary pushes R23 and R24 in addition
701+
* to BPF callee-saved registers. Exception callback uses the boundary
702+
* program's stack frame, so recover these extra registers in the above
703+
* two cases.
704+
*/
705+
if (ctx->prog->aux->exception_boundary || is_exception_cb)
706+
emit(A64_POP(A64_R(23), A64_R(24), A64_SP), ctx);
707+
669708
/* Restore x27 and x28 */
670709
emit(A64_POP(fpb, A64_R(28), A64_SP), ctx);
671710
/* Restore fs (x25) and x26 */
@@ -1575,7 +1614,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
15751614
* BPF line info needs ctx->offset[i] to be the offset of
15761615
* instruction[i] in jited image, so build prologue first.
15771616
*/
1578-
if (build_prologue(&ctx, was_classic)) {
1617+
if (build_prologue(&ctx, was_classic, prog->aux->exception_cb)) {
15791618
prog = orig_prog;
15801619
goto out_off;
15811620
}
@@ -1586,7 +1625,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
15861625
}
15871626

15881627
ctx.epilogue_offset = ctx.idx;
1589-
build_epilogue(&ctx);
1628+
build_epilogue(&ctx, prog->aux->exception_cb);
15901629
build_plt(&ctx);
15911630

15921631
extable_align = __alignof__(struct exception_table_entry);
@@ -1614,15 +1653,15 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
16141653
ctx.idx = 0;
16151654
ctx.exentry_idx = 0;
16161655

1617-
build_prologue(&ctx, was_classic);
1656+
build_prologue(&ctx, was_classic, prog->aux->exception_cb);
16181657

16191658
if (build_body(&ctx, extra_pass)) {
16201659
bpf_jit_binary_free(header);
16211660
prog = orig_prog;
16221661
goto out_off;
16231662
}
16241663

1625-
build_epilogue(&ctx);
1664+
build_epilogue(&ctx, prog->aux->exception_cb);
16261665
build_plt(&ctx);
16271666

16281667
/* 3. Extra pass to validate JITed code. */
@@ -2310,3 +2349,13 @@ bool bpf_jit_supports_ptr_xchg(void)
23102349
{
23112350
return true;
23122351
}
2352+
2353+
bool bpf_jit_supports_exceptions(void)
2354+
{
2355+
/* We unwind through both kernel frames starting from within bpf_throw
2356+
* call and BPF frames. Therefore we require FP unwinder to be enabled
2357+
* to walk kernel frames and reach BPF frames in the stack trace.
2358+
* ARM64 kernel is aways compiled with CONFIG_FRAME_POINTER=y
2359+
*/
2360+
return true;
2361+
}

tools/testing/selftests/bpf/DENYLIST.aarch64

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
bpf_cookie/multi_kprobe_attach_api # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3
22
bpf_cookie/multi_kprobe_link_api # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3
3-
exceptions # JIT does not support calling kfunc bpf_throw: -524
43
fexit_sleep # The test never returns. The remaining tests cannot start.
54
kprobe_multi_bench_attach # needs CONFIG_FPROBE
65
kprobe_multi_test # needs CONFIG_FPROBE

0 commit comments

Comments
 (0)