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> 13b2ad54e1SXu Kuohai #include <linux/memory.h> 14e54bcde3SZi Shen Lim #include <linux/printk.h> 15e54bcde3SZi Shen Lim #include <linux/slab.h> 16b569c1c6SDaniel Borkmann 17d6e2cc56SMark Rutland #include <asm/asm-extable.h> 18e54bcde3SZi Shen Lim #include <asm/byteorder.h> 19e54bcde3SZi Shen Lim #include <asm/cacheflush.h> 20b569c1c6SDaniel Borkmann #include <asm/debug-monitors.h> 213e00e39dSMark Rutland #include <asm/insn.h> 22b2ad54e1SXu Kuohai #include <asm/patching.h> 23d4bbc30bSLaura Abbott #include <asm/set_memory.h> 24e54bcde3SZi Shen Lim 25e54bcde3SZi Shen Lim #include "bpf_jit.h" 26e54bcde3SZi Shen Lim 2726eb042eSDaniel Borkmann #define TMP_REG_1 (MAX_BPF_JIT_REG + 0) 2826eb042eSDaniel Borkmann #define TMP_REG_2 (MAX_BPF_JIT_REG + 1) 29ddb55992SZi Shen Lim #define TCALL_CNT (MAX_BPF_JIT_REG + 2) 307005cadeSDaniel Borkmann #define TMP_REG_3 (MAX_BPF_JIT_REG + 3) 315b3d19b9SXu Kuohai #define FP_BOTTOM (MAX_BPF_JIT_REG + 4) 32e54bcde3SZi Shen Lim 331902472bSHou Tao #define check_imm(bits, imm) do { \ 341902472bSHou Tao if ((((imm) > 0) && ((imm) >> (bits))) || \ 351902472bSHou Tao (((imm) < 0) && (~(imm) >> (bits)))) { \ 361902472bSHou Tao pr_info("[%2d] imm=%d(0x%x) out of range\n", \ 371902472bSHou Tao i, imm, imm); \ 381902472bSHou Tao return -EINVAL; \ 391902472bSHou Tao } \ 401902472bSHou Tao } while (0) 411902472bSHou Tao #define check_imm19(imm) check_imm(19, imm) 421902472bSHou Tao #define check_imm26(imm) check_imm(26, imm) 431902472bSHou Tao 44e54bcde3SZi Shen Lim /* Map BPF registers to A64 registers */ 45e54bcde3SZi Shen Lim static const int bpf2a64[] = { 46e54bcde3SZi Shen Lim /* return value from in-kernel function, and exit value from eBPF */ 47e54bcde3SZi Shen Lim [BPF_REG_0] = A64_R(7), 48e54bcde3SZi Shen Lim /* arguments from eBPF program to in-kernel function */ 49e54bcde3SZi Shen Lim [BPF_REG_1] = A64_R(0), 50e54bcde3SZi Shen Lim [BPF_REG_2] = A64_R(1), 51e54bcde3SZi Shen Lim [BPF_REG_3] = A64_R(2), 52e54bcde3SZi Shen Lim [BPF_REG_4] = A64_R(3), 53e54bcde3SZi Shen Lim [BPF_REG_5] = A64_R(4), 54e54bcde3SZi Shen Lim /* callee saved registers that in-kernel function will preserve */ 55e54bcde3SZi Shen Lim [BPF_REG_6] = A64_R(19), 56e54bcde3SZi Shen Lim [BPF_REG_7] = A64_R(20), 57e54bcde3SZi Shen Lim [BPF_REG_8] = A64_R(21), 58e54bcde3SZi Shen Lim [BPF_REG_9] = A64_R(22), 59e54bcde3SZi Shen Lim /* read-only frame pointer to access stack */ 60ec0738dbSYang Shi [BPF_REG_FP] = A64_R(25), 6106edc59cSChristoph Hellwig /* temporary registers for BPF JIT */ 624c1cd4fdSYang Shi [TMP_REG_1] = A64_R(10), 634c1cd4fdSYang Shi [TMP_REG_2] = A64_R(11), 647005cadeSDaniel Borkmann [TMP_REG_3] = A64_R(12), 65ddb55992SZi Shen Lim /* tail_call_cnt */ 66ddb55992SZi Shen Lim [TCALL_CNT] = A64_R(26), 6726eb042eSDaniel Borkmann /* temporary register for blinding constants */ 6826eb042eSDaniel Borkmann [BPF_REG_AX] = A64_R(9), 695b3d19b9SXu Kuohai [FP_BOTTOM] = A64_R(27), 70e54bcde3SZi Shen Lim }; 71e54bcde3SZi Shen Lim 72e54bcde3SZi Shen Lim struct jit_ctx { 73e54bcde3SZi Shen Lim const struct bpf_prog *prog; 74e54bcde3SZi Shen Lim int idx; 7551c9fbb1SZi Shen Lim int epilogue_offset; 76e54bcde3SZi Shen Lim int *offset; 7780083428SJean-Philippe Brucker int exentry_idx; 78425e1ed7SLuc Van Oostenryck __le32 *image; 79f1c9eed7SDaniel Borkmann u32 stack_size; 805b3d19b9SXu Kuohai int fpb_offset; 81e54bcde3SZi Shen Lim }; 82e54bcde3SZi Shen Lim 83b2ad54e1SXu Kuohai struct bpf_plt { 84b2ad54e1SXu Kuohai u32 insn_ldr; /* load target */ 85b2ad54e1SXu Kuohai u32 insn_br; /* branch to target */ 86b2ad54e1SXu Kuohai u64 target; /* target value */ 87b2ad54e1SXu Kuohai }; 88b2ad54e1SXu Kuohai 89b2ad54e1SXu Kuohai #define PLT_TARGET_SIZE sizeof_field(struct bpf_plt, target) 90b2ad54e1SXu Kuohai #define PLT_TARGET_OFFSET offsetof(struct bpf_plt, target) 91b2ad54e1SXu Kuohai 92e54bcde3SZi Shen Lim static inline void emit(const u32 insn, struct jit_ctx *ctx) 93e54bcde3SZi Shen Lim { 94e54bcde3SZi Shen Lim if (ctx->image != NULL) 95e54bcde3SZi Shen Lim ctx->image[ctx->idx] = cpu_to_le32(insn); 96e54bcde3SZi Shen Lim 97e54bcde3SZi Shen Lim ctx->idx++; 98e54bcde3SZi Shen Lim } 99e54bcde3SZi Shen Lim 100e54bcde3SZi Shen Lim static inline void emit_a64_mov_i(const int is64, const int reg, 101e54bcde3SZi Shen Lim const s32 val, struct jit_ctx *ctx) 102e54bcde3SZi Shen Lim { 103e54bcde3SZi Shen Lim u16 hi = val >> 16; 104e54bcde3SZi Shen Lim u16 lo = val & 0xffff; 105e54bcde3SZi Shen Lim 106e54bcde3SZi Shen Lim if (hi & 0x8000) { 107e54bcde3SZi Shen Lim if (hi == 0xffff) { 108e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx); 109e54bcde3SZi Shen Lim } else { 110e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx); 1116d2eea6fSDaniel Borkmann if (lo != 0xffff) 112e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, lo, 0), ctx); 113e54bcde3SZi Shen Lim } 114e54bcde3SZi Shen Lim } else { 115e54bcde3SZi Shen Lim emit(A64_MOVZ(is64, reg, lo, 0), ctx); 116e54bcde3SZi Shen Lim if (hi) 117e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, hi, 16), ctx); 118e54bcde3SZi Shen Lim } 119e54bcde3SZi Shen Lim } 120e54bcde3SZi Shen Lim 1216d2eea6fSDaniel Borkmann static int i64_i16_blocks(const u64 val, bool inverse) 1226d2eea6fSDaniel Borkmann { 1236d2eea6fSDaniel Borkmann return (((val >> 0) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1246d2eea6fSDaniel Borkmann (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1256d2eea6fSDaniel Borkmann (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1266d2eea6fSDaniel Borkmann (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000)); 1276d2eea6fSDaniel Borkmann } 1286d2eea6fSDaniel Borkmann 1296d2eea6fSDaniel Borkmann static inline void emit_a64_mov_i64(const int reg, const u64 val, 1306d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1316d2eea6fSDaniel Borkmann { 1326d2eea6fSDaniel Borkmann u64 nrm_tmp = val, rev_tmp = ~val; 1336d2eea6fSDaniel Borkmann bool inverse; 1346d2eea6fSDaniel Borkmann int shift; 1356d2eea6fSDaniel Borkmann 1366d2eea6fSDaniel Borkmann if (!(nrm_tmp >> 32)) 1376d2eea6fSDaniel Borkmann return emit_a64_mov_i(0, reg, (u32)val, ctx); 1386d2eea6fSDaniel Borkmann 1396d2eea6fSDaniel Borkmann inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false); 1406d2eea6fSDaniel Borkmann shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) : 1416d2eea6fSDaniel Borkmann (fls64(nrm_tmp) - 1)), 16), 0); 1426d2eea6fSDaniel Borkmann if (inverse) 1436d2eea6fSDaniel Borkmann emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx); 1446d2eea6fSDaniel Borkmann else 1456d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1466d2eea6fSDaniel Borkmann shift -= 16; 1476d2eea6fSDaniel Borkmann while (shift >= 0) { 1486d2eea6fSDaniel Borkmann if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000)) 1496d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1506d2eea6fSDaniel Borkmann shift -= 16; 1516d2eea6fSDaniel Borkmann } 1526d2eea6fSDaniel Borkmann } 1536d2eea6fSDaniel Borkmann 154b2ad54e1SXu Kuohai static inline void emit_bti(u32 insn, struct jit_ctx *ctx) 155b2ad54e1SXu Kuohai { 156b2ad54e1SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) 157b2ad54e1SXu Kuohai emit(insn, ctx); 158b2ad54e1SXu Kuohai } 159b2ad54e1SXu Kuohai 1606d2eea6fSDaniel Borkmann /* 161cc2b8ed1SArd Biesheuvel * Kernel addresses in the vmalloc space use at most 48 bits, and the 162cc2b8ed1SArd Biesheuvel * remaining bits are guaranteed to be 0x1. So we can compose the address 163cc2b8ed1SArd Biesheuvel * with a fixed length movn/movk/movk sequence. 1646d2eea6fSDaniel Borkmann */ 1656d2eea6fSDaniel Borkmann static inline void emit_addr_mov_i64(const int reg, const u64 val, 1666d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1676d2eea6fSDaniel Borkmann { 1686d2eea6fSDaniel Borkmann u64 tmp = val; 1696d2eea6fSDaniel Borkmann int shift = 0; 1706d2eea6fSDaniel Borkmann 171cc2b8ed1SArd Biesheuvel emit(A64_MOVN(1, reg, ~tmp & 0xffff, shift), ctx); 172cc2b8ed1SArd Biesheuvel while (shift < 32) { 1736d2eea6fSDaniel Borkmann tmp >>= 16; 1746d2eea6fSDaniel Borkmann shift += 16; 1756d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx); 1766d2eea6fSDaniel Borkmann } 1776d2eea6fSDaniel Borkmann } 1786d2eea6fSDaniel Borkmann 179efc9909fSXu Kuohai static inline void emit_call(u64 target, struct jit_ctx *ctx) 180efc9909fSXu Kuohai { 181efc9909fSXu Kuohai u8 tmp = bpf2a64[TMP_REG_1]; 182efc9909fSXu Kuohai 183efc9909fSXu Kuohai emit_addr_mov_i64(tmp, target, ctx); 184efc9909fSXu Kuohai emit(A64_BLR(tmp), ctx); 185efc9909fSXu Kuohai } 186efc9909fSXu Kuohai 18732f6865cSIlias Apalodimas static inline int bpf2a64_offset(int bpf_insn, int off, 188e54bcde3SZi Shen Lim const struct jit_ctx *ctx) 189e54bcde3SZi Shen Lim { 19032f6865cSIlias Apalodimas /* BPF JMP offset is relative to the next instruction */ 19132f6865cSIlias Apalodimas bpf_insn++; 19232f6865cSIlias Apalodimas /* 19332f6865cSIlias Apalodimas * Whereas arm64 branch instructions encode the offset 19432f6865cSIlias Apalodimas * from the branch itself, so we must subtract 1 from the 19532f6865cSIlias Apalodimas * instruction offset. 19632f6865cSIlias Apalodimas */ 19732f6865cSIlias Apalodimas return ctx->offset[bpf_insn + off] - (ctx->offset[bpf_insn] - 1); 198e54bcde3SZi Shen Lim } 199e54bcde3SZi Shen Lim 200b569c1c6SDaniel Borkmann static void jit_fill_hole(void *area, unsigned int size) 201b569c1c6SDaniel Borkmann { 202425e1ed7SLuc Van Oostenryck __le32 *ptr; 203b569c1c6SDaniel Borkmann /* We are guaranteed to have aligned memory. */ 204b569c1c6SDaniel Borkmann for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 205b569c1c6SDaniel Borkmann *ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT); 206b569c1c6SDaniel Borkmann } 207b569c1c6SDaniel Borkmann 208e54bcde3SZi Shen Lim static inline int epilogue_offset(const struct jit_ctx *ctx) 209e54bcde3SZi Shen Lim { 21051c9fbb1SZi Shen Lim int to = ctx->epilogue_offset; 21151c9fbb1SZi Shen Lim int from = ctx->idx; 212e54bcde3SZi Shen Lim 213e54bcde3SZi Shen Lim return to - from; 214e54bcde3SZi Shen Lim } 215e54bcde3SZi Shen Lim 216fd868f14SLuke Nelson static bool is_addsub_imm(u32 imm) 217fd868f14SLuke Nelson { 218fd868f14SLuke Nelson /* Either imm12 or shifted imm12. */ 219fd868f14SLuke Nelson return !(imm & ~0xfff) || !(imm & ~0xfff000); 220fd868f14SLuke Nelson } 221fd868f14SLuke Nelson 2227db6c0f1SXu Kuohai /* 2237db6c0f1SXu Kuohai * There are 3 types of AArch64 LDR/STR (immediate) instruction: 2247db6c0f1SXu Kuohai * Post-index, Pre-index, Unsigned offset. 2257db6c0f1SXu Kuohai * 2267db6c0f1SXu Kuohai * For BPF ldr/str, the "unsigned offset" type is sufficient. 2277db6c0f1SXu Kuohai * 2287db6c0f1SXu Kuohai * "Unsigned offset" type LDR(immediate) format: 2297db6c0f1SXu Kuohai * 2307db6c0f1SXu Kuohai * 3 2 1 0 2317db6c0f1SXu 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 2327db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2337db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 1| imm12 | Rn | Rt | 2347db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2357db6c0f1SXu Kuohai * scale 2367db6c0f1SXu Kuohai * 2377db6c0f1SXu Kuohai * "Unsigned offset" type STR(immediate) format: 2387db6c0f1SXu Kuohai * 3 2 1 0 2397db6c0f1SXu 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 2407db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2417db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 0| imm12 | Rn | Rt | 2427db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2437db6c0f1SXu Kuohai * scale 2447db6c0f1SXu Kuohai * 2457db6c0f1SXu Kuohai * The offset is calculated from imm12 and scale in the following way: 2467db6c0f1SXu Kuohai * 2477db6c0f1SXu Kuohai * offset = (u64)imm12 << scale 2487db6c0f1SXu Kuohai */ 2495b3d19b9SXu Kuohai static bool is_lsi_offset(int offset, int scale) 2507db6c0f1SXu Kuohai { 2517db6c0f1SXu Kuohai if (offset < 0) 2527db6c0f1SXu Kuohai return false; 2537db6c0f1SXu Kuohai 2547db6c0f1SXu Kuohai if (offset > (0xFFF << scale)) 2557db6c0f1SXu Kuohai return false; 2567db6c0f1SXu Kuohai 2577db6c0f1SXu Kuohai if (offset & ((1 << scale) - 1)) 2587db6c0f1SXu Kuohai return false; 2597db6c0f1SXu Kuohai 2607db6c0f1SXu Kuohai return true; 2617db6c0f1SXu Kuohai } 2627db6c0f1SXu Kuohai 263b2ad54e1SXu Kuohai /* generated prologue: 264b2ad54e1SXu Kuohai * bti c // if CONFIG_ARM64_BTI_KERNEL 265b2ad54e1SXu Kuohai * mov x9, lr 266b2ad54e1SXu Kuohai * nop // POKE_OFFSET 267b2ad54e1SXu Kuohai * paciasp // if CONFIG_ARM64_PTR_AUTH_KERNEL 268b2ad54e1SXu Kuohai * stp x29, lr, [sp, #-16]! 269b2ad54e1SXu Kuohai * mov x29, sp 270b2ad54e1SXu Kuohai * stp x19, x20, [sp, #-16]! 271b2ad54e1SXu Kuohai * stp x21, x22, [sp, #-16]! 272b2ad54e1SXu Kuohai * stp x25, x26, [sp, #-16]! 273b2ad54e1SXu Kuohai * stp x27, x28, [sp, #-16]! 274b2ad54e1SXu Kuohai * mov x25, sp 275b2ad54e1SXu Kuohai * mov tcc, #0 276b2ad54e1SXu Kuohai * // PROLOGUE_OFFSET 277b2ad54e1SXu Kuohai */ 278b2ad54e1SXu Kuohai 279b2ad54e1SXu Kuohai #define BTI_INSNS (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) ? 1 : 0) 280b2ad54e1SXu Kuohai #define PAC_INSNS (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) ? 1 : 0) 281b2ad54e1SXu Kuohai 282b2ad54e1SXu Kuohai /* Offset of nop instruction in bpf prog entry to be poked */ 283b2ad54e1SXu Kuohai #define POKE_OFFSET (BTI_INSNS + 1) 284b2ad54e1SXu Kuohai 285a2284d91SDaniel Borkmann /* Tail call offset to jump into */ 286b2ad54e1SXu Kuohai #define PROLOGUE_OFFSET (BTI_INSNS + 2 + PAC_INSNS + 8) 287ddb55992SZi Shen Lim 28856ea6a8bSDaniel Borkmann static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf) 289e54bcde3SZi Shen Lim { 290f1c9eed7SDaniel Borkmann const struct bpf_prog *prog = ctx->prog; 291d4609a5dSJakub Sitnicki const bool is_main_prog = prog->aux->func_idx == 0; 292e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 293e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 294e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 295e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 296e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 297ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 2985b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 299ddb55992SZi Shen Lim const int idx0 = ctx->idx; 300ddb55992SZi Shen Lim int cur_offset; 301e54bcde3SZi Shen Lim 302ec0738dbSYang Shi /* 303ec0738dbSYang Shi * BPF prog stack layout 304ec0738dbSYang Shi * 305ec0738dbSYang Shi * high 306ec0738dbSYang Shi * original A64_SP => 0:+-----+ BPF prologue 307ec0738dbSYang Shi * |FP/LR| 308ec0738dbSYang Shi * current A64_FP => -16:+-----+ 309ec0738dbSYang Shi * | ... | callee saved registers 3104c1cd4fdSYang Shi * BPF fp register => -64:+-----+ <= (BPF_FP) 311ec0738dbSYang Shi * | | 312ec0738dbSYang Shi * | ... | BPF prog stack 313ec0738dbSYang Shi * | | 314f1c9eed7SDaniel Borkmann * +-----+ <= (BPF_FP - prog->aux->stack_depth) 31509ece3d0SDaniel Borkmann * |RSVD | padding 316f1c9eed7SDaniel Borkmann * current A64_SP => +-----+ <= (BPF_FP - ctx->stack_size) 317ec0738dbSYang Shi * | | 318ec0738dbSYang Shi * | ... | Function call stack 319ec0738dbSYang Shi * | | 320ec0738dbSYang Shi * +-----+ 321ec0738dbSYang Shi * low 322ec0738dbSYang Shi * 323ec0738dbSYang Shi */ 324ec0738dbSYang Shi 325a3f25d61SAlexander Duyck /* bpf function may be invoked by 3 instruction types: 326a3f25d61SAlexander Duyck * 1. bl, attached via freplace to bpf prog via short jump 327a3f25d61SAlexander Duyck * 2. br, attached via freplace to bpf prog via long jump 328a3f25d61SAlexander Duyck * 3. blr, working as a function pointer, used by emit_call. 329a3f25d61SAlexander Duyck * So BTI_JC should used here to support both br and blr. 330a3f25d61SAlexander Duyck */ 331a3f25d61SAlexander Duyck emit_bti(A64_BTI_JC, ctx); 332b2ad54e1SXu Kuohai 333b2ad54e1SXu Kuohai emit(A64_MOV(1, A64_R(9), A64_LR), ctx); 334b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 335b2ad54e1SXu Kuohai 336042152c2SXu Kuohai /* Sign lr */ 337042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 338042152c2SXu Kuohai emit(A64_PACIASP, ctx); 339fa76cfe6SMark Brown 340ec0738dbSYang Shi /* Save FP and LR registers to stay align with ARM64 AAPCS */ 341ec0738dbSYang Shi emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 342ec0738dbSYang Shi emit(A64_MOV(1, A64_FP, A64_SP), ctx); 343ec0738dbSYang Shi 344ddb55992SZi Shen Lim /* Save callee-saved registers */ 345e54bcde3SZi Shen Lim emit(A64_PUSH(r6, r7, A64_SP), ctx); 346e54bcde3SZi Shen Lim emit(A64_PUSH(r8, r9, A64_SP), ctx); 347ddb55992SZi Shen Lim emit(A64_PUSH(fp, tcc, A64_SP), ctx); 3485b3d19b9SXu Kuohai emit(A64_PUSH(fpb, A64_R(28), A64_SP), ctx); 349e54bcde3SZi Shen Lim 350ddb55992SZi Shen Lim /* Set up BPF prog stack base register */ 351e54bcde3SZi Shen Lim emit(A64_MOV(1, fp, A64_SP), ctx); 352e54bcde3SZi Shen Lim 353d4609a5dSJakub Sitnicki if (!ebpf_from_cbpf && is_main_prog) { 354ddb55992SZi Shen Lim /* Initialize tail_call_cnt */ 355ddb55992SZi Shen Lim emit(A64_MOVZ(1, tcc, 0, 0), ctx); 356ddb55992SZi Shen Lim 357ddb55992SZi Shen Lim cur_offset = ctx->idx - idx0; 358ddb55992SZi Shen Lim if (cur_offset != PROLOGUE_OFFSET) { 359ddb55992SZi Shen Lim pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 360ddb55992SZi Shen Lim cur_offset, PROLOGUE_OFFSET); 361ddb55992SZi Shen Lim return -1; 362ddb55992SZi Shen Lim } 363fa76cfe6SMark Brown 364fa76cfe6SMark Brown /* BTI landing pad for the tail call, done with a BR */ 365b2ad54e1SXu Kuohai emit_bti(A64_BTI_J, ctx); 36656ea6a8bSDaniel Borkmann } 367a2284d91SDaniel Borkmann 3685b3d19b9SXu Kuohai emit(A64_SUB_I(1, fpb, fp, ctx->fpb_offset), ctx); 3695b3d19b9SXu Kuohai 3703f287098STiezhu Yang /* Stack must be multiples of 16B */ 3713f287098STiezhu Yang ctx->stack_size = round_up(prog->aux->stack_depth, 16); 372a2284d91SDaniel Borkmann 373a2284d91SDaniel Borkmann /* Set up function call stack */ 374a2284d91SDaniel Borkmann emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 375ddb55992SZi Shen Lim return 0; 376ddb55992SZi Shen Lim } 377ddb55992SZi Shen Lim 378ddb55992SZi Shen Lim static int out_offset = -1; /* initialized on the first pass of build_body() */ 379ddb55992SZi Shen Lim static int emit_bpf_tail_call(struct jit_ctx *ctx) 380ddb55992SZi Shen Lim { 381ddb55992SZi Shen Lim /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 382ddb55992SZi Shen Lim const u8 r2 = bpf2a64[BPF_REG_2]; 383ddb55992SZi Shen Lim const u8 r3 = bpf2a64[BPF_REG_3]; 384ddb55992SZi Shen Lim 385ddb55992SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 386ddb55992SZi Shen Lim const u8 prg = bpf2a64[TMP_REG_2]; 387ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 388ddb55992SZi Shen Lim const int idx0 = ctx->idx; 389ddb55992SZi Shen Lim #define cur_offset (ctx->idx - idx0) 390ddb55992SZi Shen Lim #define jmp_offset (out_offset - (cur_offset)) 391ddb55992SZi Shen Lim size_t off; 392ddb55992SZi Shen Lim 393ddb55992SZi Shen Lim /* if (index >= array->map.max_entries) 394ddb55992SZi Shen Lim * goto out; 395ddb55992SZi Shen Lim */ 396ddb55992SZi Shen Lim off = offsetof(struct bpf_array, map.max_entries); 397ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 398ddb55992SZi Shen Lim emit(A64_LDR32(tmp, r2, tmp), ctx); 39916338a9bSDaniel Borkmann emit(A64_MOV(0, r3, r3), ctx); 400ddb55992SZi Shen Lim emit(A64_CMP(0, r3, tmp), ctx); 40116338a9bSDaniel Borkmann emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 402ddb55992SZi Shen Lim 403ebf7f6f0STiezhu Yang /* 404ebf7f6f0STiezhu Yang * if (tail_call_cnt >= MAX_TAIL_CALL_CNT) 405ddb55992SZi Shen Lim * goto out; 406ddb55992SZi Shen Lim * tail_call_cnt++; 407ddb55992SZi Shen Lim */ 408ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 409ddb55992SZi Shen Lim emit(A64_CMP(1, tcc, tmp), ctx); 410ebf7f6f0STiezhu Yang emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 411ddb55992SZi Shen Lim emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 412ddb55992SZi Shen Lim 413ddb55992SZi Shen Lim /* prog = array->ptrs[index]; 414ddb55992SZi Shen Lim * if (prog == NULL) 415ddb55992SZi Shen Lim * goto out; 416ddb55992SZi Shen Lim */ 417ddb55992SZi Shen Lim off = offsetof(struct bpf_array, ptrs); 418ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 419d8b54110SDaniel Borkmann emit(A64_ADD(1, tmp, r2, tmp), ctx); 420d8b54110SDaniel Borkmann emit(A64_LSL(1, prg, r3, 3), ctx); 421d8b54110SDaniel Borkmann emit(A64_LDR64(prg, tmp, prg), ctx); 422ddb55992SZi Shen Lim emit(A64_CBZ(1, prg, jmp_offset), ctx); 423ddb55992SZi Shen Lim 424a2284d91SDaniel Borkmann /* goto *(prog->bpf_func + prologue_offset); */ 425ddb55992SZi Shen Lim off = offsetof(struct bpf_prog, bpf_func); 426ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 427ddb55992SZi Shen Lim emit(A64_LDR64(tmp, prg, tmp), ctx); 428ddb55992SZi Shen Lim emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 429a2284d91SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 430ddb55992SZi Shen Lim emit(A64_BR(tmp), ctx); 431ddb55992SZi Shen Lim 432ddb55992SZi Shen Lim /* out: */ 433ddb55992SZi Shen Lim if (out_offset == -1) 434ddb55992SZi Shen Lim out_offset = cur_offset; 435ddb55992SZi Shen Lim if (cur_offset != out_offset) { 436ddb55992SZi Shen Lim pr_err_once("tail_call out_offset = %d, expected %d!\n", 437ddb55992SZi Shen Lim cur_offset, out_offset); 438ddb55992SZi Shen Lim return -1; 439ddb55992SZi Shen Lim } 440ddb55992SZi Shen Lim return 0; 441ddb55992SZi Shen Lim #undef cur_offset 442ddb55992SZi Shen Lim #undef jmp_offset 443e54bcde3SZi Shen Lim } 444e54bcde3SZi Shen Lim 4451902472bSHou Tao #ifdef CONFIG_ARM64_LSE_ATOMICS 4461902472bSHou Tao static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 4471902472bSHou Tao { 4481902472bSHou Tao const u8 code = insn->code; 4491902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 4501902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 4511902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 4521902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 4531902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 4541902472bSHou Tao const s16 off = insn->off; 4551902472bSHou Tao u8 reg; 4561902472bSHou Tao 4571902472bSHou Tao if (!off) { 4581902472bSHou Tao reg = dst; 4591902472bSHou Tao } else { 4601902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 4611902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 4621902472bSHou Tao reg = tmp; 4631902472bSHou Tao } 4641902472bSHou Tao 4651902472bSHou Tao switch (insn->imm) { 4661902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 4671902472bSHou Tao case BPF_ADD: 4681902472bSHou Tao emit(A64_STADD(isdw, reg, src), ctx); 4691902472bSHou Tao break; 4701902472bSHou Tao case BPF_AND: 4711902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 4721902472bSHou Tao emit(A64_STCLR(isdw, reg, tmp2), ctx); 4731902472bSHou Tao break; 4741902472bSHou Tao case BPF_OR: 4751902472bSHou Tao emit(A64_STSET(isdw, reg, src), ctx); 4761902472bSHou Tao break; 4771902472bSHou Tao case BPF_XOR: 4781902472bSHou Tao emit(A64_STEOR(isdw, reg, src), ctx); 4791902472bSHou Tao break; 4801902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 4811902472bSHou Tao case BPF_ADD | BPF_FETCH: 4821902472bSHou Tao emit(A64_LDADDAL(isdw, src, reg, src), ctx); 4831902472bSHou Tao break; 4841902472bSHou Tao case BPF_AND | BPF_FETCH: 4851902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 4861902472bSHou Tao emit(A64_LDCLRAL(isdw, src, reg, tmp2), ctx); 4871902472bSHou Tao break; 4881902472bSHou Tao case BPF_OR | BPF_FETCH: 4891902472bSHou Tao emit(A64_LDSETAL(isdw, src, reg, src), ctx); 4901902472bSHou Tao break; 4911902472bSHou Tao case BPF_XOR | BPF_FETCH: 4921902472bSHou Tao emit(A64_LDEORAL(isdw, src, reg, src), ctx); 4931902472bSHou Tao break; 4941902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 4951902472bSHou Tao case BPF_XCHG: 4961902472bSHou Tao emit(A64_SWPAL(isdw, src, reg, src), ctx); 4971902472bSHou Tao break; 4981902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 4991902472bSHou Tao case BPF_CMPXCHG: 5001902472bSHou Tao emit(A64_CASAL(isdw, src, reg, bpf2a64[BPF_REG_0]), ctx); 5011902472bSHou Tao break; 5021902472bSHou Tao default: 5031902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", insn->imm); 5041902472bSHou Tao return -EINVAL; 5051902472bSHou Tao } 5061902472bSHou Tao 5071902472bSHou Tao return 0; 5081902472bSHou Tao } 5091902472bSHou Tao #else 5101902472bSHou Tao static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5111902472bSHou Tao { 5121902472bSHou Tao return -EINVAL; 5131902472bSHou Tao } 5141902472bSHou Tao #endif 5151902472bSHou Tao 5161902472bSHou Tao static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5171902472bSHou Tao { 5181902472bSHou Tao const u8 code = insn->code; 5191902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 5201902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 5211902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 5221902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 5231902472bSHou Tao const u8 tmp3 = bpf2a64[TMP_REG_3]; 5241902472bSHou Tao const int i = insn - ctx->prog->insnsi; 5251902472bSHou Tao const s32 imm = insn->imm; 5261902472bSHou Tao const s16 off = insn->off; 5271902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 5281902472bSHou Tao u8 reg; 5291902472bSHou Tao s32 jmp_offset; 5301902472bSHou Tao 5311902472bSHou Tao if (!off) { 5321902472bSHou Tao reg = dst; 5331902472bSHou Tao } else { 5341902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 5351902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 5361902472bSHou Tao reg = tmp; 5371902472bSHou Tao } 5381902472bSHou Tao 5391902472bSHou Tao if (imm == BPF_ADD || imm == BPF_AND || 5401902472bSHou Tao imm == BPF_OR || imm == BPF_XOR) { 5411902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 5421902472bSHou Tao emit(A64_LDXR(isdw, tmp2, reg), ctx); 5431902472bSHou Tao if (imm == BPF_ADD) 5441902472bSHou Tao emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 5451902472bSHou Tao else if (imm == BPF_AND) 5461902472bSHou Tao emit(A64_AND(isdw, tmp2, tmp2, src), ctx); 5471902472bSHou Tao else if (imm == BPF_OR) 5481902472bSHou Tao emit(A64_ORR(isdw, tmp2, tmp2, src), ctx); 5491902472bSHou Tao else 5501902472bSHou Tao emit(A64_EOR(isdw, tmp2, tmp2, src), ctx); 5511902472bSHou Tao emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); 5521902472bSHou Tao jmp_offset = -3; 5531902472bSHou Tao check_imm19(jmp_offset); 5541902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5551902472bSHou Tao } else if (imm == (BPF_ADD | BPF_FETCH) || 5561902472bSHou Tao imm == (BPF_AND | BPF_FETCH) || 5571902472bSHou Tao imm == (BPF_OR | BPF_FETCH) || 5581902472bSHou Tao imm == (BPF_XOR | BPF_FETCH)) { 5591902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 5601902472bSHou Tao const u8 ax = bpf2a64[BPF_REG_AX]; 5611902472bSHou Tao 5621902472bSHou Tao emit(A64_MOV(isdw, ax, src), ctx); 5631902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 5641902472bSHou Tao if (imm == (BPF_ADD | BPF_FETCH)) 5651902472bSHou Tao emit(A64_ADD(isdw, tmp2, src, ax), ctx); 5661902472bSHou Tao else if (imm == (BPF_AND | BPF_FETCH)) 5671902472bSHou Tao emit(A64_AND(isdw, tmp2, src, ax), ctx); 5681902472bSHou Tao else if (imm == (BPF_OR | BPF_FETCH)) 5691902472bSHou Tao emit(A64_ORR(isdw, tmp2, src, ax), ctx); 5701902472bSHou Tao else 5711902472bSHou Tao emit(A64_EOR(isdw, tmp2, src, ax), ctx); 5721902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 5731902472bSHou Tao jmp_offset = -3; 5741902472bSHou Tao check_imm19(jmp_offset); 5751902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5761902472bSHou Tao emit(A64_DMB_ISH, ctx); 5771902472bSHou Tao } else if (imm == BPF_XCHG) { 5781902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 5791902472bSHou Tao emit(A64_MOV(isdw, tmp2, src), ctx); 5801902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 5811902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 5821902472bSHou Tao jmp_offset = -2; 5831902472bSHou Tao check_imm19(jmp_offset); 5841902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5851902472bSHou Tao emit(A64_DMB_ISH, ctx); 5861902472bSHou Tao } else if (imm == BPF_CMPXCHG) { 5871902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 5881902472bSHou Tao const u8 r0 = bpf2a64[BPF_REG_0]; 5891902472bSHou Tao 5901902472bSHou Tao emit(A64_MOV(isdw, tmp2, r0), ctx); 5911902472bSHou Tao emit(A64_LDXR(isdw, r0, reg), ctx); 5921902472bSHou Tao emit(A64_EOR(isdw, tmp3, r0, tmp2), ctx); 5931902472bSHou Tao jmp_offset = 4; 5941902472bSHou Tao check_imm19(jmp_offset); 5951902472bSHou Tao emit(A64_CBNZ(isdw, tmp3, jmp_offset), ctx); 5961902472bSHou Tao emit(A64_STLXR(isdw, src, reg, tmp3), ctx); 5971902472bSHou Tao jmp_offset = -4; 5981902472bSHou Tao check_imm19(jmp_offset); 5991902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6001902472bSHou Tao emit(A64_DMB_ISH, ctx); 6011902472bSHou Tao } else { 6021902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", imm); 6031902472bSHou Tao return -EINVAL; 6041902472bSHou Tao } 6051902472bSHou Tao 6061902472bSHou Tao return 0; 6071902472bSHou Tao } 6081902472bSHou Tao 609b2ad54e1SXu Kuohai void dummy_tramp(void); 610b2ad54e1SXu Kuohai 611b2ad54e1SXu Kuohai asm ( 612b2ad54e1SXu Kuohai " .pushsection .text, \"ax\", @progbits\n" 61333f32e50SNathan Chancellor " .global dummy_tramp\n" 614b2ad54e1SXu Kuohai " .type dummy_tramp, %function\n" 615b2ad54e1SXu Kuohai "dummy_tramp:" 616b2ad54e1SXu Kuohai #if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) 617b2ad54e1SXu Kuohai " bti j\n" /* dummy_tramp is called via "br x10" */ 618b2ad54e1SXu Kuohai #endif 619339ed900SXu Kuohai " mov x10, x30\n" 620339ed900SXu Kuohai " mov x30, x9\n" 621b2ad54e1SXu Kuohai " ret x10\n" 622b2ad54e1SXu Kuohai " .size dummy_tramp, .-dummy_tramp\n" 623b2ad54e1SXu Kuohai " .popsection\n" 624b2ad54e1SXu Kuohai ); 625b2ad54e1SXu Kuohai 626b2ad54e1SXu Kuohai /* build a plt initialized like this: 627b2ad54e1SXu Kuohai * 628b2ad54e1SXu Kuohai * plt: 629b2ad54e1SXu Kuohai * ldr tmp, target 630b2ad54e1SXu Kuohai * br tmp 631b2ad54e1SXu Kuohai * target: 632b2ad54e1SXu Kuohai * .quad dummy_tramp 633b2ad54e1SXu Kuohai * 634b2ad54e1SXu Kuohai * when a long jump trampoline is attached, target is filled with the 635b2ad54e1SXu Kuohai * trampoline address, and when the trampoline is removed, target is 636b2ad54e1SXu Kuohai * restored to dummy_tramp address. 637b2ad54e1SXu Kuohai */ 638b2ad54e1SXu Kuohai static void build_plt(struct jit_ctx *ctx) 639b2ad54e1SXu Kuohai { 640b2ad54e1SXu Kuohai const u8 tmp = bpf2a64[TMP_REG_1]; 641b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 642b2ad54e1SXu Kuohai 643b2ad54e1SXu Kuohai /* make sure target is 64-bit aligned */ 644b2ad54e1SXu Kuohai if ((ctx->idx + PLT_TARGET_OFFSET / AARCH64_INSN_SIZE) % 2) 645b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 646b2ad54e1SXu Kuohai 647b2ad54e1SXu Kuohai plt = (struct bpf_plt *)(ctx->image + ctx->idx); 648b2ad54e1SXu Kuohai /* plt is called via bl, no BTI needed here */ 649b2ad54e1SXu Kuohai emit(A64_LDR64LIT(tmp, 2 * AARCH64_INSN_SIZE), ctx); 650b2ad54e1SXu Kuohai emit(A64_BR(tmp), ctx); 651b2ad54e1SXu Kuohai 652b2ad54e1SXu Kuohai if (ctx->image) 653b2ad54e1SXu Kuohai plt->target = (u64)&dummy_tramp; 654b2ad54e1SXu Kuohai } 655b2ad54e1SXu Kuohai 656e54bcde3SZi Shen Lim static void build_epilogue(struct jit_ctx *ctx) 657e54bcde3SZi Shen Lim { 658e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 659e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 660e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 661e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 662e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 663e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 6645b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 665e54bcde3SZi Shen Lim 666e54bcde3SZi Shen Lim /* We're done with BPF stack */ 667f1c9eed7SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 668e54bcde3SZi Shen Lim 6695b3d19b9SXu Kuohai /* Restore x27 and x28 */ 6705b3d19b9SXu Kuohai emit(A64_POP(fpb, A64_R(28), A64_SP), ctx); 671ec0738dbSYang Shi /* Restore fs (x25) and x26 */ 672ec0738dbSYang Shi emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 673ec0738dbSYang Shi 674e54bcde3SZi Shen Lim /* Restore callee-saved register */ 675e54bcde3SZi Shen Lim emit(A64_POP(r8, r9, A64_SP), ctx); 676e54bcde3SZi Shen Lim emit(A64_POP(r6, r7, A64_SP), ctx); 677e54bcde3SZi Shen Lim 678ec0738dbSYang Shi /* Restore FP/LR registers */ 679ec0738dbSYang Shi emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 680e54bcde3SZi Shen Lim 681e54bcde3SZi Shen Lim /* Set return value */ 682e54bcde3SZi Shen Lim emit(A64_MOV(1, A64_R(0), r0), ctx); 683e54bcde3SZi Shen Lim 684042152c2SXu Kuohai /* Authenticate lr */ 685042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 686042152c2SXu Kuohai emit(A64_AUTIASP, ctx); 687042152c2SXu Kuohai 688e54bcde3SZi Shen Lim emit(A64_RET(A64_LR), ctx); 689e54bcde3SZi Shen Lim } 690e54bcde3SZi Shen Lim 69180083428SJean-Philippe Brucker #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) 69280083428SJean-Philippe Brucker #define BPF_FIXUP_REG_MASK GENMASK(31, 27) 69380083428SJean-Philippe Brucker 694d6e2cc56SMark Rutland bool ex_handler_bpf(const struct exception_table_entry *ex, 69580083428SJean-Philippe Brucker struct pt_regs *regs) 69680083428SJean-Philippe Brucker { 69780083428SJean-Philippe Brucker off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup); 69880083428SJean-Philippe Brucker int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup); 69980083428SJean-Philippe Brucker 70080083428SJean-Philippe Brucker regs->regs[dst_reg] = 0; 70180083428SJean-Philippe Brucker regs->pc = (unsigned long)&ex->fixup - offset; 702e8c328d7SMark Rutland return true; 70380083428SJean-Philippe Brucker } 70480083428SJean-Philippe Brucker 70580083428SJean-Philippe Brucker /* For accesses to BTF pointers, add an entry to the exception table */ 70680083428SJean-Philippe Brucker static int add_exception_handler(const struct bpf_insn *insn, 70780083428SJean-Philippe Brucker struct jit_ctx *ctx, 70880083428SJean-Philippe Brucker int dst_reg) 70980083428SJean-Philippe Brucker { 71080083428SJean-Philippe Brucker off_t offset; 71180083428SJean-Philippe Brucker unsigned long pc; 71280083428SJean-Philippe Brucker struct exception_table_entry *ex; 71380083428SJean-Philippe Brucker 71480083428SJean-Philippe Brucker if (!ctx->image) 71580083428SJean-Philippe Brucker /* First pass */ 71680083428SJean-Philippe Brucker return 0; 71780083428SJean-Philippe Brucker 718cc88f540SXu Kuohai if (BPF_MODE(insn->code) != BPF_PROBE_MEM && 719cc88f540SXu Kuohai BPF_MODE(insn->code) != BPF_PROBE_MEMSX) 72080083428SJean-Philippe Brucker return 0; 72180083428SJean-Philippe Brucker 72280083428SJean-Philippe Brucker if (!ctx->prog->aux->extable || 72380083428SJean-Philippe Brucker WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries)) 72480083428SJean-Philippe Brucker return -EINVAL; 72580083428SJean-Philippe Brucker 72680083428SJean-Philippe Brucker ex = &ctx->prog->aux->extable[ctx->exentry_idx]; 72780083428SJean-Philippe Brucker pc = (unsigned long)&ctx->image[ctx->idx - 1]; 72880083428SJean-Philippe Brucker 72980083428SJean-Philippe Brucker offset = pc - (long)&ex->insn; 73080083428SJean-Philippe Brucker if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN)) 73180083428SJean-Philippe Brucker return -ERANGE; 73280083428SJean-Philippe Brucker ex->insn = offset; 73380083428SJean-Philippe Brucker 73480083428SJean-Philippe Brucker /* 73580083428SJean-Philippe Brucker * Since the extable follows the program, the fixup offset is always 73680083428SJean-Philippe Brucker * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value 73780083428SJean-Philippe Brucker * to keep things simple, and put the destination register in the upper 73880083428SJean-Philippe Brucker * bits. We don't need to worry about buildtime or runtime sort 73980083428SJean-Philippe Brucker * modifying the upper bits because the table is already sorted, and 74080083428SJean-Philippe Brucker * isn't part of the main exception table. 74180083428SJean-Philippe Brucker */ 74280083428SJean-Philippe Brucker offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE); 74380083428SJean-Philippe Brucker if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset)) 74480083428SJean-Philippe Brucker return -ERANGE; 74580083428SJean-Philippe Brucker 74680083428SJean-Philippe Brucker ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) | 74780083428SJean-Philippe Brucker FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); 74880083428SJean-Philippe Brucker 749d6e2cc56SMark Rutland ex->type = EX_TYPE_BPF; 750d6e2cc56SMark Rutland 75180083428SJean-Philippe Brucker ctx->exentry_idx++; 75280083428SJean-Philippe Brucker return 0; 75380083428SJean-Philippe Brucker } 75480083428SJean-Philippe Brucker 75530d3d94cSZi Shen Lim /* JITs an eBPF instruction. 75630d3d94cSZi Shen Lim * Returns: 75730d3d94cSZi Shen Lim * 0 - successfully JITed an 8-byte eBPF instruction. 75830d3d94cSZi Shen Lim * >0 - successfully JITed a 16-byte eBPF instruction. 75930d3d94cSZi Shen Lim * <0 - failed to JIT. 76030d3d94cSZi Shen Lim */ 7618c11ea5cSDaniel Borkmann static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, 7628c11ea5cSDaniel Borkmann bool extra_pass) 763e54bcde3SZi Shen Lim { 764e54bcde3SZi Shen Lim const u8 code = insn->code; 765e54bcde3SZi Shen Lim const u8 dst = bpf2a64[insn->dst_reg]; 766e54bcde3SZi Shen Lim const u8 src = bpf2a64[insn->src_reg]; 767e54bcde3SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 768e54bcde3SZi Shen Lim const u8 tmp2 = bpf2a64[TMP_REG_2]; 7695b3d19b9SXu Kuohai const u8 fp = bpf2a64[BPF_REG_FP]; 7705b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 771e54bcde3SZi Shen Lim const s16 off = insn->off; 772e54bcde3SZi Shen Lim const s32 imm = insn->imm; 773e54bcde3SZi Shen Lim const int i = insn - ctx->prog->insnsi; 774654b65a0SJiong Wang const bool is64 = BPF_CLASS(code) == BPF_ALU64 || 775654b65a0SJiong Wang BPF_CLASS(code) == BPF_JMP; 7761902472bSHou Tao u8 jmp_cond; 777e54bcde3SZi Shen Lim s32 jmp_offset; 778fd49591cSLuke Nelson u32 a64_insn; 7795b3d19b9SXu Kuohai u8 src_adj; 7805b3d19b9SXu Kuohai u8 dst_adj; 7815b3d19b9SXu Kuohai int off_adj; 78280083428SJean-Philippe Brucker int ret; 783cc88f540SXu Kuohai bool sign_extend; 784e54bcde3SZi Shen Lim 785e54bcde3SZi Shen Lim switch (code) { 786e54bcde3SZi Shen Lim /* dst = src */ 787e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_X: 788e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_X: 789bb0a1d6bSXu Kuohai switch (insn->off) { 790bb0a1d6bSXu Kuohai case 0: 791e54bcde3SZi Shen Lim emit(A64_MOV(is64, dst, src), ctx); 792e54bcde3SZi Shen Lim break; 793bb0a1d6bSXu Kuohai case 8: 794bb0a1d6bSXu Kuohai emit(A64_SXTB(is64, dst, src), ctx); 795bb0a1d6bSXu Kuohai break; 796bb0a1d6bSXu Kuohai case 16: 797bb0a1d6bSXu Kuohai emit(A64_SXTH(is64, dst, src), ctx); 798bb0a1d6bSXu Kuohai break; 799bb0a1d6bSXu Kuohai case 32: 800bb0a1d6bSXu Kuohai emit(A64_SXTW(is64, dst, src), ctx); 801bb0a1d6bSXu Kuohai break; 802bb0a1d6bSXu Kuohai } 803bb0a1d6bSXu Kuohai break; 804e54bcde3SZi Shen Lim /* dst = dst OP src */ 805e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_X: 806e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_X: 807e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, src), ctx); 808e54bcde3SZi Shen Lim break; 809e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_X: 810e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_X: 811e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, src), ctx); 812e54bcde3SZi Shen Lim break; 813e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_X: 814e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_X: 815e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, src), ctx); 816e54bcde3SZi Shen Lim break; 817e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_X: 818e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_X: 819e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, src), ctx); 820e54bcde3SZi Shen Lim break; 821e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_X: 822e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_X: 823e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, src), ctx); 824e54bcde3SZi Shen Lim break; 825e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_X: 826e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_X: 827e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, src), ctx); 828e54bcde3SZi Shen Lim break; 829e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_X: 830e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_X: 831e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, src), ctx); 832e54bcde3SZi Shen Lim break; 833119220d8STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 834119220d8STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 835e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, src), ctx); 836504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, src), ctx); 837e54bcde3SZi Shen Lim break; 838d65a634aSZi Shen Lim case BPF_ALU | BPF_LSH | BPF_X: 839d65a634aSZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_X: 840d65a634aSZi Shen Lim emit(A64_LSLV(is64, dst, dst, src), ctx); 841d65a634aSZi Shen Lim break; 842d65a634aSZi Shen Lim case BPF_ALU | BPF_RSH | BPF_X: 843d65a634aSZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_X: 844d65a634aSZi Shen Lim emit(A64_LSRV(is64, dst, dst, src), ctx); 845d65a634aSZi Shen Lim break; 846d65a634aSZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_X: 847d65a634aSZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_X: 848d65a634aSZi Shen Lim emit(A64_ASRV(is64, dst, dst, src), ctx); 849d65a634aSZi Shen Lim break; 850e54bcde3SZi Shen Lim /* dst = -dst */ 851e54bcde3SZi Shen Lim case BPF_ALU | BPF_NEG: 852e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_NEG: 853e54bcde3SZi Shen Lim emit(A64_NEG(is64, dst, dst), ctx); 854e54bcde3SZi Shen Lim break; 855e54bcde3SZi Shen Lim /* dst = BSWAP##imm(dst) */ 856e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_LE: 857e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_BE: 858*1104247fSXu Kuohai case BPF_ALU64 | BPF_END | BPF_FROM_LE: 859e54bcde3SZi Shen Lim #ifdef CONFIG_CPU_BIG_ENDIAN 860*1104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_BE) 861d63903bbSXi Wang goto emit_bswap_uxt; 862e54bcde3SZi Shen Lim #else /* !CONFIG_CPU_BIG_ENDIAN */ 863*1104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_LE) 864d63903bbSXi Wang goto emit_bswap_uxt; 865e54bcde3SZi Shen Lim #endif 866e54bcde3SZi Shen Lim switch (imm) { 867e54bcde3SZi Shen Lim case 16: 868e54bcde3SZi Shen Lim emit(A64_REV16(is64, dst, dst), ctx); 869d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 870d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 871e54bcde3SZi Shen Lim break; 872e54bcde3SZi Shen Lim case 32: 873e54bcde3SZi Shen Lim emit(A64_REV32(is64, dst, dst), ctx); 874d63903bbSXi Wang /* upper 32 bits already cleared */ 875e54bcde3SZi Shen Lim break; 876e54bcde3SZi Shen Lim case 64: 877e54bcde3SZi Shen Lim emit(A64_REV64(dst, dst), ctx); 878e54bcde3SZi Shen Lim break; 879e54bcde3SZi Shen Lim } 880e54bcde3SZi Shen Lim break; 881d63903bbSXi Wang emit_bswap_uxt: 882d63903bbSXi Wang switch (imm) { 883d63903bbSXi Wang case 16: 884d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 885d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 886d63903bbSXi Wang break; 887d63903bbSXi Wang case 32: 888d63903bbSXi Wang /* zero-extend 32 bits into 64 bits */ 889d63903bbSXi Wang emit(A64_UXTW(is64, dst, dst), ctx); 890d63903bbSXi Wang break; 891d63903bbSXi Wang case 64: 892d63903bbSXi Wang /* nop */ 893d63903bbSXi Wang break; 894d63903bbSXi Wang } 895d63903bbSXi Wang break; 896e54bcde3SZi Shen Lim /* dst = imm */ 897e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_K: 898e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_K: 899e54bcde3SZi Shen Lim emit_a64_mov_i(is64, dst, imm, ctx); 900e54bcde3SZi Shen Lim break; 901e54bcde3SZi Shen Lim /* dst = dst OP imm */ 902e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_K: 903e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_K: 904fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 905fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, imm), ctx); 906fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 907fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, -imm), ctx); 908fd868f14SLuke Nelson } else { 909e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 910e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, tmp), ctx); 911fd868f14SLuke Nelson } 912e54bcde3SZi Shen Lim break; 913e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_K: 914e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_K: 915fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 916fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, imm), ctx); 917fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 918fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, -imm), ctx); 919fd868f14SLuke Nelson } else { 920e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 921e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 922fd868f14SLuke Nelson } 923e54bcde3SZi Shen Lim break; 924e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_K: 925e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_K: 926fd49591cSLuke Nelson a64_insn = A64_AND_I(is64, dst, dst, imm); 927fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 928fd49591cSLuke Nelson emit(a64_insn, ctx); 929fd49591cSLuke Nelson } else { 930e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 931e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, tmp), ctx); 932fd49591cSLuke Nelson } 933e54bcde3SZi Shen Lim break; 934e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_K: 935e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_K: 936fd49591cSLuke Nelson a64_insn = A64_ORR_I(is64, dst, dst, imm); 937fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 938fd49591cSLuke Nelson emit(a64_insn, ctx); 939fd49591cSLuke Nelson } else { 940e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 941e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, tmp), ctx); 942fd49591cSLuke Nelson } 943e54bcde3SZi Shen Lim break; 944e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_K: 945e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_K: 946fd49591cSLuke Nelson a64_insn = A64_EOR_I(is64, dst, dst, imm); 947fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 948fd49591cSLuke Nelson emit(a64_insn, ctx); 949fd49591cSLuke Nelson } else { 950e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 951e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, tmp), ctx); 952fd49591cSLuke Nelson } 953e54bcde3SZi Shen Lim break; 954e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_K: 955e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_K: 956e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 957e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, tmp), ctx); 958e54bcde3SZi Shen Lim break; 959e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_K: 960e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_K: 961e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 962e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, tmp), ctx); 963e54bcde3SZi Shen Lim break; 964e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_K: 965e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_K: 966e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp2, imm, ctx); 967e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 968504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, tmp2), ctx); 969e54bcde3SZi Shen Lim break; 970e54bcde3SZi Shen Lim case BPF_ALU | BPF_LSH | BPF_K: 971e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_K: 972e54bcde3SZi Shen Lim emit(A64_LSL(is64, dst, dst, imm), ctx); 973e54bcde3SZi Shen Lim break; 974e54bcde3SZi Shen Lim case BPF_ALU | BPF_RSH | BPF_K: 975e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_K: 976e54bcde3SZi Shen Lim emit(A64_LSR(is64, dst, dst, imm), ctx); 977e54bcde3SZi Shen Lim break; 978e54bcde3SZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_K: 979e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_K: 980e54bcde3SZi Shen Lim emit(A64_ASR(is64, dst, dst, imm), ctx); 981e54bcde3SZi Shen Lim break; 982e54bcde3SZi Shen Lim 983e54bcde3SZi Shen Lim /* JUMP off */ 984e54bcde3SZi Shen Lim case BPF_JMP | BPF_JA: 98532f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 986e54bcde3SZi Shen Lim check_imm26(jmp_offset); 987e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 988e54bcde3SZi Shen Lim break; 989e54bcde3SZi Shen Lim /* IF (dst COND src) JUMP off */ 990e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_X: 991e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_X: 992c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_X: 993e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_X: 994c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_X: 995e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_X: 996e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_X: 997c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_X: 998e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_X: 999c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_X: 1000654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_X: 1001654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_X: 1002654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_X: 1003654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_X: 1004654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_X: 1005654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_X: 1006654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_X: 1007654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_X: 1008654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_X: 1009654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_X: 1010654b65a0SJiong Wang emit(A64_CMP(is64, dst, src), ctx); 1011e54bcde3SZi Shen Lim emit_cond_jmp: 101232f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 1013e54bcde3SZi Shen Lim check_imm19(jmp_offset); 1014e54bcde3SZi Shen Lim switch (BPF_OP(code)) { 1015e54bcde3SZi Shen Lim case BPF_JEQ: 1016e54bcde3SZi Shen Lim jmp_cond = A64_COND_EQ; 1017e54bcde3SZi Shen Lim break; 1018e54bcde3SZi Shen Lim case BPF_JGT: 1019e54bcde3SZi Shen Lim jmp_cond = A64_COND_HI; 1020e54bcde3SZi Shen Lim break; 1021c362b2f3SDaniel Borkmann case BPF_JLT: 1022c362b2f3SDaniel Borkmann jmp_cond = A64_COND_CC; 1023c362b2f3SDaniel Borkmann break; 1024e54bcde3SZi Shen Lim case BPF_JGE: 1025e54bcde3SZi Shen Lim jmp_cond = A64_COND_CS; 1026e54bcde3SZi Shen Lim break; 1027c362b2f3SDaniel Borkmann case BPF_JLE: 1028c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LS; 1029c362b2f3SDaniel Borkmann break; 103098397fc5SZi Shen Lim case BPF_JSET: 1031e54bcde3SZi Shen Lim case BPF_JNE: 1032e54bcde3SZi Shen Lim jmp_cond = A64_COND_NE; 1033e54bcde3SZi Shen Lim break; 1034e54bcde3SZi Shen Lim case BPF_JSGT: 1035e54bcde3SZi Shen Lim jmp_cond = A64_COND_GT; 1036e54bcde3SZi Shen Lim break; 1037c362b2f3SDaniel Borkmann case BPF_JSLT: 1038c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LT; 1039c362b2f3SDaniel Borkmann break; 1040e54bcde3SZi Shen Lim case BPF_JSGE: 1041e54bcde3SZi Shen Lim jmp_cond = A64_COND_GE; 1042e54bcde3SZi Shen Lim break; 1043c362b2f3SDaniel Borkmann case BPF_JSLE: 1044c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LE; 1045c362b2f3SDaniel Borkmann break; 1046e54bcde3SZi Shen Lim default: 1047e54bcde3SZi Shen Lim return -EFAULT; 1048e54bcde3SZi Shen Lim } 1049e54bcde3SZi Shen Lim emit(A64_B_(jmp_cond, jmp_offset), ctx); 1050e54bcde3SZi Shen Lim break; 1051e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_X: 1052654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_X: 1053654b65a0SJiong Wang emit(A64_TST(is64, dst, src), ctx); 1054e54bcde3SZi Shen Lim goto emit_cond_jmp; 1055e54bcde3SZi Shen Lim /* IF (dst COND imm) JUMP off */ 1056e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_K: 1057e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_K: 1058c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_K: 1059e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_K: 1060c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_K: 1061e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_K: 1062e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_K: 1063c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_K: 1064e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_K: 1065c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_K: 1066654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_K: 1067654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_K: 1068654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_K: 1069654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_K: 1070654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_K: 1071654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_K: 1072654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_K: 1073654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_K: 1074654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_K: 1075654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_K: 1076fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1077fd868f14SLuke Nelson emit(A64_CMP_I(is64, dst, imm), ctx); 1078fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1079fd868f14SLuke Nelson emit(A64_CMN_I(is64, dst, -imm), ctx); 1080fd868f14SLuke Nelson } else { 1081654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1082654b65a0SJiong Wang emit(A64_CMP(is64, dst, tmp), ctx); 1083fd868f14SLuke Nelson } 1084e54bcde3SZi Shen Lim goto emit_cond_jmp; 1085e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_K: 1086654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_K: 1087fd49591cSLuke Nelson a64_insn = A64_TST_I(is64, dst, imm); 1088fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1089fd49591cSLuke Nelson emit(a64_insn, ctx); 1090fd49591cSLuke Nelson } else { 1091654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1092654b65a0SJiong Wang emit(A64_TST(is64, dst, tmp), ctx); 1093fd49591cSLuke Nelson } 1094e54bcde3SZi Shen Lim goto emit_cond_jmp; 1095e54bcde3SZi Shen Lim /* function call */ 1096e54bcde3SZi Shen Lim case BPF_JMP | BPF_CALL: 1097e54bcde3SZi Shen Lim { 1098e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 10998c11ea5cSDaniel Borkmann bool func_addr_fixed; 11008c11ea5cSDaniel Borkmann u64 func_addr; 1101e54bcde3SZi Shen Lim 11028c11ea5cSDaniel Borkmann ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 11038c11ea5cSDaniel Borkmann &func_addr, &func_addr_fixed); 11048c11ea5cSDaniel Borkmann if (ret < 0) 11058c11ea5cSDaniel Borkmann return ret; 1106efc9909fSXu Kuohai emit_call(func_addr, ctx); 1107e54bcde3SZi Shen Lim emit(A64_MOV(1, r0, A64_R(0)), ctx); 1108e54bcde3SZi Shen Lim break; 1109e54bcde3SZi Shen Lim } 1110ddb55992SZi Shen Lim /* tail call */ 111171189fa9SAlexei Starovoitov case BPF_JMP | BPF_TAIL_CALL: 1112ddb55992SZi Shen Lim if (emit_bpf_tail_call(ctx)) 1113ddb55992SZi Shen Lim return -EFAULT; 1114ddb55992SZi Shen Lim break; 1115e54bcde3SZi Shen Lim /* function return */ 1116e54bcde3SZi Shen Lim case BPF_JMP | BPF_EXIT: 111751c9fbb1SZi Shen Lim /* Optimization: when last instruction is EXIT, 111851c9fbb1SZi Shen Lim simply fallthrough to epilogue. */ 1119e54bcde3SZi Shen Lim if (i == ctx->prog->len - 1) 1120e54bcde3SZi Shen Lim break; 1121e54bcde3SZi Shen Lim jmp_offset = epilogue_offset(ctx); 1122e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1123e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1124e54bcde3SZi Shen Lim break; 1125e54bcde3SZi Shen Lim 112630d3d94cSZi Shen Lim /* dst = imm64 */ 112730d3d94cSZi Shen Lim case BPF_LD | BPF_IMM | BPF_DW: 112830d3d94cSZi Shen Lim { 112930d3d94cSZi Shen Lim const struct bpf_insn insn1 = insn[1]; 113030d3d94cSZi Shen Lim u64 imm64; 113130d3d94cSZi Shen Lim 11321e4df6b7SXi Wang imm64 = (u64)insn1.imm << 32 | (u32)imm; 1133e4a41c2cSHou Tao if (bpf_pseudo_func(insn)) 1134e4a41c2cSHou Tao emit_addr_mov_i64(dst, imm64, ctx); 1135e4a41c2cSHou Tao else 113630d3d94cSZi Shen Lim emit_a64_mov_i64(dst, imm64, ctx); 113730d3d94cSZi Shen Lim 113830d3d94cSZi Shen Lim return 1; 113930d3d94cSZi Shen Lim } 114030d3d94cSZi Shen Lim 1141cc88f540SXu Kuohai /* LDX: dst = (u64)*(unsigned size *)(src + off) */ 1142e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_W: 1143e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_H: 1144e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_B: 1145e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_DW: 114680083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 114780083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_W: 114880083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_H: 114980083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_B: 1150cc88f540SXu Kuohai /* LDXS: dst_reg = (s64)*(signed size *)(src_reg + off) */ 1151cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_B: 1152cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_H: 1153cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_W: 1154cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_B: 1155cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_H: 1156cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_W: 11575b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && src == fp) { 11585b3d19b9SXu Kuohai src_adj = fpb; 11595b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 11605b3d19b9SXu Kuohai } else { 11615b3d19b9SXu Kuohai src_adj = src; 11625b3d19b9SXu Kuohai off_adj = off; 11635b3d19b9SXu Kuohai } 1164cc88f540SXu Kuohai sign_extend = (BPF_MODE(insn->code) == BPF_MEMSX || 1165cc88f540SXu Kuohai BPF_MODE(insn->code) == BPF_PROBE_MEMSX); 1166e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1167e54bcde3SZi Shen Lim case BPF_W: 11685b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 1169cc88f540SXu Kuohai if (sign_extend) 1170cc88f540SXu Kuohai emit(A64_LDRSWI(dst, src_adj, off_adj), ctx); 1171cc88f540SXu Kuohai else 11725b3d19b9SXu Kuohai emit(A64_LDR32I(dst, src_adj, off_adj), ctx); 11737db6c0f1SXu Kuohai } else { 11747db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1175cc88f540SXu Kuohai if (sign_extend) 1176cc88f540SXu Kuohai emit(A64_LDRSW(dst, src_adj, off_adj), ctx); 1177cc88f540SXu Kuohai else 1178e54bcde3SZi Shen Lim emit(A64_LDR32(dst, src, tmp), ctx); 11797db6c0f1SXu Kuohai } 1180e54bcde3SZi Shen Lim break; 1181e54bcde3SZi Shen Lim case BPF_H: 11825b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 1183cc88f540SXu Kuohai if (sign_extend) 1184cc88f540SXu Kuohai emit(A64_LDRSHI(dst, src_adj, off_adj), ctx); 1185cc88f540SXu Kuohai else 11865b3d19b9SXu Kuohai emit(A64_LDRHI(dst, src_adj, off_adj), ctx); 11877db6c0f1SXu Kuohai } else { 11887db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1189cc88f540SXu Kuohai if (sign_extend) 1190cc88f540SXu Kuohai emit(A64_LDRSH(dst, src, tmp), ctx); 1191cc88f540SXu Kuohai else 1192e54bcde3SZi Shen Lim emit(A64_LDRH(dst, src, tmp), ctx); 11937db6c0f1SXu Kuohai } 1194e54bcde3SZi Shen Lim break; 1195e54bcde3SZi Shen Lim case BPF_B: 11965b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 1197cc88f540SXu Kuohai if (sign_extend) 1198cc88f540SXu Kuohai emit(A64_LDRSBI(dst, src_adj, off_adj), ctx); 1199cc88f540SXu Kuohai else 12005b3d19b9SXu Kuohai emit(A64_LDRBI(dst, src_adj, off_adj), ctx); 12017db6c0f1SXu Kuohai } else { 12027db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1203cc88f540SXu Kuohai if (sign_extend) 1204cc88f540SXu Kuohai emit(A64_LDRSB(dst, src, tmp), ctx); 1205cc88f540SXu Kuohai else 1206e54bcde3SZi Shen Lim emit(A64_LDRB(dst, src, tmp), ctx); 12077db6c0f1SXu Kuohai } 1208e54bcde3SZi Shen Lim break; 1209e54bcde3SZi Shen Lim case BPF_DW: 12105b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 12115b3d19b9SXu Kuohai emit(A64_LDR64I(dst, src_adj, off_adj), ctx); 12127db6c0f1SXu Kuohai } else { 12137db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1214e54bcde3SZi Shen Lim emit(A64_LDR64(dst, src, tmp), ctx); 12157db6c0f1SXu Kuohai } 1216e54bcde3SZi Shen Lim break; 1217e54bcde3SZi Shen Lim } 121880083428SJean-Philippe Brucker 121980083428SJean-Philippe Brucker ret = add_exception_handler(insn, ctx, dst); 122080083428SJean-Philippe Brucker if (ret) 122180083428SJean-Philippe Brucker return ret; 1222e54bcde3SZi Shen Lim break; 1223e54bcde3SZi Shen Lim 1224f5e81d11SDaniel Borkmann /* speculation barrier */ 1225f5e81d11SDaniel Borkmann case BPF_ST | BPF_NOSPEC: 1226f5e81d11SDaniel Borkmann /* 1227f5e81d11SDaniel Borkmann * Nothing required here. 1228f5e81d11SDaniel Borkmann * 1229f5e81d11SDaniel Borkmann * In case of arm64, we rely on the firmware mitigation of 1230f5e81d11SDaniel Borkmann * Speculative Store Bypass as controlled via the ssbd kernel 1231f5e81d11SDaniel Borkmann * parameter. Whenever the mitigation is enabled, it works 1232f5e81d11SDaniel Borkmann * for all of the kernel code with no need to provide any 1233f5e81d11SDaniel Borkmann * additional instructions. 1234f5e81d11SDaniel Borkmann */ 1235f5e81d11SDaniel Borkmann break; 1236f5e81d11SDaniel Borkmann 1237e54bcde3SZi Shen Lim /* ST: *(size *)(dst + off) = imm */ 1238e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_W: 1239e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_H: 1240e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_B: 1241e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_DW: 12425b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && dst == fp) { 12435b3d19b9SXu Kuohai dst_adj = fpb; 12445b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 12455b3d19b9SXu Kuohai } else { 12465b3d19b9SXu Kuohai dst_adj = dst; 12475b3d19b9SXu Kuohai off_adj = off; 12485b3d19b9SXu Kuohai } 1249df849ba3SYang Shi /* Load imm to a register then store it */ 1250df849ba3SYang Shi emit_a64_mov_i(1, tmp, imm, ctx); 1251df849ba3SYang Shi switch (BPF_SIZE(code)) { 1252df849ba3SYang Shi case BPF_W: 12535b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 12545b3d19b9SXu Kuohai emit(A64_STR32I(tmp, dst_adj, off_adj), ctx); 12557db6c0f1SXu Kuohai } else { 12567db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1257df849ba3SYang Shi emit(A64_STR32(tmp, dst, tmp2), ctx); 12587db6c0f1SXu Kuohai } 1259df849ba3SYang Shi break; 1260df849ba3SYang Shi case BPF_H: 12615b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 12625b3d19b9SXu Kuohai emit(A64_STRHI(tmp, dst_adj, off_adj), ctx); 12637db6c0f1SXu Kuohai } else { 12647db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1265df849ba3SYang Shi emit(A64_STRH(tmp, dst, tmp2), ctx); 12667db6c0f1SXu Kuohai } 1267df849ba3SYang Shi break; 1268df849ba3SYang Shi case BPF_B: 12695b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 12705b3d19b9SXu Kuohai emit(A64_STRBI(tmp, dst_adj, off_adj), ctx); 12717db6c0f1SXu Kuohai } else { 12727db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1273df849ba3SYang Shi emit(A64_STRB(tmp, dst, tmp2), ctx); 12747db6c0f1SXu Kuohai } 1275df849ba3SYang Shi break; 1276df849ba3SYang Shi case BPF_DW: 12775b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 12785b3d19b9SXu Kuohai emit(A64_STR64I(tmp, dst_adj, off_adj), ctx); 12797db6c0f1SXu Kuohai } else { 12807db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1281df849ba3SYang Shi emit(A64_STR64(tmp, dst, tmp2), ctx); 12827db6c0f1SXu Kuohai } 1283df849ba3SYang Shi break; 1284df849ba3SYang Shi } 1285df849ba3SYang Shi break; 1286e54bcde3SZi Shen Lim 1287e54bcde3SZi Shen Lim /* STX: *(size *)(dst + off) = src */ 1288e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_W: 1289e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_H: 1290e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_B: 1291e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_DW: 12925b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && dst == fp) { 12935b3d19b9SXu Kuohai dst_adj = fpb; 12945b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 12955b3d19b9SXu Kuohai } else { 12965b3d19b9SXu Kuohai dst_adj = dst; 12975b3d19b9SXu Kuohai off_adj = off; 12985b3d19b9SXu Kuohai } 1299e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1300e54bcde3SZi Shen Lim case BPF_W: 13015b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 13025b3d19b9SXu Kuohai emit(A64_STR32I(src, dst_adj, off_adj), ctx); 13037db6c0f1SXu Kuohai } else { 13047db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1305e54bcde3SZi Shen Lim emit(A64_STR32(src, dst, tmp), ctx); 13067db6c0f1SXu Kuohai } 1307e54bcde3SZi Shen Lim break; 1308e54bcde3SZi Shen Lim case BPF_H: 13095b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 13105b3d19b9SXu Kuohai emit(A64_STRHI(src, dst_adj, off_adj), ctx); 13117db6c0f1SXu Kuohai } else { 13127db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1313e54bcde3SZi Shen Lim emit(A64_STRH(src, dst, tmp), ctx); 13147db6c0f1SXu Kuohai } 1315e54bcde3SZi Shen Lim break; 1316e54bcde3SZi Shen Lim case BPF_B: 13175b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 13185b3d19b9SXu Kuohai emit(A64_STRBI(src, dst_adj, off_adj), ctx); 13197db6c0f1SXu Kuohai } else { 13207db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1321e54bcde3SZi Shen Lim emit(A64_STRB(src, dst, tmp), ctx); 13227db6c0f1SXu Kuohai } 1323e54bcde3SZi Shen Lim break; 1324e54bcde3SZi Shen Lim case BPF_DW: 13255b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 13265b3d19b9SXu Kuohai emit(A64_STR64I(src, dst_adj, off_adj), ctx); 13277db6c0f1SXu Kuohai } else { 13287db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1329e54bcde3SZi Shen Lim emit(A64_STR64(src, dst, tmp), ctx); 13307db6c0f1SXu Kuohai } 1331e54bcde3SZi Shen Lim break; 1332e54bcde3SZi Shen Lim } 1333e54bcde3SZi Shen Lim break; 133434b8ab09SDaniel Borkmann 133591c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_W: 133691c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_DW: 13371902472bSHou Tao if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) 13381902472bSHou Tao ret = emit_lse_atomic(insn, ctx); 13391902472bSHou Tao else 13401902472bSHou Tao ret = emit_ll_sc_atomic(insn, ctx); 13411902472bSHou Tao if (ret) 13421902472bSHou Tao return ret; 134385f68fe8SDaniel Borkmann break; 1344e54bcde3SZi Shen Lim 1345e54bcde3SZi Shen Lim default: 1346e54bcde3SZi Shen Lim pr_err_once("unknown opcode %02x\n", code); 1347e54bcde3SZi Shen Lim return -EINVAL; 1348e54bcde3SZi Shen Lim } 1349e54bcde3SZi Shen Lim 1350e54bcde3SZi Shen Lim return 0; 1351e54bcde3SZi Shen Lim } 1352e54bcde3SZi Shen Lim 13535b3d19b9SXu Kuohai /* 13545b3d19b9SXu Kuohai * Return 0 if FP may change at runtime, otherwise find the minimum negative 13555b3d19b9SXu Kuohai * offset to FP, converts it to positive number, and align down to 8 bytes. 13565b3d19b9SXu Kuohai */ 13575b3d19b9SXu Kuohai static int find_fpb_offset(struct bpf_prog *prog) 13585b3d19b9SXu Kuohai { 13595b3d19b9SXu Kuohai int i; 13605b3d19b9SXu Kuohai int offset = 0; 13615b3d19b9SXu Kuohai 13625b3d19b9SXu Kuohai for (i = 0; i < prog->len; i++) { 13635b3d19b9SXu Kuohai const struct bpf_insn *insn = &prog->insnsi[i]; 13645b3d19b9SXu Kuohai const u8 class = BPF_CLASS(insn->code); 13655b3d19b9SXu Kuohai const u8 mode = BPF_MODE(insn->code); 13665b3d19b9SXu Kuohai const u8 src = insn->src_reg; 13675b3d19b9SXu Kuohai const u8 dst = insn->dst_reg; 13685b3d19b9SXu Kuohai const s32 imm = insn->imm; 13695b3d19b9SXu Kuohai const s16 off = insn->off; 13705b3d19b9SXu Kuohai 13715b3d19b9SXu Kuohai switch (class) { 13725b3d19b9SXu Kuohai case BPF_STX: 13735b3d19b9SXu Kuohai case BPF_ST: 13745b3d19b9SXu Kuohai /* fp holds atomic operation result */ 13755b3d19b9SXu Kuohai if (class == BPF_STX && mode == BPF_ATOMIC && 13765b3d19b9SXu Kuohai ((imm == BPF_XCHG || 13775b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_ADD) || 13785b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_AND) || 13795b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_XOR) || 13805b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_OR)) && 13815b3d19b9SXu Kuohai src == BPF_REG_FP)) 13825b3d19b9SXu Kuohai return 0; 13835b3d19b9SXu Kuohai 13845b3d19b9SXu Kuohai if (mode == BPF_MEM && dst == BPF_REG_FP && 13855b3d19b9SXu Kuohai off < offset) 13865b3d19b9SXu Kuohai offset = insn->off; 13875b3d19b9SXu Kuohai break; 13885b3d19b9SXu Kuohai 13895b3d19b9SXu Kuohai case BPF_JMP32: 13905b3d19b9SXu Kuohai case BPF_JMP: 13915b3d19b9SXu Kuohai break; 13925b3d19b9SXu Kuohai 13935b3d19b9SXu Kuohai case BPF_LDX: 13945b3d19b9SXu Kuohai case BPF_LD: 13955b3d19b9SXu Kuohai /* fp holds load result */ 13965b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 13975b3d19b9SXu Kuohai return 0; 13985b3d19b9SXu Kuohai 13995b3d19b9SXu Kuohai if (class == BPF_LDX && mode == BPF_MEM && 14005b3d19b9SXu Kuohai src == BPF_REG_FP && off < offset) 14015b3d19b9SXu Kuohai offset = off; 14025b3d19b9SXu Kuohai break; 14035b3d19b9SXu Kuohai 14045b3d19b9SXu Kuohai case BPF_ALU: 14055b3d19b9SXu Kuohai case BPF_ALU64: 14065b3d19b9SXu Kuohai default: 14075b3d19b9SXu Kuohai /* fp holds ALU result */ 14085b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 14095b3d19b9SXu Kuohai return 0; 14105b3d19b9SXu Kuohai } 14115b3d19b9SXu Kuohai } 14125b3d19b9SXu Kuohai 14135b3d19b9SXu Kuohai if (offset < 0) { 14145b3d19b9SXu Kuohai /* 14155b3d19b9SXu Kuohai * safely be converted to a positive 'int', since insn->off 14165b3d19b9SXu Kuohai * is 's16' 14175b3d19b9SXu Kuohai */ 14185b3d19b9SXu Kuohai offset = -offset; 14195b3d19b9SXu Kuohai /* align down to 8 bytes */ 14205b3d19b9SXu Kuohai offset = ALIGN_DOWN(offset, 8); 14215b3d19b9SXu Kuohai } 14225b3d19b9SXu Kuohai 14235b3d19b9SXu Kuohai return offset; 14245b3d19b9SXu Kuohai } 14255b3d19b9SXu Kuohai 14268c11ea5cSDaniel Borkmann static int build_body(struct jit_ctx *ctx, bool extra_pass) 1427e54bcde3SZi Shen Lim { 1428e54bcde3SZi Shen Lim const struct bpf_prog *prog = ctx->prog; 1429e54bcde3SZi Shen Lim int i; 1430e54bcde3SZi Shen Lim 143132f6865cSIlias Apalodimas /* 143232f6865cSIlias Apalodimas * - offset[0] offset of the end of prologue, 143332f6865cSIlias Apalodimas * start of the 1st instruction. 143432f6865cSIlias Apalodimas * - offset[1] - offset of the end of 1st instruction, 143532f6865cSIlias Apalodimas * start of the 2nd instruction 143632f6865cSIlias Apalodimas * [....] 143732f6865cSIlias Apalodimas * - offset[3] - offset of the end of 3rd instruction, 143832f6865cSIlias Apalodimas * start of 4th instruction 143932f6865cSIlias Apalodimas */ 1440e54bcde3SZi Shen Lim for (i = 0; i < prog->len; i++) { 1441e54bcde3SZi Shen Lim const struct bpf_insn *insn = &prog->insnsi[i]; 1442e54bcde3SZi Shen Lim int ret; 1443e54bcde3SZi Shen Lim 144432f6865cSIlias Apalodimas if (ctx->image == NULL) 144532f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 14468c11ea5cSDaniel Borkmann ret = build_insn(insn, ctx, extra_pass); 144730d3d94cSZi Shen Lim if (ret > 0) { 144830d3d94cSZi Shen Lim i++; 1449ddc665a4SDaniel Borkmann if (ctx->image == NULL) 1450ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 145130d3d94cSZi Shen Lim continue; 145230d3d94cSZi Shen Lim } 1453e54bcde3SZi Shen Lim if (ret) 1454e54bcde3SZi Shen Lim return ret; 1455e54bcde3SZi Shen Lim } 145632f6865cSIlias Apalodimas /* 145732f6865cSIlias Apalodimas * offset is allocated with prog->len + 1 so fill in 145832f6865cSIlias Apalodimas * the last element with the offset after the last 145932f6865cSIlias Apalodimas * instruction (end of program) 146032f6865cSIlias Apalodimas */ 146132f6865cSIlias Apalodimas if (ctx->image == NULL) 146232f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 1463e54bcde3SZi Shen Lim 1464e54bcde3SZi Shen Lim return 0; 1465e54bcde3SZi Shen Lim } 1466e54bcde3SZi Shen Lim 146742ff712bSZi Shen Lim static int validate_code(struct jit_ctx *ctx) 146842ff712bSZi Shen Lim { 146942ff712bSZi Shen Lim int i; 147042ff712bSZi Shen Lim 147142ff712bSZi Shen Lim for (i = 0; i < ctx->idx; i++) { 147242ff712bSZi Shen Lim u32 a64_insn = le32_to_cpu(ctx->image[i]); 147342ff712bSZi Shen Lim 147442ff712bSZi Shen Lim if (a64_insn == AARCH64_BREAK_FAULT) 147542ff712bSZi Shen Lim return -1; 147642ff712bSZi Shen Lim } 1477efc9909fSXu Kuohai return 0; 1478efc9909fSXu Kuohai } 1479efc9909fSXu Kuohai 1480efc9909fSXu Kuohai static int validate_ctx(struct jit_ctx *ctx) 1481efc9909fSXu Kuohai { 1482efc9909fSXu Kuohai if (validate_code(ctx)) 1483efc9909fSXu Kuohai return -1; 148442ff712bSZi Shen Lim 148580083428SJean-Philippe Brucker if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries)) 148680083428SJean-Philippe Brucker return -1; 148780083428SJean-Philippe Brucker 148842ff712bSZi Shen Lim return 0; 148942ff712bSZi Shen Lim } 149042ff712bSZi Shen Lim 1491e54bcde3SZi Shen Lim static inline void bpf_flush_icache(void *start, void *end) 1492e54bcde3SZi Shen Lim { 1493e54bcde3SZi Shen Lim flush_icache_range((unsigned long)start, (unsigned long)end); 1494e54bcde3SZi Shen Lim } 1495e54bcde3SZi Shen Lim 1496db496944SAlexei Starovoitov struct arm64_jit_data { 1497db496944SAlexei Starovoitov struct bpf_binary_header *header; 1498db496944SAlexei Starovoitov u8 *image; 1499db496944SAlexei Starovoitov struct jit_ctx ctx; 1500db496944SAlexei Starovoitov }; 1501db496944SAlexei Starovoitov 1502d1c55ab5SDaniel Borkmann struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 1503e54bcde3SZi Shen Lim { 1504b2ad54e1SXu Kuohai int image_size, prog_size, extable_size, extable_align, extable_offset; 150526eb042eSDaniel Borkmann struct bpf_prog *tmp, *orig_prog = prog; 1506b569c1c6SDaniel Borkmann struct bpf_binary_header *header; 1507db496944SAlexei Starovoitov struct arm64_jit_data *jit_data; 150856ea6a8bSDaniel Borkmann bool was_classic = bpf_prog_was_classic(prog); 150926eb042eSDaniel Borkmann bool tmp_blinded = false; 1510db496944SAlexei Starovoitov bool extra_pass = false; 1511e54bcde3SZi Shen Lim struct jit_ctx ctx; 1512b569c1c6SDaniel Borkmann u8 *image_ptr; 1513e54bcde3SZi Shen Lim 151460b58afcSAlexei Starovoitov if (!prog->jit_requested) 151526eb042eSDaniel Borkmann return orig_prog; 151626eb042eSDaniel Borkmann 151726eb042eSDaniel Borkmann tmp = bpf_jit_blind_constants(prog); 151826eb042eSDaniel Borkmann /* If blinding was requested and we failed during blinding, 151926eb042eSDaniel Borkmann * we must fall back to the interpreter. 152026eb042eSDaniel Borkmann */ 152126eb042eSDaniel Borkmann if (IS_ERR(tmp)) 152226eb042eSDaniel Borkmann return orig_prog; 152326eb042eSDaniel Borkmann if (tmp != prog) { 152426eb042eSDaniel Borkmann tmp_blinded = true; 152526eb042eSDaniel Borkmann prog = tmp; 152626eb042eSDaniel Borkmann } 1527e54bcde3SZi Shen Lim 1528db496944SAlexei Starovoitov jit_data = prog->aux->jit_data; 1529db496944SAlexei Starovoitov if (!jit_data) { 1530db496944SAlexei Starovoitov jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 1531db496944SAlexei Starovoitov if (!jit_data) { 1532db496944SAlexei Starovoitov prog = orig_prog; 1533db496944SAlexei Starovoitov goto out; 1534db496944SAlexei Starovoitov } 1535db496944SAlexei Starovoitov prog->aux->jit_data = jit_data; 1536db496944SAlexei Starovoitov } 1537db496944SAlexei Starovoitov if (jit_data->ctx.offset) { 1538db496944SAlexei Starovoitov ctx = jit_data->ctx; 1539db496944SAlexei Starovoitov image_ptr = jit_data->image; 1540db496944SAlexei Starovoitov header = jit_data->header; 1541db496944SAlexei Starovoitov extra_pass = true; 154280083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1543db496944SAlexei Starovoitov goto skip_init_ctx; 1544db496944SAlexei Starovoitov } 1545e54bcde3SZi Shen Lim memset(&ctx, 0, sizeof(ctx)); 1546e54bcde3SZi Shen Lim ctx.prog = prog; 1547e54bcde3SZi Shen Lim 154819f68ed6SAijun Sun ctx.offset = kvcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); 154926eb042eSDaniel Borkmann if (ctx.offset == NULL) { 155026eb042eSDaniel Borkmann prog = orig_prog; 1551db496944SAlexei Starovoitov goto out_off; 155226eb042eSDaniel Borkmann } 1553e54bcde3SZi Shen Lim 15545b3d19b9SXu Kuohai ctx.fpb_offset = find_fpb_offset(prog); 15555b3d19b9SXu Kuohai 155668e4f238SHou Tao /* 155768e4f238SHou Tao * 1. Initial fake pass to compute ctx->idx and ctx->offset. 155868e4f238SHou Tao * 155968e4f238SHou Tao * BPF line info needs ctx->offset[i] to be the offset of 156068e4f238SHou Tao * instruction[i] in jited image, so build prologue first. 156168e4f238SHou Tao */ 156268e4f238SHou Tao if (build_prologue(&ctx, was_classic)) { 156326eb042eSDaniel Borkmann prog = orig_prog; 156426eb042eSDaniel Borkmann goto out_off; 156526eb042eSDaniel Borkmann } 1566e54bcde3SZi Shen Lim 156768e4f238SHou Tao if (build_body(&ctx, extra_pass)) { 1568ddb55992SZi Shen Lim prog = orig_prog; 1569ddb55992SZi Shen Lim goto out_off; 1570ddb55992SZi Shen Lim } 157151c9fbb1SZi Shen Lim 157251c9fbb1SZi Shen Lim ctx.epilogue_offset = ctx.idx; 1573e54bcde3SZi Shen Lim build_epilogue(&ctx); 1574b2ad54e1SXu Kuohai build_plt(&ctx); 1575e54bcde3SZi Shen Lim 1576b2ad54e1SXu Kuohai extable_align = __alignof__(struct exception_table_entry); 157780083428SJean-Philippe Brucker extable_size = prog->aux->num_exentries * 157880083428SJean-Philippe Brucker sizeof(struct exception_table_entry); 157980083428SJean-Philippe Brucker 1580e54bcde3SZi Shen Lim /* Now we know the actual image size. */ 158180083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1582b2ad54e1SXu Kuohai /* also allocate space for plt target */ 1583b2ad54e1SXu Kuohai extable_offset = round_up(prog_size + PLT_TARGET_SIZE, extable_align); 1584b2ad54e1SXu Kuohai image_size = extable_offset + extable_size; 1585b569c1c6SDaniel Borkmann header = bpf_jit_binary_alloc(image_size, &image_ptr, 1586b569c1c6SDaniel Borkmann sizeof(u32), jit_fill_hole); 158726eb042eSDaniel Borkmann if (header == NULL) { 158826eb042eSDaniel Borkmann prog = orig_prog; 158926eb042eSDaniel Borkmann goto out_off; 159026eb042eSDaniel Borkmann } 1591e54bcde3SZi Shen Lim 1592e54bcde3SZi Shen Lim /* 2. Now, the actual pass. */ 1593e54bcde3SZi Shen Lim 1594425e1ed7SLuc Van Oostenryck ctx.image = (__le32 *)image_ptr; 159580083428SJean-Philippe Brucker if (extable_size) 1596b2ad54e1SXu Kuohai prog->aux->extable = (void *)image_ptr + extable_offset; 1597db496944SAlexei Starovoitov skip_init_ctx: 1598e54bcde3SZi Shen Lim ctx.idx = 0; 159980083428SJean-Philippe Brucker ctx.exentry_idx = 0; 1600b569c1c6SDaniel Borkmann 160156ea6a8bSDaniel Borkmann build_prologue(&ctx, was_classic); 1602e54bcde3SZi Shen Lim 16038c11ea5cSDaniel Borkmann if (build_body(&ctx, extra_pass)) { 1604b569c1c6SDaniel Borkmann bpf_jit_binary_free(header); 160526eb042eSDaniel Borkmann prog = orig_prog; 160626eb042eSDaniel Borkmann goto out_off; 160760ef0494SDaniel Borkmann } 1608e54bcde3SZi Shen Lim 1609e54bcde3SZi Shen Lim build_epilogue(&ctx); 1610b2ad54e1SXu Kuohai build_plt(&ctx); 1611e54bcde3SZi Shen Lim 161242ff712bSZi Shen Lim /* 3. Extra pass to validate JITed code. */ 1613efc9909fSXu Kuohai if (validate_ctx(&ctx)) { 161442ff712bSZi Shen Lim bpf_jit_binary_free(header); 161526eb042eSDaniel Borkmann prog = orig_prog; 161626eb042eSDaniel Borkmann goto out_off; 161742ff712bSZi Shen Lim } 161842ff712bSZi Shen Lim 1619e54bcde3SZi Shen Lim /* And we're done. */ 1620e54bcde3SZi Shen Lim if (bpf_jit_enable > 1) 162180083428SJean-Philippe Brucker bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 1622e54bcde3SZi Shen Lim 1623c3d4c682SDaniel Borkmann bpf_flush_icache(header, ctx.image + ctx.idx); 1624b569c1c6SDaniel Borkmann 1625db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1626db496944SAlexei Starovoitov if (extra_pass && ctx.idx != jit_data->ctx.idx) { 1627db496944SAlexei Starovoitov pr_err_once("multi-func JIT bug %d != %d\n", 1628db496944SAlexei Starovoitov ctx.idx, jit_data->ctx.idx); 1629db496944SAlexei Starovoitov bpf_jit_binary_free(header); 1630db496944SAlexei Starovoitov prog->bpf_func = NULL; 1631db496944SAlexei Starovoitov prog->jited = 0; 163210f3b29cSEric Dumazet prog->jited_len = 0; 1633db496944SAlexei Starovoitov goto out_off; 1634db496944SAlexei Starovoitov } 16359d876e79SDaniel Borkmann bpf_jit_binary_lock_ro(header); 1636db496944SAlexei Starovoitov } else { 1637db496944SAlexei Starovoitov jit_data->ctx = ctx; 1638db496944SAlexei Starovoitov jit_data->image = image_ptr; 1639db496944SAlexei Starovoitov jit_data->header = header; 1640db496944SAlexei Starovoitov } 1641e54bcde3SZi Shen Lim prog->bpf_func = (void *)ctx.image; 1642a91263d5SDaniel Borkmann prog->jited = 1; 164380083428SJean-Philippe Brucker prog->jited_len = prog_size; 164426eb042eSDaniel Borkmann 1645db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1646dda7596cSHou Tao int i; 1647dda7596cSHou Tao 1648dda7596cSHou Tao /* offset[prog->len] is the size of program */ 1649dda7596cSHou Tao for (i = 0; i <= prog->len; i++) 1650dda7596cSHou Tao ctx.offset[i] *= AARCH64_INSN_SIZE; 165132f6865cSIlias Apalodimas bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 165226eb042eSDaniel Borkmann out_off: 165319f68ed6SAijun Sun kvfree(ctx.offset); 1654db496944SAlexei Starovoitov kfree(jit_data); 1655db496944SAlexei Starovoitov prog->aux->jit_data = NULL; 1656db496944SAlexei Starovoitov } 165726eb042eSDaniel Borkmann out: 165826eb042eSDaniel Borkmann if (tmp_blinded) 165926eb042eSDaniel Borkmann bpf_jit_prog_release_other(prog, prog == orig_prog ? 166026eb042eSDaniel Borkmann tmp : orig_prog); 1661d1c55ab5SDaniel Borkmann return prog; 1662e54bcde3SZi Shen Lim } 166391fc957cSArd Biesheuvel 1664b5e975d2SHou Tao bool bpf_jit_supports_kfunc_call(void) 1665b5e975d2SHou Tao { 1666b5e975d2SHou Tao return true; 1667b5e975d2SHou Tao } 1668b5e975d2SHou Tao 16695d63ae90SLorenz Bauer u64 bpf_jit_alloc_exec_limit(void) 16705d63ae90SLorenz Bauer { 1671b89ddf4cSRussell King return VMALLOC_END - VMALLOC_START; 16725d63ae90SLorenz Bauer } 16735d63ae90SLorenz Bauer 167491fc957cSArd Biesheuvel void *bpf_jit_alloc_exec(unsigned long size) 167591fc957cSArd Biesheuvel { 167636c4a73bSAndrey Konovalov /* Memory is intended to be executable, reset the pointer tag. */ 167736c4a73bSAndrey Konovalov return kasan_reset_tag(vmalloc(size)); 167891fc957cSArd Biesheuvel } 167991fc957cSArd Biesheuvel 168091fc957cSArd Biesheuvel void bpf_jit_free_exec(void *addr) 168191fc957cSArd Biesheuvel { 168291fc957cSArd Biesheuvel return vfree(addr); 168391fc957cSArd Biesheuvel } 1684d4609a5dSJakub Sitnicki 1685d4609a5dSJakub Sitnicki /* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */ 1686d4609a5dSJakub Sitnicki bool bpf_jit_supports_subprog_tailcalls(void) 1687d4609a5dSJakub Sitnicki { 1688d4609a5dSJakub Sitnicki return true; 1689d4609a5dSJakub Sitnicki } 1690b2ad54e1SXu Kuohai 1691efc9909fSXu Kuohai static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, 1692efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1693efc9909fSXu Kuohai bool save_ret) 1694efc9909fSXu Kuohai { 1695aada4766SXu Kuohai __le32 *branch; 1696efc9909fSXu Kuohai u64 enter_prog; 1697efc9909fSXu Kuohai u64 exit_prog; 1698efc9909fSXu Kuohai struct bpf_prog *p = l->link.prog; 1699efc9909fSXu Kuohai int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); 1700efc9909fSXu Kuohai 1701271de525SMartin KaFai Lau enter_prog = (u64)bpf_trampoline_enter(p); 1702271de525SMartin KaFai Lau exit_prog = (u64)bpf_trampoline_exit(p); 1703efc9909fSXu Kuohai 1704efc9909fSXu Kuohai if (l->cookie == 0) { 1705efc9909fSXu Kuohai /* if cookie is zero, one instruction is enough to store it */ 1706efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, run_ctx_off + cookie_off), ctx); 1707efc9909fSXu Kuohai } else { 1708efc9909fSXu Kuohai emit_a64_mov_i64(A64_R(10), l->cookie, ctx); 1709efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, run_ctx_off + cookie_off), 1710efc9909fSXu Kuohai ctx); 1711efc9909fSXu Kuohai } 1712efc9909fSXu Kuohai 1713efc9909fSXu Kuohai /* save p to callee saved register x19 to avoid loading p with mov_i64 1714efc9909fSXu Kuohai * each time. 1715efc9909fSXu Kuohai */ 1716efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(19), (const u64)p, ctx); 1717efc9909fSXu Kuohai 1718efc9909fSXu Kuohai /* arg1: prog */ 1719efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1720efc9909fSXu Kuohai /* arg2: &run_ctx */ 1721efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(1), A64_SP, run_ctx_off), ctx); 1722efc9909fSXu Kuohai 1723efc9909fSXu Kuohai emit_call(enter_prog, ctx); 1724efc9909fSXu Kuohai 1725efc9909fSXu Kuohai /* if (__bpf_prog_enter(prog) == 0) 1726efc9909fSXu Kuohai * goto skip_exec_of_prog; 1727efc9909fSXu Kuohai */ 1728efc9909fSXu Kuohai branch = ctx->image + ctx->idx; 1729efc9909fSXu Kuohai emit(A64_NOP, ctx); 1730efc9909fSXu Kuohai 1731efc9909fSXu Kuohai /* save return value to callee saved register x20 */ 1732efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(20), A64_R(0)), ctx); 1733efc9909fSXu Kuohai 1734efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx); 1735efc9909fSXu Kuohai if (!p->jited) 1736efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx); 1737efc9909fSXu Kuohai 1738efc9909fSXu Kuohai emit_call((const u64)p->bpf_func, ctx); 1739efc9909fSXu Kuohai 1740efc9909fSXu Kuohai if (save_ret) 1741efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 1742efc9909fSXu Kuohai 1743efc9909fSXu Kuohai if (ctx->image) { 1744efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branch; 1745aada4766SXu Kuohai *branch = cpu_to_le32(A64_CBZ(1, A64_R(0), offset)); 1746efc9909fSXu Kuohai } 1747efc9909fSXu Kuohai 1748efc9909fSXu Kuohai /* arg1: prog */ 1749efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1750efc9909fSXu Kuohai /* arg2: start time */ 1751efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(1), A64_R(20)), ctx); 1752efc9909fSXu Kuohai /* arg3: &run_ctx */ 1753efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(2), A64_SP, run_ctx_off), ctx); 1754efc9909fSXu Kuohai 1755efc9909fSXu Kuohai emit_call(exit_prog, ctx); 1756efc9909fSXu Kuohai } 1757efc9909fSXu Kuohai 1758efc9909fSXu Kuohai static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, 1759efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1760aada4766SXu Kuohai __le32 **branches) 1761efc9909fSXu Kuohai { 1762efc9909fSXu Kuohai int i; 1763efc9909fSXu Kuohai 1764efc9909fSXu Kuohai /* The first fmod_ret program will receive a garbage return value. 1765efc9909fSXu Kuohai * Set this to 0 to avoid confusing the program. 1766efc9909fSXu Kuohai */ 1767efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx); 1768efc9909fSXu Kuohai for (i = 0; i < tl->nr_links; i++) { 1769efc9909fSXu Kuohai invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off, 1770efc9909fSXu Kuohai run_ctx_off, true); 1771efc9909fSXu Kuohai /* if (*(u64 *)(sp + retval_off) != 0) 1772efc9909fSXu Kuohai * goto do_fexit; 1773efc9909fSXu Kuohai */ 1774efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retval_off), ctx); 1775efc9909fSXu Kuohai /* Save the location of branch, and generate a nop. 1776efc9909fSXu Kuohai * This nop will be replaced with a cbnz later. 1777efc9909fSXu Kuohai */ 1778efc9909fSXu Kuohai branches[i] = ctx->image + ctx->idx; 1779efc9909fSXu Kuohai emit(A64_NOP, ctx); 1780efc9909fSXu Kuohai } 1781efc9909fSXu Kuohai } 1782efc9909fSXu Kuohai 178390564f1eSFlorent Revest static void save_args(struct jit_ctx *ctx, int args_off, int nregs) 1784efc9909fSXu Kuohai { 1785efc9909fSXu Kuohai int i; 1786efc9909fSXu Kuohai 178790564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 1788efc9909fSXu Kuohai emit(A64_STR64I(i, A64_SP, args_off), ctx); 1789efc9909fSXu Kuohai args_off += 8; 1790efc9909fSXu Kuohai } 1791efc9909fSXu Kuohai } 1792efc9909fSXu Kuohai 179390564f1eSFlorent Revest static void restore_args(struct jit_ctx *ctx, int args_off, int nregs) 1794efc9909fSXu Kuohai { 1795efc9909fSXu Kuohai int i; 1796efc9909fSXu Kuohai 179790564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 1798efc9909fSXu Kuohai emit(A64_LDR64I(i, A64_SP, args_off), ctx); 1799efc9909fSXu Kuohai args_off += 8; 1800efc9909fSXu Kuohai } 1801efc9909fSXu Kuohai } 1802efc9909fSXu Kuohai 1803efc9909fSXu Kuohai /* Based on the x86's implementation of arch_prepare_bpf_trampoline(). 1804efc9909fSXu Kuohai * 1805efc9909fSXu Kuohai * bpf prog and function entry before bpf trampoline hooked: 1806efc9909fSXu Kuohai * mov x9, lr 1807efc9909fSXu Kuohai * nop 1808efc9909fSXu Kuohai * 1809efc9909fSXu Kuohai * bpf prog and function entry after bpf trampoline hooked: 1810efc9909fSXu Kuohai * mov x9, lr 1811efc9909fSXu Kuohai * bl <bpf_trampoline or plt> 1812efc9909fSXu Kuohai * 1813efc9909fSXu Kuohai */ 1814efc9909fSXu Kuohai static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, 1815efc9909fSXu Kuohai struct bpf_tramp_links *tlinks, void *orig_call, 181690564f1eSFlorent Revest int nregs, u32 flags) 1817efc9909fSXu Kuohai { 1818efc9909fSXu Kuohai int i; 1819efc9909fSXu Kuohai int stack_size; 1820efc9909fSXu Kuohai int retaddr_off; 1821efc9909fSXu Kuohai int regs_off; 1822efc9909fSXu Kuohai int retval_off; 1823efc9909fSXu Kuohai int args_off; 182490564f1eSFlorent Revest int nregs_off; 1825efc9909fSXu Kuohai int ip_off; 1826efc9909fSXu Kuohai int run_ctx_off; 1827efc9909fSXu Kuohai struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; 1828efc9909fSXu Kuohai struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; 1829efc9909fSXu Kuohai struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; 1830efc9909fSXu Kuohai bool save_ret; 1831aada4766SXu Kuohai __le32 **branches = NULL; 1832efc9909fSXu Kuohai 1833efc9909fSXu Kuohai /* trampoline stack layout: 1834efc9909fSXu Kuohai * [ parent ip ] 1835efc9909fSXu Kuohai * [ FP ] 1836efc9909fSXu Kuohai * SP + retaddr_off [ self ip ] 1837efc9909fSXu Kuohai * [ FP ] 1838efc9909fSXu Kuohai * 1839efc9909fSXu Kuohai * [ padding ] align SP to multiples of 16 1840efc9909fSXu Kuohai * 1841efc9909fSXu Kuohai * [ x20 ] callee saved reg x20 1842efc9909fSXu Kuohai * SP + regs_off [ x19 ] callee saved reg x19 1843efc9909fSXu Kuohai * 1844efc9909fSXu Kuohai * SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or 1845efc9909fSXu Kuohai * BPF_TRAMP_F_RET_FENTRY_RET 1846efc9909fSXu Kuohai * 184790564f1eSFlorent Revest * [ arg reg N ] 1848efc9909fSXu Kuohai * [ ... ] 184990564f1eSFlorent Revest * SP + args_off [ arg reg 1 ] 1850efc9909fSXu Kuohai * 185190564f1eSFlorent Revest * SP + nregs_off [ arg regs count ] 1852efc9909fSXu Kuohai * 1853efc9909fSXu Kuohai * SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag 1854efc9909fSXu Kuohai * 1855efc9909fSXu Kuohai * SP + run_ctx_off [ bpf_tramp_run_ctx ] 1856efc9909fSXu Kuohai */ 1857efc9909fSXu Kuohai 1858efc9909fSXu Kuohai stack_size = 0; 1859efc9909fSXu Kuohai run_ctx_off = stack_size; 1860efc9909fSXu Kuohai /* room for bpf_tramp_run_ctx */ 1861efc9909fSXu Kuohai stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); 1862efc9909fSXu Kuohai 1863efc9909fSXu Kuohai ip_off = stack_size; 1864efc9909fSXu Kuohai /* room for IP address argument */ 1865efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) 1866efc9909fSXu Kuohai stack_size += 8; 1867efc9909fSXu Kuohai 186890564f1eSFlorent Revest nregs_off = stack_size; 1869efc9909fSXu Kuohai /* room for args count */ 1870efc9909fSXu Kuohai stack_size += 8; 1871efc9909fSXu Kuohai 1872efc9909fSXu Kuohai args_off = stack_size; 1873efc9909fSXu Kuohai /* room for args */ 187490564f1eSFlorent Revest stack_size += nregs * 8; 1875efc9909fSXu Kuohai 1876efc9909fSXu Kuohai /* room for return value */ 1877efc9909fSXu Kuohai retval_off = stack_size; 1878efc9909fSXu Kuohai save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET); 1879efc9909fSXu Kuohai if (save_ret) 1880efc9909fSXu Kuohai stack_size += 8; 1881efc9909fSXu Kuohai 1882efc9909fSXu Kuohai /* room for callee saved registers, currently x19 and x20 are used */ 1883efc9909fSXu Kuohai regs_off = stack_size; 1884efc9909fSXu Kuohai stack_size += 16; 1885efc9909fSXu Kuohai 1886efc9909fSXu Kuohai /* round up to multiples of 16 to avoid SPAlignmentFault */ 1887efc9909fSXu Kuohai stack_size = round_up(stack_size, 16); 1888efc9909fSXu Kuohai 1889efc9909fSXu Kuohai /* return address locates above FP */ 1890efc9909fSXu Kuohai retaddr_off = stack_size + 8; 1891efc9909fSXu Kuohai 1892efc9909fSXu Kuohai /* bpf trampoline may be invoked by 3 instruction types: 1893efc9909fSXu Kuohai * 1. bl, attached to bpf prog or kernel function via short jump 1894efc9909fSXu Kuohai * 2. br, attached to bpf prog or kernel function via long jump 1895efc9909fSXu Kuohai * 3. blr, working as a function pointer, used by struct_ops. 1896efc9909fSXu Kuohai * So BTI_JC should used here to support both br and blr. 1897efc9909fSXu Kuohai */ 1898efc9909fSXu Kuohai emit_bti(A64_BTI_JC, ctx); 1899efc9909fSXu Kuohai 1900efc9909fSXu Kuohai /* frame for parent function */ 1901efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx); 1902efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 1903efc9909fSXu Kuohai 1904efc9909fSXu Kuohai /* frame for patched function */ 1905efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 1906efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 1907efc9909fSXu Kuohai 1908efc9909fSXu Kuohai /* allocate stack space */ 1909efc9909fSXu Kuohai emit(A64_SUB_I(1, A64_SP, A64_SP, stack_size), ctx); 1910efc9909fSXu Kuohai 1911efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) { 1912efc9909fSXu Kuohai /* save ip address of the traced function */ 1913efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(10), (const u64)orig_call, ctx); 1914efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, ip_off), ctx); 1915efc9909fSXu Kuohai } 1916efc9909fSXu Kuohai 191790564f1eSFlorent Revest /* save arg regs count*/ 191890564f1eSFlorent Revest emit(A64_MOVZ(1, A64_R(10), nregs, 0), ctx); 191990564f1eSFlorent Revest emit(A64_STR64I(A64_R(10), A64_SP, nregs_off), ctx); 1920efc9909fSXu Kuohai 192190564f1eSFlorent Revest /* save arg regs */ 192290564f1eSFlorent Revest save_args(ctx, args_off, nregs); 1923efc9909fSXu Kuohai 1924efc9909fSXu Kuohai /* save callee saved registers */ 1925efc9909fSXu Kuohai emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx); 1926efc9909fSXu Kuohai emit(A64_STR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 1927efc9909fSXu Kuohai 1928efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 1929efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 1930efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_enter, ctx); 1931efc9909fSXu Kuohai } 1932efc9909fSXu Kuohai 1933efc9909fSXu Kuohai for (i = 0; i < fentry->nr_links; i++) 1934efc9909fSXu Kuohai invoke_bpf_prog(ctx, fentry->links[i], args_off, 1935efc9909fSXu Kuohai retval_off, run_ctx_off, 1936efc9909fSXu Kuohai flags & BPF_TRAMP_F_RET_FENTRY_RET); 1937efc9909fSXu Kuohai 1938efc9909fSXu Kuohai if (fmod_ret->nr_links) { 1939aada4766SXu Kuohai branches = kcalloc(fmod_ret->nr_links, sizeof(__le32 *), 1940efc9909fSXu Kuohai GFP_KERNEL); 1941efc9909fSXu Kuohai if (!branches) 1942efc9909fSXu Kuohai return -ENOMEM; 1943efc9909fSXu Kuohai 1944efc9909fSXu Kuohai invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off, 1945efc9909fSXu Kuohai run_ctx_off, branches); 1946efc9909fSXu Kuohai } 1947efc9909fSXu Kuohai 1948efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 194990564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 1950efc9909fSXu Kuohai /* call original func */ 1951efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx); 1952738a96c4SXu Kuohai emit(A64_ADR(A64_LR, AARCH64_INSN_SIZE * 2), ctx); 1953738a96c4SXu Kuohai emit(A64_RET(A64_R(10)), ctx); 1954efc9909fSXu Kuohai /* store return value */ 1955efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 1956efc9909fSXu Kuohai /* reserve a nop for bpf_tramp_image_put */ 1957efc9909fSXu Kuohai im->ip_after_call = ctx->image + ctx->idx; 1958efc9909fSXu Kuohai emit(A64_NOP, ctx); 1959efc9909fSXu Kuohai } 1960efc9909fSXu Kuohai 1961efc9909fSXu Kuohai /* update the branches saved in invoke_bpf_mod_ret with cbnz */ 1962efc9909fSXu Kuohai for (i = 0; i < fmod_ret->nr_links && ctx->image != NULL; i++) { 1963efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branches[i]; 1964aada4766SXu Kuohai *branches[i] = cpu_to_le32(A64_CBNZ(1, A64_R(10), offset)); 1965efc9909fSXu Kuohai } 1966efc9909fSXu Kuohai 1967efc9909fSXu Kuohai for (i = 0; i < fexit->nr_links; i++) 1968efc9909fSXu Kuohai invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off, 1969efc9909fSXu Kuohai run_ctx_off, false); 1970efc9909fSXu Kuohai 1971efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 1972efc9909fSXu Kuohai im->ip_epilogue = ctx->image + ctx->idx; 1973efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 1974efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_exit, ctx); 1975efc9909fSXu Kuohai } 1976efc9909fSXu Kuohai 1977efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_RESTORE_REGS) 197890564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 1979efc9909fSXu Kuohai 1980efc9909fSXu Kuohai /* restore callee saved register x19 and x20 */ 1981efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx); 1982efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 1983efc9909fSXu Kuohai 1984efc9909fSXu Kuohai if (save_ret) 1985efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(0), A64_SP, retval_off), ctx); 1986efc9909fSXu Kuohai 1987efc9909fSXu Kuohai /* reset SP */ 1988efc9909fSXu Kuohai emit(A64_MOV(1, A64_SP, A64_FP), ctx); 1989efc9909fSXu Kuohai 1990efc9909fSXu Kuohai /* pop frames */ 1991efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 1992efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx); 1993efc9909fSXu Kuohai 1994efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_SKIP_FRAME) { 1995efc9909fSXu Kuohai /* skip patched function, return to parent */ 1996efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 1997efc9909fSXu Kuohai emit(A64_RET(A64_R(9)), ctx); 1998efc9909fSXu Kuohai } else { 1999efc9909fSXu Kuohai /* return to patched function */ 2000efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(10), A64_LR), ctx); 2001efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 2002efc9909fSXu Kuohai emit(A64_RET(A64_R(10)), ctx); 2003efc9909fSXu Kuohai } 2004efc9909fSXu Kuohai 2005efc9909fSXu Kuohai if (ctx->image) 2006efc9909fSXu Kuohai bpf_flush_icache(ctx->image, ctx->image + ctx->idx); 2007efc9909fSXu Kuohai 2008efc9909fSXu Kuohai kfree(branches); 2009efc9909fSXu Kuohai 2010efc9909fSXu Kuohai return ctx->idx; 2011efc9909fSXu Kuohai } 2012efc9909fSXu Kuohai 2013efc9909fSXu Kuohai int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, 2014efc9909fSXu Kuohai void *image_end, const struct btf_func_model *m, 2015efc9909fSXu Kuohai u32 flags, struct bpf_tramp_links *tlinks, 2016efc9909fSXu Kuohai void *orig_call) 2017efc9909fSXu Kuohai { 2018eb707ddeSYonghong Song int i, ret; 201990564f1eSFlorent Revest int nregs = m->nr_args; 2020efc9909fSXu Kuohai int max_insns = ((long)image_end - (long)image) / AARCH64_INSN_SIZE; 2021efc9909fSXu Kuohai struct jit_ctx ctx = { 2022efc9909fSXu Kuohai .image = NULL, 2023efc9909fSXu Kuohai .idx = 0, 2024efc9909fSXu Kuohai }; 2025efc9909fSXu Kuohai 202690564f1eSFlorent Revest /* extra registers needed for struct argument */ 2027eb707ddeSYonghong Song for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) { 202890564f1eSFlorent Revest /* The arg_size is at most 16 bytes, enforced by the verifier. */ 2029eb707ddeSYonghong Song if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) 203090564f1eSFlorent Revest nregs += (m->arg_size[i] + 7) / 8 - 1; 2031eb707ddeSYonghong Song } 2032eb707ddeSYonghong Song 203390564f1eSFlorent Revest /* the first 8 registers are used for arguments */ 203490564f1eSFlorent Revest if (nregs > 8) 203590564f1eSFlorent Revest return -ENOTSUPP; 203690564f1eSFlorent Revest 203790564f1eSFlorent Revest ret = prepare_trampoline(&ctx, im, tlinks, orig_call, nregs, flags); 2038efc9909fSXu Kuohai if (ret < 0) 2039efc9909fSXu Kuohai return ret; 2040efc9909fSXu Kuohai 2041efc9909fSXu Kuohai if (ret > max_insns) 2042efc9909fSXu Kuohai return -EFBIG; 2043efc9909fSXu Kuohai 2044efc9909fSXu Kuohai ctx.image = image; 2045efc9909fSXu Kuohai ctx.idx = 0; 2046efc9909fSXu Kuohai 2047efc9909fSXu Kuohai jit_fill_hole(image, (unsigned int)(image_end - image)); 204890564f1eSFlorent Revest ret = prepare_trampoline(&ctx, im, tlinks, orig_call, nregs, flags); 2049efc9909fSXu Kuohai 2050efc9909fSXu Kuohai if (ret > 0 && validate_code(&ctx) < 0) 2051efc9909fSXu Kuohai ret = -EINVAL; 2052efc9909fSXu Kuohai 2053efc9909fSXu Kuohai if (ret > 0) 2054efc9909fSXu Kuohai ret *= AARCH64_INSN_SIZE; 2055efc9909fSXu Kuohai 2056efc9909fSXu Kuohai return ret; 2057efc9909fSXu Kuohai } 2058efc9909fSXu Kuohai 2059b2ad54e1SXu Kuohai static bool is_long_jump(void *ip, void *target) 2060b2ad54e1SXu Kuohai { 2061b2ad54e1SXu Kuohai long offset; 2062b2ad54e1SXu Kuohai 2063b2ad54e1SXu Kuohai /* NULL target means this is a NOP */ 2064b2ad54e1SXu Kuohai if (!target) 2065b2ad54e1SXu Kuohai return false; 2066b2ad54e1SXu Kuohai 2067b2ad54e1SXu Kuohai offset = (long)target - (long)ip; 2068b2ad54e1SXu Kuohai return offset < -SZ_128M || offset >= SZ_128M; 2069b2ad54e1SXu Kuohai } 2070b2ad54e1SXu Kuohai 2071b2ad54e1SXu Kuohai static int gen_branch_or_nop(enum aarch64_insn_branch_type type, void *ip, 2072b2ad54e1SXu Kuohai void *addr, void *plt, u32 *insn) 2073b2ad54e1SXu Kuohai { 2074b2ad54e1SXu Kuohai void *target; 2075b2ad54e1SXu Kuohai 2076b2ad54e1SXu Kuohai if (!addr) { 2077b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_nop(); 2078b2ad54e1SXu Kuohai return 0; 2079b2ad54e1SXu Kuohai } 2080b2ad54e1SXu Kuohai 2081b2ad54e1SXu Kuohai if (is_long_jump(ip, addr)) 2082b2ad54e1SXu Kuohai target = plt; 2083b2ad54e1SXu Kuohai else 2084b2ad54e1SXu Kuohai target = addr; 2085b2ad54e1SXu Kuohai 2086b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_branch_imm((unsigned long)ip, 2087b2ad54e1SXu Kuohai (unsigned long)target, 2088b2ad54e1SXu Kuohai type); 2089b2ad54e1SXu Kuohai 2090b2ad54e1SXu Kuohai return *insn != AARCH64_BREAK_FAULT ? 0 : -EFAULT; 2091b2ad54e1SXu Kuohai } 2092b2ad54e1SXu Kuohai 2093b2ad54e1SXu Kuohai /* Replace the branch instruction from @ip to @old_addr in a bpf prog or a bpf 2094b2ad54e1SXu Kuohai * trampoline with the branch instruction from @ip to @new_addr. If @old_addr 2095b2ad54e1SXu Kuohai * or @new_addr is NULL, the old or new instruction is NOP. 2096b2ad54e1SXu Kuohai * 2097b2ad54e1SXu Kuohai * When @ip is the bpf prog entry, a bpf trampoline is being attached or 2098b2ad54e1SXu Kuohai * detached. Since bpf trampoline and bpf prog are allocated separately with 2099b2ad54e1SXu Kuohai * vmalloc, the address distance may exceed 128MB, the maximum branch range. 2100b2ad54e1SXu Kuohai * So long jump should be handled. 2101b2ad54e1SXu Kuohai * 2102b2ad54e1SXu Kuohai * When a bpf prog is constructed, a plt pointing to empty trampoline 2103b2ad54e1SXu Kuohai * dummy_tramp is placed at the end: 2104b2ad54e1SXu Kuohai * 2105b2ad54e1SXu Kuohai * bpf_prog: 2106b2ad54e1SXu Kuohai * mov x9, lr 2107b2ad54e1SXu Kuohai * nop // patchsite 2108b2ad54e1SXu Kuohai * ... 2109b2ad54e1SXu Kuohai * ret 2110b2ad54e1SXu Kuohai * 2111b2ad54e1SXu Kuohai * plt: 2112b2ad54e1SXu Kuohai * ldr x10, target 2113b2ad54e1SXu Kuohai * br x10 2114b2ad54e1SXu Kuohai * target: 2115b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2116b2ad54e1SXu Kuohai * 2117b2ad54e1SXu Kuohai * This is also the state when no trampoline is attached. 2118b2ad54e1SXu Kuohai * 2119b2ad54e1SXu Kuohai * When a short-jump bpf trampoline is attached, the patchsite is patched 2120b2ad54e1SXu Kuohai * to a bl instruction to the trampoline directly: 2121b2ad54e1SXu Kuohai * 2122b2ad54e1SXu Kuohai * bpf_prog: 2123b2ad54e1SXu Kuohai * mov x9, lr 2124b2ad54e1SXu Kuohai * bl <short-jump bpf trampoline address> // patchsite 2125b2ad54e1SXu Kuohai * ... 2126b2ad54e1SXu Kuohai * ret 2127b2ad54e1SXu Kuohai * 2128b2ad54e1SXu Kuohai * plt: 2129b2ad54e1SXu Kuohai * ldr x10, target 2130b2ad54e1SXu Kuohai * br x10 2131b2ad54e1SXu Kuohai * target: 2132b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2133b2ad54e1SXu Kuohai * 2134b2ad54e1SXu Kuohai * When a long-jump bpf trampoline is attached, the plt target is filled with 2135b2ad54e1SXu Kuohai * the trampoline address and the patchsite is patched to a bl instruction to 2136b2ad54e1SXu Kuohai * the plt: 2137b2ad54e1SXu Kuohai * 2138b2ad54e1SXu Kuohai * bpf_prog: 2139b2ad54e1SXu Kuohai * mov x9, lr 2140b2ad54e1SXu Kuohai * bl plt // patchsite 2141b2ad54e1SXu Kuohai * ... 2142b2ad54e1SXu Kuohai * ret 2143b2ad54e1SXu Kuohai * 2144b2ad54e1SXu Kuohai * plt: 2145b2ad54e1SXu Kuohai * ldr x10, target 2146b2ad54e1SXu Kuohai * br x10 2147b2ad54e1SXu Kuohai * target: 2148b2ad54e1SXu Kuohai * .quad <long-jump bpf trampoline address> // plt target 2149b2ad54e1SXu Kuohai * 2150b2ad54e1SXu Kuohai * The dummy_tramp is used to prevent another CPU from jumping to unknown 2151b2ad54e1SXu Kuohai * locations during the patching process, making the patching process easier. 2152b2ad54e1SXu Kuohai */ 2153b2ad54e1SXu Kuohai int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, 2154b2ad54e1SXu Kuohai void *old_addr, void *new_addr) 2155b2ad54e1SXu Kuohai { 2156b2ad54e1SXu Kuohai int ret; 2157b2ad54e1SXu Kuohai u32 old_insn; 2158b2ad54e1SXu Kuohai u32 new_insn; 2159b2ad54e1SXu Kuohai u32 replaced; 2160b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 2161b2ad54e1SXu Kuohai unsigned long size = 0UL; 2162b2ad54e1SXu Kuohai unsigned long offset = ~0UL; 2163b2ad54e1SXu Kuohai enum aarch64_insn_branch_type branch_type; 2164b2ad54e1SXu Kuohai char namebuf[KSYM_NAME_LEN]; 2165b2ad54e1SXu Kuohai void *image = NULL; 2166b2ad54e1SXu Kuohai u64 plt_target = 0ULL; 2167b2ad54e1SXu Kuohai bool poking_bpf_entry; 2168b2ad54e1SXu Kuohai 2169b2ad54e1SXu Kuohai if (!__bpf_address_lookup((unsigned long)ip, &size, &offset, namebuf)) 2170b2ad54e1SXu Kuohai /* Only poking bpf text is supported. Since kernel function 2171b2ad54e1SXu Kuohai * entry is set up by ftrace, we reply on ftrace to poke kernel 2172b2ad54e1SXu Kuohai * functions. 2173b2ad54e1SXu Kuohai */ 2174b2ad54e1SXu Kuohai return -ENOTSUPP; 2175b2ad54e1SXu Kuohai 2176b2ad54e1SXu Kuohai image = ip - offset; 2177b2ad54e1SXu Kuohai /* zero offset means we're poking bpf prog entry */ 2178b2ad54e1SXu Kuohai poking_bpf_entry = (offset == 0UL); 2179b2ad54e1SXu Kuohai 2180b2ad54e1SXu Kuohai /* bpf prog entry, find plt and the real patchsite */ 2181b2ad54e1SXu Kuohai if (poking_bpf_entry) { 2182b2ad54e1SXu Kuohai /* plt locates at the end of bpf prog */ 2183b2ad54e1SXu Kuohai plt = image + size - PLT_TARGET_OFFSET; 2184b2ad54e1SXu Kuohai 2185b2ad54e1SXu Kuohai /* skip to the nop instruction in bpf prog entry: 2186b2ad54e1SXu Kuohai * bti c // if BTI enabled 2187b2ad54e1SXu Kuohai * mov x9, x30 2188b2ad54e1SXu Kuohai * nop 2189b2ad54e1SXu Kuohai */ 2190b2ad54e1SXu Kuohai ip = image + POKE_OFFSET * AARCH64_INSN_SIZE; 2191b2ad54e1SXu Kuohai } 2192b2ad54e1SXu Kuohai 2193b2ad54e1SXu Kuohai /* long jump is only possible at bpf prog entry */ 2194b2ad54e1SXu Kuohai if (WARN_ON((is_long_jump(ip, new_addr) || is_long_jump(ip, old_addr)) && 2195b2ad54e1SXu Kuohai !poking_bpf_entry)) 2196b2ad54e1SXu Kuohai return -EINVAL; 2197b2ad54e1SXu Kuohai 2198b2ad54e1SXu Kuohai if (poke_type == BPF_MOD_CALL) 2199b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_LINK; 2200b2ad54e1SXu Kuohai else 2201b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_NOLINK; 2202b2ad54e1SXu Kuohai 2203b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, old_addr, plt, &old_insn) < 0) 2204b2ad54e1SXu Kuohai return -EFAULT; 2205b2ad54e1SXu Kuohai 2206b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, new_addr, plt, &new_insn) < 0) 2207b2ad54e1SXu Kuohai return -EFAULT; 2208b2ad54e1SXu Kuohai 2209b2ad54e1SXu Kuohai if (is_long_jump(ip, new_addr)) 2210b2ad54e1SXu Kuohai plt_target = (u64)new_addr; 2211b2ad54e1SXu Kuohai else if (is_long_jump(ip, old_addr)) 2212b2ad54e1SXu Kuohai /* if the old target is a long jump and the new target is not, 2213b2ad54e1SXu Kuohai * restore the plt target to dummy_tramp, so there is always a 2214b2ad54e1SXu Kuohai * legal and harmless address stored in plt target, and we'll 2215b2ad54e1SXu Kuohai * never jump from plt to an unknown place. 2216b2ad54e1SXu Kuohai */ 2217b2ad54e1SXu Kuohai plt_target = (u64)&dummy_tramp; 2218b2ad54e1SXu Kuohai 2219b2ad54e1SXu Kuohai if (plt_target) { 2220b2ad54e1SXu Kuohai /* non-zero plt_target indicates we're patching a bpf prog, 2221b2ad54e1SXu Kuohai * which is read only. 2222b2ad54e1SXu Kuohai */ 2223b2ad54e1SXu Kuohai if (set_memory_rw(PAGE_MASK & ((uintptr_t)&plt->target), 1)) 2224b2ad54e1SXu Kuohai return -EFAULT; 2225b2ad54e1SXu Kuohai WRITE_ONCE(plt->target, plt_target); 2226b2ad54e1SXu Kuohai set_memory_ro(PAGE_MASK & ((uintptr_t)&plt->target), 1); 2227b2ad54e1SXu Kuohai /* since plt target points to either the new trampoline 2228b2ad54e1SXu Kuohai * or dummy_tramp, even if another CPU reads the old plt 2229b2ad54e1SXu Kuohai * target value before fetching the bl instruction to plt, 2230b2ad54e1SXu Kuohai * it will be brought back by dummy_tramp, so no barrier is 2231b2ad54e1SXu Kuohai * required here. 2232b2ad54e1SXu Kuohai */ 2233b2ad54e1SXu Kuohai } 2234b2ad54e1SXu Kuohai 2235b2ad54e1SXu Kuohai /* if the old target and the new target are both long jumps, no 2236b2ad54e1SXu Kuohai * patching is required 2237b2ad54e1SXu Kuohai */ 2238b2ad54e1SXu Kuohai if (old_insn == new_insn) 2239b2ad54e1SXu Kuohai return 0; 2240b2ad54e1SXu Kuohai 2241b2ad54e1SXu Kuohai mutex_lock(&text_mutex); 2242b2ad54e1SXu Kuohai if (aarch64_insn_read(ip, &replaced)) { 2243b2ad54e1SXu Kuohai ret = -EFAULT; 2244b2ad54e1SXu Kuohai goto out; 2245b2ad54e1SXu Kuohai } 2246b2ad54e1SXu Kuohai 2247b2ad54e1SXu Kuohai if (replaced != old_insn) { 2248b2ad54e1SXu Kuohai ret = -EFAULT; 2249b2ad54e1SXu Kuohai goto out; 2250b2ad54e1SXu Kuohai } 2251b2ad54e1SXu Kuohai 2252b2ad54e1SXu Kuohai /* We call aarch64_insn_patch_text_nosync() to replace instruction 2253b2ad54e1SXu Kuohai * atomically, so no other CPUs will fetch a half-new and half-old 2254b2ad54e1SXu Kuohai * instruction. But there is chance that another CPU executes the 2255b2ad54e1SXu Kuohai * old instruction after the patching operation finishes (e.g., 2256b2ad54e1SXu Kuohai * pipeline not flushed, or icache not synchronized yet). 2257b2ad54e1SXu Kuohai * 2258b2ad54e1SXu Kuohai * 1. when a new trampoline is attached, it is not a problem for 2259b2ad54e1SXu Kuohai * different CPUs to jump to different trampolines temporarily. 2260b2ad54e1SXu Kuohai * 2261b2ad54e1SXu Kuohai * 2. when an old trampoline is freed, we should wait for all other 2262b2ad54e1SXu Kuohai * CPUs to exit the trampoline and make sure the trampoline is no 2263b2ad54e1SXu Kuohai * longer reachable, since bpf_tramp_image_put() function already 2264b2ad54e1SXu Kuohai * uses percpu_ref and task-based rcu to do the sync, no need to call 2265b2ad54e1SXu Kuohai * the sync version here, see bpf_tramp_image_put() for details. 2266b2ad54e1SXu Kuohai */ 2267b2ad54e1SXu Kuohai ret = aarch64_insn_patch_text_nosync(ip, new_insn); 2268b2ad54e1SXu Kuohai out: 2269b2ad54e1SXu Kuohai mutex_unlock(&text_mutex); 2270b2ad54e1SXu Kuohai 2271b2ad54e1SXu Kuohai return ret; 2272b2ad54e1SXu Kuohai } 2273