aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64/net
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm64/net')
-rw-r--r--arch/arm64/net/bpf_jit.h3
-rw-r--r--arch/arm64/net/bpf_jit_comp.c105
2 files changed, 98 insertions, 10 deletions
diff --git a/arch/arm64/net/bpf_jit.h b/arch/arm64/net/bpf_jit.h
index aee5637ea436..7c16e547ccb2 100644
--- a/arch/arm64/net/bpf_jit.h
+++ b/arch/arm64/net/bpf_jit.h
@@ -1,7 +1,7 @@
1/* 1/*
2 * BPF JIT compiler for ARM64 2 * BPF JIT compiler for ARM64
3 * 3 *
4 * Copyright (C) 2014-2015 Zi Shen Lim <zlim.lnx@gmail.com> 4 * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com>
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as 7 * it under the terms of the GNU General Public License version 2 as
@@ -55,6 +55,7 @@
55#define A64_BL(imm26) A64_BRANCH((imm26) << 2, LINK) 55#define A64_BL(imm26) A64_BRANCH((imm26) << 2, LINK)
56 56
57/* Unconditional branch (register) */ 57/* Unconditional branch (register) */
58#define A64_BR(Rn) aarch64_insn_gen_branch_reg(Rn, AARCH64_INSN_BRANCH_NOLINK)
58#define A64_BLR(Rn) aarch64_insn_gen_branch_reg(Rn, AARCH64_INSN_BRANCH_LINK) 59#define A64_BLR(Rn) aarch64_insn_gen_branch_reg(Rn, AARCH64_INSN_BRANCH_LINK)
59#define A64_RET(Rn) aarch64_insn_gen_branch_reg(Rn, AARCH64_INSN_BRANCH_RETURN) 60#define A64_RET(Rn) aarch64_insn_gen_branch_reg(Rn, AARCH64_INSN_BRANCH_RETURN)
60 61
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index 49ba37e4bfc0..51abc979d6cb 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -18,6 +18,7 @@
18 18
19#define pr_fmt(fmt) "bpf_jit: " fmt 19#define pr_fmt(fmt) "bpf_jit: " fmt
20 20
21#include <linux/bpf.h>
21#include <linux/filter.h> 22#include <linux/filter.h>
22#include <linux/printk.h> 23#include <linux/printk.h>
23#include <linux/skbuff.h> 24#include <linux/skbuff.h>
@@ -33,6 +34,7 @@ int bpf_jit_enable __read_mostly;
33 34
34#define TMP_REG_1 (MAX_BPF_JIT_REG + 0) 35#define TMP_REG_1 (MAX_BPF_JIT_REG + 0)
35#define TMP_REG_2 (MAX_BPF_JIT_REG + 1) 36#define TMP_REG_2 (MAX_BPF_JIT_REG + 1)
37#define TCALL_CNT (MAX_BPF_JIT_REG + 2)
36 38
37/* Map BPF registers to A64 registers */ 39/* Map BPF registers to A64 registers */
38static const int bpf2a64[] = { 40static const int bpf2a64[] = {
@@ -54,6 +56,8 @@ static const int bpf2a64[] = {
54 /* temporary registers for internal BPF JIT */ 56 /* temporary registers for internal BPF JIT */
55 [TMP_REG_1] = A64_R(10), 57 [TMP_REG_1] = A64_R(10),
56 [TMP_REG_2] = A64_R(11), 58 [TMP_REG_2] = A64_R(11),
59 /* tail_call_cnt */
60 [TCALL_CNT] = A64_R(26),
57 /* temporary register for blinding constants */ 61 /* temporary register for blinding constants */
58 [BPF_REG_AX] = A64_R(9), 62 [BPF_REG_AX] = A64_R(9),
59}; 63};
@@ -146,13 +150,18 @@ static inline int epilogue_offset(const struct jit_ctx *ctx)
146 150
147#define STACK_SIZE STACK_ALIGN(_STACK_SIZE) 151#define STACK_SIZE STACK_ALIGN(_STACK_SIZE)
148 152
149static void build_prologue(struct jit_ctx *ctx) 153#define PROLOGUE_OFFSET 8
154
155static int build_prologue(struct jit_ctx *ctx)
150{ 156{
151 const u8 r6 = bpf2a64[BPF_REG_6]; 157 const u8 r6 = bpf2a64[BPF_REG_6];
152 const u8 r7 = bpf2a64[BPF_REG_7]; 158 const u8 r7 = bpf2a64[BPF_REG_7];
153 const u8 r8 = bpf2a64[BPF_REG_8]; 159 const u8 r8 = bpf2a64[BPF_REG_8];
154 const u8 r9 = bpf2a64[BPF_REG_9]; 160 const u8 r9 = bpf2a64[BPF_REG_9];
155 const u8 fp = bpf2a64[BPF_REG_FP]; 161 const u8 fp = bpf2a64[BPF_REG_FP];
162 const u8 tcc = bpf2a64[TCALL_CNT];
163 const int idx0 = ctx->idx;
164 int cur_offset;
156 165
157 /* 166 /*
158 * BPF prog stack layout 167 * BPF prog stack layout
@@ -162,8 +171,6 @@ static void build_prologue(struct jit_ctx *ctx)
162 * |FP/LR| 171 * |FP/LR|
163 * current A64_FP => -16:+-----+ 172 * current A64_FP => -16:+-----+
164 * | ... | callee saved registers 173 * | ... | callee saved registers
165 * +-----+
166 * | | x25/x26
167 * BPF fp register => -64:+-----+ <= (BPF_FP) 174 * BPF fp register => -64:+-----+ <= (BPF_FP)
168 * | | 175 * | |
169 * | ... | BPF prog stack 176 * | ... | BPF prog stack
@@ -183,18 +190,90 @@ static void build_prologue(struct jit_ctx *ctx)
183 emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 190 emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
184 emit(A64_MOV(1, A64_FP, A64_SP), ctx); 191 emit(A64_MOV(1, A64_FP, A64_SP), ctx);
185 192
186 /* Save callee-saved register */ 193 /* Save callee-saved registers */
187 emit(A64_PUSH(r6, r7, A64_SP), ctx); 194 emit(A64_PUSH(r6, r7, A64_SP), ctx);
188 emit(A64_PUSH(r8, r9, A64_SP), ctx); 195 emit(A64_PUSH(r8, r9, A64_SP), ctx);
196 emit(A64_PUSH(fp, tcc, A64_SP), ctx);
189 197
190 /* Save fp (x25) and x26. SP requires 16 bytes alignment */ 198 /* Set up BPF prog stack base register */
191 emit(A64_PUSH(fp, A64_R(26), A64_SP), ctx);
192
193 /* Set up BPF prog stack base register (x25) */
194 emit(A64_MOV(1, fp, A64_SP), ctx); 199 emit(A64_MOV(1, fp, A64_SP), ctx);
195 200
201 /* Initialize tail_call_cnt */
202 emit(A64_MOVZ(1, tcc, 0, 0), ctx);
203
196 /* Set up function call stack */ 204 /* Set up function call stack */
197 emit(A64_SUB_I(1, A64_SP, A64_SP, STACK_SIZE), ctx); 205 emit(A64_SUB_I(1, A64_SP, A64_SP, STACK_SIZE), ctx);
206
207 cur_offset = ctx->idx - idx0;
208 if (cur_offset != PROLOGUE_OFFSET) {
209 pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n",
210 cur_offset, PROLOGUE_OFFSET);
211 return -1;
212 }
213 return 0;
214}
215
216static int out_offset = -1; /* initialized on the first pass of build_body() */
217static int emit_bpf_tail_call(struct jit_ctx *ctx)
218{
219 /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */
220 const u8 r2 = bpf2a64[BPF_REG_2];
221 const u8 r3 = bpf2a64[BPF_REG_3];
222
223 const u8 tmp = bpf2a64[TMP_REG_1];
224 const u8 prg = bpf2a64[TMP_REG_2];
225 const u8 tcc = bpf2a64[TCALL_CNT];
226 const int idx0 = ctx->idx;
227#define cur_offset (ctx->idx - idx0)
228#define jmp_offset (out_offset - (cur_offset))
229 size_t off;
230
231 /* if (index >= array->map.max_entries)
232 * goto out;
233 */
234 off = offsetof(struct bpf_array, map.max_entries);
235 emit_a64_mov_i64(tmp, off, ctx);
236 emit(A64_LDR32(tmp, r2, tmp), ctx);
237 emit(A64_CMP(0, r3, tmp), ctx);
238 emit(A64_B_(A64_COND_GE, jmp_offset), ctx);
239
240 /* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
241 * goto out;
242 * tail_call_cnt++;
243 */
244 emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx);
245 emit(A64_CMP(1, tcc, tmp), ctx);
246 emit(A64_B_(A64_COND_GT, jmp_offset), ctx);
247 emit(A64_ADD_I(1, tcc, tcc, 1), ctx);
248
249 /* prog = array->ptrs[index];
250 * if (prog == NULL)
251 * goto out;
252 */
253 off = offsetof(struct bpf_array, ptrs);
254 emit_a64_mov_i64(tmp, off, ctx);
255 emit(A64_LDR64(tmp, r2, tmp), ctx);
256 emit(A64_LDR64(prg, tmp, r3), ctx);
257 emit(A64_CBZ(1, prg, jmp_offset), ctx);
258
259 /* goto *(prog->bpf_func + prologue_size); */
260 off = offsetof(struct bpf_prog, bpf_func);
261 emit_a64_mov_i64(tmp, off, ctx);
262 emit(A64_LDR64(tmp, prg, tmp), ctx);
263 emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx);
264 emit(A64_BR(tmp), ctx);
265
266 /* out: */
267 if (out_offset == -1)
268 out_offset = cur_offset;
269 if (cur_offset != out_offset) {
270 pr_err_once("tail_call out_offset = %d, expected %d!\n",
271 cur_offset, out_offset);
272 return -1;
273 }
274 return 0;
275#undef cur_offset
276#undef jmp_offset
198} 277}
199 278
200static void build_epilogue(struct jit_ctx *ctx) 279static void build_epilogue(struct jit_ctx *ctx)
@@ -506,6 +585,11 @@ emit_cond_jmp:
506 emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 585 emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx);
507 break; 586 break;
508 } 587 }
588 /* tail call */
589 case BPF_JMP | BPF_CALL | BPF_X:
590 if (emit_bpf_tail_call(ctx))
591 return -EFAULT;
592 break;
509 /* function return */ 593 /* function return */
510 case BPF_JMP | BPF_EXIT: 594 case BPF_JMP | BPF_EXIT:
511 /* Optimization: when last instruction is EXIT, 595 /* Optimization: when last instruction is EXIT,
@@ -780,7 +864,10 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
780 goto out_off; 864 goto out_off;
781 } 865 }
782 866
783 build_prologue(&ctx); 867 if (build_prologue(&ctx)) {
868 prog = orig_prog;
869 goto out_off;
870 }
784 871
785 ctx.epilogue_offset = ctx.idx; 872 ctx.epilogue_offset = ctx.idx;
786 build_epilogue(&ctx); 873 build_epilogue(&ctx);