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 325b2ad54e1SXu Kuohai emit_bti(A64_BTI_C, ctx); 326b2ad54e1SXu Kuohai 327b2ad54e1SXu Kuohai emit(A64_MOV(1, A64_R(9), A64_LR), ctx); 328b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 329b2ad54e1SXu Kuohai 330042152c2SXu Kuohai /* Sign lr */ 331042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 332042152c2SXu Kuohai emit(A64_PACIASP, ctx); 333fa76cfe6SMark Brown 334ec0738dbSYang Shi /* Save FP and LR registers to stay align with ARM64 AAPCS */ 335ec0738dbSYang Shi emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 336ec0738dbSYang Shi emit(A64_MOV(1, A64_FP, A64_SP), ctx); 337ec0738dbSYang Shi 338ddb55992SZi Shen Lim /* Save callee-saved registers */ 339e54bcde3SZi Shen Lim emit(A64_PUSH(r6, r7, A64_SP), ctx); 340e54bcde3SZi Shen Lim emit(A64_PUSH(r8, r9, A64_SP), ctx); 341ddb55992SZi Shen Lim emit(A64_PUSH(fp, tcc, A64_SP), ctx); 3425b3d19b9SXu Kuohai emit(A64_PUSH(fpb, A64_R(28), A64_SP), ctx); 343e54bcde3SZi Shen Lim 344ddb55992SZi Shen Lim /* Set up BPF prog stack base register */ 345e54bcde3SZi Shen Lim emit(A64_MOV(1, fp, A64_SP), ctx); 346e54bcde3SZi Shen Lim 347d4609a5dSJakub Sitnicki if (!ebpf_from_cbpf && is_main_prog) { 348ddb55992SZi Shen Lim /* Initialize tail_call_cnt */ 349ddb55992SZi Shen Lim emit(A64_MOVZ(1, tcc, 0, 0), ctx); 350ddb55992SZi Shen Lim 351ddb55992SZi Shen Lim cur_offset = ctx->idx - idx0; 352ddb55992SZi Shen Lim if (cur_offset != PROLOGUE_OFFSET) { 353ddb55992SZi Shen Lim pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 354ddb55992SZi Shen Lim cur_offset, PROLOGUE_OFFSET); 355ddb55992SZi Shen Lim return -1; 356ddb55992SZi Shen Lim } 357fa76cfe6SMark Brown 358fa76cfe6SMark Brown /* BTI landing pad for the tail call, done with a BR */ 359b2ad54e1SXu Kuohai emit_bti(A64_BTI_J, ctx); 36056ea6a8bSDaniel Borkmann } 361a2284d91SDaniel Borkmann 3625b3d19b9SXu Kuohai emit(A64_SUB_I(1, fpb, fp, ctx->fpb_offset), ctx); 3635b3d19b9SXu Kuohai 3643f287098STiezhu Yang /* Stack must be multiples of 16B */ 3653f287098STiezhu Yang ctx->stack_size = round_up(prog->aux->stack_depth, 16); 366a2284d91SDaniel Borkmann 367a2284d91SDaniel Borkmann /* Set up function call stack */ 368a2284d91SDaniel Borkmann emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 369ddb55992SZi Shen Lim return 0; 370ddb55992SZi Shen Lim } 371ddb55992SZi Shen Lim 372ddb55992SZi Shen Lim static int out_offset = -1; /* initialized on the first pass of build_body() */ 373ddb55992SZi Shen Lim static int emit_bpf_tail_call(struct jit_ctx *ctx) 374ddb55992SZi Shen Lim { 375ddb55992SZi Shen Lim /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 376ddb55992SZi Shen Lim const u8 r2 = bpf2a64[BPF_REG_2]; 377ddb55992SZi Shen Lim const u8 r3 = bpf2a64[BPF_REG_3]; 378ddb55992SZi Shen Lim 379ddb55992SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 380ddb55992SZi Shen Lim const u8 prg = bpf2a64[TMP_REG_2]; 381ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 382ddb55992SZi Shen Lim const int idx0 = ctx->idx; 383ddb55992SZi Shen Lim #define cur_offset (ctx->idx - idx0) 384ddb55992SZi Shen Lim #define jmp_offset (out_offset - (cur_offset)) 385ddb55992SZi Shen Lim size_t off; 386ddb55992SZi Shen Lim 387ddb55992SZi Shen Lim /* if (index >= array->map.max_entries) 388ddb55992SZi Shen Lim * goto out; 389ddb55992SZi Shen Lim */ 390ddb55992SZi Shen Lim off = offsetof(struct bpf_array, map.max_entries); 391ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 392ddb55992SZi Shen Lim emit(A64_LDR32(tmp, r2, tmp), ctx); 39316338a9bSDaniel Borkmann emit(A64_MOV(0, r3, r3), ctx); 394ddb55992SZi Shen Lim emit(A64_CMP(0, r3, tmp), ctx); 39516338a9bSDaniel Borkmann emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 396ddb55992SZi Shen Lim 397ebf7f6f0STiezhu Yang /* 398ebf7f6f0STiezhu Yang * if (tail_call_cnt >= MAX_TAIL_CALL_CNT) 399ddb55992SZi Shen Lim * goto out; 400ddb55992SZi Shen Lim * tail_call_cnt++; 401ddb55992SZi Shen Lim */ 402ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 403ddb55992SZi Shen Lim emit(A64_CMP(1, tcc, tmp), ctx); 404ebf7f6f0STiezhu Yang emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 405ddb55992SZi Shen Lim emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 406ddb55992SZi Shen Lim 407ddb55992SZi Shen Lim /* prog = array->ptrs[index]; 408ddb55992SZi Shen Lim * if (prog == NULL) 409ddb55992SZi Shen Lim * goto out; 410ddb55992SZi Shen Lim */ 411ddb55992SZi Shen Lim off = offsetof(struct bpf_array, ptrs); 412ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 413d8b54110SDaniel Borkmann emit(A64_ADD(1, tmp, r2, tmp), ctx); 414d8b54110SDaniel Borkmann emit(A64_LSL(1, prg, r3, 3), ctx); 415d8b54110SDaniel Borkmann emit(A64_LDR64(prg, tmp, prg), ctx); 416ddb55992SZi Shen Lim emit(A64_CBZ(1, prg, jmp_offset), ctx); 417ddb55992SZi Shen Lim 418a2284d91SDaniel Borkmann /* goto *(prog->bpf_func + prologue_offset); */ 419ddb55992SZi Shen Lim off = offsetof(struct bpf_prog, bpf_func); 420ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 421ddb55992SZi Shen Lim emit(A64_LDR64(tmp, prg, tmp), ctx); 422ddb55992SZi Shen Lim emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 423a2284d91SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 424ddb55992SZi Shen Lim emit(A64_BR(tmp), ctx); 425ddb55992SZi Shen Lim 426ddb55992SZi Shen Lim /* out: */ 427ddb55992SZi Shen Lim if (out_offset == -1) 428ddb55992SZi Shen Lim out_offset = cur_offset; 429ddb55992SZi Shen Lim if (cur_offset != out_offset) { 430ddb55992SZi Shen Lim pr_err_once("tail_call out_offset = %d, expected %d!\n", 431ddb55992SZi Shen Lim cur_offset, out_offset); 432ddb55992SZi Shen Lim return -1; 433ddb55992SZi Shen Lim } 434ddb55992SZi Shen Lim return 0; 435ddb55992SZi Shen Lim #undef cur_offset 436ddb55992SZi Shen Lim #undef jmp_offset 437e54bcde3SZi Shen Lim } 438e54bcde3SZi Shen Lim 4391902472bSHou Tao #ifdef CONFIG_ARM64_LSE_ATOMICS 4401902472bSHou Tao static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 4411902472bSHou Tao { 4421902472bSHou Tao const u8 code = insn->code; 4431902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 4441902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 4451902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 4461902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 4471902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 4481902472bSHou Tao const s16 off = insn->off; 4491902472bSHou Tao u8 reg; 4501902472bSHou Tao 4511902472bSHou Tao if (!off) { 4521902472bSHou Tao reg = dst; 4531902472bSHou Tao } else { 4541902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 4551902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 4561902472bSHou Tao reg = tmp; 4571902472bSHou Tao } 4581902472bSHou Tao 4591902472bSHou Tao switch (insn->imm) { 4601902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 4611902472bSHou Tao case BPF_ADD: 4621902472bSHou Tao emit(A64_STADD(isdw, reg, src), ctx); 4631902472bSHou Tao break; 4641902472bSHou Tao case BPF_AND: 4651902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 4661902472bSHou Tao emit(A64_STCLR(isdw, reg, tmp2), ctx); 4671902472bSHou Tao break; 4681902472bSHou Tao case BPF_OR: 4691902472bSHou Tao emit(A64_STSET(isdw, reg, src), ctx); 4701902472bSHou Tao break; 4711902472bSHou Tao case BPF_XOR: 4721902472bSHou Tao emit(A64_STEOR(isdw, reg, src), ctx); 4731902472bSHou Tao break; 4741902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 4751902472bSHou Tao case BPF_ADD | BPF_FETCH: 4761902472bSHou Tao emit(A64_LDADDAL(isdw, src, reg, src), ctx); 4771902472bSHou Tao break; 4781902472bSHou Tao case BPF_AND | BPF_FETCH: 4791902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 4801902472bSHou Tao emit(A64_LDCLRAL(isdw, src, reg, tmp2), ctx); 4811902472bSHou Tao break; 4821902472bSHou Tao case BPF_OR | BPF_FETCH: 4831902472bSHou Tao emit(A64_LDSETAL(isdw, src, reg, src), ctx); 4841902472bSHou Tao break; 4851902472bSHou Tao case BPF_XOR | BPF_FETCH: 4861902472bSHou Tao emit(A64_LDEORAL(isdw, src, reg, src), ctx); 4871902472bSHou Tao break; 4881902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 4891902472bSHou Tao case BPF_XCHG: 4901902472bSHou Tao emit(A64_SWPAL(isdw, src, reg, src), ctx); 4911902472bSHou Tao break; 4921902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 4931902472bSHou Tao case BPF_CMPXCHG: 4941902472bSHou Tao emit(A64_CASAL(isdw, src, reg, bpf2a64[BPF_REG_0]), ctx); 4951902472bSHou Tao break; 4961902472bSHou Tao default: 4971902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", insn->imm); 4981902472bSHou Tao return -EINVAL; 4991902472bSHou Tao } 5001902472bSHou Tao 5011902472bSHou Tao return 0; 5021902472bSHou Tao } 5031902472bSHou Tao #else 5041902472bSHou Tao static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5051902472bSHou Tao { 5061902472bSHou Tao return -EINVAL; 5071902472bSHou Tao } 5081902472bSHou Tao #endif 5091902472bSHou Tao 5101902472bSHou Tao static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5111902472bSHou Tao { 5121902472bSHou Tao const u8 code = insn->code; 5131902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 5141902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 5151902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 5161902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 5171902472bSHou Tao const u8 tmp3 = bpf2a64[TMP_REG_3]; 5181902472bSHou Tao const int i = insn - ctx->prog->insnsi; 5191902472bSHou Tao const s32 imm = insn->imm; 5201902472bSHou Tao const s16 off = insn->off; 5211902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 5221902472bSHou Tao u8 reg; 5231902472bSHou Tao s32 jmp_offset; 5241902472bSHou Tao 5251902472bSHou Tao if (!off) { 5261902472bSHou Tao reg = dst; 5271902472bSHou Tao } else { 5281902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 5291902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 5301902472bSHou Tao reg = tmp; 5311902472bSHou Tao } 5321902472bSHou Tao 5331902472bSHou Tao if (imm == BPF_ADD || imm == BPF_AND || 5341902472bSHou Tao imm == BPF_OR || imm == BPF_XOR) { 5351902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 5361902472bSHou Tao emit(A64_LDXR(isdw, tmp2, reg), ctx); 5371902472bSHou Tao if (imm == BPF_ADD) 5381902472bSHou Tao emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 5391902472bSHou Tao else if (imm == BPF_AND) 5401902472bSHou Tao emit(A64_AND(isdw, tmp2, tmp2, src), ctx); 5411902472bSHou Tao else if (imm == BPF_OR) 5421902472bSHou Tao emit(A64_ORR(isdw, tmp2, tmp2, src), ctx); 5431902472bSHou Tao else 5441902472bSHou Tao emit(A64_EOR(isdw, tmp2, tmp2, src), ctx); 5451902472bSHou Tao emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); 5461902472bSHou Tao jmp_offset = -3; 5471902472bSHou Tao check_imm19(jmp_offset); 5481902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5491902472bSHou Tao } else if (imm == (BPF_ADD | BPF_FETCH) || 5501902472bSHou Tao imm == (BPF_AND | BPF_FETCH) || 5511902472bSHou Tao imm == (BPF_OR | BPF_FETCH) || 5521902472bSHou Tao imm == (BPF_XOR | BPF_FETCH)) { 5531902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 5541902472bSHou Tao const u8 ax = bpf2a64[BPF_REG_AX]; 5551902472bSHou Tao 5561902472bSHou Tao emit(A64_MOV(isdw, ax, src), ctx); 5571902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 5581902472bSHou Tao if (imm == (BPF_ADD | BPF_FETCH)) 5591902472bSHou Tao emit(A64_ADD(isdw, tmp2, src, ax), ctx); 5601902472bSHou Tao else if (imm == (BPF_AND | BPF_FETCH)) 5611902472bSHou Tao emit(A64_AND(isdw, tmp2, src, ax), ctx); 5621902472bSHou Tao else if (imm == (BPF_OR | BPF_FETCH)) 5631902472bSHou Tao emit(A64_ORR(isdw, tmp2, src, ax), ctx); 5641902472bSHou Tao else 5651902472bSHou Tao emit(A64_EOR(isdw, tmp2, src, ax), ctx); 5661902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 5671902472bSHou Tao jmp_offset = -3; 5681902472bSHou Tao check_imm19(jmp_offset); 5691902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5701902472bSHou Tao emit(A64_DMB_ISH, ctx); 5711902472bSHou Tao } else if (imm == BPF_XCHG) { 5721902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 5731902472bSHou Tao emit(A64_MOV(isdw, tmp2, src), ctx); 5741902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 5751902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 5761902472bSHou Tao jmp_offset = -2; 5771902472bSHou Tao check_imm19(jmp_offset); 5781902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5791902472bSHou Tao emit(A64_DMB_ISH, ctx); 5801902472bSHou Tao } else if (imm == BPF_CMPXCHG) { 5811902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 5821902472bSHou Tao const u8 r0 = bpf2a64[BPF_REG_0]; 5831902472bSHou Tao 5841902472bSHou Tao emit(A64_MOV(isdw, tmp2, r0), ctx); 5851902472bSHou Tao emit(A64_LDXR(isdw, r0, reg), ctx); 5861902472bSHou Tao emit(A64_EOR(isdw, tmp3, r0, tmp2), ctx); 5871902472bSHou Tao jmp_offset = 4; 5881902472bSHou Tao check_imm19(jmp_offset); 5891902472bSHou Tao emit(A64_CBNZ(isdw, tmp3, jmp_offset), ctx); 5901902472bSHou Tao emit(A64_STLXR(isdw, src, reg, tmp3), ctx); 5911902472bSHou Tao jmp_offset = -4; 5921902472bSHou Tao check_imm19(jmp_offset); 5931902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5941902472bSHou Tao emit(A64_DMB_ISH, ctx); 5951902472bSHou Tao } else { 5961902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", imm); 5971902472bSHou Tao return -EINVAL; 5981902472bSHou Tao } 5991902472bSHou Tao 6001902472bSHou Tao return 0; 6011902472bSHou Tao } 6021902472bSHou Tao 603b2ad54e1SXu Kuohai void dummy_tramp(void); 604b2ad54e1SXu Kuohai 605b2ad54e1SXu Kuohai asm ( 606b2ad54e1SXu Kuohai " .pushsection .text, \"ax\", @progbits\n" 60733f32e50SNathan Chancellor " .global dummy_tramp\n" 608b2ad54e1SXu Kuohai " .type dummy_tramp, %function\n" 609b2ad54e1SXu Kuohai "dummy_tramp:" 610b2ad54e1SXu Kuohai #if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) 611b2ad54e1SXu Kuohai " bti j\n" /* dummy_tramp is called via "br x10" */ 612b2ad54e1SXu Kuohai #endif 613*339ed900SXu Kuohai " mov x10, x30\n" 614*339ed900SXu Kuohai " mov x30, x9\n" 615b2ad54e1SXu Kuohai " ret x10\n" 616b2ad54e1SXu Kuohai " .size dummy_tramp, .-dummy_tramp\n" 617b2ad54e1SXu Kuohai " .popsection\n" 618b2ad54e1SXu Kuohai ); 619b2ad54e1SXu Kuohai 620b2ad54e1SXu Kuohai /* build a plt initialized like this: 621b2ad54e1SXu Kuohai * 622b2ad54e1SXu Kuohai * plt: 623b2ad54e1SXu Kuohai * ldr tmp, target 624b2ad54e1SXu Kuohai * br tmp 625b2ad54e1SXu Kuohai * target: 626b2ad54e1SXu Kuohai * .quad dummy_tramp 627b2ad54e1SXu Kuohai * 628b2ad54e1SXu Kuohai * when a long jump trampoline is attached, target is filled with the 629b2ad54e1SXu Kuohai * trampoline address, and when the trampoline is removed, target is 630b2ad54e1SXu Kuohai * restored to dummy_tramp address. 631b2ad54e1SXu Kuohai */ 632b2ad54e1SXu Kuohai static void build_plt(struct jit_ctx *ctx) 633b2ad54e1SXu Kuohai { 634b2ad54e1SXu Kuohai const u8 tmp = bpf2a64[TMP_REG_1]; 635b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 636b2ad54e1SXu Kuohai 637b2ad54e1SXu Kuohai /* make sure target is 64-bit aligned */ 638b2ad54e1SXu Kuohai if ((ctx->idx + PLT_TARGET_OFFSET / AARCH64_INSN_SIZE) % 2) 639b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 640b2ad54e1SXu Kuohai 641b2ad54e1SXu Kuohai plt = (struct bpf_plt *)(ctx->image + ctx->idx); 642b2ad54e1SXu Kuohai /* plt is called via bl, no BTI needed here */ 643b2ad54e1SXu Kuohai emit(A64_LDR64LIT(tmp, 2 * AARCH64_INSN_SIZE), ctx); 644b2ad54e1SXu Kuohai emit(A64_BR(tmp), ctx); 645b2ad54e1SXu Kuohai 646b2ad54e1SXu Kuohai if (ctx->image) 647b2ad54e1SXu Kuohai plt->target = (u64)&dummy_tramp; 648b2ad54e1SXu Kuohai } 649b2ad54e1SXu Kuohai 650e54bcde3SZi Shen Lim static void build_epilogue(struct jit_ctx *ctx) 651e54bcde3SZi Shen Lim { 652e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 653e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 654e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 655e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 656e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 657e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 6585b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 659e54bcde3SZi Shen Lim 660e54bcde3SZi Shen Lim /* We're done with BPF stack */ 661f1c9eed7SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 662e54bcde3SZi Shen Lim 6635b3d19b9SXu Kuohai /* Restore x27 and x28 */ 6645b3d19b9SXu Kuohai emit(A64_POP(fpb, A64_R(28), A64_SP), ctx); 665ec0738dbSYang Shi /* Restore fs (x25) and x26 */ 666ec0738dbSYang Shi emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 667ec0738dbSYang Shi 668e54bcde3SZi Shen Lim /* Restore callee-saved register */ 669e54bcde3SZi Shen Lim emit(A64_POP(r8, r9, A64_SP), ctx); 670e54bcde3SZi Shen Lim emit(A64_POP(r6, r7, A64_SP), ctx); 671e54bcde3SZi Shen Lim 672ec0738dbSYang Shi /* Restore FP/LR registers */ 673ec0738dbSYang Shi emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 674e54bcde3SZi Shen Lim 675e54bcde3SZi Shen Lim /* Set return value */ 676e54bcde3SZi Shen Lim emit(A64_MOV(1, A64_R(0), r0), ctx); 677e54bcde3SZi Shen Lim 678042152c2SXu Kuohai /* Authenticate lr */ 679042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 680042152c2SXu Kuohai emit(A64_AUTIASP, ctx); 681042152c2SXu Kuohai 682e54bcde3SZi Shen Lim emit(A64_RET(A64_LR), ctx); 683e54bcde3SZi Shen Lim } 684e54bcde3SZi Shen Lim 68580083428SJean-Philippe Brucker #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) 68680083428SJean-Philippe Brucker #define BPF_FIXUP_REG_MASK GENMASK(31, 27) 68780083428SJean-Philippe Brucker 688d6e2cc56SMark Rutland bool ex_handler_bpf(const struct exception_table_entry *ex, 68980083428SJean-Philippe Brucker struct pt_regs *regs) 69080083428SJean-Philippe Brucker { 69180083428SJean-Philippe Brucker off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup); 69280083428SJean-Philippe Brucker int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup); 69380083428SJean-Philippe Brucker 69480083428SJean-Philippe Brucker regs->regs[dst_reg] = 0; 69580083428SJean-Philippe Brucker regs->pc = (unsigned long)&ex->fixup - offset; 696e8c328d7SMark Rutland return true; 69780083428SJean-Philippe Brucker } 69880083428SJean-Philippe Brucker 69980083428SJean-Philippe Brucker /* For accesses to BTF pointers, add an entry to the exception table */ 70080083428SJean-Philippe Brucker static int add_exception_handler(const struct bpf_insn *insn, 70180083428SJean-Philippe Brucker struct jit_ctx *ctx, 70280083428SJean-Philippe Brucker int dst_reg) 70380083428SJean-Philippe Brucker { 70480083428SJean-Philippe Brucker off_t offset; 70580083428SJean-Philippe Brucker unsigned long pc; 70680083428SJean-Philippe Brucker struct exception_table_entry *ex; 70780083428SJean-Philippe Brucker 70880083428SJean-Philippe Brucker if (!ctx->image) 70980083428SJean-Philippe Brucker /* First pass */ 71080083428SJean-Philippe Brucker return 0; 71180083428SJean-Philippe Brucker 71280083428SJean-Philippe Brucker if (BPF_MODE(insn->code) != BPF_PROBE_MEM) 71380083428SJean-Philippe Brucker return 0; 71480083428SJean-Philippe Brucker 71580083428SJean-Philippe Brucker if (!ctx->prog->aux->extable || 71680083428SJean-Philippe Brucker WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries)) 71780083428SJean-Philippe Brucker return -EINVAL; 71880083428SJean-Philippe Brucker 71980083428SJean-Philippe Brucker ex = &ctx->prog->aux->extable[ctx->exentry_idx]; 72080083428SJean-Philippe Brucker pc = (unsigned long)&ctx->image[ctx->idx - 1]; 72180083428SJean-Philippe Brucker 72280083428SJean-Philippe Brucker offset = pc - (long)&ex->insn; 72380083428SJean-Philippe Brucker if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN)) 72480083428SJean-Philippe Brucker return -ERANGE; 72580083428SJean-Philippe Brucker ex->insn = offset; 72680083428SJean-Philippe Brucker 72780083428SJean-Philippe Brucker /* 72880083428SJean-Philippe Brucker * Since the extable follows the program, the fixup offset is always 72980083428SJean-Philippe Brucker * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value 73080083428SJean-Philippe Brucker * to keep things simple, and put the destination register in the upper 73180083428SJean-Philippe Brucker * bits. We don't need to worry about buildtime or runtime sort 73280083428SJean-Philippe Brucker * modifying the upper bits because the table is already sorted, and 73380083428SJean-Philippe Brucker * isn't part of the main exception table. 73480083428SJean-Philippe Brucker */ 73580083428SJean-Philippe Brucker offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE); 73680083428SJean-Philippe Brucker if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset)) 73780083428SJean-Philippe Brucker return -ERANGE; 73880083428SJean-Philippe Brucker 73980083428SJean-Philippe Brucker ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) | 74080083428SJean-Philippe Brucker FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); 74180083428SJean-Philippe Brucker 742d6e2cc56SMark Rutland ex->type = EX_TYPE_BPF; 743d6e2cc56SMark Rutland 74480083428SJean-Philippe Brucker ctx->exentry_idx++; 74580083428SJean-Philippe Brucker return 0; 74680083428SJean-Philippe Brucker } 74780083428SJean-Philippe Brucker 74830d3d94cSZi Shen Lim /* JITs an eBPF instruction. 74930d3d94cSZi Shen Lim * Returns: 75030d3d94cSZi Shen Lim * 0 - successfully JITed an 8-byte eBPF instruction. 75130d3d94cSZi Shen Lim * >0 - successfully JITed a 16-byte eBPF instruction. 75230d3d94cSZi Shen Lim * <0 - failed to JIT. 75330d3d94cSZi Shen Lim */ 7548c11ea5cSDaniel Borkmann static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, 7558c11ea5cSDaniel Borkmann bool extra_pass) 756e54bcde3SZi Shen Lim { 757e54bcde3SZi Shen Lim const u8 code = insn->code; 758e54bcde3SZi Shen Lim const u8 dst = bpf2a64[insn->dst_reg]; 759e54bcde3SZi Shen Lim const u8 src = bpf2a64[insn->src_reg]; 760e54bcde3SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 761e54bcde3SZi Shen Lim const u8 tmp2 = bpf2a64[TMP_REG_2]; 7625b3d19b9SXu Kuohai const u8 fp = bpf2a64[BPF_REG_FP]; 7635b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 764e54bcde3SZi Shen Lim const s16 off = insn->off; 765e54bcde3SZi Shen Lim const s32 imm = insn->imm; 766e54bcde3SZi Shen Lim const int i = insn - ctx->prog->insnsi; 767654b65a0SJiong Wang const bool is64 = BPF_CLASS(code) == BPF_ALU64 || 768654b65a0SJiong Wang BPF_CLASS(code) == BPF_JMP; 7691902472bSHou Tao u8 jmp_cond; 770e54bcde3SZi Shen Lim s32 jmp_offset; 771fd49591cSLuke Nelson u32 a64_insn; 7725b3d19b9SXu Kuohai u8 src_adj; 7735b3d19b9SXu Kuohai u8 dst_adj; 7745b3d19b9SXu Kuohai int off_adj; 77580083428SJean-Philippe Brucker int ret; 776e54bcde3SZi Shen Lim 777e54bcde3SZi Shen Lim switch (code) { 778e54bcde3SZi Shen Lim /* dst = src */ 779e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_X: 780e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_X: 781e54bcde3SZi Shen Lim emit(A64_MOV(is64, dst, src), ctx); 782e54bcde3SZi Shen Lim break; 783e54bcde3SZi Shen Lim /* dst = dst OP src */ 784e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_X: 785e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_X: 786e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, src), ctx); 787e54bcde3SZi Shen Lim break; 788e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_X: 789e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_X: 790e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, src), ctx); 791e54bcde3SZi Shen Lim break; 792e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_X: 793e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_X: 794e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, src), ctx); 795e54bcde3SZi Shen Lim break; 796e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_X: 797e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_X: 798e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, src), ctx); 799e54bcde3SZi Shen Lim break; 800e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_X: 801e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_X: 802e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, src), ctx); 803e54bcde3SZi Shen Lim break; 804e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_X: 805e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_X: 806e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, src), ctx); 807e54bcde3SZi Shen Lim break; 808e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_X: 809e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_X: 810e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, src), ctx); 811e54bcde3SZi Shen Lim break; 812119220d8STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 813119220d8STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 814e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, src), ctx); 815504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, src), ctx); 816e54bcde3SZi Shen Lim break; 817d65a634aSZi Shen Lim case BPF_ALU | BPF_LSH | BPF_X: 818d65a634aSZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_X: 819d65a634aSZi Shen Lim emit(A64_LSLV(is64, dst, dst, src), ctx); 820d65a634aSZi Shen Lim break; 821d65a634aSZi Shen Lim case BPF_ALU | BPF_RSH | BPF_X: 822d65a634aSZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_X: 823d65a634aSZi Shen Lim emit(A64_LSRV(is64, dst, dst, src), ctx); 824d65a634aSZi Shen Lim break; 825d65a634aSZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_X: 826d65a634aSZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_X: 827d65a634aSZi Shen Lim emit(A64_ASRV(is64, dst, dst, src), ctx); 828d65a634aSZi Shen Lim break; 829e54bcde3SZi Shen Lim /* dst = -dst */ 830e54bcde3SZi Shen Lim case BPF_ALU | BPF_NEG: 831e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_NEG: 832e54bcde3SZi Shen Lim emit(A64_NEG(is64, dst, dst), ctx); 833e54bcde3SZi Shen Lim break; 834e54bcde3SZi Shen Lim /* dst = BSWAP##imm(dst) */ 835e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_LE: 836e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_BE: 837e54bcde3SZi Shen Lim #ifdef CONFIG_CPU_BIG_ENDIAN 838e54bcde3SZi Shen Lim if (BPF_SRC(code) == BPF_FROM_BE) 839d63903bbSXi Wang goto emit_bswap_uxt; 840e54bcde3SZi Shen Lim #else /* !CONFIG_CPU_BIG_ENDIAN */ 841e54bcde3SZi Shen Lim if (BPF_SRC(code) == BPF_FROM_LE) 842d63903bbSXi Wang goto emit_bswap_uxt; 843e54bcde3SZi Shen Lim #endif 844e54bcde3SZi Shen Lim switch (imm) { 845e54bcde3SZi Shen Lim case 16: 846e54bcde3SZi Shen Lim emit(A64_REV16(is64, dst, dst), ctx); 847d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 848d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 849e54bcde3SZi Shen Lim break; 850e54bcde3SZi Shen Lim case 32: 851e54bcde3SZi Shen Lim emit(A64_REV32(is64, dst, dst), ctx); 852d63903bbSXi Wang /* upper 32 bits already cleared */ 853e54bcde3SZi Shen Lim break; 854e54bcde3SZi Shen Lim case 64: 855e54bcde3SZi Shen Lim emit(A64_REV64(dst, dst), ctx); 856e54bcde3SZi Shen Lim break; 857e54bcde3SZi Shen Lim } 858e54bcde3SZi Shen Lim break; 859d63903bbSXi Wang emit_bswap_uxt: 860d63903bbSXi Wang switch (imm) { 861d63903bbSXi Wang case 16: 862d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 863d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 864d63903bbSXi Wang break; 865d63903bbSXi Wang case 32: 866d63903bbSXi Wang /* zero-extend 32 bits into 64 bits */ 867d63903bbSXi Wang emit(A64_UXTW(is64, dst, dst), ctx); 868d63903bbSXi Wang break; 869d63903bbSXi Wang case 64: 870d63903bbSXi Wang /* nop */ 871d63903bbSXi Wang break; 872d63903bbSXi Wang } 873d63903bbSXi Wang break; 874e54bcde3SZi Shen Lim /* dst = imm */ 875e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_K: 876e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_K: 877e54bcde3SZi Shen Lim emit_a64_mov_i(is64, dst, imm, ctx); 878e54bcde3SZi Shen Lim break; 879e54bcde3SZi Shen Lim /* dst = dst OP imm */ 880e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_K: 881e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_K: 882fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 883fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, imm), ctx); 884fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 885fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, -imm), ctx); 886fd868f14SLuke Nelson } else { 887e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 888e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, tmp), ctx); 889fd868f14SLuke Nelson } 890e54bcde3SZi Shen Lim break; 891e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_K: 892e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_K: 893fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 894fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, imm), ctx); 895fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 896fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, -imm), ctx); 897fd868f14SLuke Nelson } else { 898e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 899e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 900fd868f14SLuke Nelson } 901e54bcde3SZi Shen Lim break; 902e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_K: 903e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_K: 904fd49591cSLuke Nelson a64_insn = A64_AND_I(is64, dst, dst, imm); 905fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 906fd49591cSLuke Nelson emit(a64_insn, ctx); 907fd49591cSLuke Nelson } else { 908e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 909e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, tmp), ctx); 910fd49591cSLuke Nelson } 911e54bcde3SZi Shen Lim break; 912e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_K: 913e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_K: 914fd49591cSLuke Nelson a64_insn = A64_ORR_I(is64, dst, dst, imm); 915fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 916fd49591cSLuke Nelson emit(a64_insn, ctx); 917fd49591cSLuke Nelson } else { 918e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 919e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, tmp), ctx); 920fd49591cSLuke Nelson } 921e54bcde3SZi Shen Lim break; 922e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_K: 923e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_K: 924fd49591cSLuke Nelson a64_insn = A64_EOR_I(is64, dst, dst, imm); 925fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 926fd49591cSLuke Nelson emit(a64_insn, ctx); 927fd49591cSLuke Nelson } else { 928e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 929e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, tmp), ctx); 930fd49591cSLuke Nelson } 931e54bcde3SZi Shen Lim break; 932e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_K: 933e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_K: 934e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 935e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, tmp), ctx); 936e54bcde3SZi Shen Lim break; 937e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_K: 938e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_K: 939e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 940e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, tmp), ctx); 941e54bcde3SZi Shen Lim break; 942e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_K: 943e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_K: 944e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp2, imm, ctx); 945e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 946504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, tmp2), ctx); 947e54bcde3SZi Shen Lim break; 948e54bcde3SZi Shen Lim case BPF_ALU | BPF_LSH | BPF_K: 949e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_K: 950e54bcde3SZi Shen Lim emit(A64_LSL(is64, dst, dst, imm), ctx); 951e54bcde3SZi Shen Lim break; 952e54bcde3SZi Shen Lim case BPF_ALU | BPF_RSH | BPF_K: 953e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_K: 954e54bcde3SZi Shen Lim emit(A64_LSR(is64, dst, dst, imm), ctx); 955e54bcde3SZi Shen Lim break; 956e54bcde3SZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_K: 957e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_K: 958e54bcde3SZi Shen Lim emit(A64_ASR(is64, dst, dst, imm), ctx); 959e54bcde3SZi Shen Lim break; 960e54bcde3SZi Shen Lim 961e54bcde3SZi Shen Lim /* JUMP off */ 962e54bcde3SZi Shen Lim case BPF_JMP | BPF_JA: 96332f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 964e54bcde3SZi Shen Lim check_imm26(jmp_offset); 965e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 966e54bcde3SZi Shen Lim break; 967e54bcde3SZi Shen Lim /* IF (dst COND src) JUMP off */ 968e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_X: 969e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_X: 970c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_X: 971e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_X: 972c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_X: 973e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_X: 974e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_X: 975c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_X: 976e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_X: 977c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_X: 978654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_X: 979654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_X: 980654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_X: 981654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_X: 982654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_X: 983654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_X: 984654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_X: 985654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_X: 986654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_X: 987654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_X: 988654b65a0SJiong Wang emit(A64_CMP(is64, dst, src), ctx); 989e54bcde3SZi Shen Lim emit_cond_jmp: 99032f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 991e54bcde3SZi Shen Lim check_imm19(jmp_offset); 992e54bcde3SZi Shen Lim switch (BPF_OP(code)) { 993e54bcde3SZi Shen Lim case BPF_JEQ: 994e54bcde3SZi Shen Lim jmp_cond = A64_COND_EQ; 995e54bcde3SZi Shen Lim break; 996e54bcde3SZi Shen Lim case BPF_JGT: 997e54bcde3SZi Shen Lim jmp_cond = A64_COND_HI; 998e54bcde3SZi Shen Lim break; 999c362b2f3SDaniel Borkmann case BPF_JLT: 1000c362b2f3SDaniel Borkmann jmp_cond = A64_COND_CC; 1001c362b2f3SDaniel Borkmann break; 1002e54bcde3SZi Shen Lim case BPF_JGE: 1003e54bcde3SZi Shen Lim jmp_cond = A64_COND_CS; 1004e54bcde3SZi Shen Lim break; 1005c362b2f3SDaniel Borkmann case BPF_JLE: 1006c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LS; 1007c362b2f3SDaniel Borkmann break; 100898397fc5SZi Shen Lim case BPF_JSET: 1009e54bcde3SZi Shen Lim case BPF_JNE: 1010e54bcde3SZi Shen Lim jmp_cond = A64_COND_NE; 1011e54bcde3SZi Shen Lim break; 1012e54bcde3SZi Shen Lim case BPF_JSGT: 1013e54bcde3SZi Shen Lim jmp_cond = A64_COND_GT; 1014e54bcde3SZi Shen Lim break; 1015c362b2f3SDaniel Borkmann case BPF_JSLT: 1016c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LT; 1017c362b2f3SDaniel Borkmann break; 1018e54bcde3SZi Shen Lim case BPF_JSGE: 1019e54bcde3SZi Shen Lim jmp_cond = A64_COND_GE; 1020e54bcde3SZi Shen Lim break; 1021c362b2f3SDaniel Borkmann case BPF_JSLE: 1022c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LE; 1023c362b2f3SDaniel Borkmann break; 1024e54bcde3SZi Shen Lim default: 1025e54bcde3SZi Shen Lim return -EFAULT; 1026e54bcde3SZi Shen Lim } 1027e54bcde3SZi Shen Lim emit(A64_B_(jmp_cond, jmp_offset), ctx); 1028e54bcde3SZi Shen Lim break; 1029e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_X: 1030654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_X: 1031654b65a0SJiong Wang emit(A64_TST(is64, dst, src), ctx); 1032e54bcde3SZi Shen Lim goto emit_cond_jmp; 1033e54bcde3SZi Shen Lim /* IF (dst COND imm) JUMP off */ 1034e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_K: 1035e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_K: 1036c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_K: 1037e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_K: 1038c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_K: 1039e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_K: 1040e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_K: 1041c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_K: 1042e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_K: 1043c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_K: 1044654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_K: 1045654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_K: 1046654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_K: 1047654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_K: 1048654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_K: 1049654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_K: 1050654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_K: 1051654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_K: 1052654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_K: 1053654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_K: 1054fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1055fd868f14SLuke Nelson emit(A64_CMP_I(is64, dst, imm), ctx); 1056fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1057fd868f14SLuke Nelson emit(A64_CMN_I(is64, dst, -imm), ctx); 1058fd868f14SLuke Nelson } else { 1059654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1060654b65a0SJiong Wang emit(A64_CMP(is64, dst, tmp), ctx); 1061fd868f14SLuke Nelson } 1062e54bcde3SZi Shen Lim goto emit_cond_jmp; 1063e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_K: 1064654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_K: 1065fd49591cSLuke Nelson a64_insn = A64_TST_I(is64, dst, imm); 1066fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1067fd49591cSLuke Nelson emit(a64_insn, ctx); 1068fd49591cSLuke Nelson } else { 1069654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1070654b65a0SJiong Wang emit(A64_TST(is64, dst, tmp), ctx); 1071fd49591cSLuke Nelson } 1072e54bcde3SZi Shen Lim goto emit_cond_jmp; 1073e54bcde3SZi Shen Lim /* function call */ 1074e54bcde3SZi Shen Lim case BPF_JMP | BPF_CALL: 1075e54bcde3SZi Shen Lim { 1076e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 10778c11ea5cSDaniel Borkmann bool func_addr_fixed; 10788c11ea5cSDaniel Borkmann u64 func_addr; 1079e54bcde3SZi Shen Lim 10808c11ea5cSDaniel Borkmann ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 10818c11ea5cSDaniel Borkmann &func_addr, &func_addr_fixed); 10828c11ea5cSDaniel Borkmann if (ret < 0) 10838c11ea5cSDaniel Borkmann return ret; 1084efc9909fSXu Kuohai emit_call(func_addr, ctx); 1085e54bcde3SZi Shen Lim emit(A64_MOV(1, r0, A64_R(0)), ctx); 1086e54bcde3SZi Shen Lim break; 1087e54bcde3SZi Shen Lim } 1088ddb55992SZi Shen Lim /* tail call */ 108971189fa9SAlexei Starovoitov case BPF_JMP | BPF_TAIL_CALL: 1090ddb55992SZi Shen Lim if (emit_bpf_tail_call(ctx)) 1091ddb55992SZi Shen Lim return -EFAULT; 1092ddb55992SZi Shen Lim break; 1093e54bcde3SZi Shen Lim /* function return */ 1094e54bcde3SZi Shen Lim case BPF_JMP | BPF_EXIT: 109551c9fbb1SZi Shen Lim /* Optimization: when last instruction is EXIT, 109651c9fbb1SZi Shen Lim simply fallthrough to epilogue. */ 1097e54bcde3SZi Shen Lim if (i == ctx->prog->len - 1) 1098e54bcde3SZi Shen Lim break; 1099e54bcde3SZi Shen Lim jmp_offset = epilogue_offset(ctx); 1100e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1101e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1102e54bcde3SZi Shen Lim break; 1103e54bcde3SZi Shen Lim 110430d3d94cSZi Shen Lim /* dst = imm64 */ 110530d3d94cSZi Shen Lim case BPF_LD | BPF_IMM | BPF_DW: 110630d3d94cSZi Shen Lim { 110730d3d94cSZi Shen Lim const struct bpf_insn insn1 = insn[1]; 110830d3d94cSZi Shen Lim u64 imm64; 110930d3d94cSZi Shen Lim 11101e4df6b7SXi Wang imm64 = (u64)insn1.imm << 32 | (u32)imm; 1111e4a41c2cSHou Tao if (bpf_pseudo_func(insn)) 1112e4a41c2cSHou Tao emit_addr_mov_i64(dst, imm64, ctx); 1113e4a41c2cSHou Tao else 111430d3d94cSZi Shen Lim emit_a64_mov_i64(dst, imm64, ctx); 111530d3d94cSZi Shen Lim 111630d3d94cSZi Shen Lim return 1; 111730d3d94cSZi Shen Lim } 111830d3d94cSZi Shen Lim 1119e54bcde3SZi Shen Lim /* LDX: dst = *(size *)(src + off) */ 1120e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_W: 1121e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_H: 1122e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_B: 1123e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_DW: 112480083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 112580083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_W: 112680083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_H: 112780083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_B: 11285b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && src == fp) { 11295b3d19b9SXu Kuohai src_adj = fpb; 11305b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 11315b3d19b9SXu Kuohai } else { 11325b3d19b9SXu Kuohai src_adj = src; 11335b3d19b9SXu Kuohai off_adj = off; 11345b3d19b9SXu Kuohai } 1135e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1136e54bcde3SZi Shen Lim case BPF_W: 11375b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 11385b3d19b9SXu Kuohai emit(A64_LDR32I(dst, src_adj, off_adj), ctx); 11397db6c0f1SXu Kuohai } else { 11407db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1141e54bcde3SZi Shen Lim emit(A64_LDR32(dst, src, tmp), ctx); 11427db6c0f1SXu Kuohai } 1143e54bcde3SZi Shen Lim break; 1144e54bcde3SZi Shen Lim case BPF_H: 11455b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 11465b3d19b9SXu Kuohai emit(A64_LDRHI(dst, src_adj, off_adj), ctx); 11477db6c0f1SXu Kuohai } else { 11487db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1149e54bcde3SZi Shen Lim emit(A64_LDRH(dst, src, tmp), ctx); 11507db6c0f1SXu Kuohai } 1151e54bcde3SZi Shen Lim break; 1152e54bcde3SZi Shen Lim case BPF_B: 11535b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 11545b3d19b9SXu Kuohai emit(A64_LDRBI(dst, src_adj, off_adj), ctx); 11557db6c0f1SXu Kuohai } else { 11567db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1157e54bcde3SZi Shen Lim emit(A64_LDRB(dst, src, tmp), ctx); 11587db6c0f1SXu Kuohai } 1159e54bcde3SZi Shen Lim break; 1160e54bcde3SZi Shen Lim case BPF_DW: 11615b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 11625b3d19b9SXu Kuohai emit(A64_LDR64I(dst, src_adj, off_adj), ctx); 11637db6c0f1SXu Kuohai } else { 11647db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1165e54bcde3SZi Shen Lim emit(A64_LDR64(dst, src, tmp), ctx); 11667db6c0f1SXu Kuohai } 1167e54bcde3SZi Shen Lim break; 1168e54bcde3SZi Shen Lim } 116980083428SJean-Philippe Brucker 117080083428SJean-Philippe Brucker ret = add_exception_handler(insn, ctx, dst); 117180083428SJean-Philippe Brucker if (ret) 117280083428SJean-Philippe Brucker return ret; 1173e54bcde3SZi Shen Lim break; 1174e54bcde3SZi Shen Lim 1175f5e81d11SDaniel Borkmann /* speculation barrier */ 1176f5e81d11SDaniel Borkmann case BPF_ST | BPF_NOSPEC: 1177f5e81d11SDaniel Borkmann /* 1178f5e81d11SDaniel Borkmann * Nothing required here. 1179f5e81d11SDaniel Borkmann * 1180f5e81d11SDaniel Borkmann * In case of arm64, we rely on the firmware mitigation of 1181f5e81d11SDaniel Borkmann * Speculative Store Bypass as controlled via the ssbd kernel 1182f5e81d11SDaniel Borkmann * parameter. Whenever the mitigation is enabled, it works 1183f5e81d11SDaniel Borkmann * for all of the kernel code with no need to provide any 1184f5e81d11SDaniel Borkmann * additional instructions. 1185f5e81d11SDaniel Borkmann */ 1186f5e81d11SDaniel Borkmann break; 1187f5e81d11SDaniel Borkmann 1188e54bcde3SZi Shen Lim /* ST: *(size *)(dst + off) = imm */ 1189e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_W: 1190e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_H: 1191e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_B: 1192e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_DW: 11935b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && dst == fp) { 11945b3d19b9SXu Kuohai dst_adj = fpb; 11955b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 11965b3d19b9SXu Kuohai } else { 11975b3d19b9SXu Kuohai dst_adj = dst; 11985b3d19b9SXu Kuohai off_adj = off; 11995b3d19b9SXu Kuohai } 1200df849ba3SYang Shi /* Load imm to a register then store it */ 1201df849ba3SYang Shi emit_a64_mov_i(1, tmp, imm, ctx); 1202df849ba3SYang Shi switch (BPF_SIZE(code)) { 1203df849ba3SYang Shi case BPF_W: 12045b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 12055b3d19b9SXu Kuohai emit(A64_STR32I(tmp, dst_adj, off_adj), ctx); 12067db6c0f1SXu Kuohai } else { 12077db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1208df849ba3SYang Shi emit(A64_STR32(tmp, dst, tmp2), ctx); 12097db6c0f1SXu Kuohai } 1210df849ba3SYang Shi break; 1211df849ba3SYang Shi case BPF_H: 12125b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 12135b3d19b9SXu Kuohai emit(A64_STRHI(tmp, dst_adj, off_adj), ctx); 12147db6c0f1SXu Kuohai } else { 12157db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1216df849ba3SYang Shi emit(A64_STRH(tmp, dst, tmp2), ctx); 12177db6c0f1SXu Kuohai } 1218df849ba3SYang Shi break; 1219df849ba3SYang Shi case BPF_B: 12205b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 12215b3d19b9SXu Kuohai emit(A64_STRBI(tmp, dst_adj, off_adj), ctx); 12227db6c0f1SXu Kuohai } else { 12237db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1224df849ba3SYang Shi emit(A64_STRB(tmp, dst, tmp2), ctx); 12257db6c0f1SXu Kuohai } 1226df849ba3SYang Shi break; 1227df849ba3SYang Shi case BPF_DW: 12285b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 12295b3d19b9SXu Kuohai emit(A64_STR64I(tmp, dst_adj, off_adj), ctx); 12307db6c0f1SXu Kuohai } else { 12317db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1232df849ba3SYang Shi emit(A64_STR64(tmp, dst, tmp2), ctx); 12337db6c0f1SXu Kuohai } 1234df849ba3SYang Shi break; 1235df849ba3SYang Shi } 1236df849ba3SYang Shi break; 1237e54bcde3SZi Shen Lim 1238e54bcde3SZi Shen Lim /* STX: *(size *)(dst + off) = src */ 1239e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_W: 1240e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_H: 1241e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_B: 1242e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_DW: 12435b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && dst == fp) { 12445b3d19b9SXu Kuohai dst_adj = fpb; 12455b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 12465b3d19b9SXu Kuohai } else { 12475b3d19b9SXu Kuohai dst_adj = dst; 12485b3d19b9SXu Kuohai off_adj = off; 12495b3d19b9SXu Kuohai } 1250e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1251e54bcde3SZi Shen Lim case BPF_W: 12525b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 12535b3d19b9SXu Kuohai emit(A64_STR32I(src, dst_adj, off_adj), ctx); 12547db6c0f1SXu Kuohai } else { 12557db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1256e54bcde3SZi Shen Lim emit(A64_STR32(src, dst, tmp), ctx); 12577db6c0f1SXu Kuohai } 1258e54bcde3SZi Shen Lim break; 1259e54bcde3SZi Shen Lim case BPF_H: 12605b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 12615b3d19b9SXu Kuohai emit(A64_STRHI(src, dst_adj, off_adj), ctx); 12627db6c0f1SXu Kuohai } else { 12637db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1264e54bcde3SZi Shen Lim emit(A64_STRH(src, dst, tmp), ctx); 12657db6c0f1SXu Kuohai } 1266e54bcde3SZi Shen Lim break; 1267e54bcde3SZi Shen Lim case BPF_B: 12685b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 12695b3d19b9SXu Kuohai emit(A64_STRBI(src, dst_adj, off_adj), ctx); 12707db6c0f1SXu Kuohai } else { 12717db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1272e54bcde3SZi Shen Lim emit(A64_STRB(src, dst, tmp), ctx); 12737db6c0f1SXu Kuohai } 1274e54bcde3SZi Shen Lim break; 1275e54bcde3SZi Shen Lim case BPF_DW: 12765b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 12775b3d19b9SXu Kuohai emit(A64_STR64I(src, dst_adj, off_adj), ctx); 12787db6c0f1SXu Kuohai } else { 12797db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1280e54bcde3SZi Shen Lim emit(A64_STR64(src, dst, tmp), ctx); 12817db6c0f1SXu Kuohai } 1282e54bcde3SZi Shen Lim break; 1283e54bcde3SZi Shen Lim } 1284e54bcde3SZi Shen Lim break; 128534b8ab09SDaniel Borkmann 128691c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_W: 128791c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_DW: 12881902472bSHou Tao if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) 12891902472bSHou Tao ret = emit_lse_atomic(insn, ctx); 12901902472bSHou Tao else 12911902472bSHou Tao ret = emit_ll_sc_atomic(insn, ctx); 12921902472bSHou Tao if (ret) 12931902472bSHou Tao return ret; 129485f68fe8SDaniel Borkmann break; 1295e54bcde3SZi Shen Lim 1296e54bcde3SZi Shen Lim default: 1297e54bcde3SZi Shen Lim pr_err_once("unknown opcode %02x\n", code); 1298e54bcde3SZi Shen Lim return -EINVAL; 1299e54bcde3SZi Shen Lim } 1300e54bcde3SZi Shen Lim 1301e54bcde3SZi Shen Lim return 0; 1302e54bcde3SZi Shen Lim } 1303e54bcde3SZi Shen Lim 13045b3d19b9SXu Kuohai /* 13055b3d19b9SXu Kuohai * Return 0 if FP may change at runtime, otherwise find the minimum negative 13065b3d19b9SXu Kuohai * offset to FP, converts it to positive number, and align down to 8 bytes. 13075b3d19b9SXu Kuohai */ 13085b3d19b9SXu Kuohai static int find_fpb_offset(struct bpf_prog *prog) 13095b3d19b9SXu Kuohai { 13105b3d19b9SXu Kuohai int i; 13115b3d19b9SXu Kuohai int offset = 0; 13125b3d19b9SXu Kuohai 13135b3d19b9SXu Kuohai for (i = 0; i < prog->len; i++) { 13145b3d19b9SXu Kuohai const struct bpf_insn *insn = &prog->insnsi[i]; 13155b3d19b9SXu Kuohai const u8 class = BPF_CLASS(insn->code); 13165b3d19b9SXu Kuohai const u8 mode = BPF_MODE(insn->code); 13175b3d19b9SXu Kuohai const u8 src = insn->src_reg; 13185b3d19b9SXu Kuohai const u8 dst = insn->dst_reg; 13195b3d19b9SXu Kuohai const s32 imm = insn->imm; 13205b3d19b9SXu Kuohai const s16 off = insn->off; 13215b3d19b9SXu Kuohai 13225b3d19b9SXu Kuohai switch (class) { 13235b3d19b9SXu Kuohai case BPF_STX: 13245b3d19b9SXu Kuohai case BPF_ST: 13255b3d19b9SXu Kuohai /* fp holds atomic operation result */ 13265b3d19b9SXu Kuohai if (class == BPF_STX && mode == BPF_ATOMIC && 13275b3d19b9SXu Kuohai ((imm == BPF_XCHG || 13285b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_ADD) || 13295b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_AND) || 13305b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_XOR) || 13315b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_OR)) && 13325b3d19b9SXu Kuohai src == BPF_REG_FP)) 13335b3d19b9SXu Kuohai return 0; 13345b3d19b9SXu Kuohai 13355b3d19b9SXu Kuohai if (mode == BPF_MEM && dst == BPF_REG_FP && 13365b3d19b9SXu Kuohai off < offset) 13375b3d19b9SXu Kuohai offset = insn->off; 13385b3d19b9SXu Kuohai break; 13395b3d19b9SXu Kuohai 13405b3d19b9SXu Kuohai case BPF_JMP32: 13415b3d19b9SXu Kuohai case BPF_JMP: 13425b3d19b9SXu Kuohai break; 13435b3d19b9SXu Kuohai 13445b3d19b9SXu Kuohai case BPF_LDX: 13455b3d19b9SXu Kuohai case BPF_LD: 13465b3d19b9SXu Kuohai /* fp holds load result */ 13475b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 13485b3d19b9SXu Kuohai return 0; 13495b3d19b9SXu Kuohai 13505b3d19b9SXu Kuohai if (class == BPF_LDX && mode == BPF_MEM && 13515b3d19b9SXu Kuohai src == BPF_REG_FP && off < offset) 13525b3d19b9SXu Kuohai offset = off; 13535b3d19b9SXu Kuohai break; 13545b3d19b9SXu Kuohai 13555b3d19b9SXu Kuohai case BPF_ALU: 13565b3d19b9SXu Kuohai case BPF_ALU64: 13575b3d19b9SXu Kuohai default: 13585b3d19b9SXu Kuohai /* fp holds ALU result */ 13595b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 13605b3d19b9SXu Kuohai return 0; 13615b3d19b9SXu Kuohai } 13625b3d19b9SXu Kuohai } 13635b3d19b9SXu Kuohai 13645b3d19b9SXu Kuohai if (offset < 0) { 13655b3d19b9SXu Kuohai /* 13665b3d19b9SXu Kuohai * safely be converted to a positive 'int', since insn->off 13675b3d19b9SXu Kuohai * is 's16' 13685b3d19b9SXu Kuohai */ 13695b3d19b9SXu Kuohai offset = -offset; 13705b3d19b9SXu Kuohai /* align down to 8 bytes */ 13715b3d19b9SXu Kuohai offset = ALIGN_DOWN(offset, 8); 13725b3d19b9SXu Kuohai } 13735b3d19b9SXu Kuohai 13745b3d19b9SXu Kuohai return offset; 13755b3d19b9SXu Kuohai } 13765b3d19b9SXu Kuohai 13778c11ea5cSDaniel Borkmann static int build_body(struct jit_ctx *ctx, bool extra_pass) 1378e54bcde3SZi Shen Lim { 1379e54bcde3SZi Shen Lim const struct bpf_prog *prog = ctx->prog; 1380e54bcde3SZi Shen Lim int i; 1381e54bcde3SZi Shen Lim 138232f6865cSIlias Apalodimas /* 138332f6865cSIlias Apalodimas * - offset[0] offset of the end of prologue, 138432f6865cSIlias Apalodimas * start of the 1st instruction. 138532f6865cSIlias Apalodimas * - offset[1] - offset of the end of 1st instruction, 138632f6865cSIlias Apalodimas * start of the 2nd instruction 138732f6865cSIlias Apalodimas * [....] 138832f6865cSIlias Apalodimas * - offset[3] - offset of the end of 3rd instruction, 138932f6865cSIlias Apalodimas * start of 4th instruction 139032f6865cSIlias Apalodimas */ 1391e54bcde3SZi Shen Lim for (i = 0; i < prog->len; i++) { 1392e54bcde3SZi Shen Lim const struct bpf_insn *insn = &prog->insnsi[i]; 1393e54bcde3SZi Shen Lim int ret; 1394e54bcde3SZi Shen Lim 139532f6865cSIlias Apalodimas if (ctx->image == NULL) 139632f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 13978c11ea5cSDaniel Borkmann ret = build_insn(insn, ctx, extra_pass); 139830d3d94cSZi Shen Lim if (ret > 0) { 139930d3d94cSZi Shen Lim i++; 1400ddc665a4SDaniel Borkmann if (ctx->image == NULL) 1401ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 140230d3d94cSZi Shen Lim continue; 140330d3d94cSZi Shen Lim } 1404e54bcde3SZi Shen Lim if (ret) 1405e54bcde3SZi Shen Lim return ret; 1406e54bcde3SZi Shen Lim } 140732f6865cSIlias Apalodimas /* 140832f6865cSIlias Apalodimas * offset is allocated with prog->len + 1 so fill in 140932f6865cSIlias Apalodimas * the last element with the offset after the last 141032f6865cSIlias Apalodimas * instruction (end of program) 141132f6865cSIlias Apalodimas */ 141232f6865cSIlias Apalodimas if (ctx->image == NULL) 141332f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 1414e54bcde3SZi Shen Lim 1415e54bcde3SZi Shen Lim return 0; 1416e54bcde3SZi Shen Lim } 1417e54bcde3SZi Shen Lim 141842ff712bSZi Shen Lim static int validate_code(struct jit_ctx *ctx) 141942ff712bSZi Shen Lim { 142042ff712bSZi Shen Lim int i; 142142ff712bSZi Shen Lim 142242ff712bSZi Shen Lim for (i = 0; i < ctx->idx; i++) { 142342ff712bSZi Shen Lim u32 a64_insn = le32_to_cpu(ctx->image[i]); 142442ff712bSZi Shen Lim 142542ff712bSZi Shen Lim if (a64_insn == AARCH64_BREAK_FAULT) 142642ff712bSZi Shen Lim return -1; 142742ff712bSZi Shen Lim } 1428efc9909fSXu Kuohai return 0; 1429efc9909fSXu Kuohai } 1430efc9909fSXu Kuohai 1431efc9909fSXu Kuohai static int validate_ctx(struct jit_ctx *ctx) 1432efc9909fSXu Kuohai { 1433efc9909fSXu Kuohai if (validate_code(ctx)) 1434efc9909fSXu Kuohai return -1; 143542ff712bSZi Shen Lim 143680083428SJean-Philippe Brucker if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries)) 143780083428SJean-Philippe Brucker return -1; 143880083428SJean-Philippe Brucker 143942ff712bSZi Shen Lim return 0; 144042ff712bSZi Shen Lim } 144142ff712bSZi Shen Lim 1442e54bcde3SZi Shen Lim static inline void bpf_flush_icache(void *start, void *end) 1443e54bcde3SZi Shen Lim { 1444e54bcde3SZi Shen Lim flush_icache_range((unsigned long)start, (unsigned long)end); 1445e54bcde3SZi Shen Lim } 1446e54bcde3SZi Shen Lim 1447db496944SAlexei Starovoitov struct arm64_jit_data { 1448db496944SAlexei Starovoitov struct bpf_binary_header *header; 1449db496944SAlexei Starovoitov u8 *image; 1450db496944SAlexei Starovoitov struct jit_ctx ctx; 1451db496944SAlexei Starovoitov }; 1452db496944SAlexei Starovoitov 1453d1c55ab5SDaniel Borkmann struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 1454e54bcde3SZi Shen Lim { 1455b2ad54e1SXu Kuohai int image_size, prog_size, extable_size, extable_align, extable_offset; 145626eb042eSDaniel Borkmann struct bpf_prog *tmp, *orig_prog = prog; 1457b569c1c6SDaniel Borkmann struct bpf_binary_header *header; 1458db496944SAlexei Starovoitov struct arm64_jit_data *jit_data; 145956ea6a8bSDaniel Borkmann bool was_classic = bpf_prog_was_classic(prog); 146026eb042eSDaniel Borkmann bool tmp_blinded = false; 1461db496944SAlexei Starovoitov bool extra_pass = false; 1462e54bcde3SZi Shen Lim struct jit_ctx ctx; 1463b569c1c6SDaniel Borkmann u8 *image_ptr; 1464e54bcde3SZi Shen Lim 146560b58afcSAlexei Starovoitov if (!prog->jit_requested) 146626eb042eSDaniel Borkmann return orig_prog; 146726eb042eSDaniel Borkmann 146826eb042eSDaniel Borkmann tmp = bpf_jit_blind_constants(prog); 146926eb042eSDaniel Borkmann /* If blinding was requested and we failed during blinding, 147026eb042eSDaniel Borkmann * we must fall back to the interpreter. 147126eb042eSDaniel Borkmann */ 147226eb042eSDaniel Borkmann if (IS_ERR(tmp)) 147326eb042eSDaniel Borkmann return orig_prog; 147426eb042eSDaniel Borkmann if (tmp != prog) { 147526eb042eSDaniel Borkmann tmp_blinded = true; 147626eb042eSDaniel Borkmann prog = tmp; 147726eb042eSDaniel Borkmann } 1478e54bcde3SZi Shen Lim 1479db496944SAlexei Starovoitov jit_data = prog->aux->jit_data; 1480db496944SAlexei Starovoitov if (!jit_data) { 1481db496944SAlexei Starovoitov jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 1482db496944SAlexei Starovoitov if (!jit_data) { 1483db496944SAlexei Starovoitov prog = orig_prog; 1484db496944SAlexei Starovoitov goto out; 1485db496944SAlexei Starovoitov } 1486db496944SAlexei Starovoitov prog->aux->jit_data = jit_data; 1487db496944SAlexei Starovoitov } 1488db496944SAlexei Starovoitov if (jit_data->ctx.offset) { 1489db496944SAlexei Starovoitov ctx = jit_data->ctx; 1490db496944SAlexei Starovoitov image_ptr = jit_data->image; 1491db496944SAlexei Starovoitov header = jit_data->header; 1492db496944SAlexei Starovoitov extra_pass = true; 149380083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1494db496944SAlexei Starovoitov goto skip_init_ctx; 1495db496944SAlexei Starovoitov } 1496e54bcde3SZi Shen Lim memset(&ctx, 0, sizeof(ctx)); 1497e54bcde3SZi Shen Lim ctx.prog = prog; 1498e54bcde3SZi Shen Lim 149932f6865cSIlias Apalodimas ctx.offset = kcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); 150026eb042eSDaniel Borkmann if (ctx.offset == NULL) { 150126eb042eSDaniel Borkmann prog = orig_prog; 1502db496944SAlexei Starovoitov goto out_off; 150326eb042eSDaniel Borkmann } 1504e54bcde3SZi Shen Lim 15055b3d19b9SXu Kuohai ctx.fpb_offset = find_fpb_offset(prog); 15065b3d19b9SXu Kuohai 150768e4f238SHou Tao /* 150868e4f238SHou Tao * 1. Initial fake pass to compute ctx->idx and ctx->offset. 150968e4f238SHou Tao * 151068e4f238SHou Tao * BPF line info needs ctx->offset[i] to be the offset of 151168e4f238SHou Tao * instruction[i] in jited image, so build prologue first. 151268e4f238SHou Tao */ 151368e4f238SHou Tao if (build_prologue(&ctx, was_classic)) { 151426eb042eSDaniel Borkmann prog = orig_prog; 151526eb042eSDaniel Borkmann goto out_off; 151626eb042eSDaniel Borkmann } 1517e54bcde3SZi Shen Lim 151868e4f238SHou Tao if (build_body(&ctx, extra_pass)) { 1519ddb55992SZi Shen Lim prog = orig_prog; 1520ddb55992SZi Shen Lim goto out_off; 1521ddb55992SZi Shen Lim } 152251c9fbb1SZi Shen Lim 152351c9fbb1SZi Shen Lim ctx.epilogue_offset = ctx.idx; 1524e54bcde3SZi Shen Lim build_epilogue(&ctx); 1525b2ad54e1SXu Kuohai build_plt(&ctx); 1526e54bcde3SZi Shen Lim 1527b2ad54e1SXu Kuohai extable_align = __alignof__(struct exception_table_entry); 152880083428SJean-Philippe Brucker extable_size = prog->aux->num_exentries * 152980083428SJean-Philippe Brucker sizeof(struct exception_table_entry); 153080083428SJean-Philippe Brucker 1531e54bcde3SZi Shen Lim /* Now we know the actual image size. */ 153280083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1533b2ad54e1SXu Kuohai /* also allocate space for plt target */ 1534b2ad54e1SXu Kuohai extable_offset = round_up(prog_size + PLT_TARGET_SIZE, extable_align); 1535b2ad54e1SXu Kuohai image_size = extable_offset + extable_size; 1536b569c1c6SDaniel Borkmann header = bpf_jit_binary_alloc(image_size, &image_ptr, 1537b569c1c6SDaniel Borkmann sizeof(u32), jit_fill_hole); 153826eb042eSDaniel Borkmann if (header == NULL) { 153926eb042eSDaniel Borkmann prog = orig_prog; 154026eb042eSDaniel Borkmann goto out_off; 154126eb042eSDaniel Borkmann } 1542e54bcde3SZi Shen Lim 1543e54bcde3SZi Shen Lim /* 2. Now, the actual pass. */ 1544e54bcde3SZi Shen Lim 1545425e1ed7SLuc Van Oostenryck ctx.image = (__le32 *)image_ptr; 154680083428SJean-Philippe Brucker if (extable_size) 1547b2ad54e1SXu Kuohai prog->aux->extable = (void *)image_ptr + extable_offset; 1548db496944SAlexei Starovoitov skip_init_ctx: 1549e54bcde3SZi Shen Lim ctx.idx = 0; 155080083428SJean-Philippe Brucker ctx.exentry_idx = 0; 1551b569c1c6SDaniel Borkmann 155256ea6a8bSDaniel Borkmann build_prologue(&ctx, was_classic); 1553e54bcde3SZi Shen Lim 15548c11ea5cSDaniel Borkmann if (build_body(&ctx, extra_pass)) { 1555b569c1c6SDaniel Borkmann bpf_jit_binary_free(header); 155626eb042eSDaniel Borkmann prog = orig_prog; 155726eb042eSDaniel Borkmann goto out_off; 155860ef0494SDaniel Borkmann } 1559e54bcde3SZi Shen Lim 1560e54bcde3SZi Shen Lim build_epilogue(&ctx); 1561b2ad54e1SXu Kuohai build_plt(&ctx); 1562e54bcde3SZi Shen Lim 156342ff712bSZi Shen Lim /* 3. Extra pass to validate JITed code. */ 1564efc9909fSXu Kuohai if (validate_ctx(&ctx)) { 156542ff712bSZi Shen Lim bpf_jit_binary_free(header); 156626eb042eSDaniel Borkmann prog = orig_prog; 156726eb042eSDaniel Borkmann goto out_off; 156842ff712bSZi Shen Lim } 156942ff712bSZi Shen Lim 1570e54bcde3SZi Shen Lim /* And we're done. */ 1571e54bcde3SZi Shen Lim if (bpf_jit_enable > 1) 157280083428SJean-Philippe Brucker bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 1573e54bcde3SZi Shen Lim 1574c3d4c682SDaniel Borkmann bpf_flush_icache(header, ctx.image + ctx.idx); 1575b569c1c6SDaniel Borkmann 1576db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1577db496944SAlexei Starovoitov if (extra_pass && ctx.idx != jit_data->ctx.idx) { 1578db496944SAlexei Starovoitov pr_err_once("multi-func JIT bug %d != %d\n", 1579db496944SAlexei Starovoitov ctx.idx, jit_data->ctx.idx); 1580db496944SAlexei Starovoitov bpf_jit_binary_free(header); 1581db496944SAlexei Starovoitov prog->bpf_func = NULL; 1582db496944SAlexei Starovoitov prog->jited = 0; 158310f3b29cSEric Dumazet prog->jited_len = 0; 1584db496944SAlexei Starovoitov goto out_off; 1585db496944SAlexei Starovoitov } 15869d876e79SDaniel Borkmann bpf_jit_binary_lock_ro(header); 1587db496944SAlexei Starovoitov } else { 1588db496944SAlexei Starovoitov jit_data->ctx = ctx; 1589db496944SAlexei Starovoitov jit_data->image = image_ptr; 1590db496944SAlexei Starovoitov jit_data->header = header; 1591db496944SAlexei Starovoitov } 1592e54bcde3SZi Shen Lim prog->bpf_func = (void *)ctx.image; 1593a91263d5SDaniel Borkmann prog->jited = 1; 159480083428SJean-Philippe Brucker prog->jited_len = prog_size; 159526eb042eSDaniel Borkmann 1596db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1597dda7596cSHou Tao int i; 1598dda7596cSHou Tao 1599dda7596cSHou Tao /* offset[prog->len] is the size of program */ 1600dda7596cSHou Tao for (i = 0; i <= prog->len; i++) 1601dda7596cSHou Tao ctx.offset[i] *= AARCH64_INSN_SIZE; 160232f6865cSIlias Apalodimas bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 160326eb042eSDaniel Borkmann out_off: 1604e54bcde3SZi Shen Lim kfree(ctx.offset); 1605db496944SAlexei Starovoitov kfree(jit_data); 1606db496944SAlexei Starovoitov prog->aux->jit_data = NULL; 1607db496944SAlexei Starovoitov } 160826eb042eSDaniel Borkmann out: 160926eb042eSDaniel Borkmann if (tmp_blinded) 161026eb042eSDaniel Borkmann bpf_jit_prog_release_other(prog, prog == orig_prog ? 161126eb042eSDaniel Borkmann tmp : orig_prog); 1612d1c55ab5SDaniel Borkmann return prog; 1613e54bcde3SZi Shen Lim } 161491fc957cSArd Biesheuvel 1615b5e975d2SHou Tao bool bpf_jit_supports_kfunc_call(void) 1616b5e975d2SHou Tao { 1617b5e975d2SHou Tao return true; 1618b5e975d2SHou Tao } 1619b5e975d2SHou Tao 16205d63ae90SLorenz Bauer u64 bpf_jit_alloc_exec_limit(void) 16215d63ae90SLorenz Bauer { 1622b89ddf4cSRussell King return VMALLOC_END - VMALLOC_START; 16235d63ae90SLorenz Bauer } 16245d63ae90SLorenz Bauer 162591fc957cSArd Biesheuvel void *bpf_jit_alloc_exec(unsigned long size) 162691fc957cSArd Biesheuvel { 162736c4a73bSAndrey Konovalov /* Memory is intended to be executable, reset the pointer tag. */ 162836c4a73bSAndrey Konovalov return kasan_reset_tag(vmalloc(size)); 162991fc957cSArd Biesheuvel } 163091fc957cSArd Biesheuvel 163191fc957cSArd Biesheuvel void bpf_jit_free_exec(void *addr) 163291fc957cSArd Biesheuvel { 163391fc957cSArd Biesheuvel return vfree(addr); 163491fc957cSArd Biesheuvel } 1635d4609a5dSJakub Sitnicki 1636d4609a5dSJakub Sitnicki /* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */ 1637d4609a5dSJakub Sitnicki bool bpf_jit_supports_subprog_tailcalls(void) 1638d4609a5dSJakub Sitnicki { 1639d4609a5dSJakub Sitnicki return true; 1640d4609a5dSJakub Sitnicki } 1641b2ad54e1SXu Kuohai 1642efc9909fSXu Kuohai static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, 1643efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1644efc9909fSXu Kuohai bool save_ret) 1645efc9909fSXu Kuohai { 1646efc9909fSXu Kuohai u32 *branch; 1647efc9909fSXu Kuohai u64 enter_prog; 1648efc9909fSXu Kuohai u64 exit_prog; 1649efc9909fSXu Kuohai struct bpf_prog *p = l->link.prog; 1650efc9909fSXu Kuohai int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); 1651efc9909fSXu Kuohai 1652efc9909fSXu Kuohai if (p->aux->sleepable) { 1653efc9909fSXu Kuohai enter_prog = (u64)__bpf_prog_enter_sleepable; 1654efc9909fSXu Kuohai exit_prog = (u64)__bpf_prog_exit_sleepable; 1655efc9909fSXu Kuohai } else { 1656efc9909fSXu Kuohai enter_prog = (u64)__bpf_prog_enter; 1657efc9909fSXu Kuohai exit_prog = (u64)__bpf_prog_exit; 1658efc9909fSXu Kuohai } 1659efc9909fSXu Kuohai 1660efc9909fSXu Kuohai if (l->cookie == 0) { 1661efc9909fSXu Kuohai /* if cookie is zero, one instruction is enough to store it */ 1662efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, run_ctx_off + cookie_off), ctx); 1663efc9909fSXu Kuohai } else { 1664efc9909fSXu Kuohai emit_a64_mov_i64(A64_R(10), l->cookie, ctx); 1665efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, run_ctx_off + cookie_off), 1666efc9909fSXu Kuohai ctx); 1667efc9909fSXu Kuohai } 1668efc9909fSXu Kuohai 1669efc9909fSXu Kuohai /* save p to callee saved register x19 to avoid loading p with mov_i64 1670efc9909fSXu Kuohai * each time. 1671efc9909fSXu Kuohai */ 1672efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(19), (const u64)p, ctx); 1673efc9909fSXu Kuohai 1674efc9909fSXu Kuohai /* arg1: prog */ 1675efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1676efc9909fSXu Kuohai /* arg2: &run_ctx */ 1677efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(1), A64_SP, run_ctx_off), ctx); 1678efc9909fSXu Kuohai 1679efc9909fSXu Kuohai emit_call(enter_prog, ctx); 1680efc9909fSXu Kuohai 1681efc9909fSXu Kuohai /* if (__bpf_prog_enter(prog) == 0) 1682efc9909fSXu Kuohai * goto skip_exec_of_prog; 1683efc9909fSXu Kuohai */ 1684efc9909fSXu Kuohai branch = ctx->image + ctx->idx; 1685efc9909fSXu Kuohai emit(A64_NOP, ctx); 1686efc9909fSXu Kuohai 1687efc9909fSXu Kuohai /* save return value to callee saved register x20 */ 1688efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(20), A64_R(0)), ctx); 1689efc9909fSXu Kuohai 1690efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx); 1691efc9909fSXu Kuohai if (!p->jited) 1692efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx); 1693efc9909fSXu Kuohai 1694efc9909fSXu Kuohai emit_call((const u64)p->bpf_func, ctx); 1695efc9909fSXu Kuohai 1696efc9909fSXu Kuohai if (save_ret) 1697efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 1698efc9909fSXu Kuohai 1699efc9909fSXu Kuohai if (ctx->image) { 1700efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branch; 1701efc9909fSXu Kuohai *branch = A64_CBZ(1, A64_R(0), offset); 1702efc9909fSXu Kuohai } 1703efc9909fSXu Kuohai 1704efc9909fSXu Kuohai /* arg1: prog */ 1705efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1706efc9909fSXu Kuohai /* arg2: start time */ 1707efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(1), A64_R(20)), ctx); 1708efc9909fSXu Kuohai /* arg3: &run_ctx */ 1709efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(2), A64_SP, run_ctx_off), ctx); 1710efc9909fSXu Kuohai 1711efc9909fSXu Kuohai emit_call(exit_prog, ctx); 1712efc9909fSXu Kuohai } 1713efc9909fSXu Kuohai 1714efc9909fSXu Kuohai static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, 1715efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1716efc9909fSXu Kuohai u32 **branches) 1717efc9909fSXu Kuohai { 1718efc9909fSXu Kuohai int i; 1719efc9909fSXu Kuohai 1720efc9909fSXu Kuohai /* The first fmod_ret program will receive a garbage return value. 1721efc9909fSXu Kuohai * Set this to 0 to avoid confusing the program. 1722efc9909fSXu Kuohai */ 1723efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx); 1724efc9909fSXu Kuohai for (i = 0; i < tl->nr_links; i++) { 1725efc9909fSXu Kuohai invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off, 1726efc9909fSXu Kuohai run_ctx_off, true); 1727efc9909fSXu Kuohai /* if (*(u64 *)(sp + retval_off) != 0) 1728efc9909fSXu Kuohai * goto do_fexit; 1729efc9909fSXu Kuohai */ 1730efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retval_off), ctx); 1731efc9909fSXu Kuohai /* Save the location of branch, and generate a nop. 1732efc9909fSXu Kuohai * This nop will be replaced with a cbnz later. 1733efc9909fSXu Kuohai */ 1734efc9909fSXu Kuohai branches[i] = ctx->image + ctx->idx; 1735efc9909fSXu Kuohai emit(A64_NOP, ctx); 1736efc9909fSXu Kuohai } 1737efc9909fSXu Kuohai } 1738efc9909fSXu Kuohai 1739efc9909fSXu Kuohai static void save_args(struct jit_ctx *ctx, int args_off, int nargs) 1740efc9909fSXu Kuohai { 1741efc9909fSXu Kuohai int i; 1742efc9909fSXu Kuohai 1743efc9909fSXu Kuohai for (i = 0; i < nargs; i++) { 1744efc9909fSXu Kuohai emit(A64_STR64I(i, A64_SP, args_off), ctx); 1745efc9909fSXu Kuohai args_off += 8; 1746efc9909fSXu Kuohai } 1747efc9909fSXu Kuohai } 1748efc9909fSXu Kuohai 1749efc9909fSXu Kuohai static void restore_args(struct jit_ctx *ctx, int args_off, int nargs) 1750efc9909fSXu Kuohai { 1751efc9909fSXu Kuohai int i; 1752efc9909fSXu Kuohai 1753efc9909fSXu Kuohai for (i = 0; i < nargs; i++) { 1754efc9909fSXu Kuohai emit(A64_LDR64I(i, A64_SP, args_off), ctx); 1755efc9909fSXu Kuohai args_off += 8; 1756efc9909fSXu Kuohai } 1757efc9909fSXu Kuohai } 1758efc9909fSXu Kuohai 1759efc9909fSXu Kuohai /* Based on the x86's implementation of arch_prepare_bpf_trampoline(). 1760efc9909fSXu Kuohai * 1761efc9909fSXu Kuohai * bpf prog and function entry before bpf trampoline hooked: 1762efc9909fSXu Kuohai * mov x9, lr 1763efc9909fSXu Kuohai * nop 1764efc9909fSXu Kuohai * 1765efc9909fSXu Kuohai * bpf prog and function entry after bpf trampoline hooked: 1766efc9909fSXu Kuohai * mov x9, lr 1767efc9909fSXu Kuohai * bl <bpf_trampoline or plt> 1768efc9909fSXu Kuohai * 1769efc9909fSXu Kuohai */ 1770efc9909fSXu Kuohai static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, 1771efc9909fSXu Kuohai struct bpf_tramp_links *tlinks, void *orig_call, 1772efc9909fSXu Kuohai int nargs, u32 flags) 1773efc9909fSXu Kuohai { 1774efc9909fSXu Kuohai int i; 1775efc9909fSXu Kuohai int stack_size; 1776efc9909fSXu Kuohai int retaddr_off; 1777efc9909fSXu Kuohai int regs_off; 1778efc9909fSXu Kuohai int retval_off; 1779efc9909fSXu Kuohai int args_off; 1780efc9909fSXu Kuohai int nargs_off; 1781efc9909fSXu Kuohai int ip_off; 1782efc9909fSXu Kuohai int run_ctx_off; 1783efc9909fSXu Kuohai struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; 1784efc9909fSXu Kuohai struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; 1785efc9909fSXu Kuohai struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; 1786efc9909fSXu Kuohai bool save_ret; 1787efc9909fSXu Kuohai u32 **branches = NULL; 1788efc9909fSXu Kuohai 1789efc9909fSXu Kuohai /* trampoline stack layout: 1790efc9909fSXu Kuohai * [ parent ip ] 1791efc9909fSXu Kuohai * [ FP ] 1792efc9909fSXu Kuohai * SP + retaddr_off [ self ip ] 1793efc9909fSXu Kuohai * [ FP ] 1794efc9909fSXu Kuohai * 1795efc9909fSXu Kuohai * [ padding ] align SP to multiples of 16 1796efc9909fSXu Kuohai * 1797efc9909fSXu Kuohai * [ x20 ] callee saved reg x20 1798efc9909fSXu Kuohai * SP + regs_off [ x19 ] callee saved reg x19 1799efc9909fSXu Kuohai * 1800efc9909fSXu Kuohai * SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or 1801efc9909fSXu Kuohai * BPF_TRAMP_F_RET_FENTRY_RET 1802efc9909fSXu Kuohai * 1803efc9909fSXu Kuohai * [ argN ] 1804efc9909fSXu Kuohai * [ ... ] 1805efc9909fSXu Kuohai * SP + args_off [ arg1 ] 1806efc9909fSXu Kuohai * 1807efc9909fSXu Kuohai * SP + nargs_off [ args count ] 1808efc9909fSXu Kuohai * 1809efc9909fSXu Kuohai * SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag 1810efc9909fSXu Kuohai * 1811efc9909fSXu Kuohai * SP + run_ctx_off [ bpf_tramp_run_ctx ] 1812efc9909fSXu Kuohai */ 1813efc9909fSXu Kuohai 1814efc9909fSXu Kuohai stack_size = 0; 1815efc9909fSXu Kuohai run_ctx_off = stack_size; 1816efc9909fSXu Kuohai /* room for bpf_tramp_run_ctx */ 1817efc9909fSXu Kuohai stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); 1818efc9909fSXu Kuohai 1819efc9909fSXu Kuohai ip_off = stack_size; 1820efc9909fSXu Kuohai /* room for IP address argument */ 1821efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) 1822efc9909fSXu Kuohai stack_size += 8; 1823efc9909fSXu Kuohai 1824efc9909fSXu Kuohai nargs_off = stack_size; 1825efc9909fSXu Kuohai /* room for args count */ 1826efc9909fSXu Kuohai stack_size += 8; 1827efc9909fSXu Kuohai 1828efc9909fSXu Kuohai args_off = stack_size; 1829efc9909fSXu Kuohai /* room for args */ 1830efc9909fSXu Kuohai stack_size += nargs * 8; 1831efc9909fSXu Kuohai 1832efc9909fSXu Kuohai /* room for return value */ 1833efc9909fSXu Kuohai retval_off = stack_size; 1834efc9909fSXu Kuohai save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET); 1835efc9909fSXu Kuohai if (save_ret) 1836efc9909fSXu Kuohai stack_size += 8; 1837efc9909fSXu Kuohai 1838efc9909fSXu Kuohai /* room for callee saved registers, currently x19 and x20 are used */ 1839efc9909fSXu Kuohai regs_off = stack_size; 1840efc9909fSXu Kuohai stack_size += 16; 1841efc9909fSXu Kuohai 1842efc9909fSXu Kuohai /* round up to multiples of 16 to avoid SPAlignmentFault */ 1843efc9909fSXu Kuohai stack_size = round_up(stack_size, 16); 1844efc9909fSXu Kuohai 1845efc9909fSXu Kuohai /* return address locates above FP */ 1846efc9909fSXu Kuohai retaddr_off = stack_size + 8; 1847efc9909fSXu Kuohai 1848efc9909fSXu Kuohai /* bpf trampoline may be invoked by 3 instruction types: 1849efc9909fSXu Kuohai * 1. bl, attached to bpf prog or kernel function via short jump 1850efc9909fSXu Kuohai * 2. br, attached to bpf prog or kernel function via long jump 1851efc9909fSXu Kuohai * 3. blr, working as a function pointer, used by struct_ops. 1852efc9909fSXu Kuohai * So BTI_JC should used here to support both br and blr. 1853efc9909fSXu Kuohai */ 1854efc9909fSXu Kuohai emit_bti(A64_BTI_JC, ctx); 1855efc9909fSXu Kuohai 1856efc9909fSXu Kuohai /* frame for parent function */ 1857efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx); 1858efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 1859efc9909fSXu Kuohai 1860efc9909fSXu Kuohai /* frame for patched function */ 1861efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 1862efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 1863efc9909fSXu Kuohai 1864efc9909fSXu Kuohai /* allocate stack space */ 1865efc9909fSXu Kuohai emit(A64_SUB_I(1, A64_SP, A64_SP, stack_size), ctx); 1866efc9909fSXu Kuohai 1867efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) { 1868efc9909fSXu Kuohai /* save ip address of the traced function */ 1869efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(10), (const u64)orig_call, ctx); 1870efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, ip_off), ctx); 1871efc9909fSXu Kuohai } 1872efc9909fSXu Kuohai 1873efc9909fSXu Kuohai /* save args count*/ 1874efc9909fSXu Kuohai emit(A64_MOVZ(1, A64_R(10), nargs, 0), ctx); 1875efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, nargs_off), ctx); 1876efc9909fSXu Kuohai 1877efc9909fSXu Kuohai /* save args */ 1878efc9909fSXu Kuohai save_args(ctx, args_off, nargs); 1879efc9909fSXu Kuohai 1880efc9909fSXu Kuohai /* save callee saved registers */ 1881efc9909fSXu Kuohai emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx); 1882efc9909fSXu Kuohai emit(A64_STR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 1883efc9909fSXu Kuohai 1884efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 1885efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 1886efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_enter, ctx); 1887efc9909fSXu Kuohai } 1888efc9909fSXu Kuohai 1889efc9909fSXu Kuohai for (i = 0; i < fentry->nr_links; i++) 1890efc9909fSXu Kuohai invoke_bpf_prog(ctx, fentry->links[i], args_off, 1891efc9909fSXu Kuohai retval_off, run_ctx_off, 1892efc9909fSXu Kuohai flags & BPF_TRAMP_F_RET_FENTRY_RET); 1893efc9909fSXu Kuohai 1894efc9909fSXu Kuohai if (fmod_ret->nr_links) { 1895efc9909fSXu Kuohai branches = kcalloc(fmod_ret->nr_links, sizeof(u32 *), 1896efc9909fSXu Kuohai GFP_KERNEL); 1897efc9909fSXu Kuohai if (!branches) 1898efc9909fSXu Kuohai return -ENOMEM; 1899efc9909fSXu Kuohai 1900efc9909fSXu Kuohai invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off, 1901efc9909fSXu Kuohai run_ctx_off, branches); 1902efc9909fSXu Kuohai } 1903efc9909fSXu Kuohai 1904efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 1905efc9909fSXu Kuohai restore_args(ctx, args_off, nargs); 1906efc9909fSXu Kuohai /* call original func */ 1907efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx); 1908efc9909fSXu Kuohai emit(A64_BLR(A64_R(10)), ctx); 1909efc9909fSXu Kuohai /* store return value */ 1910efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 1911efc9909fSXu Kuohai /* reserve a nop for bpf_tramp_image_put */ 1912efc9909fSXu Kuohai im->ip_after_call = ctx->image + ctx->idx; 1913efc9909fSXu Kuohai emit(A64_NOP, ctx); 1914efc9909fSXu Kuohai } 1915efc9909fSXu Kuohai 1916efc9909fSXu Kuohai /* update the branches saved in invoke_bpf_mod_ret with cbnz */ 1917efc9909fSXu Kuohai for (i = 0; i < fmod_ret->nr_links && ctx->image != NULL; i++) { 1918efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branches[i]; 1919efc9909fSXu Kuohai *branches[i] = A64_CBNZ(1, A64_R(10), offset); 1920efc9909fSXu Kuohai } 1921efc9909fSXu Kuohai 1922efc9909fSXu Kuohai for (i = 0; i < fexit->nr_links; i++) 1923efc9909fSXu Kuohai invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off, 1924efc9909fSXu Kuohai run_ctx_off, false); 1925efc9909fSXu Kuohai 1926efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 1927efc9909fSXu Kuohai im->ip_epilogue = ctx->image + ctx->idx; 1928efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 1929efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_exit, ctx); 1930efc9909fSXu Kuohai } 1931efc9909fSXu Kuohai 1932efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_RESTORE_REGS) 1933efc9909fSXu Kuohai restore_args(ctx, args_off, nargs); 1934efc9909fSXu Kuohai 1935efc9909fSXu Kuohai /* restore callee saved register x19 and x20 */ 1936efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx); 1937efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 1938efc9909fSXu Kuohai 1939efc9909fSXu Kuohai if (save_ret) 1940efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(0), A64_SP, retval_off), ctx); 1941efc9909fSXu Kuohai 1942efc9909fSXu Kuohai /* reset SP */ 1943efc9909fSXu Kuohai emit(A64_MOV(1, A64_SP, A64_FP), ctx); 1944efc9909fSXu Kuohai 1945efc9909fSXu Kuohai /* pop frames */ 1946efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 1947efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx); 1948efc9909fSXu Kuohai 1949efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_SKIP_FRAME) { 1950efc9909fSXu Kuohai /* skip patched function, return to parent */ 1951efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 1952efc9909fSXu Kuohai emit(A64_RET(A64_R(9)), ctx); 1953efc9909fSXu Kuohai } else { 1954efc9909fSXu Kuohai /* return to patched function */ 1955efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(10), A64_LR), ctx); 1956efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 1957efc9909fSXu Kuohai emit(A64_RET(A64_R(10)), ctx); 1958efc9909fSXu Kuohai } 1959efc9909fSXu Kuohai 1960efc9909fSXu Kuohai if (ctx->image) 1961efc9909fSXu Kuohai bpf_flush_icache(ctx->image, ctx->image + ctx->idx); 1962efc9909fSXu Kuohai 1963efc9909fSXu Kuohai kfree(branches); 1964efc9909fSXu Kuohai 1965efc9909fSXu Kuohai return ctx->idx; 1966efc9909fSXu Kuohai } 1967efc9909fSXu Kuohai 1968efc9909fSXu Kuohai int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, 1969efc9909fSXu Kuohai void *image_end, const struct btf_func_model *m, 1970efc9909fSXu Kuohai u32 flags, struct bpf_tramp_links *tlinks, 1971efc9909fSXu Kuohai void *orig_call) 1972efc9909fSXu Kuohai { 1973efc9909fSXu Kuohai int ret; 1974efc9909fSXu Kuohai int nargs = m->nr_args; 1975efc9909fSXu Kuohai int max_insns = ((long)image_end - (long)image) / AARCH64_INSN_SIZE; 1976efc9909fSXu Kuohai struct jit_ctx ctx = { 1977efc9909fSXu Kuohai .image = NULL, 1978efc9909fSXu Kuohai .idx = 0, 1979efc9909fSXu Kuohai }; 1980efc9909fSXu Kuohai 1981efc9909fSXu Kuohai /* the first 8 arguments are passed by registers */ 1982efc9909fSXu Kuohai if (nargs > 8) 1983efc9909fSXu Kuohai return -ENOTSUPP; 1984efc9909fSXu Kuohai 1985efc9909fSXu Kuohai ret = prepare_trampoline(&ctx, im, tlinks, orig_call, nargs, flags); 1986efc9909fSXu Kuohai if (ret < 0) 1987efc9909fSXu Kuohai return ret; 1988efc9909fSXu Kuohai 1989efc9909fSXu Kuohai if (ret > max_insns) 1990efc9909fSXu Kuohai return -EFBIG; 1991efc9909fSXu Kuohai 1992efc9909fSXu Kuohai ctx.image = image; 1993efc9909fSXu Kuohai ctx.idx = 0; 1994efc9909fSXu Kuohai 1995efc9909fSXu Kuohai jit_fill_hole(image, (unsigned int)(image_end - image)); 1996efc9909fSXu Kuohai ret = prepare_trampoline(&ctx, im, tlinks, orig_call, nargs, flags); 1997efc9909fSXu Kuohai 1998efc9909fSXu Kuohai if (ret > 0 && validate_code(&ctx) < 0) 1999efc9909fSXu Kuohai ret = -EINVAL; 2000efc9909fSXu Kuohai 2001efc9909fSXu Kuohai if (ret > 0) 2002efc9909fSXu Kuohai ret *= AARCH64_INSN_SIZE; 2003efc9909fSXu Kuohai 2004efc9909fSXu Kuohai return ret; 2005efc9909fSXu Kuohai } 2006efc9909fSXu Kuohai 2007b2ad54e1SXu Kuohai static bool is_long_jump(void *ip, void *target) 2008b2ad54e1SXu Kuohai { 2009b2ad54e1SXu Kuohai long offset; 2010b2ad54e1SXu Kuohai 2011b2ad54e1SXu Kuohai /* NULL target means this is a NOP */ 2012b2ad54e1SXu Kuohai if (!target) 2013b2ad54e1SXu Kuohai return false; 2014b2ad54e1SXu Kuohai 2015b2ad54e1SXu Kuohai offset = (long)target - (long)ip; 2016b2ad54e1SXu Kuohai return offset < -SZ_128M || offset >= SZ_128M; 2017b2ad54e1SXu Kuohai } 2018b2ad54e1SXu Kuohai 2019b2ad54e1SXu Kuohai static int gen_branch_or_nop(enum aarch64_insn_branch_type type, void *ip, 2020b2ad54e1SXu Kuohai void *addr, void *plt, u32 *insn) 2021b2ad54e1SXu Kuohai { 2022b2ad54e1SXu Kuohai void *target; 2023b2ad54e1SXu Kuohai 2024b2ad54e1SXu Kuohai if (!addr) { 2025b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_nop(); 2026b2ad54e1SXu Kuohai return 0; 2027b2ad54e1SXu Kuohai } 2028b2ad54e1SXu Kuohai 2029b2ad54e1SXu Kuohai if (is_long_jump(ip, addr)) 2030b2ad54e1SXu Kuohai target = plt; 2031b2ad54e1SXu Kuohai else 2032b2ad54e1SXu Kuohai target = addr; 2033b2ad54e1SXu Kuohai 2034b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_branch_imm((unsigned long)ip, 2035b2ad54e1SXu Kuohai (unsigned long)target, 2036b2ad54e1SXu Kuohai type); 2037b2ad54e1SXu Kuohai 2038b2ad54e1SXu Kuohai return *insn != AARCH64_BREAK_FAULT ? 0 : -EFAULT; 2039b2ad54e1SXu Kuohai } 2040b2ad54e1SXu Kuohai 2041b2ad54e1SXu Kuohai /* Replace the branch instruction from @ip to @old_addr in a bpf prog or a bpf 2042b2ad54e1SXu Kuohai * trampoline with the branch instruction from @ip to @new_addr. If @old_addr 2043b2ad54e1SXu Kuohai * or @new_addr is NULL, the old or new instruction is NOP. 2044b2ad54e1SXu Kuohai * 2045b2ad54e1SXu Kuohai * When @ip is the bpf prog entry, a bpf trampoline is being attached or 2046b2ad54e1SXu Kuohai * detached. Since bpf trampoline and bpf prog are allocated separately with 2047b2ad54e1SXu Kuohai * vmalloc, the address distance may exceed 128MB, the maximum branch range. 2048b2ad54e1SXu Kuohai * So long jump should be handled. 2049b2ad54e1SXu Kuohai * 2050b2ad54e1SXu Kuohai * When a bpf prog is constructed, a plt pointing to empty trampoline 2051b2ad54e1SXu Kuohai * dummy_tramp is placed at the end: 2052b2ad54e1SXu Kuohai * 2053b2ad54e1SXu Kuohai * bpf_prog: 2054b2ad54e1SXu Kuohai * mov x9, lr 2055b2ad54e1SXu Kuohai * nop // patchsite 2056b2ad54e1SXu Kuohai * ... 2057b2ad54e1SXu Kuohai * ret 2058b2ad54e1SXu Kuohai * 2059b2ad54e1SXu Kuohai * plt: 2060b2ad54e1SXu Kuohai * ldr x10, target 2061b2ad54e1SXu Kuohai * br x10 2062b2ad54e1SXu Kuohai * target: 2063b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2064b2ad54e1SXu Kuohai * 2065b2ad54e1SXu Kuohai * This is also the state when no trampoline is attached. 2066b2ad54e1SXu Kuohai * 2067b2ad54e1SXu Kuohai * When a short-jump bpf trampoline is attached, the patchsite is patched 2068b2ad54e1SXu Kuohai * to a bl instruction to the trampoline directly: 2069b2ad54e1SXu Kuohai * 2070b2ad54e1SXu Kuohai * bpf_prog: 2071b2ad54e1SXu Kuohai * mov x9, lr 2072b2ad54e1SXu Kuohai * bl <short-jump bpf trampoline address> // patchsite 2073b2ad54e1SXu Kuohai * ... 2074b2ad54e1SXu Kuohai * ret 2075b2ad54e1SXu Kuohai * 2076b2ad54e1SXu Kuohai * plt: 2077b2ad54e1SXu Kuohai * ldr x10, target 2078b2ad54e1SXu Kuohai * br x10 2079b2ad54e1SXu Kuohai * target: 2080b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2081b2ad54e1SXu Kuohai * 2082b2ad54e1SXu Kuohai * When a long-jump bpf trampoline is attached, the plt target is filled with 2083b2ad54e1SXu Kuohai * the trampoline address and the patchsite is patched to a bl instruction to 2084b2ad54e1SXu Kuohai * the plt: 2085b2ad54e1SXu Kuohai * 2086b2ad54e1SXu Kuohai * bpf_prog: 2087b2ad54e1SXu Kuohai * mov x9, lr 2088b2ad54e1SXu Kuohai * bl plt // patchsite 2089b2ad54e1SXu Kuohai * ... 2090b2ad54e1SXu Kuohai * ret 2091b2ad54e1SXu Kuohai * 2092b2ad54e1SXu Kuohai * plt: 2093b2ad54e1SXu Kuohai * ldr x10, target 2094b2ad54e1SXu Kuohai * br x10 2095b2ad54e1SXu Kuohai * target: 2096b2ad54e1SXu Kuohai * .quad <long-jump bpf trampoline address> // plt target 2097b2ad54e1SXu Kuohai * 2098b2ad54e1SXu Kuohai * The dummy_tramp is used to prevent another CPU from jumping to unknown 2099b2ad54e1SXu Kuohai * locations during the patching process, making the patching process easier. 2100b2ad54e1SXu Kuohai */ 2101b2ad54e1SXu Kuohai int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, 2102b2ad54e1SXu Kuohai void *old_addr, void *new_addr) 2103b2ad54e1SXu Kuohai { 2104b2ad54e1SXu Kuohai int ret; 2105b2ad54e1SXu Kuohai u32 old_insn; 2106b2ad54e1SXu Kuohai u32 new_insn; 2107b2ad54e1SXu Kuohai u32 replaced; 2108b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 2109b2ad54e1SXu Kuohai unsigned long size = 0UL; 2110b2ad54e1SXu Kuohai unsigned long offset = ~0UL; 2111b2ad54e1SXu Kuohai enum aarch64_insn_branch_type branch_type; 2112b2ad54e1SXu Kuohai char namebuf[KSYM_NAME_LEN]; 2113b2ad54e1SXu Kuohai void *image = NULL; 2114b2ad54e1SXu Kuohai u64 plt_target = 0ULL; 2115b2ad54e1SXu Kuohai bool poking_bpf_entry; 2116b2ad54e1SXu Kuohai 2117b2ad54e1SXu Kuohai if (!__bpf_address_lookup((unsigned long)ip, &size, &offset, namebuf)) 2118b2ad54e1SXu Kuohai /* Only poking bpf text is supported. Since kernel function 2119b2ad54e1SXu Kuohai * entry is set up by ftrace, we reply on ftrace to poke kernel 2120b2ad54e1SXu Kuohai * functions. 2121b2ad54e1SXu Kuohai */ 2122b2ad54e1SXu Kuohai return -ENOTSUPP; 2123b2ad54e1SXu Kuohai 2124b2ad54e1SXu Kuohai image = ip - offset; 2125b2ad54e1SXu Kuohai /* zero offset means we're poking bpf prog entry */ 2126b2ad54e1SXu Kuohai poking_bpf_entry = (offset == 0UL); 2127b2ad54e1SXu Kuohai 2128b2ad54e1SXu Kuohai /* bpf prog entry, find plt and the real patchsite */ 2129b2ad54e1SXu Kuohai if (poking_bpf_entry) { 2130b2ad54e1SXu Kuohai /* plt locates at the end of bpf prog */ 2131b2ad54e1SXu Kuohai plt = image + size - PLT_TARGET_OFFSET; 2132b2ad54e1SXu Kuohai 2133b2ad54e1SXu Kuohai /* skip to the nop instruction in bpf prog entry: 2134b2ad54e1SXu Kuohai * bti c // if BTI enabled 2135b2ad54e1SXu Kuohai * mov x9, x30 2136b2ad54e1SXu Kuohai * nop 2137b2ad54e1SXu Kuohai */ 2138b2ad54e1SXu Kuohai ip = image + POKE_OFFSET * AARCH64_INSN_SIZE; 2139b2ad54e1SXu Kuohai } 2140b2ad54e1SXu Kuohai 2141b2ad54e1SXu Kuohai /* long jump is only possible at bpf prog entry */ 2142b2ad54e1SXu Kuohai if (WARN_ON((is_long_jump(ip, new_addr) || is_long_jump(ip, old_addr)) && 2143b2ad54e1SXu Kuohai !poking_bpf_entry)) 2144b2ad54e1SXu Kuohai return -EINVAL; 2145b2ad54e1SXu Kuohai 2146b2ad54e1SXu Kuohai if (poke_type == BPF_MOD_CALL) 2147b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_LINK; 2148b2ad54e1SXu Kuohai else 2149b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_NOLINK; 2150b2ad54e1SXu Kuohai 2151b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, old_addr, plt, &old_insn) < 0) 2152b2ad54e1SXu Kuohai return -EFAULT; 2153b2ad54e1SXu Kuohai 2154b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, new_addr, plt, &new_insn) < 0) 2155b2ad54e1SXu Kuohai return -EFAULT; 2156b2ad54e1SXu Kuohai 2157b2ad54e1SXu Kuohai if (is_long_jump(ip, new_addr)) 2158b2ad54e1SXu Kuohai plt_target = (u64)new_addr; 2159b2ad54e1SXu Kuohai else if (is_long_jump(ip, old_addr)) 2160b2ad54e1SXu Kuohai /* if the old target is a long jump and the new target is not, 2161b2ad54e1SXu Kuohai * restore the plt target to dummy_tramp, so there is always a 2162b2ad54e1SXu Kuohai * legal and harmless address stored in plt target, and we'll 2163b2ad54e1SXu Kuohai * never jump from plt to an unknown place. 2164b2ad54e1SXu Kuohai */ 2165b2ad54e1SXu Kuohai plt_target = (u64)&dummy_tramp; 2166b2ad54e1SXu Kuohai 2167b2ad54e1SXu Kuohai if (plt_target) { 2168b2ad54e1SXu Kuohai /* non-zero plt_target indicates we're patching a bpf prog, 2169b2ad54e1SXu Kuohai * which is read only. 2170b2ad54e1SXu Kuohai */ 2171b2ad54e1SXu Kuohai if (set_memory_rw(PAGE_MASK & ((uintptr_t)&plt->target), 1)) 2172b2ad54e1SXu Kuohai return -EFAULT; 2173b2ad54e1SXu Kuohai WRITE_ONCE(plt->target, plt_target); 2174b2ad54e1SXu Kuohai set_memory_ro(PAGE_MASK & ((uintptr_t)&plt->target), 1); 2175b2ad54e1SXu Kuohai /* since plt target points to either the new trampoline 2176b2ad54e1SXu Kuohai * or dummy_tramp, even if another CPU reads the old plt 2177b2ad54e1SXu Kuohai * target value before fetching the bl instruction to plt, 2178b2ad54e1SXu Kuohai * it will be brought back by dummy_tramp, so no barrier is 2179b2ad54e1SXu Kuohai * required here. 2180b2ad54e1SXu Kuohai */ 2181b2ad54e1SXu Kuohai } 2182b2ad54e1SXu Kuohai 2183b2ad54e1SXu Kuohai /* if the old target and the new target are both long jumps, no 2184b2ad54e1SXu Kuohai * patching is required 2185b2ad54e1SXu Kuohai */ 2186b2ad54e1SXu Kuohai if (old_insn == new_insn) 2187b2ad54e1SXu Kuohai return 0; 2188b2ad54e1SXu Kuohai 2189b2ad54e1SXu Kuohai mutex_lock(&text_mutex); 2190b2ad54e1SXu Kuohai if (aarch64_insn_read(ip, &replaced)) { 2191b2ad54e1SXu Kuohai ret = -EFAULT; 2192b2ad54e1SXu Kuohai goto out; 2193b2ad54e1SXu Kuohai } 2194b2ad54e1SXu Kuohai 2195b2ad54e1SXu Kuohai if (replaced != old_insn) { 2196b2ad54e1SXu Kuohai ret = -EFAULT; 2197b2ad54e1SXu Kuohai goto out; 2198b2ad54e1SXu Kuohai } 2199b2ad54e1SXu Kuohai 2200b2ad54e1SXu Kuohai /* We call aarch64_insn_patch_text_nosync() to replace instruction 2201b2ad54e1SXu Kuohai * atomically, so no other CPUs will fetch a half-new and half-old 2202b2ad54e1SXu Kuohai * instruction. But there is chance that another CPU executes the 2203b2ad54e1SXu Kuohai * old instruction after the patching operation finishes (e.g., 2204b2ad54e1SXu Kuohai * pipeline not flushed, or icache not synchronized yet). 2205b2ad54e1SXu Kuohai * 2206b2ad54e1SXu Kuohai * 1. when a new trampoline is attached, it is not a problem for 2207b2ad54e1SXu Kuohai * different CPUs to jump to different trampolines temporarily. 2208b2ad54e1SXu Kuohai * 2209b2ad54e1SXu Kuohai * 2. when an old trampoline is freed, we should wait for all other 2210b2ad54e1SXu Kuohai * CPUs to exit the trampoline and make sure the trampoline is no 2211b2ad54e1SXu Kuohai * longer reachable, since bpf_tramp_image_put() function already 2212b2ad54e1SXu Kuohai * uses percpu_ref and task-based rcu to do the sync, no need to call 2213b2ad54e1SXu Kuohai * the sync version here, see bpf_tramp_image_put() for details. 2214b2ad54e1SXu Kuohai */ 2215b2ad54e1SXu Kuohai ret = aarch64_insn_patch_text_nosync(ip, new_insn); 2216b2ad54e1SXu Kuohai out: 2217b2ad54e1SXu Kuohai mutex_unlock(&text_mutex); 2218b2ad54e1SXu Kuohai 2219b2ad54e1SXu Kuohai return ret; 2220b2ad54e1SXu Kuohai } 2221