diff options
Diffstat (limited to 'arch/arm64/net')
-rw-r--r-- | arch/arm64/net/bpf_jit.h | 3 | ||||
-rw-r--r-- | arch/arm64/net/bpf_jit_comp.c | 105 |
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 */ |
38 | static const int bpf2a64[] = { | 40 | static 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 | ||
149 | static void build_prologue(struct jit_ctx *ctx) | 153 | #define PROLOGUE_OFFSET 8 |
154 | |||
155 | static 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 | |||
216 | static int out_offset = -1; /* initialized on the first pass of build_body() */ | ||
217 | static 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 | ||
200 | static void build_epilogue(struct jit_ctx *ctx) | 279 | static 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); |