summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-14 15:06:50 +0100
committerMark Brown <broonie@kernel.org>2026-09-14 15:06:50 +0100
commit576cd892ee3378f2d6f6e7ad3c7a773fb782ba8c (patch)
treefe83a743110fabc356cee958e4a38d462826b2d4 /arch
parent9e86316f48fcaa24c0fd332aba17b76ce8dfcb12 (diff)
parent29fcf9ce4bbdf4cc339def776860e1c0095bca98 (diff)
downloadlinux-next-576cd892ee3378f2d6f6e7ad3c7a773fb782ba8c.tar.gz
linux-next-576cd892ee3378f2d6f6e7ad3c7a773fb782ba8c.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
# Conflicts: # tools/testing/selftests/bpf/prog_tests/verifier.c
Diffstat (limited to 'arch')
-rw-r--r--arch/arm64/net/bpf_jit_comp.c151
-rw-r--r--arch/riscv/net/bpf_jit.h4
-rw-r--r--arch/riscv/net/bpf_jit_comp64.c111
-rw-r--r--arch/riscv/net/bpf_jit_core.c7
-rw-r--r--arch/riscv/net/bpf_timed_may_goto.S8
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/net/bpf_jit_comp.c409
7 files changed, 563 insertions, 128 deletions
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index c5f55d6161fe..95363210bd4b 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -1222,6 +1222,12 @@ static int add_exception_handler(const struct bpf_insn *insn,
return 0;
}
+static const struct bpf_jit_arg_abi arm64_arg_abi = {
+ .nr_arg_regs = 8,
+ .even_reg_align = true,
+ .even_stack_align = true,
+};
+
static const u8 stack_arg_reg[] = { A64_R(5), A64_R(6), A64_R(7) };
#define NR_STACK_ARG_REGS ARRAY_SIZE(stack_arg_reg)
@@ -1264,19 +1270,20 @@ static void emit_stack_arg_store_imm(s32 imm, s16 bpf_off, const u8 tmp, struct
* kern_vm_start. A nullable arg preserves NULL by skipping the add, tested
* on the truncated value as arena NULL is offset 0.
*/
-static int emit_kfunc_arena_args(struct jit_ctx *ctx, const struct bpf_insn *insn)
+static int emit_kfunc_arena_args(struct jit_ctx *ctx, const struct btf_func_model *fm)
{
const u8 arena_vm_base = bpf2a64[ARENA_VM_START];
- const struct btf_func_model *fm;
- int i;
+ int i, slot;
- fm = bpf_jit_find_kfunc_model(ctx->prog, insn);
- if (!fm)
- return -EINVAL;
-
- for (i = 0; i < min_t(int, fm->nr_args, MAX_BPF_FUNC_REG_ARGS); i++) {
- const u8 reg = bpf2a64[BPF_REG_1 + i];
+ for (i = 0, slot = 0; i < fm->nr_args; i++) {
+ u32 arg_regs = (fm->arg_size[i] + 7) / 8;
u8 flags = fm->arg_flags[i];
+ u8 reg;
+
+ if (slot + arg_regs > MAX_BPF_FUNC_REG_ARGS)
+ break;
+ reg = bpf2a64[BPF_REG_1 + slot];
+ slot += arg_regs;
if (!(flags & BTF_FMODEL_ARENA_ARG))
continue;
@@ -1295,6 +1302,52 @@ static int emit_kfunc_arena_args(struct jit_ctx *ctx, const struct bpf_insn *ins
return 0;
}
+static bool a64_arg_on_stack(u8 slot)
+{
+ return slot >= arm64_arg_abi.nr_arg_regs;
+}
+
+static s32 a64_arg_stack_off(u8 slot)
+{
+ return (slot - arm64_arg_abi.nr_arg_regs) * sizeof(u64);
+}
+
+/*
+ * Move the arguments AAPCS64 places somewhere other than the argument slot the
+ * BPF calling convention gave them. Slot N is X(N) up to the eighth, and the
+ * outgoing stack argument area from SP beyond it, both for the slot an
+ * argument comes from and for the one it goes to.
+ *
+ * AAPCS64 only ever moves an argument to a higher slot, so no move here ever
+ * takes BPF_JIT_ARG_TMP: bpf_jit_plan_arg_moves() hands out the scratch only
+ * for a convention that moves one down, which needs a register to carry the
+ * value past its own destination.
+ */
+static void emit_kfunc_arg_moves(struct jit_ctx *ctx, const struct btf_func_model *fm)
+{
+ struct bpf_jit_arg_move moves[BPF_JIT_MAX_ARG_MOVES];
+ const u8 tmp = bpf2a64[TMP_REG_1];
+ u32 i, n;
+
+ n = bpf_jit_plan_arg_moves(&arm64_arg_abi, fm, moves);
+
+ for (i = 0; i < n; i++) {
+ u8 dst = moves[i].dst, src = moves[i].src, reg;
+
+ if (a64_arg_on_stack(src)) {
+ reg = tmp;
+ emit(A64_LDR64I(reg, A64_SP, a64_arg_stack_off(src)), ctx);
+ } else {
+ reg = src;
+ }
+
+ if (a64_arg_on_stack(dst))
+ emit(A64_STR64I(reg, A64_SP, a64_arg_stack_off(dst)), ctx);
+ else if (reg != dst)
+ emit(A64_MOV(1, dst, reg), ctx);
+ }
+}
+
/* JITs an eBPF instruction.
* Returns:
* 0 - successfully JITed an 8-byte eBPF instruction.
@@ -1718,9 +1771,15 @@ emit_cond_jmp:
if (ret < 0)
return ret;
if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
- ret = emit_kfunc_arena_args(ctx, insn);
+ const struct btf_func_model *fm;
+
+ fm = bpf_jit_find_kfunc_model(ctx->prog, insn);
+ if (!fm)
+ return -EINVAL;
+ ret = emit_kfunc_arena_args(ctx, fm);
if (ret < 0)
return ret;
+ emit_kfunc_arg_moves(ctx, fm);
}
emit_call(func_addr, ctx);
/*
@@ -2390,6 +2449,16 @@ bool bpf_jit_supports_kfunc_call(void)
return true;
}
+bool bpf_jit_supports_kfunc_ret_reg_pair(void)
+{
+ return true;
+}
+
+const struct bpf_jit_arg_abi *bpf_jit_arg_abi(void)
+{
+ return &arm64_arg_abi;
+}
+
bool bpf_jit_supports_stack_args(void)
{
return true;
@@ -2526,33 +2595,41 @@ struct arg_aux {
* arguments to be properly aligned)
*/
int ostack_for_args;
+ /* where AAPCS64 puts each argument slot: an argument register below
+ * the eighth, an on-stack argument slot from it up
+ */
+ u8 pos_of_slot[MAX_BPF_FUNC_ARG_SLOTS];
};
static int calc_arg_aux(const struct btf_func_model *m,
struct arg_aux *a)
{
- int stack_slots, nregs, slots, i;
+ int slots, i, slot, total;
+
+ total = bpf_jit_place_args(&arm64_arg_abi, m, a->pos_of_slot);
+ if (total > MAX_BPF_FUNC_ARGS)
+ return -ENOTSUPP;
/* verifier ensures m->nr_args <= MAX_BPF_FUNC_ARGS */
- for (i = 0, nregs = 0; i < m->nr_args; i++) {
+ for (i = 0, slot = 0; i < m->nr_args; i++) {
slots = (m->arg_size[i] + 7) / 8;
- if (nregs + slots <= 8) /* passed through register ? */
- nregs += slots;
- else
+ if (a64_arg_on_stack(a->pos_of_slot[slot])) /* passed through register ? */
break;
+ slot += slots;
}
a->args_in_regs = i;
- a->regs_for_args = nregs;
+ a->regs_for_args = slot;
a->ostack_for_args = 0;
a->bstack_for_args = 0;
/* the rest arguments are passed through stack */
- for (; i < m->nr_args; i++) {
- stack_slots = (m->arg_size[i] + 7) / 8;
- a->bstack_for_args += stack_slots * 8;
- a->ostack_for_args = a->ostack_for_args + stack_slots * 8;
- }
+ for (; i < m->nr_args; i++)
+ a->bstack_for_args += ((m->arg_size[i] + 7) / 8) * 8;
+
+ /* the outgoing area reaches the last slot, over any alignment hole */
+ if (a->bstack_for_args)
+ a->ostack_for_args = a64_arg_stack_off(a->pos_of_slot[total - 1]) + 8;
return 0;
}
@@ -2599,7 +2676,7 @@ static void save_args(struct jit_ctx *ctx, int bargs_off, int oargs_off,
{
u8 tmp = bpf2a64[TMP_REG_1];
u8 base_lo = bpf2a64[TMP_REG_2];
- int i, reg, doff, soff, slots;
+ int i, reg, slot, soff, slots;
/* only the low 32 bits of the base take part in the subtraction */
if (arena_base)
@@ -2608,12 +2685,13 @@ static void save_args(struct jit_ctx *ctx, int bargs_off, int oargs_off,
/* store arguments to the stack for the bpf program, or restore
* arguments from stack for the original function
*/
- for (i = 0, reg = 0; i < a->args_in_regs; i++) {
+ for (i = 0, slot = 0; i < a->args_in_regs; i++) {
bool arena_arg = arena_base && (m->arg_flags[i] & BTF_FMODEL_ARENA_ARG);
bool nullable = m->arg_flags[i] & BTF_FMODEL_NULLABLE_ARG;
slots = (m->arg_size[i] + 7) / 8;
while (slots-- > 0) {
+ reg = a->pos_of_slot[slot++];
if (for_call_origin) {
emit(A64_LDR64I(reg, A64_SP, bargs_off), ctx);
} else if (arena_arg) {
@@ -2622,7 +2700,6 @@ static void save_args(struct jit_ctx *ctx, int bargs_off, int oargs_off,
} else {
emit(A64_STR64I(reg, A64_SP, bargs_off), ctx);
}
- reg++;
bargs_off += 8;
}
}
@@ -2634,9 +2711,11 @@ static void save_args(struct jit_ctx *ctx, int bargs_off, int oargs_off,
* (FP/LR) frames, so the arguments start at FP + 32. A struct_ops
* callback is called indirectly and only the FP/LR frame is saved, so
* they start at FP + 16.
+ *
+ * The outgoing area mirrors the incoming one, hole and all; only the
+ * bpf program takes the arguments packed.
*/
soff = is_struct_ops ? 16 : 32;
- doff = (for_call_origin ? oargs_off : bargs_off);
/* save on stack arguments */
for (i = a->args_in_regs; i < m->nr_args; i++) {
@@ -2646,7 +2725,9 @@ static void save_args(struct jit_ctx *ctx, int bargs_off, int oargs_off,
slots = (m->arg_size[i] + 7) / 8;
/* verifier ensures arg_size <= 16, so slots equals 1 or 2 */
while (slots-- > 0) {
- emit(A64_LDR64I(tmp, A64_FP, soff), ctx);
+ int off = a64_arg_stack_off(a->pos_of_slot[slot++]);
+
+ emit(A64_LDR64I(tmp, A64_FP, soff + off), ctx);
/* if there is unused space in the last slot, clear
* the garbage contained in the space.
*/
@@ -2661,19 +2742,21 @@ static void save_args(struct jit_ctx *ctx, int bargs_off, int oargs_off,
*/
if (arena_arg)
emit_arena_arg_conv(ctx, tmp, tmp, nullable, base_lo);
- emit(A64_STR64I(tmp, A64_SP, doff), ctx);
- soff += 8;
- doff += 8;
+ if (for_call_origin)
+ emit(A64_STR64I(tmp, A64_SP, oargs_off + off), ctx);
+ else
+ emit(A64_STR64I(tmp, A64_SP, bargs_off), ctx);
+ bargs_off += 8;
}
}
}
-static void restore_args(struct jit_ctx *ctx, int bargs_off, int nregs)
+static void restore_args(struct jit_ctx *ctx, int bargs_off, const struct arg_aux *a)
{
- int reg;
+ int slot;
- for (reg = 0; reg < nregs; reg++) {
- emit(A64_LDR64I(reg, A64_SP, bargs_off), ctx);
+ for (slot = 0; slot < a->regs_for_args; slot++) {
+ emit(A64_LDR64I(a->pos_of_slot[slot], A64_SP, bargs_off), ctx);
bargs_off += 8;
}
}
@@ -2945,7 +3028,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
}
if (flags & BPF_TRAMP_F_RESTORE_REGS)
- restore_args(ctx, bargs_off, a->regs_for_args);
+ restore_args(ctx, bargs_off, a);
/* restore callee saved register x19 and x20 */
emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx);
diff --git a/arch/riscv/net/bpf_jit.h b/arch/riscv/net/bpf_jit.h
index 419b9d795f2a..5c4f53060c91 100644
--- a/arch/riscv/net/bpf_jit.h
+++ b/arch/riscv/net/bpf_jit.h
@@ -12,6 +12,9 @@
#include <linux/bpf.h>
#include <linux/filter.h>
+#define RV_MAX_REG_ARGS 8
+#define RV_EXTRA_STK_ARGS (RV_MAX_REG_ARGS - MAX_BPF_FUNC_REG_ARGS)
+
/* verify runtime detection extension status */
#define rv_ext_enabled(ext) \
(IS_ENABLED(CONFIG_RISCV_ISA_##ext) && riscv_has_extension_likely(RISCV_ISA_EXT_##ext))
@@ -82,6 +85,7 @@ struct rv_jit_context {
unsigned long flags;
int stack_size;
int tcc_offset;
+ int stack_arg_sz;
u64 arena_vm_start;
u64 user_vm_start;
};
diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index 151031e97a24..ed0a6f871dea 100644
--- a/arch/riscv/net/bpf_jit_comp64.c
+++ b/arch/riscv/net/bpf_jit_comp64.c
@@ -15,7 +15,6 @@
#include <asm/percpu.h>
#include "bpf_jit.h"
-#define RV_MAX_REG_ARGS 8
#define RV_FENTRY_NINSNS 2
#define RV_FENTRY_NBYTES (RV_FENTRY_NINSNS * 4)
/* imm that allows emit_imm to emit max count insns */
@@ -23,11 +22,11 @@
/* fentry and TCC init insns will be skipped on tailcall */
#define RV_TAILCALL_OFFSET ((RV_FENTRY_NINSNS + 1) * 4)
-#define RV_REG_TCC RV_REG_A6
+#define RV_REG_TCC RV_REG_T5
#define RV_REG_ARENA RV_REG_S7 /* For storing arena_vm_start */
static const int regmap[] = {
- [BPF_REG_0] = RV_REG_A5,
+ [BPF_REG_0] = RV_REG_T6,
[BPF_REG_1] = RV_REG_A0,
[BPF_REG_2] = RV_REG_A1,
[BPF_REG_3] = RV_REG_A2,
@@ -47,13 +46,13 @@ static const int pt_regmap[] = {
[RV_REG_A2] = offsetof(struct pt_regs, a2),
[RV_REG_A3] = offsetof(struct pt_regs, a3),
[RV_REG_A4] = offsetof(struct pt_regs, a4),
- [RV_REG_A5] = offsetof(struct pt_regs, a5),
[RV_REG_S1] = offsetof(struct pt_regs, s1),
[RV_REG_S2] = offsetof(struct pt_regs, s2),
[RV_REG_S3] = offsetof(struct pt_regs, s3),
[RV_REG_S4] = offsetof(struct pt_regs, s4),
[RV_REG_S5] = offsetof(struct pt_regs, s5),
[RV_REG_T0] = offsetof(struct pt_regs, t0),
+ [RV_REG_T6] = offsetof(struct pt_regs, t6),
};
enum {
@@ -239,7 +238,7 @@ static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx)
emit_addi(RV_REG_SP, RV_REG_SP, stack_adjust, ctx);
/* Set return value. */
if (!is_tail_call)
- emit_addiw(RV_REG_A0, RV_REG_A5, 0, ctx);
+ emit_addiw(RV_REG_A0, regmap[BPF_REG_0], 0, ctx);
emit_jalr(RV_REG_ZERO, is_tail_call ? RV_REG_T3 : RV_REG_RA,
is_tail_call ? RV_TAILCALL_OFFSET : 0, ctx);
}
@@ -498,6 +497,18 @@ static void emit_ldx(u8 rd, s16 off, u8 rs, u8 size, bool sign_ext,
ctx->ex_jmp_off = ctx->ninsns;
}
+static void emit_stack_arg_ldx(u8 rd, s16 off, struct rv_jit_context *ctx)
+{
+ int idx = off / 8 - 1;
+
+ if (idx < RV_EXTRA_STK_ARGS) {
+ emit_mv(rd, RV_REG_A5 + idx, ctx);
+ return;
+ }
+
+ emit_ldx_insn(rd, (idx - RV_EXTRA_STK_ARGS) * 8, RV_REG_FP, BPF_DW, false, ctx);
+}
+
static void emit_st(u8 rd, s16 off, s32 imm, u8 size, struct rv_jit_context *ctx)
{
emit_imm(RV_REG_T1, imm, ctx);
@@ -515,6 +526,19 @@ static void emit_st(u8 rd, s16 off, s32 imm, u8 size, struct rv_jit_context *ctx
ctx->ex_jmp_off = ctx->ninsns;
}
+static void emit_stack_arg_st(s16 off, s32 imm, struct rv_jit_context *ctx)
+{
+ int idx = -off / 8 - 1;
+
+ if (idx < RV_EXTRA_STK_ARGS) {
+ emit_imm(RV_REG_A5 + idx, imm, ctx);
+ return;
+ }
+
+ emit_imm(RV_REG_T1, imm, ctx);
+ emit_stx_insn(RV_REG_SP, (idx - RV_EXTRA_STK_ARGS) * 8, RV_REG_T1, BPF_DW, ctx);
+}
+
static void emit_stx(u8 rd, s16 off, u8 rs, u8 size, struct rv_jit_context *ctx)
{
if (is_12b_int(off)) {
@@ -531,6 +555,18 @@ static void emit_stx(u8 rd, s16 off, u8 rs, u8 size, struct rv_jit_context *ctx)
ctx->ex_jmp_off = ctx->ninsns;
}
+static void emit_stack_arg_stx(s16 off, u8 rs, struct rv_jit_context *ctx)
+{
+ int idx = -off / 8 - 1;
+
+ if (idx < RV_EXTRA_STK_ARGS) {
+ emit_mv(RV_REG_A5 + idx, rs, ctx);
+ return;
+ }
+
+ emit_stx_insn(RV_REG_SP, (idx - RV_EXTRA_STK_ARGS) * 8, rs, BPF_DW, ctx);
+}
+
static int emit_atomic_ld_st(u8 rd, u8 rs, const struct bpf_insn *insn,
struct rv_jit_context *ctx)
{
@@ -747,6 +783,8 @@ static int add_exception_handler(const struct bpf_insn *insn, int dst_reg,
if (BPF_MODE(insn->code) != BPF_PROBE_MEM &&
BPF_MODE(insn->code) != BPF_PROBE_MEMSX &&
BPF_MODE(insn->code) != BPF_PROBE_MEM32 &&
+ !(BPF_MODE(insn->code) == BPF_PROBE_MEM32SX &&
+ BPF_CLASS(insn->code) == BPF_LDX) &&
BPF_MODE(insn->code) != BPF_PROBE_ATOMIC)
return 0;
@@ -1073,7 +1111,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET);
if (save_ret)
- stack_size += 16; /* Save both A5 (BPF R0) and A0 */
+ stack_size += 16; /* Save both RV_REG_T6 (BPF R0) and RV_REG_A0 */
retval_off = stack_size;
stack_size += nr_arg_slots * 8;
@@ -1822,11 +1860,21 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
return -EINVAL;
for (idx = 0; idx < fm->nr_args; idx++) {
- u8 reg = bpf_to_rv_reg(BPF_REG_1 + idx, ctx);
bool sign = fm->arg_flags[idx] & BTF_FMODEL_SIGNED_ARG;
-
- if (sign_extend(reg, reg, fm->arg_size[idx], sign, ctx))
- return -EINVAL;
+ u8 arg_sz = fm->arg_size[idx];
+
+ if (arg_sz == 8 || (arg_sz != 4 && !sign))
+ continue;
+
+ if (idx < RV_MAX_REG_ARGS) {
+ if (sign_extend(RV_REG_A0 + idx, RV_REG_A0 + idx, arg_sz, sign, ctx))
+ return -EINVAL;
+ } else {
+ emit_ld(RV_REG_T1, (idx - RV_MAX_REG_ARGS) * 8, RV_REG_SP, ctx);
+ if (sign_extend(RV_REG_T1, RV_REG_T1, arg_sz, sign, ctx))
+ return -EINVAL;
+ emit_sd(RV_REG_SP, (idx - RV_MAX_REG_ARGS) * 8, RV_REG_T1, ctx);
+ }
}
}
@@ -1908,18 +1956,27 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
case BPF_LDX | BPF_PROBE_MEM32 | BPF_H:
case BPF_LDX | BPF_PROBE_MEM32 | BPF_W:
case BPF_LDX | BPF_PROBE_MEM32 | BPF_DW:
+ /* LDX | PROBE_MEM32SX: dst = *(signed size *)(src + RV_REG_ARENA + off) */
+ case BPF_LDX | BPF_PROBE_MEM32SX | BPF_B:
+ case BPF_LDX | BPF_PROBE_MEM32SX | BPF_H:
+ case BPF_LDX | BPF_PROBE_MEM32SX | BPF_W:
{
bool sign_ext;
sign_ext = BPF_MODE(insn->code) == BPF_MEMSX ||
- BPF_MODE(insn->code) == BPF_PROBE_MEMSX;
+ BPF_MODE(insn->code) == BPF_PROBE_MEMSX ||
+ BPF_MODE(insn->code) == BPF_PROBE_MEM32SX;
- if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) {
+ if (BPF_MODE(insn->code) == BPF_PROBE_MEM32 ||
+ BPF_MODE(insn->code) == BPF_PROBE_MEM32SX) {
emit_add(RV_REG_T2, rs, RV_REG_ARENA, ctx);
rs = RV_REG_T2;
}
- emit_ldx(rd, off, rs, BPF_SIZE(code), sign_ext, ctx);
+ if (is_stack_arg_ldx(insn))
+ emit_stack_arg_ldx(rd, off, ctx);
+ else
+ emit_ldx(rd, off, rs, BPF_SIZE(code), sign_ext, ctx);
ret = add_exception_handler(insn, rd, ctx);
if (ret)
@@ -1949,7 +2006,10 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
rd = RV_REG_T3;
}
- emit_st(rd, off, imm, BPF_SIZE(code), ctx);
+ if (is_stack_arg_st(insn))
+ emit_stack_arg_st(off, imm, ctx);
+ else
+ emit_st(rd, off, imm, BPF_SIZE(code), ctx);
ret = add_exception_handler(insn, REG_DONT_CLEAR_MARKER, ctx);
if (ret)
@@ -1971,7 +2031,10 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
rd = RV_REG_T2;
}
- emit_stx(rd, off, rs, BPF_SIZE(code), ctx);
+ if (is_stack_arg_stx(insn))
+ emit_stack_arg_stx(off, rs, ctx);
+ else
+ emit_stx(rd, off, rs, BPF_SIZE(code), ctx);
ret = add_exception_handler(insn, REG_DONT_CLEAR_MARKER, ctx);
if (ret)
@@ -2021,9 +2084,9 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
{
- int i, stack_adjust = 0, store_offset, bpf_stack_adjust;
+ int i, stack_adjust = 0, store_offset, bpf_stack_adjust = ctx->stack_arg_sz;
- bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, STACK_ALIGN);
+ bpf_stack_adjust += round_up(ctx->prog->aux->stack_depth, STACK_ALIGN);
if (bpf_stack_adjust)
mark_fp(ctx);
@@ -2121,6 +2184,11 @@ bool bpf_jit_supports_kfunc_call(void)
return true;
}
+bool bpf_jit_supports_kfunc_ret_reg_pair(void)
+{
+ return true;
+}
+
bool bpf_jit_supports_ptr_xchg(void)
{
return true;
@@ -2148,10 +2216,6 @@ bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)
if (insn->imm == BPF_CMPXCHG)
return rv_ext_enabled(ZACAS);
break;
- case BPF_LDX | BPF_MEMSX | BPF_B:
- case BPF_LDX | BPF_MEMSX | BPF_H:
- case BPF_LDX | BPF_MEMSX | BPF_W:
- return false;
}
}
@@ -2189,3 +2253,8 @@ bool bpf_jit_supports_timed_may_goto(void)
{
return true;
}
+
+bool bpf_jit_supports_stack_args(void)
+{
+ return true;
+}
diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c
index cbfcd287ea16..2fb0b4e198b9 100644
--- a/arch/riscv/net/bpf_jit_core.c
+++ b/arch/riscv/net/bpf_jit_core.c
@@ -48,6 +48,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
int pass = 0, prev_ninsns = 0, i;
struct rv_jit_data *jit_data;
struct rv_jit_context *ctx;
+ u16 stack_arg_cnt;
if (!prog->jit_requested)
return prog;
@@ -71,6 +72,12 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
ctx->arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena);
ctx->user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena);
+
+ stack_arg_cnt = bpf_out_stack_arg_cnt(env, prog);
+ /* First 3 stack args in regs, rest on stack */
+ ctx->stack_arg_sz = stack_arg_cnt > RV_EXTRA_STK_ARGS ?
+ round_up((stack_arg_cnt - RV_EXTRA_STK_ARGS) * 8, STACK_ALIGN) : 0;
+
ctx->prog = prog;
ctx->offset = kvzalloc_objs(int, prog->len);
if (!ctx->offset)
diff --git a/arch/riscv/net/bpf_timed_may_goto.S b/arch/riscv/net/bpf_timed_may_goto.S
index 02c637d87420..fcf850f244c9 100644
--- a/arch/riscv/net/bpf_timed_may_goto.S
+++ b/arch/riscv/net/bpf_timed_may_goto.S
@@ -10,7 +10,7 @@
* - output: updated count in BPF_REG_AX (t0)
*
* Calls bpf_check_timed_may_goto(ptr) with the standard RISC-V ABI, where
- * ptr = BPF_REG_FP (s5) + BPF_REG_AX (t0). BPF R0-R5 (a5, a0-a4) are saved
+ * ptr = BPF_REG_FP (s5) + BPF_REG_AX (t0). BPF R0-R5 (t6, a0-a4) are saved
* across the call; BPF_REG_FP (s5) is callee-saved and needs no saving.
*/
@@ -20,8 +20,8 @@ SYM_FUNC_START(arch_bpf_timed_may_goto)
REG_S s0, 6*SZREG(sp)
addi s0, sp, 8*SZREG
- /* Save BPF registers R0-R5 (a5, a0-a4) */
- REG_S a5, 5*SZREG(sp)
+ /* Save BPF registers R0-R5 (t6, a0-a4) */
+ REG_S t6, 5*SZREG(sp)
REG_S a0, 4*SZREG(sp)
REG_S a1, 3*SZREG(sp)
REG_S a2, 2*SZREG(sp)
@@ -38,7 +38,7 @@ SYM_FUNC_START(arch_bpf_timed_may_goto)
REG_L a2, 2*SZREG(sp)
REG_L a1, 3*SZREG(sp)
REG_L a0, 4*SZREG(sp)
- REG_L a5, 5*SZREG(sp)
+ REG_L t6, 5*SZREG(sp)
REG_L s0, 6*SZREG(sp)
REG_L ra, 7*SZREG(sp)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 6e5e462ec059..8ae8a3544f01 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -234,6 +234,7 @@ config X86
select HAVE_SAMPLE_FTRACE_DIRECT if X86_64
select HAVE_SAMPLE_FTRACE_DIRECT_MULTI if X86_64
select HAVE_EBPF_JIT
+ select HAVE_EBPF_JIT_KASAN if X86_64
select HAVE_EFFICIENT_UNALIGNED_ACCESS
select HAVE_EISA if X86_32
select HAVE_EXIT_THREAD
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 2853e87797a7..d4a980140b48 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -9,6 +9,7 @@
#include <linux/filter.h>
#include <linux/if_vlan.h>
#include <linux/bitfield.h>
+#include <linux/init.h>
#include <linux/bpf.h>
#include <linux/bpf_verifier.h>
#include <linux/memory.h>
@@ -22,8 +23,34 @@
#include <asm/unwind.h>
#include <asm/cfi.h>
+#if IS_ENABLED(CONFIG_BPF_JIT_KASAN)
+void __asan_load1(void *p);
+void __asan_store1(void *p);
+void __asan_load2(void *p);
+void __asan_store2(void *p);
+void __asan_load4(void *p);
+void __asan_store4(void *p);
+void __asan_load8(void *p);
+void __asan_store8(void *p);
+#endif
+
static bool all_callee_regs_used[4] = {true, true, true, true};
+static void *trampoline_size_image;
+
+static int __init init_trampoline_size_image(void)
+{
+ /*
+ * The generated trampoline contains calls and jumps with 32bit relative
+ * offsets, so the scratch image must be in the execmem range.
+ * On x86, module data and executable memory share the same address range,
+ * so using EXECMEM_MODULE_DATA to get writable memory.
+ */
+ trampoline_size_image = execmem_alloc(EXECMEM_MODULE_DATA, PAGE_SIZE);
+ return trampoline_size_image ? 0 : -ENOMEM;
+}
+late_initcall(init_trampoline_size_image);
+
static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
{
if (len == 1)
@@ -1111,6 +1138,93 @@ static void maybe_emit_1mod(u8 **pprog, u32 reg, bool is64)
*pprog = prog;
}
+static int emit_kasan_check(struct bpf_verifier_env *env, u8 **pprog,
+ u32 addr_reg, s32 off, u32 bpf_size, u8 *ip,
+ bool is_write)
+{
+#ifdef CONFIG_BPF_JIT_KASAN
+ u8 *prog = *pprog;
+ void *kasan_func;
+
+ if (!env)
+ return 0;
+
+ if (addr_reg == BPF_REG_FP || addr_reg == BPF_REG_PARAMS)
+ return 0;
+
+ /* Derive KASAN check function from access type and size */
+ switch (bpf_size) {
+ case BPF_B:
+ kasan_func = is_write ? __asan_store1 : __asan_load1;
+ break;
+ case BPF_H:
+ kasan_func = is_write ? __asan_store2 : __asan_load2;
+ break;
+ case BPF_W:
+ kasan_func = is_write ? __asan_store4 : __asan_load4;
+ break;
+ case BPF_DW:
+ kasan_func = is_write ? __asan_store8 : __asan_load8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Save rax */
+ EMIT1(0x50);
+ /* Save rcx */
+ EMIT1(0x51);
+ /* Save rdx */
+ EMIT1(0x52);
+ /* Save rsi */
+ EMIT1(0x56);
+ /* Save rdi */
+ EMIT1(0x57);
+ /* Save r8 */
+ EMIT2(0x41, 0x50);
+ /* Save r9 */
+ EMIT2(0x41, 0x51);
+ /* Save r10 */
+ EMIT2(0x41, 0x52);
+ /* Save r11 */
+ EMIT2(0x41, 0x53);
+ /* mov rdi, addr_reg */
+ EMIT_mov(BPF_REG_1, addr_reg);
+
+ /* add rdi, off (if offset is non-zero) */
+ if (off) {
+ if (is_imm8(off)) {
+ /* add rdi, imm8 */
+ EMIT4(0x48, 0x83, 0xC7, (u8)off);
+ } else {
+ /* add rdi, imm32 */
+ EMIT3_off32(0x48, 0x81, 0xC7, off);
+ }
+ }
+
+ /* Adjust ip to account for the instrumentation generated so far */
+ ip += (prog - *pprog);
+ /* We emit a call, so update call depth counting */
+ ip += x86_call_depth_emit_accounting(&prog, kasan_func, ip);
+ /* call kasan_func */
+ if (emit_call(&prog, kasan_func, ip))
+ return -ERANGE;
+
+ EMIT2(0x41, 0x5B);
+ EMIT2(0x41, 0x5A);
+ EMIT2(0x41, 0x59);
+ EMIT2(0x41, 0x58);
+ EMIT1(0x5F);
+ EMIT1(0x5E);
+ EMIT1(0x5A);
+ EMIT1(0x59);
+ EMIT1(0x58);
+
+ *pprog = prog;
+#endif /* CONFIG_BPF_JIT_KASAN */
+ return 0;
+}
+
/* LDX: dst_reg = *(u8*)(src_reg + off) */
static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
{
@@ -1316,6 +1430,46 @@ static void emit_st_index(u8 **pprog, u32 size, u32 dst_reg, u32 index_reg, int
*pprog = prog;
}
+/* ST: *(u8*)(dst_reg + off) = imm */
+static void emit_st(u8 **pprog, struct bpf_insn *insn, u32 dst_reg,
+ s32 insn_off)
+{
+ s32 imm32 = insn->imm;
+ u8 *prog = *pprog;
+
+ switch (BPF_SIZE(insn->code)) {
+ case BPF_B:
+ if (is_ereg(dst_reg))
+ EMIT2(0x41, 0xC6);
+ else
+ EMIT1(0xC6);
+ break;
+ case BPF_H:
+ if (is_ereg(dst_reg))
+ EMIT3(0x66, 0x41, 0xC7);
+ else
+ EMIT2(0x66, 0xC7);
+ break;
+ case BPF_W:
+ if (is_ereg(dst_reg))
+ EMIT2(0x41, 0xC7);
+ else
+ EMIT1(0xC7);
+ break;
+ case BPF_DW:
+ EMIT2(add_1mod(0x48, dst_reg), 0xC7);
+ break;
+ }
+
+ if (is_imm8(insn_off))
+ EMIT2(add_1reg(0x40, dst_reg), insn_off);
+ else
+ EMIT1_off32(add_1reg(0x80, dst_reg), insn_off);
+
+ EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn->code)));
+ *pprog = prog;
+}
+
static void emit_st_r12(u8 **pprog, u32 size, u32 dst_reg, int off, int imm)
{
emit_st_index(pprog, size, dst_reg, X86_REG_R12, off, imm);
@@ -1424,17 +1578,35 @@ static int emit_atomic_rmw_index(u8 **pprog, u32 atomic_op, u32 size,
return 0;
}
-static int emit_atomic_ld_st(u8 **pprog, u32 atomic_op, u32 dst_reg,
- u32 src_reg, s16 off, u8 bpf_size)
+static int emit_atomic_ld_st(struct bpf_verifier_env *env, u8 **pprog,
+ struct bpf_insn *insn, u8 *ip, u32 dst_reg,
+ u32 src_reg, bool accesses_stack_only)
{
+ u32 atomic_op = insn->imm;
+ int err;
+
switch (atomic_op) {
case BPF_LOAD_ACQ:
+ if (!accesses_stack_only) {
+ err = emit_kasan_check(env, pprog, src_reg, insn->off,
+ BPF_SIZE(insn->code), ip, false);
+ if (err)
+ return err;
+ }
/* dst_reg = smp_load_acquire(src_reg + off16) */
- emit_ldx(pprog, bpf_size, dst_reg, src_reg, off);
+ emit_ldx(pprog, BPF_SIZE(insn->code), dst_reg, src_reg,
+ insn->off);
break;
case BPF_STORE_REL:
+ if (!accesses_stack_only) {
+ err = emit_kasan_check(env, pprog, dst_reg, insn->off,
+ BPF_SIZE(insn->code), ip, true);
+ if (err)
+ return err;
+ }
/* smp_store_release(dst_reg + off16, src_reg) */
- emit_stx(pprog, bpf_size, dst_reg, src_reg, off);
+ emit_stx(pprog, BPF_SIZE(insn->code), dst_reg, src_reg,
+ insn->off);
break;
default:
pr_err("bpf_jit: unknown atomic load/store opcode %02x\n",
@@ -1683,6 +1855,65 @@ static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip,
return 0;
}
+static const struct bpf_jit_arg_abi x86_arg_abi = {
+ .nr_arg_regs = 6,
+ .backfill_after_stack = true,
+ .even_stack_align = true,
+};
+
+static const u8 x86_arg_reg[] = {
+ BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5, X86_REG_R9,
+};
+
+/*
+ * Move the arguments the x86-64 ABI places somewhere other than the argument
+ * slot the BPF calling convention gave them. @stack_base addresses the
+ * outgoing stack argument area from RBP. Return the number of emitted bytes.
+ */
+static int emit_kfunc_arg_moves(const struct btf_func_model *fm, s32 stack_base, u8 **pprog)
+{
+ struct bpf_jit_arg_move moves[BPF_JIT_MAX_ARG_MOVES];
+ const u8 nreg = x86_arg_abi.nr_arg_regs;
+ u8 *prog = *pprog, *start = prog;
+ u32 i, n;
+
+ n = bpf_jit_plan_arg_moves(&x86_arg_abi, fm, moves);
+
+ for (i = 0; i < n; i++) {
+ u8 dst = moves[i].dst, src = moves[i].src, reg;
+ bool dst_mem = dst != BPF_JIT_ARG_TMP && dst >= nreg;
+ bool src_mem = src != BPF_JIT_ARG_TMP && src >= nreg;
+
+ /*
+ * Take the value into a register: the one it belongs in, the
+ * scratch when it is carried past its own destination, and
+ * BPF_REG_AX only to pass one stack slot to another.
+ */
+ if (src == BPF_JIT_ARG_TMP) {
+ reg = AUX_REG;
+ } else if (src_mem) {
+ reg = dst == BPF_JIT_ARG_TMP ? AUX_REG :
+ dst_mem ? BPF_REG_AX : x86_arg_reg[dst];
+ emit_ldx(&prog, BPF_DW, reg, BPF_REG_FP,
+ stack_base + (src - nreg) * 8);
+ } else {
+ reg = x86_arg_reg[src];
+ }
+
+ /* And leave it where the argument belongs. */
+ if (dst == BPF_JIT_ARG_TMP)
+ emit_mov_reg(&prog, true, AUX_REG, reg);
+ else if (dst_mem)
+ emit_stx(&prog, BPF_DW, BPF_REG_FP, reg,
+ stack_base + (dst - nreg) * 8);
+ else if (reg != x86_arg_reg[dst])
+ emit_mov_reg(&prog, true, x86_arg_reg[dst], reg);
+ }
+
+ *pprog = prog;
+ return prog - start;
+}
+
/*
* Rebase the __arena args of a kfunc call to arena kernel addresses,
* rN = kern_vm_start + (u32)rN, with R12 holding kern_vm_start. A nullable
@@ -1690,20 +1921,21 @@ static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip,
* arena NULL is offset 0. Return the number of emitted bytes.
*/
static int emit_kfunc_arena_args(struct bpf_prog *bpf_prog,
- const struct bpf_insn *insn, u8 **pprog)
+ const struct btf_func_model *fm, u8 **pprog)
{
- const struct btf_func_model *fm;
u8 *prog = *pprog;
u8 *start = prog;
- int i;
-
- fm = bpf_jit_find_kfunc_model(bpf_prog, insn);
- if (!fm)
- return -EINVAL;
+ int i, slot;
- for (i = 0; i < min_t(int, fm->nr_args, MAX_BPF_FUNC_REG_ARGS); i++) {
+ for (i = 0, slot = 0; i < fm->nr_args; i++) {
+ u32 arg_regs = (fm->arg_size[i] + 7) / 8;
u8 flags = fm->arg_flags[i];
- u32 reg = BPF_REG_1 + i;
+ u32 reg;
+
+ if (slot + arg_regs > MAX_BPF_FUNC_REG_ARGS)
+ break;
+ reg = BPF_REG_1 + slot;
+ slot += arg_regs;
if (!(flags & BTF_FMODEL_ARENA_ARG))
continue;
@@ -1860,10 +2092,12 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
const s32 imm32 = insn->imm;
u32 dst_reg = insn->dst_reg;
u32 src_reg = insn->src_reg;
+ bool accesses_stack_only;
u8 b2 = 0, b3 = 0;
u8 *start_of_ldx;
s64 jmp_offset;
s32 insn_off;
+ int insn_idx;
u8 jmp_cond;
u8 *func;
int nops;
@@ -1880,6 +2114,10 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
EMIT_ENDBR();
ip = image + addrs[i - 1] + (prog - temp);
+ insn_idx = i - 1 + bpf_prog->aux->subprog_start;
+ accesses_stack_only =
+ env ? !env->insn_aux_data[insn_idx].non_stack_access :
+ false;
switch (insn->code) {
/* ALU */
@@ -2256,49 +2494,37 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
EMIT_LFENCE();
break;
- /* ST: *(u8*)(dst_reg + off) = imm */
case BPF_ST | BPF_MEM | BPF_B:
- if (is_ereg(dst_reg))
- EMIT2(0x41, 0xC6);
- else
- EMIT1(0xC6);
- goto st;
case BPF_ST | BPF_MEM | BPF_H:
- if (is_ereg(dst_reg))
- EMIT3(0x66, 0x41, 0xC7);
- else
- EMIT2(0x66, 0xC7);
- goto st;
case BPF_ST | BPF_MEM | BPF_W:
- if (is_ereg(dst_reg))
- EMIT2(0x41, 0xC7);
- else
- EMIT1(0xC7);
- goto st;
case BPF_ST | BPF_MEM | BPF_DW:
if (dst_reg == BPF_REG_PARAMS && insn->off == -8) {
/* Arg 6: store immediate in r9 register */
- emit_mov_imm64(&prog, X86_REG_R9, imm32 >> 31, (u32)imm32);
+ emit_mov_imm64(&prog, X86_REG_R9, imm32 >> 31,
+ imm32);
break;
}
- EMIT2(add_1mod(0x48, dst_reg), 0xC7);
-
-st: insn_off = insn->off;
+ insn_off = insn->off;
if (dst_reg == BPF_REG_PARAMS) {
/*
* Args 7+: reverse BPF negative offsets to
* x86 positive rsp offsets.
* BPF off=-16 → [rsp+0], off=-24 → [rsp+8], ...
*/
- insn_off = outgoing_arg_base - outgoing_rsp - insn_off - 16;
+ insn_off = outgoing_arg_base - outgoing_rsp -
+ insn_off - 16;
dst_reg = BPF_REG_FP;
}
- if (is_imm8(insn_off))
- EMIT2(add_1reg(0x40, dst_reg), insn_off);
- else
- EMIT1_off32(add_1reg(0x80, dst_reg), insn_off);
+ if (!accesses_stack_only) {
+ err = emit_kasan_check(env, &prog, dst_reg,
+ insn_off,
+ BPF_SIZE(insn->code), ip,
+ true);
+ if (err)
+ return err;
+ }
- EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn->code)));
+ emit_st(&prog, insn, dst_reg, insn_off);
break;
/* STX: *(u8*)(dst_reg + off) = src_reg */
@@ -2316,6 +2542,14 @@ st: insn_off = insn->off;
insn_off = outgoing_arg_base - outgoing_rsp - insn_off - 16;
dst_reg = BPF_REG_FP;
}
+ if (!accesses_stack_only) {
+ err = emit_kasan_check(env, &prog, dst_reg,
+ insn_off,
+ BPF_SIZE(insn->code), ip,
+ true);
+ if (err)
+ return err;
+ }
emit_stx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn_off);
break;
@@ -2497,6 +2731,13 @@ populate_extable:
/* populate jmp_offset for JAE above to jump to start_of_ldx */
start_of_ldx = prog;
end_of_jmp[-1] = start_of_ldx - end_of_jmp;
+ } else if (!accesses_stack_only) {
+ err = emit_kasan_check(env, &prog, src_reg,
+ insn_off,
+ BPF_SIZE(insn->code), ip,
+ false);
+ if (err)
+ return err;
}
if (BPF_MODE(insn->code) == BPF_PROBE_MEMSX ||
BPF_MODE(insn->code) == BPF_MEMSX)
@@ -2558,28 +2799,42 @@ populate_extable:
}
fallthrough;
case BPF_STX | BPF_ATOMIC | BPF_W:
- case BPF_STX | BPF_ATOMIC | BPF_DW:
- if (insn->imm == (BPF_AND | BPF_FETCH) ||
- insn->imm == (BPF_OR | BPF_FETCH) ||
- insn->imm == (BPF_XOR | BPF_FETCH)) {
- bool is64 = BPF_SIZE(insn->code) == BPF_DW;
- u32 real_src_reg = src_reg;
- u32 real_dst_reg = dst_reg;
- u8 *branch_target;
-
+ case BPF_STX | BPF_ATOMIC | BPF_DW: {
+ bool is64 = BPF_SIZE(insn->code) == BPF_DW;
+ u32 real_src_reg = src_reg;
+ u32 real_dst_reg = dst_reg;
+ u8 *old_prog;
+ bool is_atomic_fetch =
+ (insn->imm == (BPF_AND | BPF_FETCH) ||
+ insn->imm == (BPF_OR | BPF_FETCH) ||
+ insn->imm == (BPF_XOR | BPF_FETCH));
+ if (is_atomic_fetch) {
/*
* Can't be implemented with a single x86 insn.
* Need to do a CMPXCHG loop.
*/
/* Will need RAX as a CMPXCHG operand so save R0 */
+ old_prog = prog;
emit_mov_reg(&prog, true, BPF_REG_AX, BPF_REG_0);
if (src_reg == BPF_REG_0)
real_src_reg = BPF_REG_AX;
if (dst_reg == BPF_REG_0)
real_dst_reg = BPF_REG_AX;
-
- branch_target = prog;
+ ip += (prog - old_prog);
+ }
+ if (!bpf_atomic_is_load_store(insn)) {
+ if (!accesses_stack_only) {
+ err = emit_kasan_check(
+ env, &prog, real_dst_reg,
+ insn->off, BPF_SIZE(insn->code),
+ ip, true);
+ if (err)
+ return err;
+ }
+ }
+ if (is_atomic_fetch) {
+ u8 *branch_target = prog;
/* Load old value */
emit_ldx(&prog, BPF_SIZE(insn->code),
BPF_REG_0, real_dst_reg, insn->off);
@@ -2611,15 +2866,16 @@ populate_extable:
}
if (bpf_atomic_is_load_store(insn))
- err = emit_atomic_ld_st(&prog, insn->imm, dst_reg, src_reg,
- insn->off, BPF_SIZE(insn->code));
+ err = emit_atomic_ld_st(env, &prog, insn, ip,
+ dst_reg, src_reg,
+ accesses_stack_only);
else
err = emit_atomic_rmw(&prog, insn->imm, dst_reg, src_reg,
insn->off, BPF_SIZE(insn->code));
if (err)
return err;
break;
-
+ }
case BPF_STX | BPF_PROBE_ATOMIC | BPF_B:
case BPF_STX | BPF_PROBE_ATOMIC | BPF_H:
if (!bpf_atomic_is_load_store(insn)) {
@@ -2645,6 +2901,8 @@ populate_extable:
/* call */
case BPF_JMP | BPF_CALL: {
+ const struct btf_func_model *fm = NULL;
+
func = (u8 *) __bpf_call_base + imm32;
if (src_reg == BPF_PSEUDO_CALL && tail_call_reachable) {
LOAD_TAIL_CALL_CNT_PTR(stack_depth);
@@ -2653,10 +2911,15 @@ populate_extable:
if (!imm32)
return -EINVAL;
if (src_reg == BPF_PSEUDO_KFUNC_CALL) {
- err = emit_kfunc_arena_args(bpf_prog, insn, &prog);
+ fm = bpf_jit_find_kfunc_model(bpf_prog, insn);
+ if (!fm)
+ return -EINVAL;
+ err = emit_kfunc_arena_args(bpf_prog, fm, &prog);
if (err < 0)
return err;
ip += err;
+ ip += emit_kfunc_arg_moves(fm, outgoing_arg_base -
+ outgoing_rsp, &prog);
}
if (priv_frame_ptr) {
push_r9(&prog);
@@ -2667,6 +2930,14 @@ populate_extable:
return -EINVAL;
if (priv_frame_ptr)
pop_r9(&prog);
+ /*
+ * A kfunc returning more than 8 bytes hands the second
+ * half back in RDX (the native ABI's second return reg),
+ * but BPF expects it in R0:R2. BPF R0 is RAX (no move
+ * needed), while BPF R2 is RSI, so copy RDX into RSI.
+ */
+ if (fm && fm->ret_size > 8)
+ emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_3);
break;
}
@@ -3812,24 +4083,14 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
struct bpf_tramp_nodes *tnodes, void *func_addr)
{
struct bpf_tramp_image im;
- void *image;
- int ret;
- /* Allocate a temporary buffer for __arch_prepare_bpf_trampoline().
- *
- * We cannot use kvmalloc here, because we need image to be in
- * module memory range.
- * Since it must be writable use execmem_alloc(EXECMEM_MODULE_DATA)
- * that returns writable memory in the module address space.
- */
- image = execmem_alloc(EXECMEM_MODULE_DATA, PAGE_SIZE);
- if (!image)
+ if (!trampoline_size_image)
return -ENOMEM;
- ret = __arch_prepare_bpf_trampoline(&im, image, image + PAGE_SIZE, image,
- m, flags, tnodes, func_addr);
- execmem_free(image);
- return ret;
+ return __arch_prepare_bpf_trampoline(&im, trampoline_size_image,
+ trampoline_size_image + PAGE_SIZE,
+ trampoline_size_image, m, flags,
+ tnodes, func_addr);
}
static int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs, u8 *image, u8 *buf)
@@ -4158,6 +4419,16 @@ bool bpf_jit_supports_kfunc_call(void)
return true;
}
+bool bpf_jit_supports_kfunc_ret_reg_pair(void)
+{
+ return true;
+}
+
+const struct bpf_jit_arg_abi *bpf_jit_arg_abi(void)
+{
+ return &x86_arg_abi;
+}
+
bool bpf_jit_supports_stack_args(void)
{
return true;