1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2e54bcde3SZi Shen Lim /* 3e54bcde3SZi Shen Lim * BPF JIT compiler for ARM64 4e54bcde3SZi Shen Lim * 542ff712bSZi Shen Lim * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com> 6e54bcde3SZi Shen Lim */ 7e54bcde3SZi Shen Lim 8e54bcde3SZi Shen Lim #define pr_fmt(fmt) "bpf_jit: " fmt 9e54bcde3SZi Shen Lim 1080083428SJean-Philippe Brucker #include <linux/bitfield.h> 11ddb55992SZi Shen Lim #include <linux/bpf.h> 12e54bcde3SZi Shen Lim #include <linux/filter.h> 13e54bcde3SZi Shen Lim #include <linux/printk.h> 14e54bcde3SZi Shen Lim #include <linux/slab.h> 15b569c1c6SDaniel Borkmann 16d6e2cc56SMark Rutland #include <asm/asm-extable.h> 17e54bcde3SZi Shen Lim #include <asm/byteorder.h> 18e54bcde3SZi Shen Lim #include <asm/cacheflush.h> 19b569c1c6SDaniel Borkmann #include <asm/debug-monitors.h> 203e00e39dSMark Rutland #include <asm/insn.h> 21d4bbc30bSLaura Abbott #include <asm/set_memory.h> 22e54bcde3SZi Shen Lim 23e54bcde3SZi Shen Lim #include "bpf_jit.h" 24e54bcde3SZi Shen Lim 2526eb042eSDaniel Borkmann #define TMP_REG_1 (MAX_BPF_JIT_REG + 0) 2626eb042eSDaniel Borkmann #define TMP_REG_2 (MAX_BPF_JIT_REG + 1) 27ddb55992SZi Shen Lim #define TCALL_CNT (MAX_BPF_JIT_REG + 2) 287005cadeSDaniel Borkmann #define TMP_REG_3 (MAX_BPF_JIT_REG + 3) 29*5b3d19b9SXu Kuohai #define FP_BOTTOM (MAX_BPF_JIT_REG + 4) 30e54bcde3SZi Shen Lim 311902472bSHou Tao #define check_imm(bits, imm) do { \ 321902472bSHou Tao if ((((imm) > 0) && ((imm) >> (bits))) || \ 331902472bSHou Tao (((imm) < 0) && (~(imm) >> (bits)))) { \ 341902472bSHou Tao pr_info("[%2d] imm=%d(0x%x) out of range\n", \ 351902472bSHou Tao i, imm, imm); \ 361902472bSHou Tao return -EINVAL; \ 371902472bSHou Tao } \ 381902472bSHou Tao } while (0) 391902472bSHou Tao #define check_imm19(imm) check_imm(19, imm) 401902472bSHou Tao #define check_imm26(imm) check_imm(26, imm) 411902472bSHou Tao 42e54bcde3SZi Shen Lim /* Map BPF registers to A64 registers */ 43e54bcde3SZi Shen Lim static const int bpf2a64[] = { 44e54bcde3SZi Shen Lim /* return value from in-kernel function, and exit value from eBPF */ 45e54bcde3SZi Shen Lim [BPF_REG_0] = A64_R(7), 46e54bcde3SZi Shen Lim /* arguments from eBPF program to in-kernel function */ 47e54bcde3SZi Shen Lim [BPF_REG_1] = A64_R(0), 48e54bcde3SZi Shen Lim [BPF_REG_2] = A64_R(1), 49e54bcde3SZi Shen Lim [BPF_REG_3] = A64_R(2), 50e54bcde3SZi Shen Lim [BPF_REG_4] = A64_R(3), 51e54bcde3SZi Shen Lim [BPF_REG_5] = A64_R(4), 52e54bcde3SZi Shen Lim /* callee saved registers that in-kernel function will preserve */ 53e54bcde3SZi Shen Lim [BPF_REG_6] = A64_R(19), 54e54bcde3SZi Shen Lim [BPF_REG_7] = A64_R(20), 55e54bcde3SZi Shen Lim [BPF_REG_8] = A64_R(21), 56e54bcde3SZi Shen Lim [BPF_REG_9] = A64_R(22), 57e54bcde3SZi Shen Lim /* read-only frame pointer to access stack */ 58ec0738dbSYang Shi [BPF_REG_FP] = A64_R(25), 5906edc59cSChristoph Hellwig /* temporary registers for BPF JIT */ 604c1cd4fdSYang Shi [TMP_REG_1] = A64_R(10), 614c1cd4fdSYang Shi [TMP_REG_2] = A64_R(11), 627005cadeSDaniel Borkmann [TMP_REG_3] = A64_R(12), 63ddb55992SZi Shen Lim /* tail_call_cnt */ 64ddb55992SZi Shen Lim [TCALL_CNT] = A64_R(26), 6526eb042eSDaniel Borkmann /* temporary register for blinding constants */ 6626eb042eSDaniel Borkmann [BPF_REG_AX] = A64_R(9), 67*5b3d19b9SXu Kuohai [FP_BOTTOM] = A64_R(27), 68e54bcde3SZi Shen Lim }; 69e54bcde3SZi Shen Lim 70e54bcde3SZi Shen Lim struct jit_ctx { 71e54bcde3SZi Shen Lim const struct bpf_prog *prog; 72e54bcde3SZi Shen Lim int idx; 7351c9fbb1SZi Shen Lim int epilogue_offset; 74e54bcde3SZi Shen Lim int *offset; 7580083428SJean-Philippe Brucker int exentry_idx; 76425e1ed7SLuc Van Oostenryck __le32 *image; 77f1c9eed7SDaniel Borkmann u32 stack_size; 78*5b3d19b9SXu Kuohai int fpb_offset; 79e54bcde3SZi Shen Lim }; 80e54bcde3SZi Shen Lim 81e54bcde3SZi Shen Lim static inline void emit(const u32 insn, struct jit_ctx *ctx) 82e54bcde3SZi Shen Lim { 83e54bcde3SZi Shen Lim if (ctx->image != NULL) 84e54bcde3SZi Shen Lim ctx->image[ctx->idx] = cpu_to_le32(insn); 85e54bcde3SZi Shen Lim 86e54bcde3SZi Shen Lim ctx->idx++; 87e54bcde3SZi Shen Lim } 88e54bcde3SZi Shen Lim 89e54bcde3SZi Shen Lim static inline void emit_a64_mov_i(const int is64, const int reg, 90e54bcde3SZi Shen Lim const s32 val, struct jit_ctx *ctx) 91e54bcde3SZi Shen Lim { 92e54bcde3SZi Shen Lim u16 hi = val >> 16; 93e54bcde3SZi Shen Lim u16 lo = val & 0xffff; 94e54bcde3SZi Shen Lim 95e54bcde3SZi Shen Lim if (hi & 0x8000) { 96e54bcde3SZi Shen Lim if (hi == 0xffff) { 97e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx); 98e54bcde3SZi Shen Lim } else { 99e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx); 1006d2eea6fSDaniel Borkmann if (lo != 0xffff) 101e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, lo, 0), ctx); 102e54bcde3SZi Shen Lim } 103e54bcde3SZi Shen Lim } else { 104e54bcde3SZi Shen Lim emit(A64_MOVZ(is64, reg, lo, 0), ctx); 105e54bcde3SZi Shen Lim if (hi) 106e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, hi, 16), ctx); 107e54bcde3SZi Shen Lim } 108e54bcde3SZi Shen Lim } 109e54bcde3SZi Shen Lim 1106d2eea6fSDaniel Borkmann static int i64_i16_blocks(const u64 val, bool inverse) 1116d2eea6fSDaniel Borkmann { 1126d2eea6fSDaniel Borkmann return (((val >> 0) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1136d2eea6fSDaniel Borkmann (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1146d2eea6fSDaniel Borkmann (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1156d2eea6fSDaniel Borkmann (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000)); 1166d2eea6fSDaniel Borkmann } 1176d2eea6fSDaniel Borkmann 1186d2eea6fSDaniel Borkmann static inline void emit_a64_mov_i64(const int reg, const u64 val, 1196d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1206d2eea6fSDaniel Borkmann { 1216d2eea6fSDaniel Borkmann u64 nrm_tmp = val, rev_tmp = ~val; 1226d2eea6fSDaniel Borkmann bool inverse; 1236d2eea6fSDaniel Borkmann int shift; 1246d2eea6fSDaniel Borkmann 1256d2eea6fSDaniel Borkmann if (!(nrm_tmp >> 32)) 1266d2eea6fSDaniel Borkmann return emit_a64_mov_i(0, reg, (u32)val, ctx); 1276d2eea6fSDaniel Borkmann 1286d2eea6fSDaniel Borkmann inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false); 1296d2eea6fSDaniel Borkmann shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) : 1306d2eea6fSDaniel Borkmann (fls64(nrm_tmp) - 1)), 16), 0); 1316d2eea6fSDaniel Borkmann if (inverse) 1326d2eea6fSDaniel Borkmann emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx); 1336d2eea6fSDaniel Borkmann else 1346d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1356d2eea6fSDaniel Borkmann shift -= 16; 1366d2eea6fSDaniel Borkmann while (shift >= 0) { 1376d2eea6fSDaniel Borkmann if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000)) 1386d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1396d2eea6fSDaniel Borkmann shift -= 16; 1406d2eea6fSDaniel Borkmann } 1416d2eea6fSDaniel Borkmann } 1426d2eea6fSDaniel Borkmann 1436d2eea6fSDaniel Borkmann /* 144cc2b8ed1SArd Biesheuvel * Kernel addresses in the vmalloc space use at most 48 bits, and the 145cc2b8ed1SArd Biesheuvel * remaining bits are guaranteed to be 0x1. So we can compose the address 146cc2b8ed1SArd Biesheuvel * with a fixed length movn/movk/movk sequence. 1476d2eea6fSDaniel Borkmann */ 1486d2eea6fSDaniel Borkmann static inline void emit_addr_mov_i64(const int reg, const u64 val, 1496d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1506d2eea6fSDaniel Borkmann { 1516d2eea6fSDaniel Borkmann u64 tmp = val; 1526d2eea6fSDaniel Borkmann int shift = 0; 1536d2eea6fSDaniel Borkmann 154cc2b8ed1SArd Biesheuvel emit(A64_MOVN(1, reg, ~tmp & 0xffff, shift), ctx); 155cc2b8ed1SArd Biesheuvel while (shift < 32) { 1566d2eea6fSDaniel Borkmann tmp >>= 16; 1576d2eea6fSDaniel Borkmann shift += 16; 1586d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx); 1596d2eea6fSDaniel Borkmann } 1606d2eea6fSDaniel Borkmann } 1616d2eea6fSDaniel Borkmann 16232f6865cSIlias Apalodimas static inline int bpf2a64_offset(int bpf_insn, int off, 163e54bcde3SZi Shen Lim const struct jit_ctx *ctx) 164e54bcde3SZi Shen Lim { 16532f6865cSIlias Apalodimas /* BPF JMP offset is relative to the next instruction */ 16632f6865cSIlias Apalodimas bpf_insn++; 16732f6865cSIlias Apalodimas /* 16832f6865cSIlias Apalodimas * Whereas arm64 branch instructions encode the offset 16932f6865cSIlias Apalodimas * from the branch itself, so we must subtract 1 from the 17032f6865cSIlias Apalodimas * instruction offset. 17132f6865cSIlias Apalodimas */ 17232f6865cSIlias Apalodimas return ctx->offset[bpf_insn + off] - (ctx->offset[bpf_insn] - 1); 173e54bcde3SZi Shen Lim } 174e54bcde3SZi Shen Lim 175b569c1c6SDaniel Borkmann static void jit_fill_hole(void *area, unsigned int size) 176b569c1c6SDaniel Borkmann { 177425e1ed7SLuc Van Oostenryck __le32 *ptr; 178b569c1c6SDaniel Borkmann /* We are guaranteed to have aligned memory. */ 179b569c1c6SDaniel Borkmann for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 180b569c1c6SDaniel Borkmann *ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT); 181b569c1c6SDaniel Borkmann } 182b569c1c6SDaniel Borkmann 183e54bcde3SZi Shen Lim static inline int epilogue_offset(const struct jit_ctx *ctx) 184e54bcde3SZi Shen Lim { 18551c9fbb1SZi Shen Lim int to = ctx->epilogue_offset; 18651c9fbb1SZi Shen Lim int from = ctx->idx; 187e54bcde3SZi Shen Lim 188e54bcde3SZi Shen Lim return to - from; 189e54bcde3SZi Shen Lim } 190e54bcde3SZi Shen Lim 191fd868f14SLuke Nelson static bool is_addsub_imm(u32 imm) 192fd868f14SLuke Nelson { 193fd868f14SLuke Nelson /* Either imm12 or shifted imm12. */ 194fd868f14SLuke Nelson return !(imm & ~0xfff) || !(imm & ~0xfff000); 195fd868f14SLuke Nelson } 196fd868f14SLuke Nelson 1977db6c0f1SXu Kuohai /* 1987db6c0f1SXu Kuohai * There are 3 types of AArch64 LDR/STR (immediate) instruction: 1997db6c0f1SXu Kuohai * Post-index, Pre-index, Unsigned offset. 2007db6c0f1SXu Kuohai * 2017db6c0f1SXu Kuohai * For BPF ldr/str, the "unsigned offset" type is sufficient. 2027db6c0f1SXu Kuohai * 2037db6c0f1SXu Kuohai * "Unsigned offset" type LDR(immediate) format: 2047db6c0f1SXu Kuohai * 2057db6c0f1SXu Kuohai * 3 2 1 0 2067db6c0f1SXu Kuohai * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 2077db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2087db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 1| imm12 | Rn | Rt | 2097db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2107db6c0f1SXu Kuohai * scale 2117db6c0f1SXu Kuohai * 2127db6c0f1SXu Kuohai * "Unsigned offset" type STR(immediate) format: 2137db6c0f1SXu Kuohai * 3 2 1 0 2147db6c0f1SXu Kuohai * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 2157db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2167db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 0| imm12 | Rn | Rt | 2177db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2187db6c0f1SXu Kuohai * scale 2197db6c0f1SXu Kuohai * 2207db6c0f1SXu Kuohai * The offset is calculated from imm12 and scale in the following way: 2217db6c0f1SXu Kuohai * 2227db6c0f1SXu Kuohai * offset = (u64)imm12 << scale 2237db6c0f1SXu Kuohai */ 224*5b3d19b9SXu Kuohai static bool is_lsi_offset(int offset, int scale) 2257db6c0f1SXu Kuohai { 2267db6c0f1SXu Kuohai if (offset < 0) 2277db6c0f1SXu Kuohai return false; 2287db6c0f1SXu Kuohai 2297db6c0f1SXu Kuohai if (offset > (0xFFF << scale)) 2307db6c0f1SXu Kuohai return false; 2317db6c0f1SXu Kuohai 2327db6c0f1SXu Kuohai if (offset & ((1 << scale) - 1)) 2337db6c0f1SXu Kuohai return false; 2347db6c0f1SXu Kuohai 2357db6c0f1SXu Kuohai return true; 2367db6c0f1SXu Kuohai } 2377db6c0f1SXu Kuohai 238a2284d91SDaniel Borkmann /* Tail call offset to jump into */ 239fa76cfe6SMark Brown #if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) 240*5b3d19b9SXu Kuohai #define PROLOGUE_OFFSET 9 241fa76cfe6SMark Brown #else 242*5b3d19b9SXu Kuohai #define PROLOGUE_OFFSET 8 243fa76cfe6SMark Brown #endif 244ddb55992SZi Shen Lim 24556ea6a8bSDaniel Borkmann static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf) 246e54bcde3SZi Shen Lim { 247f1c9eed7SDaniel Borkmann const struct bpf_prog *prog = ctx->prog; 248e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 249e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 250e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 251e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 252e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 253ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 254*5b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 255ddb55992SZi Shen Lim const int idx0 = ctx->idx; 256ddb55992SZi Shen Lim int cur_offset; 257e54bcde3SZi Shen Lim 258ec0738dbSYang Shi /* 259ec0738dbSYang Shi * BPF prog stack layout 260ec0738dbSYang Shi * 261ec0738dbSYang Shi * high 262ec0738dbSYang Shi * original A64_SP => 0:+-----+ BPF prologue 263ec0738dbSYang Shi * |FP/LR| 264ec0738dbSYang Shi * current A64_FP => -16:+-----+ 265ec0738dbSYang Shi * | ... | callee saved registers 2664c1cd4fdSYang Shi * BPF fp register => -64:+-----+ <= (BPF_FP) 267ec0738dbSYang Shi * | | 268ec0738dbSYang Shi * | ... | BPF prog stack 269ec0738dbSYang Shi * | | 270f1c9eed7SDaniel Borkmann * +-----+ <= (BPF_FP - prog->aux->stack_depth) 27109ece3d0SDaniel Borkmann * |RSVD | padding 272f1c9eed7SDaniel Borkmann * current A64_SP => +-----+ <= (BPF_FP - ctx->stack_size) 273ec0738dbSYang Shi * | | 274ec0738dbSYang Shi * | ... | Function call stack 275ec0738dbSYang Shi * | | 276ec0738dbSYang Shi * +-----+ 277ec0738dbSYang Shi * low 278ec0738dbSYang Shi * 279ec0738dbSYang Shi */ 280ec0738dbSYang Shi 281fa76cfe6SMark Brown /* BTI landing pad */ 282fa76cfe6SMark Brown if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) 283fa76cfe6SMark Brown emit(A64_BTI_C, ctx); 284fa76cfe6SMark Brown 285ec0738dbSYang Shi /* Save FP and LR registers to stay align with ARM64 AAPCS */ 286ec0738dbSYang Shi emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 287ec0738dbSYang Shi emit(A64_MOV(1, A64_FP, A64_SP), ctx); 288ec0738dbSYang Shi 289ddb55992SZi Shen Lim /* Save callee-saved registers */ 290e54bcde3SZi Shen Lim emit(A64_PUSH(r6, r7, A64_SP), ctx); 291e54bcde3SZi Shen Lim emit(A64_PUSH(r8, r9, A64_SP), ctx); 292ddb55992SZi Shen Lim emit(A64_PUSH(fp, tcc, A64_SP), ctx); 293*5b3d19b9SXu Kuohai emit(A64_PUSH(fpb, A64_R(28), A64_SP), ctx); 294e54bcde3SZi Shen Lim 295ddb55992SZi Shen Lim /* Set up BPF prog stack base register */ 296e54bcde3SZi Shen Lim emit(A64_MOV(1, fp, A64_SP), ctx); 297e54bcde3SZi Shen Lim 29856ea6a8bSDaniel Borkmann if (!ebpf_from_cbpf) { 299ddb55992SZi Shen Lim /* Initialize tail_call_cnt */ 300ddb55992SZi Shen Lim emit(A64_MOVZ(1, tcc, 0, 0), ctx); 301ddb55992SZi Shen Lim 302ddb55992SZi Shen Lim cur_offset = ctx->idx - idx0; 303ddb55992SZi Shen Lim if (cur_offset != PROLOGUE_OFFSET) { 304ddb55992SZi Shen Lim pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 305ddb55992SZi Shen Lim cur_offset, PROLOGUE_OFFSET); 306ddb55992SZi Shen Lim return -1; 307ddb55992SZi Shen Lim } 308fa76cfe6SMark Brown 309fa76cfe6SMark Brown /* BTI landing pad for the tail call, done with a BR */ 310fa76cfe6SMark Brown if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) 311fa76cfe6SMark Brown emit(A64_BTI_J, ctx); 31256ea6a8bSDaniel Borkmann } 313a2284d91SDaniel Borkmann 314*5b3d19b9SXu Kuohai emit(A64_SUB_I(1, fpb, fp, ctx->fpb_offset), ctx); 315*5b3d19b9SXu Kuohai 3163f287098STiezhu Yang /* Stack must be multiples of 16B */ 3173f287098STiezhu Yang ctx->stack_size = round_up(prog->aux->stack_depth, 16); 318a2284d91SDaniel Borkmann 319a2284d91SDaniel Borkmann /* Set up function call stack */ 320a2284d91SDaniel Borkmann emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 321ddb55992SZi Shen Lim return 0; 322ddb55992SZi Shen Lim } 323ddb55992SZi Shen Lim 324ddb55992SZi Shen Lim static int out_offset = -1; /* initialized on the first pass of build_body() */ 325ddb55992SZi Shen Lim static int emit_bpf_tail_call(struct jit_ctx *ctx) 326ddb55992SZi Shen Lim { 327ddb55992SZi Shen Lim /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 328ddb55992SZi Shen Lim const u8 r2 = bpf2a64[BPF_REG_2]; 329ddb55992SZi Shen Lim const u8 r3 = bpf2a64[BPF_REG_3]; 330ddb55992SZi Shen Lim 331ddb55992SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 332ddb55992SZi Shen Lim const u8 prg = bpf2a64[TMP_REG_2]; 333ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 334ddb55992SZi Shen Lim const int idx0 = ctx->idx; 335ddb55992SZi Shen Lim #define cur_offset (ctx->idx - idx0) 336ddb55992SZi Shen Lim #define jmp_offset (out_offset - (cur_offset)) 337ddb55992SZi Shen Lim size_t off; 338ddb55992SZi Shen Lim 339ddb55992SZi Shen Lim /* if (index >= array->map.max_entries) 340ddb55992SZi Shen Lim * goto out; 341ddb55992SZi Shen Lim */ 342ddb55992SZi Shen Lim off = offsetof(struct bpf_array, map.max_entries); 343ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 344ddb55992SZi Shen Lim emit(A64_LDR32(tmp, r2, tmp), ctx); 34516338a9bSDaniel Borkmann emit(A64_MOV(0, r3, r3), ctx); 346ddb55992SZi Shen Lim emit(A64_CMP(0, r3, tmp), ctx); 34716338a9bSDaniel Borkmann emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 348ddb55992SZi Shen Lim 349ebf7f6f0STiezhu Yang /* 350ebf7f6f0STiezhu Yang * if (tail_call_cnt >= MAX_TAIL_CALL_CNT) 351ddb55992SZi Shen Lim * goto out; 352ddb55992SZi Shen Lim * tail_call_cnt++; 353ddb55992SZi Shen Lim */ 354ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 355ddb55992SZi Shen Lim emit(A64_CMP(1, tcc, tmp), ctx); 356ebf7f6f0STiezhu Yang emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 357ddb55992SZi Shen Lim emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 358ddb55992SZi Shen Lim 359ddb55992SZi Shen Lim /* prog = array->ptrs[index]; 360ddb55992SZi Shen Lim * if (prog == NULL) 361ddb55992SZi Shen Lim * goto out; 362ddb55992SZi Shen Lim */ 363ddb55992SZi Shen Lim off = offsetof(struct bpf_array, ptrs); 364ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 365d8b54110SDaniel Borkmann emit(A64_ADD(1, tmp, r2, tmp), ctx); 366d8b54110SDaniel Borkmann emit(A64_LSL(1, prg, r3, 3), ctx); 367d8b54110SDaniel Borkmann emit(A64_LDR64(prg, tmp, prg), ctx); 368ddb55992SZi Shen Lim emit(A64_CBZ(1, prg, jmp_offset), ctx); 369ddb55992SZi Shen Lim 370a2284d91SDaniel Borkmann /* goto *(prog->bpf_func + prologue_offset); */ 371ddb55992SZi Shen Lim off = offsetof(struct bpf_prog, bpf_func); 372ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 373ddb55992SZi Shen Lim emit(A64_LDR64(tmp, prg, tmp), ctx); 374ddb55992SZi Shen Lim emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 375a2284d91SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 376ddb55992SZi Shen Lim emit(A64_BR(tmp), ctx); 377ddb55992SZi Shen Lim 378ddb55992SZi Shen Lim /* out: */ 379ddb55992SZi Shen Lim if (out_offset == -1) 380ddb55992SZi Shen Lim out_offset = cur_offset; 381ddb55992SZi Shen Lim if (cur_offset != out_offset) { 382ddb55992SZi Shen Lim pr_err_once("tail_call out_offset = %d, expected %d!\n", 383ddb55992SZi Shen Lim cur_offset, out_offset); 384ddb55992SZi Shen Lim return -1; 385ddb55992SZi Shen Lim } 386ddb55992SZi Shen Lim return 0; 387ddb55992SZi Shen Lim #undef cur_offset 388ddb55992SZi Shen Lim #undef jmp_offset 389e54bcde3SZi Shen Lim } 390e54bcde3SZi Shen Lim 3911902472bSHou Tao #ifdef CONFIG_ARM64_LSE_ATOMICS 3921902472bSHou Tao static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 3931902472bSHou Tao { 3941902472bSHou Tao const u8 code = insn->code; 3951902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 3961902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 3971902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 3981902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 3991902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 4001902472bSHou Tao const s16 off = insn->off; 4011902472bSHou Tao u8 reg; 4021902472bSHou Tao 4031902472bSHou Tao if (!off) { 4041902472bSHou Tao reg = dst; 4051902472bSHou Tao } else { 4061902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 4071902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 4081902472bSHou Tao reg = tmp; 4091902472bSHou Tao } 4101902472bSHou Tao 4111902472bSHou Tao switch (insn->imm) { 4121902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 4131902472bSHou Tao case BPF_ADD: 4141902472bSHou Tao emit(A64_STADD(isdw, reg, src), ctx); 4151902472bSHou Tao break; 4161902472bSHou Tao case BPF_AND: 4171902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 4181902472bSHou Tao emit(A64_STCLR(isdw, reg, tmp2), ctx); 4191902472bSHou Tao break; 4201902472bSHou Tao case BPF_OR: 4211902472bSHou Tao emit(A64_STSET(isdw, reg, src), ctx); 4221902472bSHou Tao break; 4231902472bSHou Tao case BPF_XOR: 4241902472bSHou Tao emit(A64_STEOR(isdw, reg, src), ctx); 4251902472bSHou Tao break; 4261902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 4271902472bSHou Tao case BPF_ADD | BPF_FETCH: 4281902472bSHou Tao emit(A64_LDADDAL(isdw, src, reg, src), ctx); 4291902472bSHou Tao break; 4301902472bSHou Tao case BPF_AND | BPF_FETCH: 4311902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 4321902472bSHou Tao emit(A64_LDCLRAL(isdw, src, reg, tmp2), ctx); 4331902472bSHou Tao break; 4341902472bSHou Tao case BPF_OR | BPF_FETCH: 4351902472bSHou Tao emit(A64_LDSETAL(isdw, src, reg, src), ctx); 4361902472bSHou Tao break; 4371902472bSHou Tao case BPF_XOR | BPF_FETCH: 4381902472bSHou Tao emit(A64_LDEORAL(isdw, src, reg, src), ctx); 4391902472bSHou Tao break; 4401902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 4411902472bSHou Tao case BPF_XCHG: 4421902472bSHou Tao emit(A64_SWPAL(isdw, src, reg, src), ctx); 4431902472bSHou Tao break; 4441902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 4451902472bSHou Tao case BPF_CMPXCHG: 4461902472bSHou Tao emit(A64_CASAL(isdw, src, reg, bpf2a64[BPF_REG_0]), ctx); 4471902472bSHou Tao break; 4481902472bSHou Tao default: 4491902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", insn->imm); 4501902472bSHou Tao return -EINVAL; 4511902472bSHou Tao } 4521902472bSHou Tao 4531902472bSHou Tao return 0; 4541902472bSHou Tao } 4551902472bSHou Tao #else 4561902472bSHou Tao static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 4571902472bSHou Tao { 4581902472bSHou Tao return -EINVAL; 4591902472bSHou Tao } 4601902472bSHou Tao #endif 4611902472bSHou Tao 4621902472bSHou Tao static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 4631902472bSHou Tao { 4641902472bSHou Tao const u8 code = insn->code; 4651902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 4661902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 4671902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 4681902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 4691902472bSHou Tao const u8 tmp3 = bpf2a64[TMP_REG_3]; 4701902472bSHou Tao const int i = insn - ctx->prog->insnsi; 4711902472bSHou Tao const s32 imm = insn->imm; 4721902472bSHou Tao const s16 off = insn->off; 4731902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 4741902472bSHou Tao u8 reg; 4751902472bSHou Tao s32 jmp_offset; 4761902472bSHou Tao 4771902472bSHou Tao if (!off) { 4781902472bSHou Tao reg = dst; 4791902472bSHou Tao } else { 4801902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 4811902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 4821902472bSHou Tao reg = tmp; 4831902472bSHou Tao } 4841902472bSHou Tao 4851902472bSHou Tao if (imm == BPF_ADD || imm == BPF_AND || 4861902472bSHou Tao imm == BPF_OR || imm == BPF_XOR) { 4871902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 4881902472bSHou Tao emit(A64_LDXR(isdw, tmp2, reg), ctx); 4891902472bSHou Tao if (imm == BPF_ADD) 4901902472bSHou Tao emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 4911902472bSHou Tao else if (imm == BPF_AND) 4921902472bSHou Tao emit(A64_AND(isdw, tmp2, tmp2, src), ctx); 4931902472bSHou Tao else if (imm == BPF_OR) 4941902472bSHou Tao emit(A64_ORR(isdw, tmp2, tmp2, src), ctx); 4951902472bSHou Tao else 4961902472bSHou Tao emit(A64_EOR(isdw, tmp2, tmp2, src), ctx); 4971902472bSHou Tao emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); 4981902472bSHou Tao jmp_offset = -3; 4991902472bSHou Tao check_imm19(jmp_offset); 5001902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5011902472bSHou Tao } else if (imm == (BPF_ADD | BPF_FETCH) || 5021902472bSHou Tao imm == (BPF_AND | BPF_FETCH) || 5031902472bSHou Tao imm == (BPF_OR | BPF_FETCH) || 5041902472bSHou Tao imm == (BPF_XOR | BPF_FETCH)) { 5051902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 5061902472bSHou Tao const u8 ax = bpf2a64[BPF_REG_AX]; 5071902472bSHou Tao 5081902472bSHou Tao emit(A64_MOV(isdw, ax, src), ctx); 5091902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 5101902472bSHou Tao if (imm == (BPF_ADD | BPF_FETCH)) 5111902472bSHou Tao emit(A64_ADD(isdw, tmp2, src, ax), ctx); 5121902472bSHou Tao else if (imm == (BPF_AND | BPF_FETCH)) 5131902472bSHou Tao emit(A64_AND(isdw, tmp2, src, ax), ctx); 5141902472bSHou Tao else if (imm == (BPF_OR | BPF_FETCH)) 5151902472bSHou Tao emit(A64_ORR(isdw, tmp2, src, ax), ctx); 5161902472bSHou Tao else 5171902472bSHou Tao emit(A64_EOR(isdw, tmp2, src, ax), ctx); 5181902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 5191902472bSHou Tao jmp_offset = -3; 5201902472bSHou Tao check_imm19(jmp_offset); 5211902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5221902472bSHou Tao emit(A64_DMB_ISH, ctx); 5231902472bSHou Tao } else if (imm == BPF_XCHG) { 5241902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 5251902472bSHou Tao emit(A64_MOV(isdw, tmp2, src), ctx); 5261902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 5271902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 5281902472bSHou Tao jmp_offset = -2; 5291902472bSHou Tao check_imm19(jmp_offset); 5301902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5311902472bSHou Tao emit(A64_DMB_ISH, ctx); 5321902472bSHou Tao } else if (imm == BPF_CMPXCHG) { 5331902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 5341902472bSHou Tao const u8 r0 = bpf2a64[BPF_REG_0]; 5351902472bSHou Tao 5361902472bSHou Tao emit(A64_MOV(isdw, tmp2, r0), ctx); 5371902472bSHou Tao emit(A64_LDXR(isdw, r0, reg), ctx); 5381902472bSHou Tao emit(A64_EOR(isdw, tmp3, r0, tmp2), ctx); 5391902472bSHou Tao jmp_offset = 4; 5401902472bSHou Tao check_imm19(jmp_offset); 5411902472bSHou Tao emit(A64_CBNZ(isdw, tmp3, jmp_offset), ctx); 5421902472bSHou Tao emit(A64_STLXR(isdw, src, reg, tmp3), ctx); 5431902472bSHou Tao jmp_offset = -4; 5441902472bSHou Tao check_imm19(jmp_offset); 5451902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5461902472bSHou Tao emit(A64_DMB_ISH, ctx); 5471902472bSHou Tao } else { 5481902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", imm); 5491902472bSHou Tao return -EINVAL; 5501902472bSHou Tao } 5511902472bSHou Tao 5521902472bSHou Tao return 0; 5531902472bSHou Tao } 5541902472bSHou Tao 555e54bcde3SZi Shen Lim static void build_epilogue(struct jit_ctx *ctx) 556e54bcde3SZi Shen Lim { 557e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 558e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 559e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 560e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 561e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 562e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 563*5b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 564e54bcde3SZi Shen Lim 565e54bcde3SZi Shen Lim /* We're done with BPF stack */ 566f1c9eed7SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 567e54bcde3SZi Shen Lim 568*5b3d19b9SXu Kuohai /* Restore x27 and x28 */ 569*5b3d19b9SXu Kuohai emit(A64_POP(fpb, A64_R(28), A64_SP), ctx); 570ec0738dbSYang Shi /* Restore fs (x25) and x26 */ 571ec0738dbSYang Shi emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 572ec0738dbSYang Shi 573e54bcde3SZi Shen Lim /* Restore callee-saved register */ 574e54bcde3SZi Shen Lim emit(A64_POP(r8, r9, A64_SP), ctx); 575e54bcde3SZi Shen Lim emit(A64_POP(r6, r7, A64_SP), ctx); 576e54bcde3SZi Shen Lim 577ec0738dbSYang Shi /* Restore FP/LR registers */ 578ec0738dbSYang Shi emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 579e54bcde3SZi Shen Lim 580e54bcde3SZi Shen Lim /* Set return value */ 581e54bcde3SZi Shen Lim emit(A64_MOV(1, A64_R(0), r0), ctx); 582e54bcde3SZi Shen Lim 583e54bcde3SZi Shen Lim emit(A64_RET(A64_LR), ctx); 584e54bcde3SZi Shen Lim } 585e54bcde3SZi Shen Lim 58680083428SJean-Philippe Brucker #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) 58780083428SJean-Philippe Brucker #define BPF_FIXUP_REG_MASK GENMASK(31, 27) 58880083428SJean-Philippe Brucker 589d6e2cc56SMark Rutland bool ex_handler_bpf(const struct exception_table_entry *ex, 59080083428SJean-Philippe Brucker struct pt_regs *regs) 59180083428SJean-Philippe Brucker { 59280083428SJean-Philippe Brucker off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup); 59380083428SJean-Philippe Brucker int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup); 59480083428SJean-Philippe Brucker 59580083428SJean-Philippe Brucker regs->regs[dst_reg] = 0; 59680083428SJean-Philippe Brucker regs->pc = (unsigned long)&ex->fixup - offset; 597e8c328d7SMark Rutland return true; 59880083428SJean-Philippe Brucker } 59980083428SJean-Philippe Brucker 60080083428SJean-Philippe Brucker /* For accesses to BTF pointers, add an entry to the exception table */ 60180083428SJean-Philippe Brucker static int add_exception_handler(const struct bpf_insn *insn, 60280083428SJean-Philippe Brucker struct jit_ctx *ctx, 60380083428SJean-Philippe Brucker int dst_reg) 60480083428SJean-Philippe Brucker { 60580083428SJean-Philippe Brucker off_t offset; 60680083428SJean-Philippe Brucker unsigned long pc; 60780083428SJean-Philippe Brucker struct exception_table_entry *ex; 60880083428SJean-Philippe Brucker 60980083428SJean-Philippe Brucker if (!ctx->image) 61080083428SJean-Philippe Brucker /* First pass */ 61180083428SJean-Philippe Brucker return 0; 61280083428SJean-Philippe Brucker 61380083428SJean-Philippe Brucker if (BPF_MODE(insn->code) != BPF_PROBE_MEM) 61480083428SJean-Philippe Brucker return 0; 61580083428SJean-Philippe Brucker 61680083428SJean-Philippe Brucker if (!ctx->prog->aux->extable || 61780083428SJean-Philippe Brucker WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries)) 61880083428SJean-Philippe Brucker return -EINVAL; 61980083428SJean-Philippe Brucker 62080083428SJean-Philippe Brucker ex = &ctx->prog->aux->extable[ctx->exentry_idx]; 62180083428SJean-Philippe Brucker pc = (unsigned long)&ctx->image[ctx->idx - 1]; 62280083428SJean-Philippe Brucker 62380083428SJean-Philippe Brucker offset = pc - (long)&ex->insn; 62480083428SJean-Philippe Brucker if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN)) 62580083428SJean-Philippe Brucker return -ERANGE; 62680083428SJean-Philippe Brucker ex->insn = offset; 62780083428SJean-Philippe Brucker 62880083428SJean-Philippe Brucker /* 62980083428SJean-Philippe Brucker * Since the extable follows the program, the fixup offset is always 63080083428SJean-Philippe Brucker * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value 63180083428SJean-Philippe Brucker * to keep things simple, and put the destination register in the upper 63280083428SJean-Philippe Brucker * bits. We don't need to worry about buildtime or runtime sort 63380083428SJean-Philippe Brucker * modifying the upper bits because the table is already sorted, and 63480083428SJean-Philippe Brucker * isn't part of the main exception table. 63580083428SJean-Philippe Brucker */ 63680083428SJean-Philippe Brucker offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE); 63780083428SJean-Philippe Brucker if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset)) 63880083428SJean-Philippe Brucker return -ERANGE; 63980083428SJean-Philippe Brucker 64080083428SJean-Philippe Brucker ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) | 64180083428SJean-Philippe Brucker FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); 64280083428SJean-Philippe Brucker 643d6e2cc56SMark Rutland ex->type = EX_TYPE_BPF; 644d6e2cc56SMark Rutland 64580083428SJean-Philippe Brucker ctx->exentry_idx++; 64680083428SJean-Philippe Brucker return 0; 64780083428SJean-Philippe Brucker } 64880083428SJean-Philippe Brucker 64930d3d94cSZi Shen Lim /* JITs an eBPF instruction. 65030d3d94cSZi Shen Lim * Returns: 65130d3d94cSZi Shen Lim * 0 - successfully JITed an 8-byte eBPF instruction. 65230d3d94cSZi Shen Lim * >0 - successfully JITed a 16-byte eBPF instruction. 65330d3d94cSZi Shen Lim * <0 - failed to JIT. 65430d3d94cSZi Shen Lim */ 6558c11ea5cSDaniel Borkmann static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, 6568c11ea5cSDaniel Borkmann bool extra_pass) 657e54bcde3SZi Shen Lim { 658e54bcde3SZi Shen Lim const u8 code = insn->code; 659e54bcde3SZi Shen Lim const u8 dst = bpf2a64[insn->dst_reg]; 660e54bcde3SZi Shen Lim const u8 src = bpf2a64[insn->src_reg]; 661e54bcde3SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 662e54bcde3SZi Shen Lim const u8 tmp2 = bpf2a64[TMP_REG_2]; 663*5b3d19b9SXu Kuohai const u8 fp = bpf2a64[BPF_REG_FP]; 664*5b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 665e54bcde3SZi Shen Lim const s16 off = insn->off; 666e54bcde3SZi Shen Lim const s32 imm = insn->imm; 667e54bcde3SZi Shen Lim const int i = insn - ctx->prog->insnsi; 668654b65a0SJiong Wang const bool is64 = BPF_CLASS(code) == BPF_ALU64 || 669654b65a0SJiong Wang BPF_CLASS(code) == BPF_JMP; 6701902472bSHou Tao u8 jmp_cond; 671e54bcde3SZi Shen Lim s32 jmp_offset; 672fd49591cSLuke Nelson u32 a64_insn; 673*5b3d19b9SXu Kuohai u8 src_adj; 674*5b3d19b9SXu Kuohai u8 dst_adj; 675*5b3d19b9SXu Kuohai int off_adj; 67680083428SJean-Philippe Brucker int ret; 677e54bcde3SZi Shen Lim 678e54bcde3SZi Shen Lim switch (code) { 679e54bcde3SZi Shen Lim /* dst = src */ 680e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_X: 681e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_X: 682e54bcde3SZi Shen Lim emit(A64_MOV(is64, dst, src), ctx); 683e54bcde3SZi Shen Lim break; 684e54bcde3SZi Shen Lim /* dst = dst OP src */ 685e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_X: 686e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_X: 687e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, src), ctx); 688e54bcde3SZi Shen Lim break; 689e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_X: 690e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_X: 691e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, src), ctx); 692e54bcde3SZi Shen Lim break; 693e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_X: 694e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_X: 695e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, src), ctx); 696e54bcde3SZi Shen Lim break; 697e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_X: 698e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_X: 699e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, src), ctx); 700e54bcde3SZi Shen Lim break; 701e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_X: 702e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_X: 703e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, src), ctx); 704e54bcde3SZi Shen Lim break; 705e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_X: 706e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_X: 707e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, src), ctx); 708e54bcde3SZi Shen Lim break; 709e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_X: 710e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_X: 711e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, src), ctx); 712e54bcde3SZi Shen Lim break; 713119220d8STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 714119220d8STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 715e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, src), ctx); 716504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, src), ctx); 717e54bcde3SZi Shen Lim break; 718d65a634aSZi Shen Lim case BPF_ALU | BPF_LSH | BPF_X: 719d65a634aSZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_X: 720d65a634aSZi Shen Lim emit(A64_LSLV(is64, dst, dst, src), ctx); 721d65a634aSZi Shen Lim break; 722d65a634aSZi Shen Lim case BPF_ALU | BPF_RSH | BPF_X: 723d65a634aSZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_X: 724d65a634aSZi Shen Lim emit(A64_LSRV(is64, dst, dst, src), ctx); 725d65a634aSZi Shen Lim break; 726d65a634aSZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_X: 727d65a634aSZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_X: 728d65a634aSZi Shen Lim emit(A64_ASRV(is64, dst, dst, src), ctx); 729d65a634aSZi Shen Lim break; 730e54bcde3SZi Shen Lim /* dst = -dst */ 731e54bcde3SZi Shen Lim case BPF_ALU | BPF_NEG: 732e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_NEG: 733e54bcde3SZi Shen Lim emit(A64_NEG(is64, dst, dst), ctx); 734e54bcde3SZi Shen Lim break; 735e54bcde3SZi Shen Lim /* dst = BSWAP##imm(dst) */ 736e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_LE: 737e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_BE: 738e54bcde3SZi Shen Lim #ifdef CONFIG_CPU_BIG_ENDIAN 739e54bcde3SZi Shen Lim if (BPF_SRC(code) == BPF_FROM_BE) 740d63903bbSXi Wang goto emit_bswap_uxt; 741e54bcde3SZi Shen Lim #else /* !CONFIG_CPU_BIG_ENDIAN */ 742e54bcde3SZi Shen Lim if (BPF_SRC(code) == BPF_FROM_LE) 743d63903bbSXi Wang goto emit_bswap_uxt; 744e54bcde3SZi Shen Lim #endif 745e54bcde3SZi Shen Lim switch (imm) { 746e54bcde3SZi Shen Lim case 16: 747e54bcde3SZi Shen Lim emit(A64_REV16(is64, dst, dst), ctx); 748d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 749d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 750e54bcde3SZi Shen Lim break; 751e54bcde3SZi Shen Lim case 32: 752e54bcde3SZi Shen Lim emit(A64_REV32(is64, dst, dst), ctx); 753d63903bbSXi Wang /* upper 32 bits already cleared */ 754e54bcde3SZi Shen Lim break; 755e54bcde3SZi Shen Lim case 64: 756e54bcde3SZi Shen Lim emit(A64_REV64(dst, dst), ctx); 757e54bcde3SZi Shen Lim break; 758e54bcde3SZi Shen Lim } 759e54bcde3SZi Shen Lim break; 760d63903bbSXi Wang emit_bswap_uxt: 761d63903bbSXi Wang switch (imm) { 762d63903bbSXi Wang case 16: 763d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 764d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 765d63903bbSXi Wang break; 766d63903bbSXi Wang case 32: 767d63903bbSXi Wang /* zero-extend 32 bits into 64 bits */ 768d63903bbSXi Wang emit(A64_UXTW(is64, dst, dst), ctx); 769d63903bbSXi Wang break; 770d63903bbSXi Wang case 64: 771d63903bbSXi Wang /* nop */ 772d63903bbSXi Wang break; 773d63903bbSXi Wang } 774d63903bbSXi Wang break; 775e54bcde3SZi Shen Lim /* dst = imm */ 776e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_K: 777e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_K: 778e54bcde3SZi Shen Lim emit_a64_mov_i(is64, dst, imm, ctx); 779e54bcde3SZi Shen Lim break; 780e54bcde3SZi Shen Lim /* dst = dst OP imm */ 781e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_K: 782e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_K: 783fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 784fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, imm), ctx); 785fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 786fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, -imm), ctx); 787fd868f14SLuke Nelson } else { 788e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 789e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, tmp), ctx); 790fd868f14SLuke Nelson } 791e54bcde3SZi Shen Lim break; 792e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_K: 793e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_K: 794fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 795fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, imm), ctx); 796fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 797fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, -imm), ctx); 798fd868f14SLuke Nelson } else { 799e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 800e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 801fd868f14SLuke Nelson } 802e54bcde3SZi Shen Lim break; 803e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_K: 804e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_K: 805fd49591cSLuke Nelson a64_insn = A64_AND_I(is64, dst, dst, imm); 806fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 807fd49591cSLuke Nelson emit(a64_insn, ctx); 808fd49591cSLuke Nelson } else { 809e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 810e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, tmp), ctx); 811fd49591cSLuke Nelson } 812e54bcde3SZi Shen Lim break; 813e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_K: 814e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_K: 815fd49591cSLuke Nelson a64_insn = A64_ORR_I(is64, dst, dst, imm); 816fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 817fd49591cSLuke Nelson emit(a64_insn, ctx); 818fd49591cSLuke Nelson } else { 819e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 820e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, tmp), ctx); 821fd49591cSLuke Nelson } 822e54bcde3SZi Shen Lim break; 823e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_K: 824e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_K: 825fd49591cSLuke Nelson a64_insn = A64_EOR_I(is64, dst, dst, imm); 826fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 827fd49591cSLuke Nelson emit(a64_insn, ctx); 828fd49591cSLuke Nelson } else { 829e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 830e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, tmp), ctx); 831fd49591cSLuke Nelson } 832e54bcde3SZi Shen Lim break; 833e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_K: 834e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_K: 835e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 836e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, tmp), ctx); 837e54bcde3SZi Shen Lim break; 838e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_K: 839e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_K: 840e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 841e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, tmp), ctx); 842e54bcde3SZi Shen Lim break; 843e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_K: 844e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_K: 845e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp2, imm, ctx); 846e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 847504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, tmp2), ctx); 848e54bcde3SZi Shen Lim break; 849e54bcde3SZi Shen Lim case BPF_ALU | BPF_LSH | BPF_K: 850e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_K: 851e54bcde3SZi Shen Lim emit(A64_LSL(is64, dst, dst, imm), ctx); 852e54bcde3SZi Shen Lim break; 853e54bcde3SZi Shen Lim case BPF_ALU | BPF_RSH | BPF_K: 854e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_K: 855e54bcde3SZi Shen Lim emit(A64_LSR(is64, dst, dst, imm), ctx); 856e54bcde3SZi Shen Lim break; 857e54bcde3SZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_K: 858e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_K: 859e54bcde3SZi Shen Lim emit(A64_ASR(is64, dst, dst, imm), ctx); 860e54bcde3SZi Shen Lim break; 861e54bcde3SZi Shen Lim 862e54bcde3SZi Shen Lim /* JUMP off */ 863e54bcde3SZi Shen Lim case BPF_JMP | BPF_JA: 86432f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 865e54bcde3SZi Shen Lim check_imm26(jmp_offset); 866e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 867e54bcde3SZi Shen Lim break; 868e54bcde3SZi Shen Lim /* IF (dst COND src) JUMP off */ 869e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_X: 870e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_X: 871c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_X: 872e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_X: 873c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_X: 874e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_X: 875e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_X: 876c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_X: 877e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_X: 878c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_X: 879654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_X: 880654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_X: 881654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_X: 882654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_X: 883654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_X: 884654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_X: 885654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_X: 886654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_X: 887654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_X: 888654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_X: 889654b65a0SJiong Wang emit(A64_CMP(is64, dst, src), ctx); 890e54bcde3SZi Shen Lim emit_cond_jmp: 89132f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 892e54bcde3SZi Shen Lim check_imm19(jmp_offset); 893e54bcde3SZi Shen Lim switch (BPF_OP(code)) { 894e54bcde3SZi Shen Lim case BPF_JEQ: 895e54bcde3SZi Shen Lim jmp_cond = A64_COND_EQ; 896e54bcde3SZi Shen Lim break; 897e54bcde3SZi Shen Lim case BPF_JGT: 898e54bcde3SZi Shen Lim jmp_cond = A64_COND_HI; 899e54bcde3SZi Shen Lim break; 900c362b2f3SDaniel Borkmann case BPF_JLT: 901c362b2f3SDaniel Borkmann jmp_cond = A64_COND_CC; 902c362b2f3SDaniel Borkmann break; 903e54bcde3SZi Shen Lim case BPF_JGE: 904e54bcde3SZi Shen Lim jmp_cond = A64_COND_CS; 905e54bcde3SZi Shen Lim break; 906c362b2f3SDaniel Borkmann case BPF_JLE: 907c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LS; 908c362b2f3SDaniel Borkmann break; 90998397fc5SZi Shen Lim case BPF_JSET: 910e54bcde3SZi Shen Lim case BPF_JNE: 911e54bcde3SZi Shen Lim jmp_cond = A64_COND_NE; 912e54bcde3SZi Shen Lim break; 913e54bcde3SZi Shen Lim case BPF_JSGT: 914e54bcde3SZi Shen Lim jmp_cond = A64_COND_GT; 915e54bcde3SZi Shen Lim break; 916c362b2f3SDaniel Borkmann case BPF_JSLT: 917c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LT; 918c362b2f3SDaniel Borkmann break; 919e54bcde3SZi Shen Lim case BPF_JSGE: 920e54bcde3SZi Shen Lim jmp_cond = A64_COND_GE; 921e54bcde3SZi Shen Lim break; 922c362b2f3SDaniel Borkmann case BPF_JSLE: 923c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LE; 924c362b2f3SDaniel Borkmann break; 925e54bcde3SZi Shen Lim default: 926e54bcde3SZi Shen Lim return -EFAULT; 927e54bcde3SZi Shen Lim } 928e54bcde3SZi Shen Lim emit(A64_B_(jmp_cond, jmp_offset), ctx); 929e54bcde3SZi Shen Lim break; 930e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_X: 931654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_X: 932654b65a0SJiong Wang emit(A64_TST(is64, dst, src), ctx); 933e54bcde3SZi Shen Lim goto emit_cond_jmp; 934e54bcde3SZi Shen Lim /* IF (dst COND imm) JUMP off */ 935e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_K: 936e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_K: 937c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_K: 938e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_K: 939c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_K: 940e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_K: 941e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_K: 942c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_K: 943e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_K: 944c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_K: 945654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_K: 946654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_K: 947654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_K: 948654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_K: 949654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_K: 950654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_K: 951654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_K: 952654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_K: 953654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_K: 954654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_K: 955fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 956fd868f14SLuke Nelson emit(A64_CMP_I(is64, dst, imm), ctx); 957fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 958fd868f14SLuke Nelson emit(A64_CMN_I(is64, dst, -imm), ctx); 959fd868f14SLuke Nelson } else { 960654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 961654b65a0SJiong Wang emit(A64_CMP(is64, dst, tmp), ctx); 962fd868f14SLuke Nelson } 963e54bcde3SZi Shen Lim goto emit_cond_jmp; 964e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_K: 965654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_K: 966fd49591cSLuke Nelson a64_insn = A64_TST_I(is64, dst, imm); 967fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 968fd49591cSLuke Nelson emit(a64_insn, ctx); 969fd49591cSLuke Nelson } else { 970654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 971654b65a0SJiong Wang emit(A64_TST(is64, dst, tmp), ctx); 972fd49591cSLuke Nelson } 973e54bcde3SZi Shen Lim goto emit_cond_jmp; 974e54bcde3SZi Shen Lim /* function call */ 975e54bcde3SZi Shen Lim case BPF_JMP | BPF_CALL: 976e54bcde3SZi Shen Lim { 977e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 9788c11ea5cSDaniel Borkmann bool func_addr_fixed; 9798c11ea5cSDaniel Borkmann u64 func_addr; 980e54bcde3SZi Shen Lim 9818c11ea5cSDaniel Borkmann ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 9828c11ea5cSDaniel Borkmann &func_addr, &func_addr_fixed); 9838c11ea5cSDaniel Borkmann if (ret < 0) 9848c11ea5cSDaniel Borkmann return ret; 9858c11ea5cSDaniel Borkmann emit_addr_mov_i64(tmp, func_addr, ctx); 986e54bcde3SZi Shen Lim emit(A64_BLR(tmp), ctx); 987e54bcde3SZi Shen Lim emit(A64_MOV(1, r0, A64_R(0)), ctx); 988e54bcde3SZi Shen Lim break; 989e54bcde3SZi Shen Lim } 990ddb55992SZi Shen Lim /* tail call */ 99171189fa9SAlexei Starovoitov case BPF_JMP | BPF_TAIL_CALL: 992ddb55992SZi Shen Lim if (emit_bpf_tail_call(ctx)) 993ddb55992SZi Shen Lim return -EFAULT; 994ddb55992SZi Shen Lim break; 995e54bcde3SZi Shen Lim /* function return */ 996e54bcde3SZi Shen Lim case BPF_JMP | BPF_EXIT: 99751c9fbb1SZi Shen Lim /* Optimization: when last instruction is EXIT, 99851c9fbb1SZi Shen Lim simply fallthrough to epilogue. */ 999e54bcde3SZi Shen Lim if (i == ctx->prog->len - 1) 1000e54bcde3SZi Shen Lim break; 1001e54bcde3SZi Shen Lim jmp_offset = epilogue_offset(ctx); 1002e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1003e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1004e54bcde3SZi Shen Lim break; 1005e54bcde3SZi Shen Lim 100630d3d94cSZi Shen Lim /* dst = imm64 */ 100730d3d94cSZi Shen Lim case BPF_LD | BPF_IMM | BPF_DW: 100830d3d94cSZi Shen Lim { 100930d3d94cSZi Shen Lim const struct bpf_insn insn1 = insn[1]; 101030d3d94cSZi Shen Lim u64 imm64; 101130d3d94cSZi Shen Lim 10121e4df6b7SXi Wang imm64 = (u64)insn1.imm << 32 | (u32)imm; 1013e4a41c2cSHou Tao if (bpf_pseudo_func(insn)) 1014e4a41c2cSHou Tao emit_addr_mov_i64(dst, imm64, ctx); 1015e4a41c2cSHou Tao else 101630d3d94cSZi Shen Lim emit_a64_mov_i64(dst, imm64, ctx); 101730d3d94cSZi Shen Lim 101830d3d94cSZi Shen Lim return 1; 101930d3d94cSZi Shen Lim } 102030d3d94cSZi Shen Lim 1021e54bcde3SZi Shen Lim /* LDX: dst = *(size *)(src + off) */ 1022e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_W: 1023e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_H: 1024e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_B: 1025e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_DW: 102680083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 102780083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_W: 102880083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_H: 102980083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_B: 1030*5b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && src == fp) { 1031*5b3d19b9SXu Kuohai src_adj = fpb; 1032*5b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 1033*5b3d19b9SXu Kuohai } else { 1034*5b3d19b9SXu Kuohai src_adj = src; 1035*5b3d19b9SXu Kuohai off_adj = off; 1036*5b3d19b9SXu Kuohai } 1037e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1038e54bcde3SZi Shen Lim case BPF_W: 1039*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 1040*5b3d19b9SXu Kuohai emit(A64_LDR32I(dst, src_adj, off_adj), ctx); 10417db6c0f1SXu Kuohai } else { 10427db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1043e54bcde3SZi Shen Lim emit(A64_LDR32(dst, src, tmp), ctx); 10447db6c0f1SXu Kuohai } 1045e54bcde3SZi Shen Lim break; 1046e54bcde3SZi Shen Lim case BPF_H: 1047*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 1048*5b3d19b9SXu Kuohai emit(A64_LDRHI(dst, src_adj, off_adj), ctx); 10497db6c0f1SXu Kuohai } else { 10507db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1051e54bcde3SZi Shen Lim emit(A64_LDRH(dst, src, tmp), ctx); 10527db6c0f1SXu Kuohai } 1053e54bcde3SZi Shen Lim break; 1054e54bcde3SZi Shen Lim case BPF_B: 1055*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 1056*5b3d19b9SXu Kuohai emit(A64_LDRBI(dst, src_adj, off_adj), ctx); 10577db6c0f1SXu Kuohai } else { 10587db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1059e54bcde3SZi Shen Lim emit(A64_LDRB(dst, src, tmp), ctx); 10607db6c0f1SXu Kuohai } 1061e54bcde3SZi Shen Lim break; 1062e54bcde3SZi Shen Lim case BPF_DW: 1063*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 1064*5b3d19b9SXu Kuohai emit(A64_LDR64I(dst, src_adj, off_adj), ctx); 10657db6c0f1SXu Kuohai } else { 10667db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1067e54bcde3SZi Shen Lim emit(A64_LDR64(dst, src, tmp), ctx); 10687db6c0f1SXu Kuohai } 1069e54bcde3SZi Shen Lim break; 1070e54bcde3SZi Shen Lim } 107180083428SJean-Philippe Brucker 107280083428SJean-Philippe Brucker ret = add_exception_handler(insn, ctx, dst); 107380083428SJean-Philippe Brucker if (ret) 107480083428SJean-Philippe Brucker return ret; 1075e54bcde3SZi Shen Lim break; 1076e54bcde3SZi Shen Lim 1077f5e81d11SDaniel Borkmann /* speculation barrier */ 1078f5e81d11SDaniel Borkmann case BPF_ST | BPF_NOSPEC: 1079f5e81d11SDaniel Borkmann /* 1080f5e81d11SDaniel Borkmann * Nothing required here. 1081f5e81d11SDaniel Borkmann * 1082f5e81d11SDaniel Borkmann * In case of arm64, we rely on the firmware mitigation of 1083f5e81d11SDaniel Borkmann * Speculative Store Bypass as controlled via the ssbd kernel 1084f5e81d11SDaniel Borkmann * parameter. Whenever the mitigation is enabled, it works 1085f5e81d11SDaniel Borkmann * for all of the kernel code with no need to provide any 1086f5e81d11SDaniel Borkmann * additional instructions. 1087f5e81d11SDaniel Borkmann */ 1088f5e81d11SDaniel Borkmann break; 1089f5e81d11SDaniel Borkmann 1090e54bcde3SZi Shen Lim /* ST: *(size *)(dst + off) = imm */ 1091e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_W: 1092e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_H: 1093e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_B: 1094e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_DW: 1095*5b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && dst == fp) { 1096*5b3d19b9SXu Kuohai dst_adj = fpb; 1097*5b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 1098*5b3d19b9SXu Kuohai } else { 1099*5b3d19b9SXu Kuohai dst_adj = dst; 1100*5b3d19b9SXu Kuohai off_adj = off; 1101*5b3d19b9SXu Kuohai } 1102df849ba3SYang Shi /* Load imm to a register then store it */ 1103df849ba3SYang Shi emit_a64_mov_i(1, tmp, imm, ctx); 1104df849ba3SYang Shi switch (BPF_SIZE(code)) { 1105df849ba3SYang Shi case BPF_W: 1106*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 1107*5b3d19b9SXu Kuohai emit(A64_STR32I(tmp, dst_adj, off_adj), ctx); 11087db6c0f1SXu Kuohai } else { 11097db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1110df849ba3SYang Shi emit(A64_STR32(tmp, dst, tmp2), ctx); 11117db6c0f1SXu Kuohai } 1112df849ba3SYang Shi break; 1113df849ba3SYang Shi case BPF_H: 1114*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 1115*5b3d19b9SXu Kuohai emit(A64_STRHI(tmp, dst_adj, off_adj), ctx); 11167db6c0f1SXu Kuohai } else { 11177db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1118df849ba3SYang Shi emit(A64_STRH(tmp, dst, tmp2), ctx); 11197db6c0f1SXu Kuohai } 1120df849ba3SYang Shi break; 1121df849ba3SYang Shi case BPF_B: 1122*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 1123*5b3d19b9SXu Kuohai emit(A64_STRBI(tmp, dst_adj, off_adj), ctx); 11247db6c0f1SXu Kuohai } else { 11257db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1126df849ba3SYang Shi emit(A64_STRB(tmp, dst, tmp2), ctx); 11277db6c0f1SXu Kuohai } 1128df849ba3SYang Shi break; 1129df849ba3SYang Shi case BPF_DW: 1130*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 1131*5b3d19b9SXu Kuohai emit(A64_STR64I(tmp, dst_adj, off_adj), ctx); 11327db6c0f1SXu Kuohai } else { 11337db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1134df849ba3SYang Shi emit(A64_STR64(tmp, dst, tmp2), ctx); 11357db6c0f1SXu Kuohai } 1136df849ba3SYang Shi break; 1137df849ba3SYang Shi } 1138df849ba3SYang Shi break; 1139e54bcde3SZi Shen Lim 1140e54bcde3SZi Shen Lim /* STX: *(size *)(dst + off) = src */ 1141e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_W: 1142e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_H: 1143e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_B: 1144e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_DW: 1145*5b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && dst == fp) { 1146*5b3d19b9SXu Kuohai dst_adj = fpb; 1147*5b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 1148*5b3d19b9SXu Kuohai } else { 1149*5b3d19b9SXu Kuohai dst_adj = dst; 1150*5b3d19b9SXu Kuohai off_adj = off; 1151*5b3d19b9SXu Kuohai } 1152e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1153e54bcde3SZi Shen Lim case BPF_W: 1154*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 1155*5b3d19b9SXu Kuohai emit(A64_STR32I(src, dst_adj, off_adj), ctx); 11567db6c0f1SXu Kuohai } else { 11577db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1158e54bcde3SZi Shen Lim emit(A64_STR32(src, dst, tmp), ctx); 11597db6c0f1SXu Kuohai } 1160e54bcde3SZi Shen Lim break; 1161e54bcde3SZi Shen Lim case BPF_H: 1162*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 1163*5b3d19b9SXu Kuohai emit(A64_STRHI(src, dst_adj, off_adj), ctx); 11647db6c0f1SXu Kuohai } else { 11657db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1166e54bcde3SZi Shen Lim emit(A64_STRH(src, dst, tmp), ctx); 11677db6c0f1SXu Kuohai } 1168e54bcde3SZi Shen Lim break; 1169e54bcde3SZi Shen Lim case BPF_B: 1170*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 1171*5b3d19b9SXu Kuohai emit(A64_STRBI(src, dst_adj, off_adj), ctx); 11727db6c0f1SXu Kuohai } else { 11737db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1174e54bcde3SZi Shen Lim emit(A64_STRB(src, dst, tmp), ctx); 11757db6c0f1SXu Kuohai } 1176e54bcde3SZi Shen Lim break; 1177e54bcde3SZi Shen Lim case BPF_DW: 1178*5b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 1179*5b3d19b9SXu Kuohai emit(A64_STR64I(src, dst_adj, off_adj), ctx); 11807db6c0f1SXu Kuohai } else { 11817db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1182e54bcde3SZi Shen Lim emit(A64_STR64(src, dst, tmp), ctx); 11837db6c0f1SXu Kuohai } 1184e54bcde3SZi Shen Lim break; 1185e54bcde3SZi Shen Lim } 1186e54bcde3SZi Shen Lim break; 118734b8ab09SDaniel Borkmann 118891c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_W: 118991c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_DW: 11901902472bSHou Tao if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) 11911902472bSHou Tao ret = emit_lse_atomic(insn, ctx); 11921902472bSHou Tao else 11931902472bSHou Tao ret = emit_ll_sc_atomic(insn, ctx); 11941902472bSHou Tao if (ret) 11951902472bSHou Tao return ret; 119685f68fe8SDaniel Borkmann break; 1197e54bcde3SZi Shen Lim 1198e54bcde3SZi Shen Lim default: 1199e54bcde3SZi Shen Lim pr_err_once("unknown opcode %02x\n", code); 1200e54bcde3SZi Shen Lim return -EINVAL; 1201e54bcde3SZi Shen Lim } 1202e54bcde3SZi Shen Lim 1203e54bcde3SZi Shen Lim return 0; 1204e54bcde3SZi Shen Lim } 1205e54bcde3SZi Shen Lim 1206*5b3d19b9SXu Kuohai /* 1207*5b3d19b9SXu Kuohai * Return 0 if FP may change at runtime, otherwise find the minimum negative 1208*5b3d19b9SXu Kuohai * offset to FP, converts it to positive number, and align down to 8 bytes. 1209*5b3d19b9SXu Kuohai */ 1210*5b3d19b9SXu Kuohai static int find_fpb_offset(struct bpf_prog *prog) 1211*5b3d19b9SXu Kuohai { 1212*5b3d19b9SXu Kuohai int i; 1213*5b3d19b9SXu Kuohai int offset = 0; 1214*5b3d19b9SXu Kuohai 1215*5b3d19b9SXu Kuohai for (i = 0; i < prog->len; i++) { 1216*5b3d19b9SXu Kuohai const struct bpf_insn *insn = &prog->insnsi[i]; 1217*5b3d19b9SXu Kuohai const u8 class = BPF_CLASS(insn->code); 1218*5b3d19b9SXu Kuohai const u8 mode = BPF_MODE(insn->code); 1219*5b3d19b9SXu Kuohai const u8 src = insn->src_reg; 1220*5b3d19b9SXu Kuohai const u8 dst = insn->dst_reg; 1221*5b3d19b9SXu Kuohai const s32 imm = insn->imm; 1222*5b3d19b9SXu Kuohai const s16 off = insn->off; 1223*5b3d19b9SXu Kuohai 1224*5b3d19b9SXu Kuohai switch (class) { 1225*5b3d19b9SXu Kuohai case BPF_STX: 1226*5b3d19b9SXu Kuohai case BPF_ST: 1227*5b3d19b9SXu Kuohai /* fp holds atomic operation result */ 1228*5b3d19b9SXu Kuohai if (class == BPF_STX && mode == BPF_ATOMIC && 1229*5b3d19b9SXu Kuohai ((imm == BPF_XCHG || 1230*5b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_ADD) || 1231*5b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_AND) || 1232*5b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_XOR) || 1233*5b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_OR)) && 1234*5b3d19b9SXu Kuohai src == BPF_REG_FP)) 1235*5b3d19b9SXu Kuohai return 0; 1236*5b3d19b9SXu Kuohai 1237*5b3d19b9SXu Kuohai if (mode == BPF_MEM && dst == BPF_REG_FP && 1238*5b3d19b9SXu Kuohai off < offset) 1239*5b3d19b9SXu Kuohai offset = insn->off; 1240*5b3d19b9SXu Kuohai break; 1241*5b3d19b9SXu Kuohai 1242*5b3d19b9SXu Kuohai case BPF_JMP32: 1243*5b3d19b9SXu Kuohai case BPF_JMP: 1244*5b3d19b9SXu Kuohai break; 1245*5b3d19b9SXu Kuohai 1246*5b3d19b9SXu Kuohai case BPF_LDX: 1247*5b3d19b9SXu Kuohai case BPF_LD: 1248*5b3d19b9SXu Kuohai /* fp holds load result */ 1249*5b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 1250*5b3d19b9SXu Kuohai return 0; 1251*5b3d19b9SXu Kuohai 1252*5b3d19b9SXu Kuohai if (class == BPF_LDX && mode == BPF_MEM && 1253*5b3d19b9SXu Kuohai src == BPF_REG_FP && off < offset) 1254*5b3d19b9SXu Kuohai offset = off; 1255*5b3d19b9SXu Kuohai break; 1256*5b3d19b9SXu Kuohai 1257*5b3d19b9SXu Kuohai case BPF_ALU: 1258*5b3d19b9SXu Kuohai case BPF_ALU64: 1259*5b3d19b9SXu Kuohai default: 1260*5b3d19b9SXu Kuohai /* fp holds ALU result */ 1261*5b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 1262*5b3d19b9SXu Kuohai return 0; 1263*5b3d19b9SXu Kuohai } 1264*5b3d19b9SXu Kuohai } 1265*5b3d19b9SXu Kuohai 1266*5b3d19b9SXu Kuohai if (offset < 0) { 1267*5b3d19b9SXu Kuohai /* 1268*5b3d19b9SXu Kuohai * safely be converted to a positive 'int', since insn->off 1269*5b3d19b9SXu Kuohai * is 's16' 1270*5b3d19b9SXu Kuohai */ 1271*5b3d19b9SXu Kuohai offset = -offset; 1272*5b3d19b9SXu Kuohai /* align down to 8 bytes */ 1273*5b3d19b9SXu Kuohai offset = ALIGN_DOWN(offset, 8); 1274*5b3d19b9SXu Kuohai } 1275*5b3d19b9SXu Kuohai 1276*5b3d19b9SXu Kuohai return offset; 1277*5b3d19b9SXu Kuohai } 1278*5b3d19b9SXu Kuohai 12798c11ea5cSDaniel Borkmann static int build_body(struct jit_ctx *ctx, bool extra_pass) 1280e54bcde3SZi Shen Lim { 1281e54bcde3SZi Shen Lim const struct bpf_prog *prog = ctx->prog; 1282e54bcde3SZi Shen Lim int i; 1283e54bcde3SZi Shen Lim 128432f6865cSIlias Apalodimas /* 128532f6865cSIlias Apalodimas * - offset[0] offset of the end of prologue, 128632f6865cSIlias Apalodimas * start of the 1st instruction. 128732f6865cSIlias Apalodimas * - offset[1] - offset of the end of 1st instruction, 128832f6865cSIlias Apalodimas * start of the 2nd instruction 128932f6865cSIlias Apalodimas * [....] 129032f6865cSIlias Apalodimas * - offset[3] - offset of the end of 3rd instruction, 129132f6865cSIlias Apalodimas * start of 4th instruction 129232f6865cSIlias Apalodimas */ 1293e54bcde3SZi Shen Lim for (i = 0; i < prog->len; i++) { 1294e54bcde3SZi Shen Lim const struct bpf_insn *insn = &prog->insnsi[i]; 1295e54bcde3SZi Shen Lim int ret; 1296e54bcde3SZi Shen Lim 129732f6865cSIlias Apalodimas if (ctx->image == NULL) 129832f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 12998c11ea5cSDaniel Borkmann ret = build_insn(insn, ctx, extra_pass); 130030d3d94cSZi Shen Lim if (ret > 0) { 130130d3d94cSZi Shen Lim i++; 1302ddc665a4SDaniel Borkmann if (ctx->image == NULL) 1303ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 130430d3d94cSZi Shen Lim continue; 130530d3d94cSZi Shen Lim } 1306e54bcde3SZi Shen Lim if (ret) 1307e54bcde3SZi Shen Lim return ret; 1308e54bcde3SZi Shen Lim } 130932f6865cSIlias Apalodimas /* 131032f6865cSIlias Apalodimas * offset is allocated with prog->len + 1 so fill in 131132f6865cSIlias Apalodimas * the last element with the offset after the last 131232f6865cSIlias Apalodimas * instruction (end of program) 131332f6865cSIlias Apalodimas */ 131432f6865cSIlias Apalodimas if (ctx->image == NULL) 131532f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 1316e54bcde3SZi Shen Lim 1317e54bcde3SZi Shen Lim return 0; 1318e54bcde3SZi Shen Lim } 1319e54bcde3SZi Shen Lim 132042ff712bSZi Shen Lim static int validate_code(struct jit_ctx *ctx) 132142ff712bSZi Shen Lim { 132242ff712bSZi Shen Lim int i; 132342ff712bSZi Shen Lim 132442ff712bSZi Shen Lim for (i = 0; i < ctx->idx; i++) { 132542ff712bSZi Shen Lim u32 a64_insn = le32_to_cpu(ctx->image[i]); 132642ff712bSZi Shen Lim 132742ff712bSZi Shen Lim if (a64_insn == AARCH64_BREAK_FAULT) 132842ff712bSZi Shen Lim return -1; 132942ff712bSZi Shen Lim } 133042ff712bSZi Shen Lim 133180083428SJean-Philippe Brucker if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries)) 133280083428SJean-Philippe Brucker return -1; 133380083428SJean-Philippe Brucker 133442ff712bSZi Shen Lim return 0; 133542ff712bSZi Shen Lim } 133642ff712bSZi Shen Lim 1337e54bcde3SZi Shen Lim static inline void bpf_flush_icache(void *start, void *end) 1338e54bcde3SZi Shen Lim { 1339e54bcde3SZi Shen Lim flush_icache_range((unsigned long)start, (unsigned long)end); 1340e54bcde3SZi Shen Lim } 1341e54bcde3SZi Shen Lim 1342db496944SAlexei Starovoitov struct arm64_jit_data { 1343db496944SAlexei Starovoitov struct bpf_binary_header *header; 1344db496944SAlexei Starovoitov u8 *image; 1345db496944SAlexei Starovoitov struct jit_ctx ctx; 1346db496944SAlexei Starovoitov }; 1347db496944SAlexei Starovoitov 1348d1c55ab5SDaniel Borkmann struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 1349e54bcde3SZi Shen Lim { 135080083428SJean-Philippe Brucker int image_size, prog_size, extable_size; 135126eb042eSDaniel Borkmann struct bpf_prog *tmp, *orig_prog = prog; 1352b569c1c6SDaniel Borkmann struct bpf_binary_header *header; 1353db496944SAlexei Starovoitov struct arm64_jit_data *jit_data; 135456ea6a8bSDaniel Borkmann bool was_classic = bpf_prog_was_classic(prog); 135526eb042eSDaniel Borkmann bool tmp_blinded = false; 1356db496944SAlexei Starovoitov bool extra_pass = false; 1357e54bcde3SZi Shen Lim struct jit_ctx ctx; 1358b569c1c6SDaniel Borkmann u8 *image_ptr; 1359e54bcde3SZi Shen Lim 136060b58afcSAlexei Starovoitov if (!prog->jit_requested) 136126eb042eSDaniel Borkmann return orig_prog; 136226eb042eSDaniel Borkmann 136326eb042eSDaniel Borkmann tmp = bpf_jit_blind_constants(prog); 136426eb042eSDaniel Borkmann /* If blinding was requested and we failed during blinding, 136526eb042eSDaniel Borkmann * we must fall back to the interpreter. 136626eb042eSDaniel Borkmann */ 136726eb042eSDaniel Borkmann if (IS_ERR(tmp)) 136826eb042eSDaniel Borkmann return orig_prog; 136926eb042eSDaniel Borkmann if (tmp != prog) { 137026eb042eSDaniel Borkmann tmp_blinded = true; 137126eb042eSDaniel Borkmann prog = tmp; 137226eb042eSDaniel Borkmann } 1373e54bcde3SZi Shen Lim 1374db496944SAlexei Starovoitov jit_data = prog->aux->jit_data; 1375db496944SAlexei Starovoitov if (!jit_data) { 1376db496944SAlexei Starovoitov jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 1377db496944SAlexei Starovoitov if (!jit_data) { 1378db496944SAlexei Starovoitov prog = orig_prog; 1379db496944SAlexei Starovoitov goto out; 1380db496944SAlexei Starovoitov } 1381db496944SAlexei Starovoitov prog->aux->jit_data = jit_data; 1382db496944SAlexei Starovoitov } 1383db496944SAlexei Starovoitov if (jit_data->ctx.offset) { 1384db496944SAlexei Starovoitov ctx = jit_data->ctx; 1385db496944SAlexei Starovoitov image_ptr = jit_data->image; 1386db496944SAlexei Starovoitov header = jit_data->header; 1387db496944SAlexei Starovoitov extra_pass = true; 138880083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1389db496944SAlexei Starovoitov goto skip_init_ctx; 1390db496944SAlexei Starovoitov } 1391e54bcde3SZi Shen Lim memset(&ctx, 0, sizeof(ctx)); 1392e54bcde3SZi Shen Lim ctx.prog = prog; 1393e54bcde3SZi Shen Lim 139432f6865cSIlias Apalodimas ctx.offset = kcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); 139526eb042eSDaniel Borkmann if (ctx.offset == NULL) { 139626eb042eSDaniel Borkmann prog = orig_prog; 1397db496944SAlexei Starovoitov goto out_off; 139826eb042eSDaniel Borkmann } 1399e54bcde3SZi Shen Lim 1400*5b3d19b9SXu Kuohai ctx.fpb_offset = find_fpb_offset(prog); 1401*5b3d19b9SXu Kuohai 140268e4f238SHou Tao /* 140368e4f238SHou Tao * 1. Initial fake pass to compute ctx->idx and ctx->offset. 140468e4f238SHou Tao * 140568e4f238SHou Tao * BPF line info needs ctx->offset[i] to be the offset of 140668e4f238SHou Tao * instruction[i] in jited image, so build prologue first. 140768e4f238SHou Tao */ 140868e4f238SHou Tao if (build_prologue(&ctx, was_classic)) { 140926eb042eSDaniel Borkmann prog = orig_prog; 141026eb042eSDaniel Borkmann goto out_off; 141126eb042eSDaniel Borkmann } 1412e54bcde3SZi Shen Lim 141368e4f238SHou Tao if (build_body(&ctx, extra_pass)) { 1414ddb55992SZi Shen Lim prog = orig_prog; 1415ddb55992SZi Shen Lim goto out_off; 1416ddb55992SZi Shen Lim } 141751c9fbb1SZi Shen Lim 141851c9fbb1SZi Shen Lim ctx.epilogue_offset = ctx.idx; 1419e54bcde3SZi Shen Lim build_epilogue(&ctx); 1420e54bcde3SZi Shen Lim 142180083428SJean-Philippe Brucker extable_size = prog->aux->num_exentries * 142280083428SJean-Philippe Brucker sizeof(struct exception_table_entry); 142380083428SJean-Philippe Brucker 1424e54bcde3SZi Shen Lim /* Now we know the actual image size. */ 142580083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 142680083428SJean-Philippe Brucker image_size = prog_size + extable_size; 1427b569c1c6SDaniel Borkmann header = bpf_jit_binary_alloc(image_size, &image_ptr, 1428b569c1c6SDaniel Borkmann sizeof(u32), jit_fill_hole); 142926eb042eSDaniel Borkmann if (header == NULL) { 143026eb042eSDaniel Borkmann prog = orig_prog; 143126eb042eSDaniel Borkmann goto out_off; 143226eb042eSDaniel Borkmann } 1433e54bcde3SZi Shen Lim 1434e54bcde3SZi Shen Lim /* 2. Now, the actual pass. */ 1435e54bcde3SZi Shen Lim 1436425e1ed7SLuc Van Oostenryck ctx.image = (__le32 *)image_ptr; 143780083428SJean-Philippe Brucker if (extable_size) 143880083428SJean-Philippe Brucker prog->aux->extable = (void *)image_ptr + prog_size; 1439db496944SAlexei Starovoitov skip_init_ctx: 1440e54bcde3SZi Shen Lim ctx.idx = 0; 144180083428SJean-Philippe Brucker ctx.exentry_idx = 0; 1442b569c1c6SDaniel Borkmann 144356ea6a8bSDaniel Borkmann build_prologue(&ctx, was_classic); 1444e54bcde3SZi Shen Lim 14458c11ea5cSDaniel Borkmann if (build_body(&ctx, extra_pass)) { 1446b569c1c6SDaniel Borkmann bpf_jit_binary_free(header); 144726eb042eSDaniel Borkmann prog = orig_prog; 144826eb042eSDaniel Borkmann goto out_off; 144960ef0494SDaniel Borkmann } 1450e54bcde3SZi Shen Lim 1451e54bcde3SZi Shen Lim build_epilogue(&ctx); 1452e54bcde3SZi Shen Lim 145342ff712bSZi Shen Lim /* 3. Extra pass to validate JITed code. */ 145442ff712bSZi Shen Lim if (validate_code(&ctx)) { 145542ff712bSZi Shen Lim bpf_jit_binary_free(header); 145626eb042eSDaniel Borkmann prog = orig_prog; 145726eb042eSDaniel Borkmann goto out_off; 145842ff712bSZi Shen Lim } 145942ff712bSZi Shen Lim 1460e54bcde3SZi Shen Lim /* And we're done. */ 1461e54bcde3SZi Shen Lim if (bpf_jit_enable > 1) 146280083428SJean-Philippe Brucker bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 1463e54bcde3SZi Shen Lim 1464c3d4c682SDaniel Borkmann bpf_flush_icache(header, ctx.image + ctx.idx); 1465b569c1c6SDaniel Borkmann 1466db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1467db496944SAlexei Starovoitov if (extra_pass && ctx.idx != jit_data->ctx.idx) { 1468db496944SAlexei Starovoitov pr_err_once("multi-func JIT bug %d != %d\n", 1469db496944SAlexei Starovoitov ctx.idx, jit_data->ctx.idx); 1470db496944SAlexei Starovoitov bpf_jit_binary_free(header); 1471db496944SAlexei Starovoitov prog->bpf_func = NULL; 1472db496944SAlexei Starovoitov prog->jited = 0; 1473db496944SAlexei Starovoitov goto out_off; 1474db496944SAlexei Starovoitov } 14759d876e79SDaniel Borkmann bpf_jit_binary_lock_ro(header); 1476db496944SAlexei Starovoitov } else { 1477db496944SAlexei Starovoitov jit_data->ctx = ctx; 1478db496944SAlexei Starovoitov jit_data->image = image_ptr; 1479db496944SAlexei Starovoitov jit_data->header = header; 1480db496944SAlexei Starovoitov } 1481e54bcde3SZi Shen Lim prog->bpf_func = (void *)ctx.image; 1482a91263d5SDaniel Borkmann prog->jited = 1; 148380083428SJean-Philippe Brucker prog->jited_len = prog_size; 148426eb042eSDaniel Borkmann 1485db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1486dda7596cSHou Tao int i; 1487dda7596cSHou Tao 1488dda7596cSHou Tao /* offset[prog->len] is the size of program */ 1489dda7596cSHou Tao for (i = 0; i <= prog->len; i++) 1490dda7596cSHou Tao ctx.offset[i] *= AARCH64_INSN_SIZE; 149132f6865cSIlias Apalodimas bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 149226eb042eSDaniel Borkmann out_off: 1493e54bcde3SZi Shen Lim kfree(ctx.offset); 1494db496944SAlexei Starovoitov kfree(jit_data); 1495db496944SAlexei Starovoitov prog->aux->jit_data = NULL; 1496db496944SAlexei Starovoitov } 149726eb042eSDaniel Borkmann out: 149826eb042eSDaniel Borkmann if (tmp_blinded) 149926eb042eSDaniel Borkmann bpf_jit_prog_release_other(prog, prog == orig_prog ? 150026eb042eSDaniel Borkmann tmp : orig_prog); 1501d1c55ab5SDaniel Borkmann return prog; 1502e54bcde3SZi Shen Lim } 150391fc957cSArd Biesheuvel 1504b5e975d2SHou Tao bool bpf_jit_supports_kfunc_call(void) 1505b5e975d2SHou Tao { 1506b5e975d2SHou Tao return true; 1507b5e975d2SHou Tao } 1508b5e975d2SHou Tao 15095d63ae90SLorenz Bauer u64 bpf_jit_alloc_exec_limit(void) 15105d63ae90SLorenz Bauer { 1511b89ddf4cSRussell King return VMALLOC_END - VMALLOC_START; 15125d63ae90SLorenz Bauer } 15135d63ae90SLorenz Bauer 151491fc957cSArd Biesheuvel void *bpf_jit_alloc_exec(unsigned long size) 151591fc957cSArd Biesheuvel { 151636c4a73bSAndrey Konovalov /* Memory is intended to be executable, reset the pointer tag. */ 151736c4a73bSAndrey Konovalov return kasan_reset_tag(vmalloc(size)); 151891fc957cSArd Biesheuvel } 151991fc957cSArd Biesheuvel 152091fc957cSArd Biesheuvel void bpf_jit_free_exec(void *addr) 152191fc957cSArd Biesheuvel { 152291fc957cSArd Biesheuvel return vfree(addr); 152391fc957cSArd Biesheuvel } 1524