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); 93*6d2eea6fSDaniel 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 103*6d2eea6fSDaniel Borkmann static int i64_i16_blocks(const u64 val, bool inverse) 104*6d2eea6fSDaniel Borkmann { 105*6d2eea6fSDaniel Borkmann return (((val >> 0) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 106*6d2eea6fSDaniel Borkmann (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 107*6d2eea6fSDaniel Borkmann (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 108*6d2eea6fSDaniel Borkmann (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000)); 109*6d2eea6fSDaniel Borkmann } 110*6d2eea6fSDaniel Borkmann 111*6d2eea6fSDaniel Borkmann static inline void emit_a64_mov_i64(const int reg, const u64 val, 112*6d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 113*6d2eea6fSDaniel Borkmann { 114*6d2eea6fSDaniel Borkmann u64 nrm_tmp = val, rev_tmp = ~val; 115*6d2eea6fSDaniel Borkmann bool inverse; 116*6d2eea6fSDaniel Borkmann int shift; 117*6d2eea6fSDaniel Borkmann 118*6d2eea6fSDaniel Borkmann if (!(nrm_tmp >> 32)) 119*6d2eea6fSDaniel Borkmann return emit_a64_mov_i(0, reg, (u32)val, ctx); 120*6d2eea6fSDaniel Borkmann 121*6d2eea6fSDaniel Borkmann inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false); 122*6d2eea6fSDaniel Borkmann shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) : 123*6d2eea6fSDaniel Borkmann (fls64(nrm_tmp) - 1)), 16), 0); 124*6d2eea6fSDaniel Borkmann if (inverse) 125*6d2eea6fSDaniel Borkmann emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx); 126*6d2eea6fSDaniel Borkmann else 127*6d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 128*6d2eea6fSDaniel Borkmann shift -= 16; 129*6d2eea6fSDaniel Borkmann while (shift >= 0) { 130*6d2eea6fSDaniel Borkmann if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000)) 131*6d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 132*6d2eea6fSDaniel Borkmann shift -= 16; 133*6d2eea6fSDaniel Borkmann } 134*6d2eea6fSDaniel Borkmann } 135*6d2eea6fSDaniel Borkmann 136*6d2eea6fSDaniel Borkmann /* 137*6d2eea6fSDaniel Borkmann * This is an unoptimized 64 immediate emission used for BPF to BPF call 138*6d2eea6fSDaniel Borkmann * addresses. It will always do a full 64 bit decomposition as otherwise 139*6d2eea6fSDaniel Borkmann * more complexity in the last extra pass is required since we previously 140*6d2eea6fSDaniel Borkmann * reserved 4 instructions for the address. 141*6d2eea6fSDaniel Borkmann */ 142*6d2eea6fSDaniel Borkmann static inline void emit_addr_mov_i64(const int reg, const u64 val, 143*6d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 144*6d2eea6fSDaniel Borkmann { 145*6d2eea6fSDaniel Borkmann u64 tmp = val; 146*6d2eea6fSDaniel Borkmann int shift = 0; 147*6d2eea6fSDaniel Borkmann 148*6d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, tmp & 0xffff, shift), ctx); 149*6d2eea6fSDaniel Borkmann for (;shift < 48;) { 150*6d2eea6fSDaniel Borkmann tmp >>= 16; 151*6d2eea6fSDaniel Borkmann shift += 16; 152*6d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx); 153*6d2eea6fSDaniel Borkmann } 154*6d2eea6fSDaniel Borkmann } 155*6d2eea6fSDaniel 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 188ddb55992SZi Shen Lim static int build_prologue(struct jit_ctx *ctx) 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 235ddb55992SZi Shen Lim /* Initialize tail_call_cnt */ 236ddb55992SZi Shen Lim emit(A64_MOVZ(1, tcc, 0, 0), ctx); 237ddb55992SZi Shen Lim 238ddb55992SZi Shen Lim cur_offset = ctx->idx - idx0; 239ddb55992SZi Shen Lim if (cur_offset != PROLOGUE_OFFSET) { 240ddb55992SZi Shen Lim pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 241ddb55992SZi Shen Lim cur_offset, PROLOGUE_OFFSET); 242ddb55992SZi Shen Lim return -1; 243ddb55992SZi Shen Lim } 244a2284d91SDaniel Borkmann 24509ece3d0SDaniel Borkmann ctx->stack_size = STACK_ALIGN(prog->aux->stack_depth); 246a2284d91SDaniel Borkmann 247a2284d91SDaniel Borkmann /* Set up function call stack */ 248a2284d91SDaniel Borkmann emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 249ddb55992SZi Shen Lim return 0; 250ddb55992SZi Shen Lim } 251ddb55992SZi Shen Lim 252ddb55992SZi Shen Lim static int out_offset = -1; /* initialized on the first pass of build_body() */ 253ddb55992SZi Shen Lim static int emit_bpf_tail_call(struct jit_ctx *ctx) 254ddb55992SZi Shen Lim { 255ddb55992SZi Shen Lim /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 256ddb55992SZi Shen Lim const u8 r2 = bpf2a64[BPF_REG_2]; 257ddb55992SZi Shen Lim const u8 r3 = bpf2a64[BPF_REG_3]; 258ddb55992SZi Shen Lim 259ddb55992SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 260ddb55992SZi Shen Lim const u8 prg = bpf2a64[TMP_REG_2]; 261ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 262ddb55992SZi Shen Lim const int idx0 = ctx->idx; 263ddb55992SZi Shen Lim #define cur_offset (ctx->idx - idx0) 264ddb55992SZi Shen Lim #define jmp_offset (out_offset - (cur_offset)) 265ddb55992SZi Shen Lim size_t off; 266ddb55992SZi Shen Lim 267ddb55992SZi Shen Lim /* if (index >= array->map.max_entries) 268ddb55992SZi Shen Lim * goto out; 269ddb55992SZi Shen Lim */ 270ddb55992SZi Shen Lim off = offsetof(struct bpf_array, map.max_entries); 271ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 272ddb55992SZi Shen Lim emit(A64_LDR32(tmp, r2, tmp), ctx); 27316338a9bSDaniel Borkmann emit(A64_MOV(0, r3, r3), ctx); 274ddb55992SZi Shen Lim emit(A64_CMP(0, r3, tmp), ctx); 27516338a9bSDaniel Borkmann emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 276ddb55992SZi Shen Lim 277ddb55992SZi Shen Lim /* if (tail_call_cnt > MAX_TAIL_CALL_CNT) 278ddb55992SZi Shen Lim * goto out; 279ddb55992SZi Shen Lim * tail_call_cnt++; 280ddb55992SZi Shen Lim */ 281ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 282ddb55992SZi Shen Lim emit(A64_CMP(1, tcc, tmp), ctx); 28316338a9bSDaniel Borkmann emit(A64_B_(A64_COND_HI, jmp_offset), ctx); 284ddb55992SZi Shen Lim emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 285ddb55992SZi Shen Lim 286ddb55992SZi Shen Lim /* prog = array->ptrs[index]; 287ddb55992SZi Shen Lim * if (prog == NULL) 288ddb55992SZi Shen Lim * goto out; 289ddb55992SZi Shen Lim */ 290ddb55992SZi Shen Lim off = offsetof(struct bpf_array, ptrs); 291ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 292d8b54110SDaniel Borkmann emit(A64_ADD(1, tmp, r2, tmp), ctx); 293d8b54110SDaniel Borkmann emit(A64_LSL(1, prg, r3, 3), ctx); 294d8b54110SDaniel Borkmann emit(A64_LDR64(prg, tmp, prg), ctx); 295ddb55992SZi Shen Lim emit(A64_CBZ(1, prg, jmp_offset), ctx); 296ddb55992SZi Shen Lim 297a2284d91SDaniel Borkmann /* goto *(prog->bpf_func + prologue_offset); */ 298ddb55992SZi Shen Lim off = offsetof(struct bpf_prog, bpf_func); 299ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 300ddb55992SZi Shen Lim emit(A64_LDR64(tmp, prg, tmp), ctx); 301ddb55992SZi Shen Lim emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 302a2284d91SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 303ddb55992SZi Shen Lim emit(A64_BR(tmp), ctx); 304ddb55992SZi Shen Lim 305ddb55992SZi Shen Lim /* out: */ 306ddb55992SZi Shen Lim if (out_offset == -1) 307ddb55992SZi Shen Lim out_offset = cur_offset; 308ddb55992SZi Shen Lim if (cur_offset != out_offset) { 309ddb55992SZi Shen Lim pr_err_once("tail_call out_offset = %d, expected %d!\n", 310ddb55992SZi Shen Lim cur_offset, out_offset); 311ddb55992SZi Shen Lim return -1; 312ddb55992SZi Shen Lim } 313ddb55992SZi Shen Lim return 0; 314ddb55992SZi Shen Lim #undef cur_offset 315ddb55992SZi Shen Lim #undef jmp_offset 316e54bcde3SZi Shen Lim } 317e54bcde3SZi Shen Lim 318e54bcde3SZi Shen Lim static void build_epilogue(struct jit_ctx *ctx) 319e54bcde3SZi Shen Lim { 320e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 321e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 322e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 323e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 324e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 325e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 326e54bcde3SZi Shen Lim 327e54bcde3SZi Shen Lim /* We're done with BPF stack */ 328f1c9eed7SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 329e54bcde3SZi Shen Lim 330ec0738dbSYang Shi /* Restore fs (x25) and x26 */ 331ec0738dbSYang Shi emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 332ec0738dbSYang Shi 333e54bcde3SZi Shen Lim /* Restore callee-saved register */ 334e54bcde3SZi Shen Lim emit(A64_POP(r8, r9, A64_SP), ctx); 335e54bcde3SZi Shen Lim emit(A64_POP(r6, r7, A64_SP), ctx); 336e54bcde3SZi Shen Lim 337ec0738dbSYang Shi /* Restore FP/LR registers */ 338ec0738dbSYang Shi emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 339e54bcde3SZi Shen Lim 340e54bcde3SZi Shen Lim /* Set return value */ 341e54bcde3SZi Shen Lim emit(A64_MOV(1, A64_R(0), r0), ctx); 342e54bcde3SZi Shen Lim 343e54bcde3SZi Shen Lim emit(A64_RET(A64_LR), ctx); 344e54bcde3SZi Shen Lim } 345e54bcde3SZi Shen Lim 34630d3d94cSZi Shen Lim /* JITs an eBPF instruction. 34730d3d94cSZi Shen Lim * Returns: 34830d3d94cSZi Shen Lim * 0 - successfully JITed an 8-byte eBPF instruction. 34930d3d94cSZi Shen Lim * >0 - successfully JITed a 16-byte eBPF instruction. 35030d3d94cSZi Shen Lim * <0 - failed to JIT. 35130d3d94cSZi Shen Lim */ 352e54bcde3SZi Shen Lim static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx) 353e54bcde3SZi Shen Lim { 354e54bcde3SZi Shen Lim const u8 code = insn->code; 355e54bcde3SZi Shen Lim const u8 dst = bpf2a64[insn->dst_reg]; 356e54bcde3SZi Shen Lim const u8 src = bpf2a64[insn->src_reg]; 357e54bcde3SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 358e54bcde3SZi Shen Lim const u8 tmp2 = bpf2a64[TMP_REG_2]; 3597005cadeSDaniel Borkmann const u8 tmp3 = bpf2a64[TMP_REG_3]; 360e54bcde3SZi Shen Lim const s16 off = insn->off; 361e54bcde3SZi Shen Lim const s32 imm = insn->imm; 362e54bcde3SZi Shen Lim const int i = insn - ctx->prog->insnsi; 363e54bcde3SZi Shen Lim const bool is64 = BPF_CLASS(code) == BPF_ALU64; 36485f68fe8SDaniel Borkmann const bool isdw = BPF_SIZE(code) == BPF_DW; 365e54bcde3SZi Shen Lim u8 jmp_cond; 366e54bcde3SZi Shen Lim s32 jmp_offset; 367e54bcde3SZi Shen Lim 368251599e1SZi Shen Lim #define check_imm(bits, imm) do { \ 369251599e1SZi Shen Lim if ((((imm) > 0) && ((imm) >> (bits))) || \ 370251599e1SZi Shen Lim (((imm) < 0) && (~(imm) >> (bits)))) { \ 371251599e1SZi Shen Lim pr_info("[%2d] imm=%d(0x%x) out of range\n", \ 372251599e1SZi Shen Lim i, imm, imm); \ 373251599e1SZi Shen Lim return -EINVAL; \ 374251599e1SZi Shen Lim } \ 375251599e1SZi Shen Lim } while (0) 376251599e1SZi Shen Lim #define check_imm19(imm) check_imm(19, imm) 377251599e1SZi Shen Lim #define check_imm26(imm) check_imm(26, imm) 378251599e1SZi Shen Lim 379e54bcde3SZi Shen Lim switch (code) { 380e54bcde3SZi Shen Lim /* dst = src */ 381e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_X: 382e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_X: 383e54bcde3SZi Shen Lim emit(A64_MOV(is64, dst, src), ctx); 384e54bcde3SZi Shen Lim break; 385e54bcde3SZi Shen Lim /* dst = dst OP src */ 386e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_X: 387e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_X: 388e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, src), ctx); 389e54bcde3SZi Shen Lim break; 390e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_X: 391e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_X: 392e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, src), ctx); 393e54bcde3SZi Shen Lim break; 394e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_X: 395e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_X: 396e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, src), ctx); 397e54bcde3SZi Shen Lim break; 398e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_X: 399e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_X: 400e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, src), ctx); 401e54bcde3SZi Shen Lim break; 402e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_X: 403e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_X: 404e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, src), ctx); 405e54bcde3SZi Shen Lim break; 406e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_X: 407e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_X: 408e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, src), ctx); 409e54bcde3SZi Shen Lim break; 410e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_X: 411e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_X: 412e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_X: 413e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_X: 41414e589ffSZi Shen Lim switch (BPF_OP(code)) { 41514e589ffSZi Shen Lim case BPF_DIV: 416e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, src), ctx); 417e54bcde3SZi Shen Lim break; 41814e589ffSZi Shen Lim case BPF_MOD: 419e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, src), ctx); 420e54bcde3SZi Shen Lim emit(A64_MUL(is64, tmp, tmp, src), ctx); 421e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 422e54bcde3SZi Shen Lim break; 42314e589ffSZi Shen Lim } 42414e589ffSZi Shen Lim break; 425d65a634aSZi Shen Lim case BPF_ALU | BPF_LSH | BPF_X: 426d65a634aSZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_X: 427d65a634aSZi Shen Lim emit(A64_LSLV(is64, dst, dst, src), ctx); 428d65a634aSZi Shen Lim break; 429d65a634aSZi Shen Lim case BPF_ALU | BPF_RSH | BPF_X: 430d65a634aSZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_X: 431d65a634aSZi Shen Lim emit(A64_LSRV(is64, dst, dst, src), ctx); 432d65a634aSZi Shen Lim break; 433d65a634aSZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_X: 434d65a634aSZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_X: 435d65a634aSZi Shen Lim emit(A64_ASRV(is64, dst, dst, src), ctx); 436d65a634aSZi Shen Lim break; 437e54bcde3SZi Shen Lim /* dst = -dst */ 438e54bcde3SZi Shen Lim case BPF_ALU | BPF_NEG: 439e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_NEG: 440e54bcde3SZi Shen Lim emit(A64_NEG(is64, dst, dst), ctx); 441e54bcde3SZi Shen Lim break; 442e54bcde3SZi Shen Lim /* dst = BSWAP##imm(dst) */ 443e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_LE: 444e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_BE: 445e54bcde3SZi Shen Lim #ifdef CONFIG_CPU_BIG_ENDIAN 446e54bcde3SZi Shen Lim if (BPF_SRC(code) == BPF_FROM_BE) 447d63903bbSXi Wang goto emit_bswap_uxt; 448e54bcde3SZi Shen Lim #else /* !CONFIG_CPU_BIG_ENDIAN */ 449e54bcde3SZi Shen Lim if (BPF_SRC(code) == BPF_FROM_LE) 450d63903bbSXi Wang goto emit_bswap_uxt; 451e54bcde3SZi Shen Lim #endif 452e54bcde3SZi Shen Lim switch (imm) { 453e54bcde3SZi Shen Lim case 16: 454e54bcde3SZi Shen Lim emit(A64_REV16(is64, dst, dst), ctx); 455d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 456d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 457e54bcde3SZi Shen Lim break; 458e54bcde3SZi Shen Lim case 32: 459e54bcde3SZi Shen Lim emit(A64_REV32(is64, dst, dst), ctx); 460d63903bbSXi Wang /* upper 32 bits already cleared */ 461e54bcde3SZi Shen Lim break; 462e54bcde3SZi Shen Lim case 64: 463e54bcde3SZi Shen Lim emit(A64_REV64(dst, dst), ctx); 464e54bcde3SZi Shen Lim break; 465e54bcde3SZi Shen Lim } 466e54bcde3SZi Shen Lim break; 467d63903bbSXi Wang emit_bswap_uxt: 468d63903bbSXi Wang switch (imm) { 469d63903bbSXi Wang case 16: 470d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 471d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 472d63903bbSXi Wang break; 473d63903bbSXi Wang case 32: 474d63903bbSXi Wang /* zero-extend 32 bits into 64 bits */ 475d63903bbSXi Wang emit(A64_UXTW(is64, dst, dst), ctx); 476d63903bbSXi Wang break; 477d63903bbSXi Wang case 64: 478d63903bbSXi Wang /* nop */ 479d63903bbSXi Wang break; 480d63903bbSXi Wang } 481d63903bbSXi Wang break; 482e54bcde3SZi Shen Lim /* dst = imm */ 483e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_K: 484e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_K: 485e54bcde3SZi Shen Lim emit_a64_mov_i(is64, dst, imm, ctx); 486e54bcde3SZi Shen Lim break; 487e54bcde3SZi Shen Lim /* dst = dst OP imm */ 488e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_K: 489e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_K: 490e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 491e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, tmp), ctx); 492e54bcde3SZi Shen Lim break; 493e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_K: 494e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_K: 495e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 496e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 497e54bcde3SZi Shen Lim break; 498e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_K: 499e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_K: 500e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 501e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, tmp), ctx); 502e54bcde3SZi Shen Lim break; 503e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_K: 504e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_K: 505e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 506e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, tmp), ctx); 507e54bcde3SZi Shen Lim break; 508e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_K: 509e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_K: 510e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 511e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, tmp), ctx); 512e54bcde3SZi Shen Lim break; 513e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_K: 514e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_K: 515e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 516e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, tmp), ctx); 517e54bcde3SZi Shen Lim break; 518e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_K: 519e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_K: 520e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 521e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, tmp), ctx); 522e54bcde3SZi Shen Lim break; 523e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_K: 524e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_K: 525e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp2, imm, ctx); 526e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 527e54bcde3SZi Shen Lim emit(A64_MUL(is64, tmp, tmp, tmp2), ctx); 528e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 529e54bcde3SZi Shen Lim break; 530e54bcde3SZi Shen Lim case BPF_ALU | BPF_LSH | BPF_K: 531e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_K: 532e54bcde3SZi Shen Lim emit(A64_LSL(is64, dst, dst, imm), ctx); 533e54bcde3SZi Shen Lim break; 534e54bcde3SZi Shen Lim case BPF_ALU | BPF_RSH | BPF_K: 535e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_K: 536e54bcde3SZi Shen Lim emit(A64_LSR(is64, dst, dst, imm), ctx); 537e54bcde3SZi Shen Lim break; 538e54bcde3SZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_K: 539e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_K: 540e54bcde3SZi Shen Lim emit(A64_ASR(is64, dst, dst, imm), ctx); 541e54bcde3SZi Shen Lim break; 542e54bcde3SZi Shen Lim 543e54bcde3SZi Shen Lim /* JUMP off */ 544e54bcde3SZi Shen Lim case BPF_JMP | BPF_JA: 545e54bcde3SZi Shen Lim jmp_offset = bpf2a64_offset(i + off, i, ctx); 546e54bcde3SZi Shen Lim check_imm26(jmp_offset); 547e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 548e54bcde3SZi Shen Lim break; 549e54bcde3SZi Shen Lim /* IF (dst COND src) JUMP off */ 550e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_X: 551e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_X: 552c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_X: 553e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_X: 554c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_X: 555e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_X: 556e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_X: 557c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_X: 558e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_X: 559c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_X: 560e54bcde3SZi Shen Lim emit(A64_CMP(1, dst, src), ctx); 561e54bcde3SZi Shen Lim emit_cond_jmp: 562e54bcde3SZi Shen Lim jmp_offset = bpf2a64_offset(i + off, i, ctx); 563e54bcde3SZi Shen Lim check_imm19(jmp_offset); 564e54bcde3SZi Shen Lim switch (BPF_OP(code)) { 565e54bcde3SZi Shen Lim case BPF_JEQ: 566e54bcde3SZi Shen Lim jmp_cond = A64_COND_EQ; 567e54bcde3SZi Shen Lim break; 568e54bcde3SZi Shen Lim case BPF_JGT: 569e54bcde3SZi Shen Lim jmp_cond = A64_COND_HI; 570e54bcde3SZi Shen Lim break; 571c362b2f3SDaniel Borkmann case BPF_JLT: 572c362b2f3SDaniel Borkmann jmp_cond = A64_COND_CC; 573c362b2f3SDaniel Borkmann break; 574e54bcde3SZi Shen Lim case BPF_JGE: 575e54bcde3SZi Shen Lim jmp_cond = A64_COND_CS; 576e54bcde3SZi Shen Lim break; 577c362b2f3SDaniel Borkmann case BPF_JLE: 578c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LS; 579c362b2f3SDaniel Borkmann break; 58098397fc5SZi Shen Lim case BPF_JSET: 581e54bcde3SZi Shen Lim case BPF_JNE: 582e54bcde3SZi Shen Lim jmp_cond = A64_COND_NE; 583e54bcde3SZi Shen Lim break; 584e54bcde3SZi Shen Lim case BPF_JSGT: 585e54bcde3SZi Shen Lim jmp_cond = A64_COND_GT; 586e54bcde3SZi Shen Lim break; 587c362b2f3SDaniel Borkmann case BPF_JSLT: 588c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LT; 589c362b2f3SDaniel Borkmann break; 590e54bcde3SZi Shen Lim case BPF_JSGE: 591e54bcde3SZi Shen Lim jmp_cond = A64_COND_GE; 592e54bcde3SZi Shen Lim break; 593c362b2f3SDaniel Borkmann case BPF_JSLE: 594c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LE; 595c362b2f3SDaniel Borkmann break; 596e54bcde3SZi Shen Lim default: 597e54bcde3SZi Shen Lim return -EFAULT; 598e54bcde3SZi Shen Lim } 599e54bcde3SZi Shen Lim emit(A64_B_(jmp_cond, jmp_offset), ctx); 600e54bcde3SZi Shen Lim break; 601e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_X: 602e54bcde3SZi Shen Lim emit(A64_TST(1, dst, src), ctx); 603e54bcde3SZi Shen Lim goto emit_cond_jmp; 604e54bcde3SZi Shen Lim /* IF (dst COND imm) JUMP off */ 605e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_K: 606e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_K: 607c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_K: 608e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_K: 609c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_K: 610e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_K: 611e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_K: 612c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_K: 613e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_K: 614c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_K: 615e54bcde3SZi Shen Lim emit_a64_mov_i(1, tmp, imm, ctx); 616e54bcde3SZi Shen Lim emit(A64_CMP(1, dst, tmp), ctx); 617e54bcde3SZi Shen Lim goto emit_cond_jmp; 618e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_K: 619e54bcde3SZi Shen Lim emit_a64_mov_i(1, tmp, imm, ctx); 620e54bcde3SZi Shen Lim emit(A64_TST(1, dst, tmp), ctx); 621e54bcde3SZi Shen Lim goto emit_cond_jmp; 622e54bcde3SZi Shen Lim /* function call */ 623e54bcde3SZi Shen Lim case BPF_JMP | BPF_CALL: 624e54bcde3SZi Shen Lim { 625e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 626e54bcde3SZi Shen Lim const u64 func = (u64)__bpf_call_base + imm; 627e54bcde3SZi Shen Lim 628db496944SAlexei Starovoitov if (ctx->prog->is_func) 629db496944SAlexei Starovoitov emit_addr_mov_i64(tmp, func, ctx); 630db496944SAlexei Starovoitov else 631e54bcde3SZi Shen Lim emit_a64_mov_i64(tmp, func, ctx); 632e54bcde3SZi Shen Lim emit(A64_BLR(tmp), ctx); 633e54bcde3SZi Shen Lim emit(A64_MOV(1, r0, A64_R(0)), ctx); 634e54bcde3SZi Shen Lim break; 635e54bcde3SZi Shen Lim } 636ddb55992SZi Shen Lim /* tail call */ 63771189fa9SAlexei Starovoitov case BPF_JMP | BPF_TAIL_CALL: 638ddb55992SZi Shen Lim if (emit_bpf_tail_call(ctx)) 639ddb55992SZi Shen Lim return -EFAULT; 640ddb55992SZi Shen Lim break; 641e54bcde3SZi Shen Lim /* function return */ 642e54bcde3SZi Shen Lim case BPF_JMP | BPF_EXIT: 64351c9fbb1SZi Shen Lim /* Optimization: when last instruction is EXIT, 64451c9fbb1SZi Shen Lim simply fallthrough to epilogue. */ 645e54bcde3SZi Shen Lim if (i == ctx->prog->len - 1) 646e54bcde3SZi Shen Lim break; 647e54bcde3SZi Shen Lim jmp_offset = epilogue_offset(ctx); 648e54bcde3SZi Shen Lim check_imm26(jmp_offset); 649e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 650e54bcde3SZi Shen Lim break; 651e54bcde3SZi Shen Lim 65230d3d94cSZi Shen Lim /* dst = imm64 */ 65330d3d94cSZi Shen Lim case BPF_LD | BPF_IMM | BPF_DW: 65430d3d94cSZi Shen Lim { 65530d3d94cSZi Shen Lim const struct bpf_insn insn1 = insn[1]; 65630d3d94cSZi Shen Lim u64 imm64; 65730d3d94cSZi Shen Lim 6581e4df6b7SXi Wang imm64 = (u64)insn1.imm << 32 | (u32)imm; 65930d3d94cSZi Shen Lim emit_a64_mov_i64(dst, imm64, ctx); 66030d3d94cSZi Shen Lim 66130d3d94cSZi Shen Lim return 1; 66230d3d94cSZi Shen Lim } 66330d3d94cSZi Shen Lim 664e54bcde3SZi Shen Lim /* LDX: dst = *(size *)(src + off) */ 665e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_W: 666e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_H: 667e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_B: 668e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_DW: 669e54bcde3SZi Shen Lim emit_a64_mov_i(1, tmp, off, ctx); 670e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 671e54bcde3SZi Shen Lim case BPF_W: 672e54bcde3SZi Shen Lim emit(A64_LDR32(dst, src, tmp), ctx); 673e54bcde3SZi Shen Lim break; 674e54bcde3SZi Shen Lim case BPF_H: 675e54bcde3SZi Shen Lim emit(A64_LDRH(dst, src, tmp), ctx); 676e54bcde3SZi Shen Lim break; 677e54bcde3SZi Shen Lim case BPF_B: 678e54bcde3SZi Shen Lim emit(A64_LDRB(dst, src, tmp), ctx); 679e54bcde3SZi Shen Lim break; 680e54bcde3SZi Shen Lim case BPF_DW: 681e54bcde3SZi Shen Lim emit(A64_LDR64(dst, src, tmp), ctx); 682e54bcde3SZi Shen Lim break; 683e54bcde3SZi Shen Lim } 684e54bcde3SZi Shen Lim break; 685e54bcde3SZi Shen Lim 686e54bcde3SZi Shen Lim /* ST: *(size *)(dst + off) = imm */ 687e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_W: 688e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_H: 689e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_B: 690e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_DW: 691df849ba3SYang Shi /* Load imm to a register then store it */ 692df849ba3SYang Shi emit_a64_mov_i(1, tmp2, off, ctx); 693df849ba3SYang Shi emit_a64_mov_i(1, tmp, imm, ctx); 694df849ba3SYang Shi switch (BPF_SIZE(code)) { 695df849ba3SYang Shi case BPF_W: 696df849ba3SYang Shi emit(A64_STR32(tmp, dst, tmp2), ctx); 697df849ba3SYang Shi break; 698df849ba3SYang Shi case BPF_H: 699df849ba3SYang Shi emit(A64_STRH(tmp, dst, tmp2), ctx); 700df849ba3SYang Shi break; 701df849ba3SYang Shi case BPF_B: 702df849ba3SYang Shi emit(A64_STRB(tmp, dst, tmp2), ctx); 703df849ba3SYang Shi break; 704df849ba3SYang Shi case BPF_DW: 705df849ba3SYang Shi emit(A64_STR64(tmp, dst, tmp2), ctx); 706df849ba3SYang Shi break; 707df849ba3SYang Shi } 708df849ba3SYang Shi break; 709e54bcde3SZi Shen Lim 710e54bcde3SZi Shen Lim /* STX: *(size *)(dst + off) = src */ 711e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_W: 712e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_H: 713e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_B: 714e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_DW: 715e54bcde3SZi Shen Lim emit_a64_mov_i(1, tmp, off, ctx); 716e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 717e54bcde3SZi Shen Lim case BPF_W: 718e54bcde3SZi Shen Lim emit(A64_STR32(src, dst, tmp), ctx); 719e54bcde3SZi Shen Lim break; 720e54bcde3SZi Shen Lim case BPF_H: 721e54bcde3SZi Shen Lim emit(A64_STRH(src, dst, tmp), ctx); 722e54bcde3SZi Shen Lim break; 723e54bcde3SZi Shen Lim case BPF_B: 724e54bcde3SZi Shen Lim emit(A64_STRB(src, dst, tmp), ctx); 725e54bcde3SZi Shen Lim break; 726e54bcde3SZi Shen Lim case BPF_DW: 727e54bcde3SZi Shen Lim emit(A64_STR64(src, dst, tmp), ctx); 728e54bcde3SZi Shen Lim break; 729e54bcde3SZi Shen Lim } 730e54bcde3SZi Shen Lim break; 731e54bcde3SZi Shen Lim /* STX XADD: lock *(u32 *)(dst + off) += src */ 732e54bcde3SZi Shen Lim case BPF_STX | BPF_XADD | BPF_W: 733e54bcde3SZi Shen Lim /* STX XADD: lock *(u64 *)(dst + off) += src */ 734e54bcde3SZi Shen Lim case BPF_STX | BPF_XADD | BPF_DW: 73585f68fe8SDaniel Borkmann emit_a64_mov_i(1, tmp, off, ctx); 73685f68fe8SDaniel Borkmann emit(A64_ADD(1, tmp, tmp, dst), ctx); 73785f68fe8SDaniel Borkmann emit(A64_PRFM(tmp, PST, L1, STRM), ctx); 73885f68fe8SDaniel Borkmann emit(A64_LDXR(isdw, tmp2, tmp), ctx); 73985f68fe8SDaniel Borkmann emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 7407005cadeSDaniel Borkmann emit(A64_STXR(isdw, tmp2, tmp, tmp3), ctx); 74185f68fe8SDaniel Borkmann jmp_offset = -3; 74285f68fe8SDaniel Borkmann check_imm19(jmp_offset); 7437005cadeSDaniel Borkmann emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 74485f68fe8SDaniel Borkmann break; 745e54bcde3SZi Shen Lim 746e54bcde3SZi Shen Lim default: 747e54bcde3SZi Shen Lim pr_err_once("unknown opcode %02x\n", code); 748e54bcde3SZi Shen Lim return -EINVAL; 749e54bcde3SZi Shen Lim } 750e54bcde3SZi Shen Lim 751e54bcde3SZi Shen Lim return 0; 752e54bcde3SZi Shen Lim } 753e54bcde3SZi Shen Lim 754e54bcde3SZi Shen Lim static int build_body(struct jit_ctx *ctx) 755e54bcde3SZi Shen Lim { 756e54bcde3SZi Shen Lim const struct bpf_prog *prog = ctx->prog; 757e54bcde3SZi Shen Lim int i; 758e54bcde3SZi Shen Lim 759e54bcde3SZi Shen Lim for (i = 0; i < prog->len; i++) { 760e54bcde3SZi Shen Lim const struct bpf_insn *insn = &prog->insnsi[i]; 761e54bcde3SZi Shen Lim int ret; 762e54bcde3SZi Shen Lim 7638eee539dSXi Wang ret = build_insn(insn, ctx); 76430d3d94cSZi Shen Lim if (ret > 0) { 76530d3d94cSZi Shen Lim i++; 766ddc665a4SDaniel Borkmann if (ctx->image == NULL) 767ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 76830d3d94cSZi Shen Lim continue; 76930d3d94cSZi Shen Lim } 770ddc665a4SDaniel Borkmann if (ctx->image == NULL) 771ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 772e54bcde3SZi Shen Lim if (ret) 773e54bcde3SZi Shen Lim return ret; 774e54bcde3SZi Shen Lim } 775e54bcde3SZi Shen Lim 776e54bcde3SZi Shen Lim return 0; 777e54bcde3SZi Shen Lim } 778e54bcde3SZi Shen Lim 77942ff712bSZi Shen Lim static int validate_code(struct jit_ctx *ctx) 78042ff712bSZi Shen Lim { 78142ff712bSZi Shen Lim int i; 78242ff712bSZi Shen Lim 78342ff712bSZi Shen Lim for (i = 0; i < ctx->idx; i++) { 78442ff712bSZi Shen Lim u32 a64_insn = le32_to_cpu(ctx->image[i]); 78542ff712bSZi Shen Lim 78642ff712bSZi Shen Lim if (a64_insn == AARCH64_BREAK_FAULT) 78742ff712bSZi Shen Lim return -1; 78842ff712bSZi Shen Lim } 78942ff712bSZi Shen Lim 79042ff712bSZi Shen Lim return 0; 79142ff712bSZi Shen Lim } 79242ff712bSZi Shen Lim 793e54bcde3SZi Shen Lim static inline void bpf_flush_icache(void *start, void *end) 794e54bcde3SZi Shen Lim { 795e54bcde3SZi Shen Lim flush_icache_range((unsigned long)start, (unsigned long)end); 796e54bcde3SZi Shen Lim } 797e54bcde3SZi Shen Lim 798db496944SAlexei Starovoitov struct arm64_jit_data { 799db496944SAlexei Starovoitov struct bpf_binary_header *header; 800db496944SAlexei Starovoitov u8 *image; 801db496944SAlexei Starovoitov struct jit_ctx ctx; 802db496944SAlexei Starovoitov }; 803db496944SAlexei Starovoitov 804d1c55ab5SDaniel Borkmann struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 805e54bcde3SZi Shen Lim { 80626eb042eSDaniel Borkmann struct bpf_prog *tmp, *orig_prog = prog; 807b569c1c6SDaniel Borkmann struct bpf_binary_header *header; 808db496944SAlexei Starovoitov struct arm64_jit_data *jit_data; 80926eb042eSDaniel Borkmann bool tmp_blinded = false; 810db496944SAlexei Starovoitov bool extra_pass = false; 811e54bcde3SZi Shen Lim struct jit_ctx ctx; 812e54bcde3SZi Shen Lim int image_size; 813b569c1c6SDaniel Borkmann u8 *image_ptr; 814e54bcde3SZi Shen Lim 81560b58afcSAlexei Starovoitov if (!prog->jit_requested) 81626eb042eSDaniel Borkmann return orig_prog; 81726eb042eSDaniel Borkmann 81826eb042eSDaniel Borkmann tmp = bpf_jit_blind_constants(prog); 81926eb042eSDaniel Borkmann /* If blinding was requested and we failed during blinding, 82026eb042eSDaniel Borkmann * we must fall back to the interpreter. 82126eb042eSDaniel Borkmann */ 82226eb042eSDaniel Borkmann if (IS_ERR(tmp)) 82326eb042eSDaniel Borkmann return orig_prog; 82426eb042eSDaniel Borkmann if (tmp != prog) { 82526eb042eSDaniel Borkmann tmp_blinded = true; 82626eb042eSDaniel Borkmann prog = tmp; 82726eb042eSDaniel Borkmann } 828e54bcde3SZi Shen Lim 829db496944SAlexei Starovoitov jit_data = prog->aux->jit_data; 830db496944SAlexei Starovoitov if (!jit_data) { 831db496944SAlexei Starovoitov jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 832db496944SAlexei Starovoitov if (!jit_data) { 833db496944SAlexei Starovoitov prog = orig_prog; 834db496944SAlexei Starovoitov goto out; 835db496944SAlexei Starovoitov } 836db496944SAlexei Starovoitov prog->aux->jit_data = jit_data; 837db496944SAlexei Starovoitov } 838db496944SAlexei Starovoitov if (jit_data->ctx.offset) { 839db496944SAlexei Starovoitov ctx = jit_data->ctx; 840db496944SAlexei Starovoitov image_ptr = jit_data->image; 841db496944SAlexei Starovoitov header = jit_data->header; 842db496944SAlexei Starovoitov extra_pass = true; 8435ee7f784SAlexei Starovoitov image_size = sizeof(u32) * ctx.idx; 844db496944SAlexei Starovoitov goto skip_init_ctx; 845db496944SAlexei Starovoitov } 846e54bcde3SZi Shen Lim memset(&ctx, 0, sizeof(ctx)); 847e54bcde3SZi Shen Lim ctx.prog = prog; 848e54bcde3SZi Shen Lim 849e54bcde3SZi Shen Lim ctx.offset = kcalloc(prog->len, sizeof(int), GFP_KERNEL); 85026eb042eSDaniel Borkmann if (ctx.offset == NULL) { 85126eb042eSDaniel Borkmann prog = orig_prog; 852db496944SAlexei Starovoitov goto out_off; 85326eb042eSDaniel Borkmann } 854e54bcde3SZi Shen Lim 855e54bcde3SZi Shen Lim /* 1. Initial fake pass to compute ctx->idx. */ 856e54bcde3SZi Shen Lim 8574c1cd4fdSYang Shi /* Fake pass to fill in ctx->offset. */ 85826eb042eSDaniel Borkmann if (build_body(&ctx)) { 85926eb042eSDaniel Borkmann prog = orig_prog; 86026eb042eSDaniel Borkmann goto out_off; 86126eb042eSDaniel Borkmann } 862e54bcde3SZi Shen Lim 863ddb55992SZi Shen Lim if (build_prologue(&ctx)) { 864ddb55992SZi Shen Lim prog = orig_prog; 865ddb55992SZi Shen Lim goto out_off; 866ddb55992SZi Shen Lim } 86751c9fbb1SZi Shen Lim 86851c9fbb1SZi Shen Lim ctx.epilogue_offset = ctx.idx; 869e54bcde3SZi Shen Lim build_epilogue(&ctx); 870e54bcde3SZi Shen Lim 871e54bcde3SZi Shen Lim /* Now we know the actual image size. */ 872e54bcde3SZi Shen Lim image_size = sizeof(u32) * ctx.idx; 873b569c1c6SDaniel Borkmann header = bpf_jit_binary_alloc(image_size, &image_ptr, 874b569c1c6SDaniel Borkmann sizeof(u32), jit_fill_hole); 87526eb042eSDaniel Borkmann if (header == NULL) { 87626eb042eSDaniel Borkmann prog = orig_prog; 87726eb042eSDaniel Borkmann goto out_off; 87826eb042eSDaniel Borkmann } 879e54bcde3SZi Shen Lim 880e54bcde3SZi Shen Lim /* 2. Now, the actual pass. */ 881e54bcde3SZi Shen Lim 882425e1ed7SLuc Van Oostenryck ctx.image = (__le32 *)image_ptr; 883db496944SAlexei Starovoitov skip_init_ctx: 884e54bcde3SZi Shen Lim ctx.idx = 0; 885b569c1c6SDaniel Borkmann 886e54bcde3SZi Shen Lim build_prologue(&ctx); 887e54bcde3SZi Shen Lim 88860ef0494SDaniel Borkmann if (build_body(&ctx)) { 889b569c1c6SDaniel Borkmann bpf_jit_binary_free(header); 89026eb042eSDaniel Borkmann prog = orig_prog; 89126eb042eSDaniel Borkmann goto out_off; 89260ef0494SDaniel Borkmann } 893e54bcde3SZi Shen Lim 894e54bcde3SZi Shen Lim build_epilogue(&ctx); 895e54bcde3SZi Shen Lim 89642ff712bSZi Shen Lim /* 3. Extra pass to validate JITed code. */ 89742ff712bSZi Shen Lim if (validate_code(&ctx)) { 89842ff712bSZi Shen Lim bpf_jit_binary_free(header); 89926eb042eSDaniel Borkmann prog = orig_prog; 90026eb042eSDaniel Borkmann goto out_off; 90142ff712bSZi Shen Lim } 90242ff712bSZi Shen Lim 903e54bcde3SZi Shen Lim /* And we're done. */ 904e54bcde3SZi Shen Lim if (bpf_jit_enable > 1) 905e54bcde3SZi Shen Lim bpf_jit_dump(prog->len, image_size, 2, ctx.image); 906e54bcde3SZi Shen Lim 907c3d4c682SDaniel Borkmann bpf_flush_icache(header, ctx.image + ctx.idx); 908b569c1c6SDaniel Borkmann 909db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 910db496944SAlexei Starovoitov if (extra_pass && ctx.idx != jit_data->ctx.idx) { 911db496944SAlexei Starovoitov pr_err_once("multi-func JIT bug %d != %d\n", 912db496944SAlexei Starovoitov ctx.idx, jit_data->ctx.idx); 913db496944SAlexei Starovoitov bpf_jit_binary_free(header); 914db496944SAlexei Starovoitov prog->bpf_func = NULL; 915db496944SAlexei Starovoitov prog->jited = 0; 916db496944SAlexei Starovoitov goto out_off; 917db496944SAlexei Starovoitov } 9189d876e79SDaniel Borkmann bpf_jit_binary_lock_ro(header); 919db496944SAlexei Starovoitov } else { 920db496944SAlexei Starovoitov jit_data->ctx = ctx; 921db496944SAlexei Starovoitov jit_data->image = image_ptr; 922db496944SAlexei Starovoitov jit_data->header = header; 923db496944SAlexei Starovoitov } 924e54bcde3SZi Shen Lim prog->bpf_func = (void *)ctx.image; 925a91263d5SDaniel Borkmann prog->jited = 1; 926783d28ddSMartin KaFai Lau prog->jited_len = image_size; 92726eb042eSDaniel Borkmann 928db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 92926eb042eSDaniel Borkmann out_off: 930e54bcde3SZi Shen Lim kfree(ctx.offset); 931db496944SAlexei Starovoitov kfree(jit_data); 932db496944SAlexei Starovoitov prog->aux->jit_data = NULL; 933db496944SAlexei Starovoitov } 93426eb042eSDaniel Borkmann out: 93526eb042eSDaniel Borkmann if (tmp_blinded) 93626eb042eSDaniel Borkmann bpf_jit_prog_release_other(prog, prog == orig_prog ? 93726eb042eSDaniel Borkmann tmp : orig_prog); 938d1c55ab5SDaniel Borkmann return prog; 939e54bcde3SZi Shen Lim } 940