1e54bcde3SZi Shen Lim /* 2e54bcde3SZi Shen Lim * BPF JIT compiler for ARM64 3e54bcde3SZi Shen Lim * 442ff712bSZi Shen Lim * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com> 5e54bcde3SZi Shen Lim * 6e54bcde3SZi Shen Lim * This program is free software; you can redistribute it and/or modify 7e54bcde3SZi Shen Lim * it under the terms of the GNU General Public License version 2 as 8e54bcde3SZi Shen Lim * published by the Free Software Foundation. 9e54bcde3SZi Shen Lim * 10e54bcde3SZi Shen Lim * This program is distributed in the hope that it will be useful, 11e54bcde3SZi Shen Lim * but WITHOUT ANY WARRANTY; without even the implied warranty of 12e54bcde3SZi Shen Lim * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13e54bcde3SZi Shen Lim * GNU General Public License for more details. 14e54bcde3SZi Shen Lim * 15e54bcde3SZi Shen Lim * You should have received a copy of the GNU General Public License 16e54bcde3SZi Shen Lim * along with this program. If not, see <http://www.gnu.org/licenses/>. 17e54bcde3SZi Shen Lim */ 18e54bcde3SZi Shen Lim 19e54bcde3SZi Shen Lim #define pr_fmt(fmt) "bpf_jit: " fmt 20e54bcde3SZi Shen Lim 21ddb55992SZi Shen Lim #include <linux/bpf.h> 22e54bcde3SZi Shen Lim #include <linux/filter.h> 23e54bcde3SZi Shen Lim #include <linux/printk.h> 24e54bcde3SZi Shen Lim #include <linux/slab.h> 25b569c1c6SDaniel Borkmann 26e54bcde3SZi Shen Lim #include <asm/byteorder.h> 27e54bcde3SZi Shen Lim #include <asm/cacheflush.h> 28b569c1c6SDaniel Borkmann #include <asm/debug-monitors.h> 29d4bbc30bSLaura Abbott #include <asm/set_memory.h> 30e54bcde3SZi Shen Lim 31e54bcde3SZi Shen Lim #include "bpf_jit.h" 32e54bcde3SZi Shen Lim 3326eb042eSDaniel Borkmann #define TMP_REG_1 (MAX_BPF_JIT_REG + 0) 3426eb042eSDaniel Borkmann #define TMP_REG_2 (MAX_BPF_JIT_REG + 1) 35ddb55992SZi Shen Lim #define TCALL_CNT (MAX_BPF_JIT_REG + 2) 367005cadeSDaniel Borkmann #define TMP_REG_3 (MAX_BPF_JIT_REG + 3) 37e54bcde3SZi Shen Lim 38e54bcde3SZi Shen Lim /* Map BPF registers to A64 registers */ 39e54bcde3SZi Shen Lim static const int bpf2a64[] = { 40e54bcde3SZi Shen Lim /* return value from in-kernel function, and exit value from eBPF */ 41e54bcde3SZi Shen Lim [BPF_REG_0] = A64_R(7), 42e54bcde3SZi Shen Lim /* arguments from eBPF program to in-kernel function */ 43e54bcde3SZi Shen Lim [BPF_REG_1] = A64_R(0), 44e54bcde3SZi Shen Lim [BPF_REG_2] = A64_R(1), 45e54bcde3SZi Shen Lim [BPF_REG_3] = A64_R(2), 46e54bcde3SZi Shen Lim [BPF_REG_4] = A64_R(3), 47e54bcde3SZi Shen Lim [BPF_REG_5] = A64_R(4), 48e54bcde3SZi Shen Lim /* callee saved registers that in-kernel function will preserve */ 49e54bcde3SZi Shen Lim [BPF_REG_6] = A64_R(19), 50e54bcde3SZi Shen Lim [BPF_REG_7] = A64_R(20), 51e54bcde3SZi Shen Lim [BPF_REG_8] = A64_R(21), 52e54bcde3SZi Shen Lim [BPF_REG_9] = A64_R(22), 53e54bcde3SZi Shen Lim /* read-only frame pointer to access stack */ 54ec0738dbSYang Shi [BPF_REG_FP] = A64_R(25), 554c1cd4fdSYang Shi /* temporary registers for internal BPF JIT */ 564c1cd4fdSYang Shi [TMP_REG_1] = A64_R(10), 574c1cd4fdSYang Shi [TMP_REG_2] = A64_R(11), 587005cadeSDaniel Borkmann [TMP_REG_3] = A64_R(12), 59ddb55992SZi Shen Lim /* tail_call_cnt */ 60ddb55992SZi Shen Lim [TCALL_CNT] = A64_R(26), 6126eb042eSDaniel Borkmann /* temporary register for blinding constants */ 6226eb042eSDaniel Borkmann [BPF_REG_AX] = A64_R(9), 63e54bcde3SZi Shen Lim }; 64e54bcde3SZi Shen Lim 65e54bcde3SZi Shen Lim struct jit_ctx { 66e54bcde3SZi Shen Lim const struct bpf_prog *prog; 67e54bcde3SZi Shen Lim int idx; 6851c9fbb1SZi Shen Lim int epilogue_offset; 69e54bcde3SZi Shen Lim int *offset; 70425e1ed7SLuc Van Oostenryck __le32 *image; 71f1c9eed7SDaniel Borkmann u32 stack_size; 72e54bcde3SZi Shen Lim }; 73e54bcde3SZi Shen Lim 74e54bcde3SZi Shen Lim static inline void emit(const u32 insn, struct jit_ctx *ctx) 75e54bcde3SZi Shen Lim { 76e54bcde3SZi Shen Lim if (ctx->image != NULL) 77e54bcde3SZi Shen Lim ctx->image[ctx->idx] = cpu_to_le32(insn); 78e54bcde3SZi Shen Lim 79e54bcde3SZi Shen Lim ctx->idx++; 80e54bcde3SZi Shen Lim } 81e54bcde3SZi Shen Lim 82e54bcde3SZi Shen Lim static inline void emit_a64_mov_i(const int is64, const int reg, 83e54bcde3SZi Shen Lim const s32 val, struct jit_ctx *ctx) 84e54bcde3SZi Shen Lim { 85e54bcde3SZi Shen Lim u16 hi = val >> 16; 86e54bcde3SZi Shen Lim u16 lo = val & 0xffff; 87e54bcde3SZi Shen Lim 88e54bcde3SZi Shen Lim if (hi & 0x8000) { 89e54bcde3SZi Shen Lim if (hi == 0xffff) { 90e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx); 91e54bcde3SZi Shen Lim } else { 92e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx); 936d2eea6fSDaniel Borkmann if (lo != 0xffff) 94e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, lo, 0), ctx); 95e54bcde3SZi Shen Lim } 96e54bcde3SZi Shen Lim } else { 97e54bcde3SZi Shen Lim emit(A64_MOVZ(is64, reg, lo, 0), ctx); 98e54bcde3SZi Shen Lim if (hi) 99e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, hi, 16), ctx); 100e54bcde3SZi Shen Lim } 101e54bcde3SZi Shen Lim } 102e54bcde3SZi Shen Lim 1036d2eea6fSDaniel Borkmann static int i64_i16_blocks(const u64 val, bool inverse) 1046d2eea6fSDaniel Borkmann { 1056d2eea6fSDaniel Borkmann return (((val >> 0) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1066d2eea6fSDaniel Borkmann (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1076d2eea6fSDaniel Borkmann (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1086d2eea6fSDaniel Borkmann (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000)); 1096d2eea6fSDaniel Borkmann } 1106d2eea6fSDaniel Borkmann 1116d2eea6fSDaniel Borkmann static inline void emit_a64_mov_i64(const int reg, const u64 val, 1126d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1136d2eea6fSDaniel Borkmann { 1146d2eea6fSDaniel Borkmann u64 nrm_tmp = val, rev_tmp = ~val; 1156d2eea6fSDaniel Borkmann bool inverse; 1166d2eea6fSDaniel Borkmann int shift; 1176d2eea6fSDaniel Borkmann 1186d2eea6fSDaniel Borkmann if (!(nrm_tmp >> 32)) 1196d2eea6fSDaniel Borkmann return emit_a64_mov_i(0, reg, (u32)val, ctx); 1206d2eea6fSDaniel Borkmann 1216d2eea6fSDaniel Borkmann inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false); 1226d2eea6fSDaniel Borkmann shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) : 1236d2eea6fSDaniel Borkmann (fls64(nrm_tmp) - 1)), 16), 0); 1246d2eea6fSDaniel Borkmann if (inverse) 1256d2eea6fSDaniel Borkmann emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx); 1266d2eea6fSDaniel Borkmann else 1276d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1286d2eea6fSDaniel Borkmann shift -= 16; 1296d2eea6fSDaniel Borkmann while (shift >= 0) { 1306d2eea6fSDaniel Borkmann if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000)) 1316d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1326d2eea6fSDaniel Borkmann shift -= 16; 1336d2eea6fSDaniel Borkmann } 1346d2eea6fSDaniel Borkmann } 1356d2eea6fSDaniel Borkmann 1366d2eea6fSDaniel Borkmann /* 1376d2eea6fSDaniel Borkmann * This is an unoptimized 64 immediate emission used for BPF to BPF call 1386d2eea6fSDaniel Borkmann * addresses. It will always do a full 64 bit decomposition as otherwise 1396d2eea6fSDaniel Borkmann * more complexity in the last extra pass is required since we previously 1406d2eea6fSDaniel Borkmann * reserved 4 instructions for the address. 1416d2eea6fSDaniel Borkmann */ 1426d2eea6fSDaniel Borkmann static inline void emit_addr_mov_i64(const int reg, const u64 val, 1436d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1446d2eea6fSDaniel Borkmann { 1456d2eea6fSDaniel Borkmann u64 tmp = val; 1466d2eea6fSDaniel Borkmann int shift = 0; 1476d2eea6fSDaniel Borkmann 1486d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, tmp & 0xffff, shift), ctx); 1496d2eea6fSDaniel Borkmann for (;shift < 48;) { 1506d2eea6fSDaniel Borkmann tmp >>= 16; 1516d2eea6fSDaniel Borkmann shift += 16; 1526d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx); 1536d2eea6fSDaniel Borkmann } 1546d2eea6fSDaniel Borkmann } 1556d2eea6fSDaniel Borkmann 156e54bcde3SZi Shen Lim static inline int bpf2a64_offset(int bpf_to, int bpf_from, 157e54bcde3SZi Shen Lim const struct jit_ctx *ctx) 158e54bcde3SZi Shen Lim { 1598eee539dSXi Wang int to = ctx->offset[bpf_to]; 160e54bcde3SZi Shen Lim /* -1 to account for the Branch instruction */ 1618eee539dSXi Wang int from = ctx->offset[bpf_from] - 1; 162e54bcde3SZi Shen Lim 163e54bcde3SZi Shen Lim return to - from; 164e54bcde3SZi Shen Lim } 165e54bcde3SZi Shen Lim 166b569c1c6SDaniel Borkmann static void jit_fill_hole(void *area, unsigned int size) 167b569c1c6SDaniel Borkmann { 168425e1ed7SLuc Van Oostenryck __le32 *ptr; 169b569c1c6SDaniel Borkmann /* We are guaranteed to have aligned memory. */ 170b569c1c6SDaniel Borkmann for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 171b569c1c6SDaniel Borkmann *ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT); 172b569c1c6SDaniel Borkmann } 173b569c1c6SDaniel Borkmann 174e54bcde3SZi Shen Lim static inline int epilogue_offset(const struct jit_ctx *ctx) 175e54bcde3SZi Shen Lim { 17651c9fbb1SZi Shen Lim int to = ctx->epilogue_offset; 17751c9fbb1SZi Shen Lim int from = ctx->idx; 178e54bcde3SZi Shen Lim 179e54bcde3SZi Shen Lim return to - from; 180e54bcde3SZi Shen Lim } 181e54bcde3SZi Shen Lim 182e54bcde3SZi Shen Lim /* Stack must be multiples of 16B */ 183e54bcde3SZi Shen Lim #define STACK_ALIGN(sz) (((sz) + 15) & ~15) 184e54bcde3SZi Shen Lim 185a2284d91SDaniel Borkmann /* Tail call offset to jump into */ 186a2284d91SDaniel Borkmann #define PROLOGUE_OFFSET 7 187ddb55992SZi Shen Lim 188*56ea6a8bSDaniel Borkmann static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf) 189e54bcde3SZi Shen Lim { 190f1c9eed7SDaniel Borkmann const struct bpf_prog *prog = ctx->prog; 191e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 192e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 193e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 194e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 195e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 196ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 197ddb55992SZi Shen Lim const int idx0 = ctx->idx; 198ddb55992SZi Shen Lim int cur_offset; 199e54bcde3SZi Shen Lim 200ec0738dbSYang Shi /* 201ec0738dbSYang Shi * BPF prog stack layout 202ec0738dbSYang Shi * 203ec0738dbSYang Shi * high 204ec0738dbSYang Shi * original A64_SP => 0:+-----+ BPF prologue 205ec0738dbSYang Shi * |FP/LR| 206ec0738dbSYang Shi * current A64_FP => -16:+-----+ 207ec0738dbSYang Shi * | ... | callee saved registers 2084c1cd4fdSYang Shi * BPF fp register => -64:+-----+ <= (BPF_FP) 209ec0738dbSYang Shi * | | 210ec0738dbSYang Shi * | ... | BPF prog stack 211ec0738dbSYang Shi * | | 212f1c9eed7SDaniel Borkmann * +-----+ <= (BPF_FP - prog->aux->stack_depth) 21309ece3d0SDaniel Borkmann * |RSVD | padding 214f1c9eed7SDaniel Borkmann * current A64_SP => +-----+ <= (BPF_FP - ctx->stack_size) 215ec0738dbSYang Shi * | | 216ec0738dbSYang Shi * | ... | Function call stack 217ec0738dbSYang Shi * | | 218ec0738dbSYang Shi * +-----+ 219ec0738dbSYang Shi * low 220ec0738dbSYang Shi * 221ec0738dbSYang Shi */ 222ec0738dbSYang Shi 223ec0738dbSYang Shi /* Save FP and LR registers to stay align with ARM64 AAPCS */ 224ec0738dbSYang Shi emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 225ec0738dbSYang Shi emit(A64_MOV(1, A64_FP, A64_SP), ctx); 226ec0738dbSYang Shi 227ddb55992SZi Shen Lim /* Save callee-saved registers */ 228e54bcde3SZi Shen Lim emit(A64_PUSH(r6, r7, A64_SP), ctx); 229e54bcde3SZi Shen Lim emit(A64_PUSH(r8, r9, A64_SP), ctx); 230ddb55992SZi Shen Lim emit(A64_PUSH(fp, tcc, A64_SP), ctx); 231e54bcde3SZi Shen Lim 232ddb55992SZi Shen Lim /* Set up BPF prog stack base register */ 233e54bcde3SZi Shen Lim emit(A64_MOV(1, fp, A64_SP), ctx); 234e54bcde3SZi Shen Lim 235*56ea6a8bSDaniel Borkmann if (!ebpf_from_cbpf) { 236ddb55992SZi Shen Lim /* Initialize tail_call_cnt */ 237ddb55992SZi Shen Lim emit(A64_MOVZ(1, tcc, 0, 0), ctx); 238ddb55992SZi Shen Lim 239ddb55992SZi Shen Lim cur_offset = ctx->idx - idx0; 240ddb55992SZi Shen Lim if (cur_offset != PROLOGUE_OFFSET) { 241ddb55992SZi Shen Lim pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 242ddb55992SZi Shen Lim cur_offset, PROLOGUE_OFFSET); 243ddb55992SZi Shen Lim return -1; 244ddb55992SZi Shen Lim } 245*56ea6a8bSDaniel Borkmann } 246a2284d91SDaniel Borkmann 24709ece3d0SDaniel Borkmann ctx->stack_size = STACK_ALIGN(prog->aux->stack_depth); 248a2284d91SDaniel Borkmann 249a2284d91SDaniel Borkmann /* Set up function call stack */ 250a2284d91SDaniel Borkmann emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 251ddb55992SZi Shen Lim return 0; 252ddb55992SZi Shen Lim } 253ddb55992SZi Shen Lim 254ddb55992SZi Shen Lim static int out_offset = -1; /* initialized on the first pass of build_body() */ 255ddb55992SZi Shen Lim static int emit_bpf_tail_call(struct jit_ctx *ctx) 256ddb55992SZi Shen Lim { 257ddb55992SZi Shen Lim /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 258ddb55992SZi Shen Lim const u8 r2 = bpf2a64[BPF_REG_2]; 259ddb55992SZi Shen Lim const u8 r3 = bpf2a64[BPF_REG_3]; 260ddb55992SZi Shen Lim 261ddb55992SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 262ddb55992SZi Shen Lim const u8 prg = bpf2a64[TMP_REG_2]; 263ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 264ddb55992SZi Shen Lim const int idx0 = ctx->idx; 265ddb55992SZi Shen Lim #define cur_offset (ctx->idx - idx0) 266ddb55992SZi Shen Lim #define jmp_offset (out_offset - (cur_offset)) 267ddb55992SZi Shen Lim size_t off; 268ddb55992SZi Shen Lim 269ddb55992SZi Shen Lim /* if (index >= array->map.max_entries) 270ddb55992SZi Shen Lim * goto out; 271ddb55992SZi Shen Lim */ 272ddb55992SZi Shen Lim off = offsetof(struct bpf_array, map.max_entries); 273ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 274ddb55992SZi Shen Lim emit(A64_LDR32(tmp, r2, tmp), ctx); 27516338a9bSDaniel Borkmann emit(A64_MOV(0, r3, r3), ctx); 276ddb55992SZi Shen Lim emit(A64_CMP(0, r3, tmp), ctx); 27716338a9bSDaniel Borkmann emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 278ddb55992SZi Shen Lim 279ddb55992SZi Shen Lim /* if (tail_call_cnt > MAX_TAIL_CALL_CNT) 280ddb55992SZi Shen Lim * goto out; 281ddb55992SZi Shen Lim * tail_call_cnt++; 282ddb55992SZi Shen Lim */ 283ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 284ddb55992SZi Shen Lim emit(A64_CMP(1, tcc, tmp), ctx); 28516338a9bSDaniel Borkmann emit(A64_B_(A64_COND_HI, jmp_offset), ctx); 286ddb55992SZi Shen Lim emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 287ddb55992SZi Shen Lim 288ddb55992SZi Shen Lim /* prog = array->ptrs[index]; 289ddb55992SZi Shen Lim * if (prog == NULL) 290ddb55992SZi Shen Lim * goto out; 291ddb55992SZi Shen Lim */ 292ddb55992SZi Shen Lim off = offsetof(struct bpf_array, ptrs); 293ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 294d8b54110SDaniel Borkmann emit(A64_ADD(1, tmp, r2, tmp), ctx); 295d8b54110SDaniel Borkmann emit(A64_LSL(1, prg, r3, 3), ctx); 296d8b54110SDaniel Borkmann emit(A64_LDR64(prg, tmp, prg), ctx); 297ddb55992SZi Shen Lim emit(A64_CBZ(1, prg, jmp_offset), ctx); 298ddb55992SZi Shen Lim 299a2284d91SDaniel Borkmann /* goto *(prog->bpf_func + prologue_offset); */ 300ddb55992SZi Shen Lim off = offsetof(struct bpf_prog, bpf_func); 301ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 302ddb55992SZi Shen Lim emit(A64_LDR64(tmp, prg, tmp), ctx); 303ddb55992SZi Shen Lim emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 304a2284d91SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 305ddb55992SZi Shen Lim emit(A64_BR(tmp), ctx); 306ddb55992SZi Shen Lim 307ddb55992SZi Shen Lim /* out: */ 308ddb55992SZi Shen Lim if (out_offset == -1) 309ddb55992SZi Shen Lim out_offset = cur_offset; 310ddb55992SZi Shen Lim if (cur_offset != out_offset) { 311ddb55992SZi Shen Lim pr_err_once("tail_call out_offset = %d, expected %d!\n", 312ddb55992SZi Shen Lim cur_offset, out_offset); 313ddb55992SZi Shen Lim return -1; 314ddb55992SZi Shen Lim } 315ddb55992SZi Shen Lim return 0; 316ddb55992SZi Shen Lim #undef cur_offset 317ddb55992SZi Shen Lim #undef jmp_offset 318e54bcde3SZi Shen Lim } 319e54bcde3SZi Shen Lim 320e54bcde3SZi Shen Lim static void build_epilogue(struct jit_ctx *ctx) 321e54bcde3SZi Shen Lim { 322e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 323e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 324e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 325e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 326e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 327e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 328e54bcde3SZi Shen Lim 329e54bcde3SZi Shen Lim /* We're done with BPF stack */ 330f1c9eed7SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 331e54bcde3SZi Shen Lim 332ec0738dbSYang Shi /* Restore fs (x25) and x26 */ 333ec0738dbSYang Shi emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 334ec0738dbSYang Shi 335e54bcde3SZi Shen Lim /* Restore callee-saved register */ 336e54bcde3SZi Shen Lim emit(A64_POP(r8, r9, A64_SP), ctx); 337e54bcde3SZi Shen Lim emit(A64_POP(r6, r7, A64_SP), ctx); 338e54bcde3SZi Shen Lim 339ec0738dbSYang Shi /* Restore FP/LR registers */ 340ec0738dbSYang Shi emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 341e54bcde3SZi Shen Lim 342e54bcde3SZi Shen Lim /* Set return value */ 343e54bcde3SZi Shen Lim emit(A64_MOV(1, A64_R(0), r0), ctx); 344e54bcde3SZi Shen Lim 345e54bcde3SZi Shen Lim emit(A64_RET(A64_LR), ctx); 346e54bcde3SZi Shen Lim } 347e54bcde3SZi Shen Lim 34830d3d94cSZi Shen Lim /* JITs an eBPF instruction. 34930d3d94cSZi Shen Lim * Returns: 35030d3d94cSZi Shen Lim * 0 - successfully JITed an 8-byte eBPF instruction. 35130d3d94cSZi Shen Lim * >0 - successfully JITed a 16-byte eBPF instruction. 35230d3d94cSZi Shen Lim * <0 - failed to JIT. 35330d3d94cSZi Shen Lim */ 354e54bcde3SZi Shen Lim static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx) 355e54bcde3SZi Shen Lim { 356e54bcde3SZi Shen Lim const u8 code = insn->code; 357e54bcde3SZi Shen Lim const u8 dst = bpf2a64[insn->dst_reg]; 358e54bcde3SZi Shen Lim const u8 src = bpf2a64[insn->src_reg]; 359e54bcde3SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 360e54bcde3SZi Shen Lim const u8 tmp2 = bpf2a64[TMP_REG_2]; 3617005cadeSDaniel Borkmann const u8 tmp3 = bpf2a64[TMP_REG_3]; 362e54bcde3SZi Shen Lim const s16 off = insn->off; 363e54bcde3SZi Shen Lim const s32 imm = insn->imm; 364e54bcde3SZi Shen Lim const int i = insn - ctx->prog->insnsi; 365e54bcde3SZi Shen Lim const bool is64 = BPF_CLASS(code) == BPF_ALU64; 36685f68fe8SDaniel Borkmann const bool isdw = BPF_SIZE(code) == BPF_DW; 367e54bcde3SZi Shen Lim u8 jmp_cond; 368e54bcde3SZi Shen Lim s32 jmp_offset; 369e54bcde3SZi Shen Lim 370251599e1SZi Shen Lim #define check_imm(bits, imm) do { \ 371251599e1SZi Shen Lim if ((((imm) > 0) && ((imm) >> (bits))) || \ 372251599e1SZi Shen Lim (((imm) < 0) && (~(imm) >> (bits)))) { \ 373251599e1SZi Shen Lim pr_info("[%2d] imm=%d(0x%x) out of range\n", \ 374251599e1SZi Shen Lim i, imm, imm); \ 375251599e1SZi Shen Lim return -EINVAL; \ 376251599e1SZi Shen Lim } \ 377251599e1SZi Shen Lim } while (0) 378251599e1SZi Shen Lim #define check_imm19(imm) check_imm(19, imm) 379251599e1SZi Shen Lim #define check_imm26(imm) check_imm(26, imm) 380251599e1SZi Shen Lim 381e54bcde3SZi Shen Lim switch (code) { 382e54bcde3SZi Shen Lim /* dst = src */ 383e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_X: 384e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_X: 385e54bcde3SZi Shen Lim emit(A64_MOV(is64, dst, src), ctx); 386e54bcde3SZi Shen Lim break; 387e54bcde3SZi Shen Lim /* dst = dst OP src */ 388e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_X: 389e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_X: 390e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, src), ctx); 391e54bcde3SZi Shen Lim break; 392e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_X: 393e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_X: 394e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, src), ctx); 395e54bcde3SZi Shen Lim break; 396e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_X: 397e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_X: 398e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, src), ctx); 399e54bcde3SZi Shen Lim break; 400e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_X: 401e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_X: 402e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, src), ctx); 403e54bcde3SZi Shen Lim break; 404e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_X: 405e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_X: 406e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, src), ctx); 407e54bcde3SZi Shen Lim break; 408e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_X: 409e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_X: 410e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, src), ctx); 411e54bcde3SZi Shen Lim break; 412e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_X: 413e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_X: 414e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_X: 415e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_X: 41614e589ffSZi Shen Lim switch (BPF_OP(code)) { 41714e589ffSZi Shen Lim case BPF_DIV: 418e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, src), ctx); 419e54bcde3SZi Shen Lim break; 42014e589ffSZi Shen Lim case BPF_MOD: 421e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, src), ctx); 422e54bcde3SZi Shen Lim emit(A64_MUL(is64, tmp, tmp, src), ctx); 423e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 424e54bcde3SZi Shen Lim break; 42514e589ffSZi Shen Lim } 42614e589ffSZi Shen Lim break; 427d65a634aSZi Shen Lim case BPF_ALU | BPF_LSH | BPF_X: 428d65a634aSZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_X: 429d65a634aSZi Shen Lim emit(A64_LSLV(is64, dst, dst, src), ctx); 430d65a634aSZi Shen Lim break; 431d65a634aSZi Shen Lim case BPF_ALU | BPF_RSH | BPF_X: 432d65a634aSZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_X: 433d65a634aSZi Shen Lim emit(A64_LSRV(is64, dst, dst, src), ctx); 434d65a634aSZi Shen Lim break; 435d65a634aSZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_X: 436d65a634aSZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_X: 437d65a634aSZi Shen Lim emit(A64_ASRV(is64, dst, dst, src), ctx); 438d65a634aSZi Shen Lim break; 439e54bcde3SZi Shen Lim /* dst = -dst */ 440e54bcde3SZi Shen Lim case BPF_ALU | BPF_NEG: 441e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_NEG: 442e54bcde3SZi Shen Lim emit(A64_NEG(is64, dst, dst), ctx); 443e54bcde3SZi Shen Lim break; 444e54bcde3SZi Shen Lim /* dst = BSWAP##imm(dst) */ 445e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_LE: 446e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_BE: 447e54bcde3SZi Shen Lim #ifdef CONFIG_CPU_BIG_ENDIAN 448e54bcde3SZi Shen Lim if (BPF_SRC(code) == BPF_FROM_BE) 449d63903bbSXi Wang goto emit_bswap_uxt; 450e54bcde3SZi Shen Lim #else /* !CONFIG_CPU_BIG_ENDIAN */ 451e54bcde3SZi Shen Lim if (BPF_SRC(code) == BPF_FROM_LE) 452d63903bbSXi Wang goto emit_bswap_uxt; 453e54bcde3SZi Shen Lim #endif 454e54bcde3SZi Shen Lim switch (imm) { 455e54bcde3SZi Shen Lim case 16: 456e54bcde3SZi Shen Lim emit(A64_REV16(is64, dst, dst), ctx); 457d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 458d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 459e54bcde3SZi Shen Lim break; 460e54bcde3SZi Shen Lim case 32: 461e54bcde3SZi Shen Lim emit(A64_REV32(is64, dst, dst), ctx); 462d63903bbSXi Wang /* upper 32 bits already cleared */ 463e54bcde3SZi Shen Lim break; 464e54bcde3SZi Shen Lim case 64: 465e54bcde3SZi Shen Lim emit(A64_REV64(dst, dst), ctx); 466e54bcde3SZi Shen Lim break; 467e54bcde3SZi Shen Lim } 468e54bcde3SZi Shen Lim break; 469d63903bbSXi Wang emit_bswap_uxt: 470d63903bbSXi Wang switch (imm) { 471d63903bbSXi Wang case 16: 472d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 473d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 474d63903bbSXi Wang break; 475d63903bbSXi Wang case 32: 476d63903bbSXi Wang /* zero-extend 32 bits into 64 bits */ 477d63903bbSXi Wang emit(A64_UXTW(is64, dst, dst), ctx); 478d63903bbSXi Wang break; 479d63903bbSXi Wang case 64: 480d63903bbSXi Wang /* nop */ 481d63903bbSXi Wang break; 482d63903bbSXi Wang } 483d63903bbSXi Wang break; 484e54bcde3SZi Shen Lim /* dst = imm */ 485e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_K: 486e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_K: 487e54bcde3SZi Shen Lim emit_a64_mov_i(is64, dst, imm, ctx); 488e54bcde3SZi Shen Lim break; 489e54bcde3SZi Shen Lim /* dst = dst OP imm */ 490e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_K: 491e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_K: 492e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 493e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, tmp), ctx); 494e54bcde3SZi Shen Lim break; 495e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_K: 496e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_K: 497e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 498e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 499e54bcde3SZi Shen Lim break; 500e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_K: 501e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_K: 502e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 503e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, tmp), ctx); 504e54bcde3SZi Shen Lim break; 505e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_K: 506e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_K: 507e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 508e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, tmp), ctx); 509e54bcde3SZi Shen Lim break; 510e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_K: 511e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_K: 512e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 513e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, tmp), ctx); 514e54bcde3SZi Shen Lim break; 515e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_K: 516e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_K: 517e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 518e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, tmp), ctx); 519e54bcde3SZi Shen Lim break; 520e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_K: 521e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_K: 522e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 523e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, tmp), ctx); 524e54bcde3SZi Shen Lim break; 525e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_K: 526e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_K: 527e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp2, imm, ctx); 528e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 529e54bcde3SZi Shen Lim emit(A64_MUL(is64, tmp, tmp, tmp2), ctx); 530e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 531e54bcde3SZi Shen Lim break; 532e54bcde3SZi Shen Lim case BPF_ALU | BPF_LSH | BPF_K: 533e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_K: 534e54bcde3SZi Shen Lim emit(A64_LSL(is64, dst, dst, imm), ctx); 535e54bcde3SZi Shen Lim break; 536e54bcde3SZi Shen Lim case BPF_ALU | BPF_RSH | BPF_K: 537e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_K: 538e54bcde3SZi Shen Lim emit(A64_LSR(is64, dst, dst, imm), ctx); 539e54bcde3SZi Shen Lim break; 540e54bcde3SZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_K: 541e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_K: 542e54bcde3SZi Shen Lim emit(A64_ASR(is64, dst, dst, imm), ctx); 543e54bcde3SZi Shen Lim break; 544e54bcde3SZi Shen Lim 545e54bcde3SZi Shen Lim /* JUMP off */ 546e54bcde3SZi Shen Lim case BPF_JMP | BPF_JA: 547e54bcde3SZi Shen Lim jmp_offset = bpf2a64_offset(i + off, i, ctx); 548e54bcde3SZi Shen Lim check_imm26(jmp_offset); 549e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 550e54bcde3SZi Shen Lim break; 551e54bcde3SZi Shen Lim /* IF (dst COND src) JUMP off */ 552e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_X: 553e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_X: 554c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_X: 555e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_X: 556c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_X: 557e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_X: 558e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_X: 559c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_X: 560e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_X: 561c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_X: 562e54bcde3SZi Shen Lim emit(A64_CMP(1, dst, src), ctx); 563e54bcde3SZi Shen Lim emit_cond_jmp: 564e54bcde3SZi Shen Lim jmp_offset = bpf2a64_offset(i + off, i, ctx); 565e54bcde3SZi Shen Lim check_imm19(jmp_offset); 566e54bcde3SZi Shen Lim switch (BPF_OP(code)) { 567e54bcde3SZi Shen Lim case BPF_JEQ: 568e54bcde3SZi Shen Lim jmp_cond = A64_COND_EQ; 569e54bcde3SZi Shen Lim break; 570e54bcde3SZi Shen Lim case BPF_JGT: 571e54bcde3SZi Shen Lim jmp_cond = A64_COND_HI; 572e54bcde3SZi Shen Lim break; 573c362b2f3SDaniel Borkmann case BPF_JLT: 574c362b2f3SDaniel Borkmann jmp_cond = A64_COND_CC; 575c362b2f3SDaniel Borkmann break; 576e54bcde3SZi Shen Lim case BPF_JGE: 577e54bcde3SZi Shen Lim jmp_cond = A64_COND_CS; 578e54bcde3SZi Shen Lim break; 579c362b2f3SDaniel Borkmann case BPF_JLE: 580c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LS; 581c362b2f3SDaniel Borkmann break; 58298397fc5SZi Shen Lim case BPF_JSET: 583e54bcde3SZi Shen Lim case BPF_JNE: 584e54bcde3SZi Shen Lim jmp_cond = A64_COND_NE; 585e54bcde3SZi Shen Lim break; 586e54bcde3SZi Shen Lim case BPF_JSGT: 587e54bcde3SZi Shen Lim jmp_cond = A64_COND_GT; 588e54bcde3SZi Shen Lim break; 589c362b2f3SDaniel Borkmann case BPF_JSLT: 590c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LT; 591c362b2f3SDaniel Borkmann break; 592e54bcde3SZi Shen Lim case BPF_JSGE: 593e54bcde3SZi Shen Lim jmp_cond = A64_COND_GE; 594e54bcde3SZi Shen Lim break; 595c362b2f3SDaniel Borkmann case BPF_JSLE: 596c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LE; 597c362b2f3SDaniel Borkmann break; 598e54bcde3SZi Shen Lim default: 599e54bcde3SZi Shen Lim return -EFAULT; 600e54bcde3SZi Shen Lim } 601e54bcde3SZi Shen Lim emit(A64_B_(jmp_cond, jmp_offset), ctx); 602e54bcde3SZi Shen Lim break; 603e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_X: 604e54bcde3SZi Shen Lim emit(A64_TST(1, dst, src), ctx); 605e54bcde3SZi Shen Lim goto emit_cond_jmp; 606e54bcde3SZi Shen Lim /* IF (dst COND imm) JUMP off */ 607e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_K: 608e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_K: 609c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_K: 610e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_K: 611c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_K: 612e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_K: 613e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_K: 614c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_K: 615e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_K: 616c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_K: 617e54bcde3SZi Shen Lim emit_a64_mov_i(1, tmp, imm, ctx); 618e54bcde3SZi Shen Lim emit(A64_CMP(1, dst, tmp), ctx); 619e54bcde3SZi Shen Lim goto emit_cond_jmp; 620e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_K: 621e54bcde3SZi Shen Lim emit_a64_mov_i(1, tmp, imm, ctx); 622e54bcde3SZi Shen Lim emit(A64_TST(1, dst, tmp), ctx); 623e54bcde3SZi Shen Lim goto emit_cond_jmp; 624e54bcde3SZi Shen Lim /* function call */ 625e54bcde3SZi Shen Lim case BPF_JMP | BPF_CALL: 626e54bcde3SZi Shen Lim { 627e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 628e54bcde3SZi Shen Lim const u64 func = (u64)__bpf_call_base + imm; 629e54bcde3SZi Shen Lim 630db496944SAlexei Starovoitov if (ctx->prog->is_func) 631db496944SAlexei Starovoitov emit_addr_mov_i64(tmp, func, ctx); 632db496944SAlexei Starovoitov else 633e54bcde3SZi Shen Lim emit_a64_mov_i64(tmp, func, ctx); 634e54bcde3SZi Shen Lim emit(A64_BLR(tmp), ctx); 635e54bcde3SZi Shen Lim emit(A64_MOV(1, r0, A64_R(0)), ctx); 636e54bcde3SZi Shen Lim break; 637e54bcde3SZi Shen Lim } 638ddb55992SZi Shen Lim /* tail call */ 63971189fa9SAlexei Starovoitov case BPF_JMP | BPF_TAIL_CALL: 640ddb55992SZi Shen Lim if (emit_bpf_tail_call(ctx)) 641ddb55992SZi Shen Lim return -EFAULT; 642ddb55992SZi Shen Lim break; 643e54bcde3SZi Shen Lim /* function return */ 644e54bcde3SZi Shen Lim case BPF_JMP | BPF_EXIT: 64551c9fbb1SZi Shen Lim /* Optimization: when last instruction is EXIT, 64651c9fbb1SZi Shen Lim simply fallthrough to epilogue. */ 647e54bcde3SZi Shen Lim if (i == ctx->prog->len - 1) 648e54bcde3SZi Shen Lim break; 649e54bcde3SZi Shen Lim jmp_offset = epilogue_offset(ctx); 650e54bcde3SZi Shen Lim check_imm26(jmp_offset); 651e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 652e54bcde3SZi Shen Lim break; 653e54bcde3SZi Shen Lim 65430d3d94cSZi Shen Lim /* dst = imm64 */ 65530d3d94cSZi Shen Lim case BPF_LD | BPF_IMM | BPF_DW: 65630d3d94cSZi Shen Lim { 65730d3d94cSZi Shen Lim const struct bpf_insn insn1 = insn[1]; 65830d3d94cSZi Shen Lim u64 imm64; 65930d3d94cSZi Shen Lim 6601e4df6b7SXi Wang imm64 = (u64)insn1.imm << 32 | (u32)imm; 66130d3d94cSZi Shen Lim emit_a64_mov_i64(dst, imm64, ctx); 66230d3d94cSZi Shen Lim 66330d3d94cSZi Shen Lim return 1; 66430d3d94cSZi Shen Lim } 66530d3d94cSZi Shen Lim 666e54bcde3SZi Shen Lim /* LDX: dst = *(size *)(src + off) */ 667e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_W: 668e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_H: 669e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_B: 670e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_DW: 671e54bcde3SZi Shen Lim emit_a64_mov_i(1, tmp, off, ctx); 672e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 673e54bcde3SZi Shen Lim case BPF_W: 674e54bcde3SZi Shen Lim emit(A64_LDR32(dst, src, tmp), ctx); 675e54bcde3SZi Shen Lim break; 676e54bcde3SZi Shen Lim case BPF_H: 677e54bcde3SZi Shen Lim emit(A64_LDRH(dst, src, tmp), ctx); 678e54bcde3SZi Shen Lim break; 679e54bcde3SZi Shen Lim case BPF_B: 680e54bcde3SZi Shen Lim emit(A64_LDRB(dst, src, tmp), ctx); 681e54bcde3SZi Shen Lim break; 682e54bcde3SZi Shen Lim case BPF_DW: 683e54bcde3SZi Shen Lim emit(A64_LDR64(dst, src, tmp), ctx); 684e54bcde3SZi Shen Lim break; 685e54bcde3SZi Shen Lim } 686e54bcde3SZi Shen Lim break; 687e54bcde3SZi Shen Lim 688e54bcde3SZi Shen Lim /* ST: *(size *)(dst + off) = imm */ 689e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_W: 690e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_H: 691e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_B: 692e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_DW: 693df849ba3SYang Shi /* Load imm to a register then store it */ 694df849ba3SYang Shi emit_a64_mov_i(1, tmp2, off, ctx); 695df849ba3SYang Shi emit_a64_mov_i(1, tmp, imm, ctx); 696df849ba3SYang Shi switch (BPF_SIZE(code)) { 697df849ba3SYang Shi case BPF_W: 698df849ba3SYang Shi emit(A64_STR32(tmp, dst, tmp2), ctx); 699df849ba3SYang Shi break; 700df849ba3SYang Shi case BPF_H: 701df849ba3SYang Shi emit(A64_STRH(tmp, dst, tmp2), ctx); 702df849ba3SYang Shi break; 703df849ba3SYang Shi case BPF_B: 704df849ba3SYang Shi emit(A64_STRB(tmp, dst, tmp2), ctx); 705df849ba3SYang Shi break; 706df849ba3SYang Shi case BPF_DW: 707df849ba3SYang Shi emit(A64_STR64(tmp, dst, tmp2), ctx); 708df849ba3SYang Shi break; 709df849ba3SYang Shi } 710df849ba3SYang Shi break; 711e54bcde3SZi Shen Lim 712e54bcde3SZi Shen Lim /* STX: *(size *)(dst + off) = src */ 713e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_W: 714e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_H: 715e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_B: 716e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_DW: 717e54bcde3SZi Shen Lim emit_a64_mov_i(1, tmp, off, ctx); 718e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 719e54bcde3SZi Shen Lim case BPF_W: 720e54bcde3SZi Shen Lim emit(A64_STR32(src, dst, tmp), ctx); 721e54bcde3SZi Shen Lim break; 722e54bcde3SZi Shen Lim case BPF_H: 723e54bcde3SZi Shen Lim emit(A64_STRH(src, dst, tmp), ctx); 724e54bcde3SZi Shen Lim break; 725e54bcde3SZi Shen Lim case BPF_B: 726e54bcde3SZi Shen Lim emit(A64_STRB(src, dst, tmp), ctx); 727e54bcde3SZi Shen Lim break; 728e54bcde3SZi Shen Lim case BPF_DW: 729e54bcde3SZi Shen Lim emit(A64_STR64(src, dst, tmp), ctx); 730e54bcde3SZi Shen Lim break; 731e54bcde3SZi Shen Lim } 732e54bcde3SZi Shen Lim break; 733e54bcde3SZi Shen Lim /* STX XADD: lock *(u32 *)(dst + off) += src */ 734e54bcde3SZi Shen Lim case BPF_STX | BPF_XADD | BPF_W: 735e54bcde3SZi Shen Lim /* STX XADD: lock *(u64 *)(dst + off) += src */ 736e54bcde3SZi Shen Lim case BPF_STX | BPF_XADD | BPF_DW: 73785f68fe8SDaniel Borkmann emit_a64_mov_i(1, tmp, off, ctx); 73885f68fe8SDaniel Borkmann emit(A64_ADD(1, tmp, tmp, dst), ctx); 73985f68fe8SDaniel Borkmann emit(A64_PRFM(tmp, PST, L1, STRM), ctx); 74085f68fe8SDaniel Borkmann emit(A64_LDXR(isdw, tmp2, tmp), ctx); 74185f68fe8SDaniel Borkmann emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 7427005cadeSDaniel Borkmann emit(A64_STXR(isdw, tmp2, tmp, tmp3), ctx); 74385f68fe8SDaniel Borkmann jmp_offset = -3; 74485f68fe8SDaniel Borkmann check_imm19(jmp_offset); 7457005cadeSDaniel Borkmann emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 74685f68fe8SDaniel Borkmann break; 747e54bcde3SZi Shen Lim 748e54bcde3SZi Shen Lim default: 749e54bcde3SZi Shen Lim pr_err_once("unknown opcode %02x\n", code); 750e54bcde3SZi Shen Lim return -EINVAL; 751e54bcde3SZi Shen Lim } 752e54bcde3SZi Shen Lim 753e54bcde3SZi Shen Lim return 0; 754e54bcde3SZi Shen Lim } 755e54bcde3SZi Shen Lim 756e54bcde3SZi Shen Lim static int build_body(struct jit_ctx *ctx) 757e54bcde3SZi Shen Lim { 758e54bcde3SZi Shen Lim const struct bpf_prog *prog = ctx->prog; 759e54bcde3SZi Shen Lim int i; 760e54bcde3SZi Shen Lim 761e54bcde3SZi Shen Lim for (i = 0; i < prog->len; i++) { 762e54bcde3SZi Shen Lim const struct bpf_insn *insn = &prog->insnsi[i]; 763e54bcde3SZi Shen Lim int ret; 764e54bcde3SZi Shen Lim 7658eee539dSXi Wang ret = build_insn(insn, ctx); 76630d3d94cSZi Shen Lim if (ret > 0) { 76730d3d94cSZi Shen Lim i++; 768ddc665a4SDaniel Borkmann if (ctx->image == NULL) 769ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 77030d3d94cSZi Shen Lim continue; 77130d3d94cSZi Shen Lim } 772ddc665a4SDaniel Borkmann if (ctx->image == NULL) 773ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 774e54bcde3SZi Shen Lim if (ret) 775e54bcde3SZi Shen Lim return ret; 776e54bcde3SZi Shen Lim } 777e54bcde3SZi Shen Lim 778e54bcde3SZi Shen Lim return 0; 779e54bcde3SZi Shen Lim } 780e54bcde3SZi Shen Lim 78142ff712bSZi Shen Lim static int validate_code(struct jit_ctx *ctx) 78242ff712bSZi Shen Lim { 78342ff712bSZi Shen Lim int i; 78442ff712bSZi Shen Lim 78542ff712bSZi Shen Lim for (i = 0; i < ctx->idx; i++) { 78642ff712bSZi Shen Lim u32 a64_insn = le32_to_cpu(ctx->image[i]); 78742ff712bSZi Shen Lim 78842ff712bSZi Shen Lim if (a64_insn == AARCH64_BREAK_FAULT) 78942ff712bSZi Shen Lim return -1; 79042ff712bSZi Shen Lim } 79142ff712bSZi Shen Lim 79242ff712bSZi Shen Lim return 0; 79342ff712bSZi Shen Lim } 79442ff712bSZi Shen Lim 795e54bcde3SZi Shen Lim static inline void bpf_flush_icache(void *start, void *end) 796e54bcde3SZi Shen Lim { 797e54bcde3SZi Shen Lim flush_icache_range((unsigned long)start, (unsigned long)end); 798e54bcde3SZi Shen Lim } 799e54bcde3SZi Shen Lim 800db496944SAlexei Starovoitov struct arm64_jit_data { 801db496944SAlexei Starovoitov struct bpf_binary_header *header; 802db496944SAlexei Starovoitov u8 *image; 803db496944SAlexei Starovoitov struct jit_ctx ctx; 804db496944SAlexei Starovoitov }; 805db496944SAlexei Starovoitov 806d1c55ab5SDaniel Borkmann struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 807e54bcde3SZi Shen Lim { 80826eb042eSDaniel Borkmann struct bpf_prog *tmp, *orig_prog = prog; 809b569c1c6SDaniel Borkmann struct bpf_binary_header *header; 810db496944SAlexei Starovoitov struct arm64_jit_data *jit_data; 811*56ea6a8bSDaniel Borkmann bool was_classic = bpf_prog_was_classic(prog); 81226eb042eSDaniel Borkmann bool tmp_blinded = false; 813db496944SAlexei Starovoitov bool extra_pass = false; 814e54bcde3SZi Shen Lim struct jit_ctx ctx; 815e54bcde3SZi Shen Lim int image_size; 816b569c1c6SDaniel Borkmann u8 *image_ptr; 817e54bcde3SZi Shen Lim 81860b58afcSAlexei Starovoitov if (!prog->jit_requested) 81926eb042eSDaniel Borkmann return orig_prog; 82026eb042eSDaniel Borkmann 82126eb042eSDaniel Borkmann tmp = bpf_jit_blind_constants(prog); 82226eb042eSDaniel Borkmann /* If blinding was requested and we failed during blinding, 82326eb042eSDaniel Borkmann * we must fall back to the interpreter. 82426eb042eSDaniel Borkmann */ 82526eb042eSDaniel Borkmann if (IS_ERR(tmp)) 82626eb042eSDaniel Borkmann return orig_prog; 82726eb042eSDaniel Borkmann if (tmp != prog) { 82826eb042eSDaniel Borkmann tmp_blinded = true; 82926eb042eSDaniel Borkmann prog = tmp; 83026eb042eSDaniel Borkmann } 831e54bcde3SZi Shen Lim 832db496944SAlexei Starovoitov jit_data = prog->aux->jit_data; 833db496944SAlexei Starovoitov if (!jit_data) { 834db496944SAlexei Starovoitov jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 835db496944SAlexei Starovoitov if (!jit_data) { 836db496944SAlexei Starovoitov prog = orig_prog; 837db496944SAlexei Starovoitov goto out; 838db496944SAlexei Starovoitov } 839db496944SAlexei Starovoitov prog->aux->jit_data = jit_data; 840db496944SAlexei Starovoitov } 841db496944SAlexei Starovoitov if (jit_data->ctx.offset) { 842db496944SAlexei Starovoitov ctx = jit_data->ctx; 843db496944SAlexei Starovoitov image_ptr = jit_data->image; 844db496944SAlexei Starovoitov header = jit_data->header; 845db496944SAlexei Starovoitov extra_pass = true; 8465ee7f784SAlexei Starovoitov image_size = sizeof(u32) * ctx.idx; 847db496944SAlexei Starovoitov goto skip_init_ctx; 848db496944SAlexei Starovoitov } 849e54bcde3SZi Shen Lim memset(&ctx, 0, sizeof(ctx)); 850e54bcde3SZi Shen Lim ctx.prog = prog; 851e54bcde3SZi Shen Lim 852e54bcde3SZi Shen Lim ctx.offset = kcalloc(prog->len, sizeof(int), GFP_KERNEL); 85326eb042eSDaniel Borkmann if (ctx.offset == NULL) { 85426eb042eSDaniel Borkmann prog = orig_prog; 855db496944SAlexei Starovoitov goto out_off; 85626eb042eSDaniel Borkmann } 857e54bcde3SZi Shen Lim 858e54bcde3SZi Shen Lim /* 1. Initial fake pass to compute ctx->idx. */ 859e54bcde3SZi Shen Lim 8604c1cd4fdSYang Shi /* Fake pass to fill in ctx->offset. */ 86126eb042eSDaniel Borkmann if (build_body(&ctx)) { 86226eb042eSDaniel Borkmann prog = orig_prog; 86326eb042eSDaniel Borkmann goto out_off; 86426eb042eSDaniel Borkmann } 865e54bcde3SZi Shen Lim 866*56ea6a8bSDaniel Borkmann if (build_prologue(&ctx, was_classic)) { 867ddb55992SZi Shen Lim prog = orig_prog; 868ddb55992SZi Shen Lim goto out_off; 869ddb55992SZi Shen Lim } 87051c9fbb1SZi Shen Lim 87151c9fbb1SZi Shen Lim ctx.epilogue_offset = ctx.idx; 872e54bcde3SZi Shen Lim build_epilogue(&ctx); 873e54bcde3SZi Shen Lim 874e54bcde3SZi Shen Lim /* Now we know the actual image size. */ 875e54bcde3SZi Shen Lim image_size = sizeof(u32) * ctx.idx; 876b569c1c6SDaniel Borkmann header = bpf_jit_binary_alloc(image_size, &image_ptr, 877b569c1c6SDaniel Borkmann sizeof(u32), jit_fill_hole); 87826eb042eSDaniel Borkmann if (header == NULL) { 87926eb042eSDaniel Borkmann prog = orig_prog; 88026eb042eSDaniel Borkmann goto out_off; 88126eb042eSDaniel Borkmann } 882e54bcde3SZi Shen Lim 883e54bcde3SZi Shen Lim /* 2. Now, the actual pass. */ 884e54bcde3SZi Shen Lim 885425e1ed7SLuc Van Oostenryck ctx.image = (__le32 *)image_ptr; 886db496944SAlexei Starovoitov skip_init_ctx: 887e54bcde3SZi Shen Lim ctx.idx = 0; 888b569c1c6SDaniel Borkmann 889*56ea6a8bSDaniel Borkmann build_prologue(&ctx, was_classic); 890e54bcde3SZi Shen Lim 89160ef0494SDaniel Borkmann if (build_body(&ctx)) { 892b569c1c6SDaniel Borkmann bpf_jit_binary_free(header); 89326eb042eSDaniel Borkmann prog = orig_prog; 89426eb042eSDaniel Borkmann goto out_off; 89560ef0494SDaniel Borkmann } 896e54bcde3SZi Shen Lim 897e54bcde3SZi Shen Lim build_epilogue(&ctx); 898e54bcde3SZi Shen Lim 89942ff712bSZi Shen Lim /* 3. Extra pass to validate JITed code. */ 90042ff712bSZi Shen Lim if (validate_code(&ctx)) { 90142ff712bSZi Shen Lim bpf_jit_binary_free(header); 90226eb042eSDaniel Borkmann prog = orig_prog; 90326eb042eSDaniel Borkmann goto out_off; 90442ff712bSZi Shen Lim } 90542ff712bSZi Shen Lim 906e54bcde3SZi Shen Lim /* And we're done. */ 907e54bcde3SZi Shen Lim if (bpf_jit_enable > 1) 908e54bcde3SZi Shen Lim bpf_jit_dump(prog->len, image_size, 2, ctx.image); 909e54bcde3SZi Shen Lim 910c3d4c682SDaniel Borkmann bpf_flush_icache(header, ctx.image + ctx.idx); 911b569c1c6SDaniel Borkmann 912db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 913db496944SAlexei Starovoitov if (extra_pass && ctx.idx != jit_data->ctx.idx) { 914db496944SAlexei Starovoitov pr_err_once("multi-func JIT bug %d != %d\n", 915db496944SAlexei Starovoitov ctx.idx, jit_data->ctx.idx); 916db496944SAlexei Starovoitov bpf_jit_binary_free(header); 917db496944SAlexei Starovoitov prog->bpf_func = NULL; 918db496944SAlexei Starovoitov prog->jited = 0; 919db496944SAlexei Starovoitov goto out_off; 920db496944SAlexei Starovoitov } 9219d876e79SDaniel Borkmann bpf_jit_binary_lock_ro(header); 922db496944SAlexei Starovoitov } else { 923db496944SAlexei Starovoitov jit_data->ctx = ctx; 924db496944SAlexei Starovoitov jit_data->image = image_ptr; 925db496944SAlexei Starovoitov jit_data->header = header; 926db496944SAlexei Starovoitov } 927e54bcde3SZi Shen Lim prog->bpf_func = (void *)ctx.image; 928a91263d5SDaniel Borkmann prog->jited = 1; 929783d28ddSMartin KaFai Lau prog->jited_len = image_size; 93026eb042eSDaniel Borkmann 931db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 93226eb042eSDaniel Borkmann out_off: 933e54bcde3SZi Shen Lim kfree(ctx.offset); 934db496944SAlexei Starovoitov kfree(jit_data); 935db496944SAlexei Starovoitov prog->aux->jit_data = NULL; 936db496944SAlexei Starovoitov } 93726eb042eSDaniel Borkmann out: 93826eb042eSDaniel Borkmann if (tmp_blinded) 93926eb042eSDaniel Borkmann bpf_jit_prog_release_other(prog, prog == orig_prog ? 94026eb042eSDaniel Borkmann tmp : orig_prog); 941d1c55ab5SDaniel Borkmann return prog; 942e54bcde3SZi Shen Lim } 943