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) 32339af577SPuranjay Mohan #define ARENA_VM_START (MAX_BPF_JIT_REG + 5) 33e54bcde3SZi Shen Lim 341902472bSHou Tao #define check_imm(bits, imm) do { \ 351902472bSHou Tao if ((((imm) > 0) && ((imm) >> (bits))) || \ 361902472bSHou Tao (((imm) < 0) && (~(imm) >> (bits)))) { \ 371902472bSHou Tao pr_info("[%2d] imm=%d(0x%x) out of range\n", \ 381902472bSHou Tao i, imm, imm); \ 391902472bSHou Tao return -EINVAL; \ 401902472bSHou Tao } \ 411902472bSHou Tao } while (0) 421902472bSHou Tao #define check_imm19(imm) check_imm(19, imm) 431902472bSHou Tao #define check_imm26(imm) check_imm(26, imm) 441902472bSHou Tao 45e54bcde3SZi Shen Lim /* Map BPF registers to A64 registers */ 46e54bcde3SZi Shen Lim static const int bpf2a64[] = { 47e54bcde3SZi Shen Lim /* return value from in-kernel function, and exit value from eBPF */ 48e54bcde3SZi Shen Lim [BPF_REG_0] = A64_R(7), 49e54bcde3SZi Shen Lim /* arguments from eBPF program to in-kernel function */ 50e54bcde3SZi Shen Lim [BPF_REG_1] = A64_R(0), 51e54bcde3SZi Shen Lim [BPF_REG_2] = A64_R(1), 52e54bcde3SZi Shen Lim [BPF_REG_3] = A64_R(2), 53e54bcde3SZi Shen Lim [BPF_REG_4] = A64_R(3), 54e54bcde3SZi Shen Lim [BPF_REG_5] = A64_R(4), 55e54bcde3SZi Shen Lim /* callee saved registers that in-kernel function will preserve */ 56e54bcde3SZi Shen Lim [BPF_REG_6] = A64_R(19), 57e54bcde3SZi Shen Lim [BPF_REG_7] = A64_R(20), 58e54bcde3SZi Shen Lim [BPF_REG_8] = A64_R(21), 59e54bcde3SZi Shen Lim [BPF_REG_9] = A64_R(22), 60e54bcde3SZi Shen Lim /* read-only frame pointer to access stack */ 61ec0738dbSYang Shi [BPF_REG_FP] = A64_R(25), 6206edc59cSChristoph Hellwig /* temporary registers for BPF JIT */ 634c1cd4fdSYang Shi [TMP_REG_1] = A64_R(10), 644c1cd4fdSYang Shi [TMP_REG_2] = A64_R(11), 657005cadeSDaniel Borkmann [TMP_REG_3] = A64_R(12), 66ddb55992SZi Shen Lim /* tail_call_cnt */ 67ddb55992SZi Shen Lim [TCALL_CNT] = A64_R(26), 6826eb042eSDaniel Borkmann /* temporary register for blinding constants */ 6926eb042eSDaniel Borkmann [BPF_REG_AX] = A64_R(9), 705b3d19b9SXu Kuohai [FP_BOTTOM] = A64_R(27), 71339af577SPuranjay Mohan /* callee saved register for kern_vm_start address */ 72339af577SPuranjay Mohan [ARENA_VM_START] = A64_R(28), 73e54bcde3SZi Shen Lim }; 74e54bcde3SZi Shen Lim 75e54bcde3SZi Shen Lim struct jit_ctx { 76e54bcde3SZi Shen Lim const struct bpf_prog *prog; 77e54bcde3SZi Shen Lim int idx; 7851c9fbb1SZi Shen Lim int epilogue_offset; 79e54bcde3SZi Shen Lim int *offset; 8080083428SJean-Philippe Brucker int exentry_idx; 81425e1ed7SLuc Van Oostenryck __le32 *image; 821dad391dSPuranjay Mohan __le32 *ro_image; 83f1c9eed7SDaniel Borkmann u32 stack_size; 845b3d19b9SXu Kuohai int fpb_offset; 85*4dd31243SPuranjay Mohan u64 user_vm_start; 86e54bcde3SZi Shen Lim }; 87e54bcde3SZi Shen Lim 88b2ad54e1SXu Kuohai struct bpf_plt { 89b2ad54e1SXu Kuohai u32 insn_ldr; /* load target */ 90b2ad54e1SXu Kuohai u32 insn_br; /* branch to target */ 91b2ad54e1SXu Kuohai u64 target; /* target value */ 92b2ad54e1SXu Kuohai }; 93b2ad54e1SXu Kuohai 94b2ad54e1SXu Kuohai #define PLT_TARGET_SIZE sizeof_field(struct bpf_plt, target) 95b2ad54e1SXu Kuohai #define PLT_TARGET_OFFSET offsetof(struct bpf_plt, target) 96b2ad54e1SXu Kuohai 97e54bcde3SZi Shen Lim static inline void emit(const u32 insn, struct jit_ctx *ctx) 98e54bcde3SZi Shen Lim { 99e54bcde3SZi Shen Lim if (ctx->image != NULL) 100e54bcde3SZi Shen Lim ctx->image[ctx->idx] = cpu_to_le32(insn); 101e54bcde3SZi Shen Lim 102e54bcde3SZi Shen Lim ctx->idx++; 103e54bcde3SZi Shen Lim } 104e54bcde3SZi Shen Lim 105e54bcde3SZi Shen Lim static inline void emit_a64_mov_i(const int is64, const int reg, 106e54bcde3SZi Shen Lim const s32 val, struct jit_ctx *ctx) 107e54bcde3SZi Shen Lim { 108e54bcde3SZi Shen Lim u16 hi = val >> 16; 109e54bcde3SZi Shen Lim u16 lo = val & 0xffff; 110e54bcde3SZi Shen Lim 111e54bcde3SZi Shen Lim if (hi & 0x8000) { 112e54bcde3SZi Shen Lim if (hi == 0xffff) { 113e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx); 114e54bcde3SZi Shen Lim } else { 115e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx); 1166d2eea6fSDaniel Borkmann if (lo != 0xffff) 117e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, lo, 0), ctx); 118e54bcde3SZi Shen Lim } 119e54bcde3SZi Shen Lim } else { 120e54bcde3SZi Shen Lim emit(A64_MOVZ(is64, reg, lo, 0), ctx); 121e54bcde3SZi Shen Lim if (hi) 122e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, hi, 16), ctx); 123e54bcde3SZi Shen Lim } 124e54bcde3SZi Shen Lim } 125e54bcde3SZi Shen Lim 1266d2eea6fSDaniel Borkmann static int i64_i16_blocks(const u64 val, bool inverse) 1276d2eea6fSDaniel Borkmann { 1286d2eea6fSDaniel Borkmann return (((val >> 0) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1296d2eea6fSDaniel Borkmann (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1306d2eea6fSDaniel Borkmann (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1316d2eea6fSDaniel Borkmann (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000)); 1326d2eea6fSDaniel Borkmann } 1336d2eea6fSDaniel Borkmann 1346d2eea6fSDaniel Borkmann static inline void emit_a64_mov_i64(const int reg, const u64 val, 1356d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1366d2eea6fSDaniel Borkmann { 1376d2eea6fSDaniel Borkmann u64 nrm_tmp = val, rev_tmp = ~val; 1386d2eea6fSDaniel Borkmann bool inverse; 1396d2eea6fSDaniel Borkmann int shift; 1406d2eea6fSDaniel Borkmann 1416d2eea6fSDaniel Borkmann if (!(nrm_tmp >> 32)) 1426d2eea6fSDaniel Borkmann return emit_a64_mov_i(0, reg, (u32)val, ctx); 1436d2eea6fSDaniel Borkmann 1446d2eea6fSDaniel Borkmann inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false); 1456d2eea6fSDaniel Borkmann shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) : 1466d2eea6fSDaniel Borkmann (fls64(nrm_tmp) - 1)), 16), 0); 1476d2eea6fSDaniel Borkmann if (inverse) 1486d2eea6fSDaniel Borkmann emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx); 1496d2eea6fSDaniel Borkmann else 1506d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1516d2eea6fSDaniel Borkmann shift -= 16; 1526d2eea6fSDaniel Borkmann while (shift >= 0) { 1536d2eea6fSDaniel Borkmann if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000)) 1546d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1556d2eea6fSDaniel Borkmann shift -= 16; 1566d2eea6fSDaniel Borkmann } 1576d2eea6fSDaniel Borkmann } 1586d2eea6fSDaniel Borkmann 159b2ad54e1SXu Kuohai static inline void emit_bti(u32 insn, struct jit_ctx *ctx) 160b2ad54e1SXu Kuohai { 161b2ad54e1SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) 162b2ad54e1SXu Kuohai emit(insn, ctx); 163b2ad54e1SXu Kuohai } 164b2ad54e1SXu Kuohai 1656d2eea6fSDaniel Borkmann /* 166cc2b8ed1SArd Biesheuvel * Kernel addresses in the vmalloc space use at most 48 bits, and the 167cc2b8ed1SArd Biesheuvel * remaining bits are guaranteed to be 0x1. So we can compose the address 168cc2b8ed1SArd Biesheuvel * with a fixed length movn/movk/movk sequence. 1696d2eea6fSDaniel Borkmann */ 1706d2eea6fSDaniel Borkmann static inline void emit_addr_mov_i64(const int reg, const u64 val, 1716d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1726d2eea6fSDaniel Borkmann { 1736d2eea6fSDaniel Borkmann u64 tmp = val; 1746d2eea6fSDaniel Borkmann int shift = 0; 1756d2eea6fSDaniel Borkmann 176cc2b8ed1SArd Biesheuvel emit(A64_MOVN(1, reg, ~tmp & 0xffff, shift), ctx); 177cc2b8ed1SArd Biesheuvel while (shift < 32) { 1786d2eea6fSDaniel Borkmann tmp >>= 16; 1796d2eea6fSDaniel Borkmann shift += 16; 1806d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx); 1816d2eea6fSDaniel Borkmann } 1826d2eea6fSDaniel Borkmann } 1836d2eea6fSDaniel Borkmann 184efc9909fSXu Kuohai static inline void emit_call(u64 target, struct jit_ctx *ctx) 185efc9909fSXu Kuohai { 186efc9909fSXu Kuohai u8 tmp = bpf2a64[TMP_REG_1]; 187efc9909fSXu Kuohai 188efc9909fSXu Kuohai emit_addr_mov_i64(tmp, target, ctx); 189efc9909fSXu Kuohai emit(A64_BLR(tmp), ctx); 190efc9909fSXu Kuohai } 191efc9909fSXu Kuohai 19232f6865cSIlias Apalodimas static inline int bpf2a64_offset(int bpf_insn, int off, 193e54bcde3SZi Shen Lim const struct jit_ctx *ctx) 194e54bcde3SZi Shen Lim { 19532f6865cSIlias Apalodimas /* BPF JMP offset is relative to the next instruction */ 19632f6865cSIlias Apalodimas bpf_insn++; 19732f6865cSIlias Apalodimas /* 19832f6865cSIlias Apalodimas * Whereas arm64 branch instructions encode the offset 19932f6865cSIlias Apalodimas * from the branch itself, so we must subtract 1 from the 20032f6865cSIlias Apalodimas * instruction offset. 20132f6865cSIlias Apalodimas */ 20232f6865cSIlias Apalodimas return ctx->offset[bpf_insn + off] - (ctx->offset[bpf_insn] - 1); 203e54bcde3SZi Shen Lim } 204e54bcde3SZi Shen Lim 205b569c1c6SDaniel Borkmann static void jit_fill_hole(void *area, unsigned int size) 206b569c1c6SDaniel Borkmann { 207425e1ed7SLuc Van Oostenryck __le32 *ptr; 208b569c1c6SDaniel Borkmann /* We are guaranteed to have aligned memory. */ 209b569c1c6SDaniel Borkmann for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 210b569c1c6SDaniel Borkmann *ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT); 211b569c1c6SDaniel Borkmann } 212b569c1c6SDaniel Borkmann 2131dad391dSPuranjay Mohan int bpf_arch_text_invalidate(void *dst, size_t len) 2141dad391dSPuranjay Mohan { 2151dad391dSPuranjay Mohan if (!aarch64_insn_set(dst, AARCH64_BREAK_FAULT, len)) 2161dad391dSPuranjay Mohan return -EINVAL; 2171dad391dSPuranjay Mohan 2181dad391dSPuranjay Mohan return 0; 2191dad391dSPuranjay Mohan } 2201dad391dSPuranjay Mohan 221e54bcde3SZi Shen Lim static inline int epilogue_offset(const struct jit_ctx *ctx) 222e54bcde3SZi Shen Lim { 22351c9fbb1SZi Shen Lim int to = ctx->epilogue_offset; 22451c9fbb1SZi Shen Lim int from = ctx->idx; 225e54bcde3SZi Shen Lim 226e54bcde3SZi Shen Lim return to - from; 227e54bcde3SZi Shen Lim } 228e54bcde3SZi Shen Lim 229fd868f14SLuke Nelson static bool is_addsub_imm(u32 imm) 230fd868f14SLuke Nelson { 231fd868f14SLuke Nelson /* Either imm12 or shifted imm12. */ 232fd868f14SLuke Nelson return !(imm & ~0xfff) || !(imm & ~0xfff000); 233fd868f14SLuke Nelson } 234fd868f14SLuke Nelson 2357db6c0f1SXu Kuohai /* 2367db6c0f1SXu Kuohai * There are 3 types of AArch64 LDR/STR (immediate) instruction: 2377db6c0f1SXu Kuohai * Post-index, Pre-index, Unsigned offset. 2387db6c0f1SXu Kuohai * 2397db6c0f1SXu Kuohai * For BPF ldr/str, the "unsigned offset" type is sufficient. 2407db6c0f1SXu Kuohai * 2417db6c0f1SXu Kuohai * "Unsigned offset" type LDR(immediate) format: 2427db6c0f1SXu Kuohai * 2437db6c0f1SXu Kuohai * 3 2 1 0 2447db6c0f1SXu 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 2457db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2467db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 1| imm12 | Rn | Rt | 2477db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2487db6c0f1SXu Kuohai * scale 2497db6c0f1SXu Kuohai * 2507db6c0f1SXu Kuohai * "Unsigned offset" type STR(immediate) format: 2517db6c0f1SXu Kuohai * 3 2 1 0 2527db6c0f1SXu 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 2537db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2547db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 0| imm12 | Rn | Rt | 2557db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2567db6c0f1SXu Kuohai * scale 2577db6c0f1SXu Kuohai * 2587db6c0f1SXu Kuohai * The offset is calculated from imm12 and scale in the following way: 2597db6c0f1SXu Kuohai * 2607db6c0f1SXu Kuohai * offset = (u64)imm12 << scale 2617db6c0f1SXu Kuohai */ 2625b3d19b9SXu Kuohai static bool is_lsi_offset(int offset, int scale) 2637db6c0f1SXu Kuohai { 2647db6c0f1SXu Kuohai if (offset < 0) 2657db6c0f1SXu Kuohai return false; 2667db6c0f1SXu Kuohai 2677db6c0f1SXu Kuohai if (offset > (0xFFF << scale)) 2687db6c0f1SXu Kuohai return false; 2697db6c0f1SXu Kuohai 2707db6c0f1SXu Kuohai if (offset & ((1 << scale) - 1)) 2717db6c0f1SXu Kuohai return false; 2727db6c0f1SXu Kuohai 2737db6c0f1SXu Kuohai return true; 2747db6c0f1SXu Kuohai } 2757db6c0f1SXu Kuohai 276b2ad54e1SXu Kuohai /* generated prologue: 277b2ad54e1SXu Kuohai * bti c // if CONFIG_ARM64_BTI_KERNEL 278b2ad54e1SXu Kuohai * mov x9, lr 279b2ad54e1SXu Kuohai * nop // POKE_OFFSET 280b2ad54e1SXu Kuohai * paciasp // if CONFIG_ARM64_PTR_AUTH_KERNEL 281b2ad54e1SXu Kuohai * stp x29, lr, [sp, #-16]! 282b2ad54e1SXu Kuohai * mov x29, sp 283b2ad54e1SXu Kuohai * stp x19, x20, [sp, #-16]! 284b2ad54e1SXu Kuohai * stp x21, x22, [sp, #-16]! 285b2ad54e1SXu Kuohai * stp x25, x26, [sp, #-16]! 286b2ad54e1SXu Kuohai * stp x27, x28, [sp, #-16]! 287b2ad54e1SXu Kuohai * mov x25, sp 288b2ad54e1SXu Kuohai * mov tcc, #0 289b2ad54e1SXu Kuohai * // PROLOGUE_OFFSET 290b2ad54e1SXu Kuohai */ 291b2ad54e1SXu Kuohai 292b2ad54e1SXu Kuohai #define BTI_INSNS (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) ? 1 : 0) 293b2ad54e1SXu Kuohai #define PAC_INSNS (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) ? 1 : 0) 294b2ad54e1SXu Kuohai 295b2ad54e1SXu Kuohai /* Offset of nop instruction in bpf prog entry to be poked */ 296b2ad54e1SXu Kuohai #define POKE_OFFSET (BTI_INSNS + 1) 297b2ad54e1SXu Kuohai 298a2284d91SDaniel Borkmann /* Tail call offset to jump into */ 299b2ad54e1SXu Kuohai #define PROLOGUE_OFFSET (BTI_INSNS + 2 + PAC_INSNS + 8) 300ddb55992SZi Shen Lim 30122fc0e80SPuranjay Mohan static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf, 302339af577SPuranjay Mohan bool is_exception_cb, u64 arena_vm_start) 303e54bcde3SZi Shen Lim { 304f1c9eed7SDaniel Borkmann const struct bpf_prog *prog = ctx->prog; 3059af27da6SKumar Kartikeya Dwivedi const bool is_main_prog = !bpf_is_subprog(prog); 306e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 307e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 308e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 309e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 310e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 311ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 3125b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 313339af577SPuranjay Mohan const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; 314ddb55992SZi Shen Lim const int idx0 = ctx->idx; 315ddb55992SZi Shen Lim int cur_offset; 316e54bcde3SZi Shen Lim 317ec0738dbSYang Shi /* 318ec0738dbSYang Shi * BPF prog stack layout 319ec0738dbSYang Shi * 320ec0738dbSYang Shi * high 321ec0738dbSYang Shi * original A64_SP => 0:+-----+ BPF prologue 322ec0738dbSYang Shi * |FP/LR| 323ec0738dbSYang Shi * current A64_FP => -16:+-----+ 324ec0738dbSYang Shi * | ... | callee saved registers 3254c1cd4fdSYang Shi * BPF fp register => -64:+-----+ <= (BPF_FP) 326ec0738dbSYang Shi * | | 327ec0738dbSYang Shi * | ... | BPF prog stack 328ec0738dbSYang Shi * | | 329f1c9eed7SDaniel Borkmann * +-----+ <= (BPF_FP - prog->aux->stack_depth) 33009ece3d0SDaniel Borkmann * |RSVD | padding 331f1c9eed7SDaniel Borkmann * current A64_SP => +-----+ <= (BPF_FP - ctx->stack_size) 332ec0738dbSYang Shi * | | 333ec0738dbSYang Shi * | ... | Function call stack 334ec0738dbSYang Shi * | | 335ec0738dbSYang Shi * +-----+ 336ec0738dbSYang Shi * low 337ec0738dbSYang Shi * 338ec0738dbSYang Shi */ 339ec0738dbSYang Shi 340a3f25d61SAlexander Duyck /* bpf function may be invoked by 3 instruction types: 341a3f25d61SAlexander Duyck * 1. bl, attached via freplace to bpf prog via short jump 342a3f25d61SAlexander Duyck * 2. br, attached via freplace to bpf prog via long jump 343a3f25d61SAlexander Duyck * 3. blr, working as a function pointer, used by emit_call. 344a3f25d61SAlexander Duyck * So BTI_JC should used here to support both br and blr. 345a3f25d61SAlexander Duyck */ 346a3f25d61SAlexander Duyck emit_bti(A64_BTI_JC, ctx); 347b2ad54e1SXu Kuohai 348b2ad54e1SXu Kuohai emit(A64_MOV(1, A64_R(9), A64_LR), ctx); 349b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 350b2ad54e1SXu Kuohai 35122fc0e80SPuranjay Mohan if (!is_exception_cb) { 352042152c2SXu Kuohai /* Sign lr */ 353042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 354042152c2SXu Kuohai emit(A64_PACIASP, ctx); 355ec0738dbSYang Shi /* Save FP and LR registers to stay align with ARM64 AAPCS */ 356ec0738dbSYang Shi emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 357ec0738dbSYang Shi emit(A64_MOV(1, A64_FP, A64_SP), ctx); 358ec0738dbSYang Shi 359ddb55992SZi Shen Lim /* Save callee-saved registers */ 360e54bcde3SZi Shen Lim emit(A64_PUSH(r6, r7, A64_SP), ctx); 361e54bcde3SZi Shen Lim emit(A64_PUSH(r8, r9, A64_SP), ctx); 362ddb55992SZi Shen Lim emit(A64_PUSH(fp, tcc, A64_SP), ctx); 3635b3d19b9SXu Kuohai emit(A64_PUSH(fpb, A64_R(28), A64_SP), ctx); 36422fc0e80SPuranjay Mohan } else { 36522fc0e80SPuranjay Mohan /* 36622fc0e80SPuranjay Mohan * Exception callback receives FP of Main Program as third 36722fc0e80SPuranjay Mohan * parameter 36822fc0e80SPuranjay Mohan */ 36922fc0e80SPuranjay Mohan emit(A64_MOV(1, A64_FP, A64_R(2)), ctx); 37022fc0e80SPuranjay Mohan /* 37122fc0e80SPuranjay Mohan * Main Program already pushed the frame record and the 37222fc0e80SPuranjay Mohan * callee-saved registers. The exception callback will not push 37322fc0e80SPuranjay Mohan * anything and re-use the main program's stack. 37422fc0e80SPuranjay Mohan * 37522fc0e80SPuranjay Mohan * 10 registers are on the stack 37622fc0e80SPuranjay Mohan */ 37722fc0e80SPuranjay Mohan emit(A64_SUB_I(1, A64_SP, A64_FP, 80), ctx); 37822fc0e80SPuranjay Mohan } 379e54bcde3SZi Shen Lim 380ddb55992SZi Shen Lim /* Set up BPF prog stack base register */ 381e54bcde3SZi Shen Lim emit(A64_MOV(1, fp, A64_SP), ctx); 382e54bcde3SZi Shen Lim 383d4609a5dSJakub Sitnicki if (!ebpf_from_cbpf && is_main_prog) { 384ddb55992SZi Shen Lim /* Initialize tail_call_cnt */ 385ddb55992SZi Shen Lim emit(A64_MOVZ(1, tcc, 0, 0), ctx); 386ddb55992SZi Shen Lim 387ddb55992SZi Shen Lim cur_offset = ctx->idx - idx0; 388ddb55992SZi Shen Lim if (cur_offset != PROLOGUE_OFFSET) { 389ddb55992SZi Shen Lim pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 390ddb55992SZi Shen Lim cur_offset, PROLOGUE_OFFSET); 391ddb55992SZi Shen Lim return -1; 392ddb55992SZi Shen Lim } 393fa76cfe6SMark Brown 394fa76cfe6SMark Brown /* BTI landing pad for the tail call, done with a BR */ 395b2ad54e1SXu Kuohai emit_bti(A64_BTI_J, ctx); 39656ea6a8bSDaniel Borkmann } 397a2284d91SDaniel Borkmann 39822fc0e80SPuranjay Mohan /* 39922fc0e80SPuranjay Mohan * Program acting as exception boundary should save all ARM64 40022fc0e80SPuranjay Mohan * Callee-saved registers as the exception callback needs to recover 40122fc0e80SPuranjay Mohan * all ARM64 Callee-saved registers in its epilogue. 40222fc0e80SPuranjay Mohan */ 40322fc0e80SPuranjay Mohan if (prog->aux->exception_boundary) { 40422fc0e80SPuranjay Mohan /* 40522fc0e80SPuranjay Mohan * As we are pushing two more registers, BPF_FP should be moved 40622fc0e80SPuranjay Mohan * 16 bytes 40722fc0e80SPuranjay Mohan */ 40822fc0e80SPuranjay Mohan emit(A64_SUB_I(1, fp, fp, 16), ctx); 40922fc0e80SPuranjay Mohan emit(A64_PUSH(A64_R(23), A64_R(24), A64_SP), ctx); 41022fc0e80SPuranjay Mohan } 41122fc0e80SPuranjay Mohan 4125b3d19b9SXu Kuohai emit(A64_SUB_I(1, fpb, fp, ctx->fpb_offset), ctx); 4135b3d19b9SXu Kuohai 4143f287098STiezhu Yang /* Stack must be multiples of 16B */ 4153f287098STiezhu Yang ctx->stack_size = round_up(prog->aux->stack_depth, 16); 416a2284d91SDaniel Borkmann 417a2284d91SDaniel Borkmann /* Set up function call stack */ 418a2284d91SDaniel Borkmann emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 419339af577SPuranjay Mohan 420339af577SPuranjay Mohan if (arena_vm_start) 421339af577SPuranjay Mohan emit_a64_mov_i64(arena_vm_base, arena_vm_start, ctx); 422339af577SPuranjay Mohan 423ddb55992SZi Shen Lim return 0; 424ddb55992SZi Shen Lim } 425ddb55992SZi Shen Lim 426ddb55992SZi Shen Lim static int out_offset = -1; /* initialized on the first pass of build_body() */ 427ddb55992SZi Shen Lim static int emit_bpf_tail_call(struct jit_ctx *ctx) 428ddb55992SZi Shen Lim { 429ddb55992SZi Shen Lim /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 430ddb55992SZi Shen Lim const u8 r2 = bpf2a64[BPF_REG_2]; 431ddb55992SZi Shen Lim const u8 r3 = bpf2a64[BPF_REG_3]; 432ddb55992SZi Shen Lim 433ddb55992SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 434ddb55992SZi Shen Lim const u8 prg = bpf2a64[TMP_REG_2]; 435ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 436ddb55992SZi Shen Lim const int idx0 = ctx->idx; 437ddb55992SZi Shen Lim #define cur_offset (ctx->idx - idx0) 438ddb55992SZi Shen Lim #define jmp_offset (out_offset - (cur_offset)) 439ddb55992SZi Shen Lim size_t off; 440ddb55992SZi Shen Lim 441ddb55992SZi Shen Lim /* if (index >= array->map.max_entries) 442ddb55992SZi Shen Lim * goto out; 443ddb55992SZi Shen Lim */ 444ddb55992SZi Shen Lim off = offsetof(struct bpf_array, map.max_entries); 445ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 446ddb55992SZi Shen Lim emit(A64_LDR32(tmp, r2, tmp), ctx); 44716338a9bSDaniel Borkmann emit(A64_MOV(0, r3, r3), ctx); 448ddb55992SZi Shen Lim emit(A64_CMP(0, r3, tmp), ctx); 44916338a9bSDaniel Borkmann emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 450ddb55992SZi Shen Lim 451ebf7f6f0STiezhu Yang /* 452ebf7f6f0STiezhu Yang * if (tail_call_cnt >= MAX_TAIL_CALL_CNT) 453ddb55992SZi Shen Lim * goto out; 454ddb55992SZi Shen Lim * tail_call_cnt++; 455ddb55992SZi Shen Lim */ 456ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 457ddb55992SZi Shen Lim emit(A64_CMP(1, tcc, tmp), ctx); 458ebf7f6f0STiezhu Yang emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 459ddb55992SZi Shen Lim emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 460ddb55992SZi Shen Lim 461ddb55992SZi Shen Lim /* prog = array->ptrs[index]; 462ddb55992SZi Shen Lim * if (prog == NULL) 463ddb55992SZi Shen Lim * goto out; 464ddb55992SZi Shen Lim */ 465ddb55992SZi Shen Lim off = offsetof(struct bpf_array, ptrs); 466ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 467d8b54110SDaniel Borkmann emit(A64_ADD(1, tmp, r2, tmp), ctx); 468d8b54110SDaniel Borkmann emit(A64_LSL(1, prg, r3, 3), ctx); 469d8b54110SDaniel Borkmann emit(A64_LDR64(prg, tmp, prg), ctx); 470ddb55992SZi Shen Lim emit(A64_CBZ(1, prg, jmp_offset), ctx); 471ddb55992SZi Shen Lim 472a2284d91SDaniel Borkmann /* goto *(prog->bpf_func + prologue_offset); */ 473ddb55992SZi Shen Lim off = offsetof(struct bpf_prog, bpf_func); 474ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 475ddb55992SZi Shen Lim emit(A64_LDR64(tmp, prg, tmp), ctx); 476ddb55992SZi Shen Lim emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 477a2284d91SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 478ddb55992SZi Shen Lim emit(A64_BR(tmp), ctx); 479ddb55992SZi Shen Lim 480ddb55992SZi Shen Lim /* out: */ 481ddb55992SZi Shen Lim if (out_offset == -1) 482ddb55992SZi Shen Lim out_offset = cur_offset; 483ddb55992SZi Shen Lim if (cur_offset != out_offset) { 484ddb55992SZi Shen Lim pr_err_once("tail_call out_offset = %d, expected %d!\n", 485ddb55992SZi Shen Lim cur_offset, out_offset); 486ddb55992SZi Shen Lim return -1; 487ddb55992SZi Shen Lim } 488ddb55992SZi Shen Lim return 0; 489ddb55992SZi Shen Lim #undef cur_offset 490ddb55992SZi Shen Lim #undef jmp_offset 491e54bcde3SZi Shen Lim } 492e54bcde3SZi Shen Lim 4931902472bSHou Tao #ifdef CONFIG_ARM64_LSE_ATOMICS 4941902472bSHou Tao static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 4951902472bSHou Tao { 4961902472bSHou Tao const u8 code = insn->code; 4971902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 4981902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 4991902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 5001902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 5011902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 5021902472bSHou Tao const s16 off = insn->off; 5031902472bSHou Tao u8 reg; 5041902472bSHou Tao 5051902472bSHou Tao if (!off) { 5061902472bSHou Tao reg = dst; 5071902472bSHou Tao } else { 5081902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 5091902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 5101902472bSHou Tao reg = tmp; 5111902472bSHou Tao } 5121902472bSHou Tao 5131902472bSHou Tao switch (insn->imm) { 5141902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 5151902472bSHou Tao case BPF_ADD: 5161902472bSHou Tao emit(A64_STADD(isdw, reg, src), ctx); 5171902472bSHou Tao break; 5181902472bSHou Tao case BPF_AND: 5191902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 5201902472bSHou Tao emit(A64_STCLR(isdw, reg, tmp2), ctx); 5211902472bSHou Tao break; 5221902472bSHou Tao case BPF_OR: 5231902472bSHou Tao emit(A64_STSET(isdw, reg, src), ctx); 5241902472bSHou Tao break; 5251902472bSHou Tao case BPF_XOR: 5261902472bSHou Tao emit(A64_STEOR(isdw, reg, src), ctx); 5271902472bSHou Tao break; 5281902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 5291902472bSHou Tao case BPF_ADD | BPF_FETCH: 5301902472bSHou Tao emit(A64_LDADDAL(isdw, src, reg, src), ctx); 5311902472bSHou Tao break; 5321902472bSHou Tao case BPF_AND | BPF_FETCH: 5331902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 5341902472bSHou Tao emit(A64_LDCLRAL(isdw, src, reg, tmp2), ctx); 5351902472bSHou Tao break; 5361902472bSHou Tao case BPF_OR | BPF_FETCH: 5371902472bSHou Tao emit(A64_LDSETAL(isdw, src, reg, src), ctx); 5381902472bSHou Tao break; 5391902472bSHou Tao case BPF_XOR | BPF_FETCH: 5401902472bSHou Tao emit(A64_LDEORAL(isdw, src, reg, src), ctx); 5411902472bSHou Tao break; 5421902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 5431902472bSHou Tao case BPF_XCHG: 5441902472bSHou Tao emit(A64_SWPAL(isdw, src, reg, src), ctx); 5451902472bSHou Tao break; 5461902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 5471902472bSHou Tao case BPF_CMPXCHG: 5481902472bSHou Tao emit(A64_CASAL(isdw, src, reg, bpf2a64[BPF_REG_0]), ctx); 5491902472bSHou Tao break; 5501902472bSHou Tao default: 5511902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", insn->imm); 5521902472bSHou Tao return -EINVAL; 5531902472bSHou Tao } 5541902472bSHou Tao 5551902472bSHou Tao return 0; 5561902472bSHou Tao } 5571902472bSHou Tao #else 5581902472bSHou Tao static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5591902472bSHou Tao { 5601902472bSHou Tao return -EINVAL; 5611902472bSHou Tao } 5621902472bSHou Tao #endif 5631902472bSHou Tao 5641902472bSHou Tao static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5651902472bSHou Tao { 5661902472bSHou Tao const u8 code = insn->code; 5671902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 5681902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 5691902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 5701902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 5711902472bSHou Tao const u8 tmp3 = bpf2a64[TMP_REG_3]; 5721902472bSHou Tao const int i = insn - ctx->prog->insnsi; 5731902472bSHou Tao const s32 imm = insn->imm; 5741902472bSHou Tao const s16 off = insn->off; 5751902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 5761902472bSHou Tao u8 reg; 5771902472bSHou Tao s32 jmp_offset; 5781902472bSHou Tao 5791902472bSHou Tao if (!off) { 5801902472bSHou Tao reg = dst; 5811902472bSHou Tao } else { 5821902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 5831902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 5841902472bSHou Tao reg = tmp; 5851902472bSHou Tao } 5861902472bSHou Tao 5871902472bSHou Tao if (imm == BPF_ADD || imm == BPF_AND || 5881902472bSHou Tao imm == BPF_OR || imm == BPF_XOR) { 5891902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 5901902472bSHou Tao emit(A64_LDXR(isdw, tmp2, reg), ctx); 5911902472bSHou Tao if (imm == BPF_ADD) 5921902472bSHou Tao emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 5931902472bSHou Tao else if (imm == BPF_AND) 5941902472bSHou Tao emit(A64_AND(isdw, tmp2, tmp2, src), ctx); 5951902472bSHou Tao else if (imm == BPF_OR) 5961902472bSHou Tao emit(A64_ORR(isdw, tmp2, tmp2, src), ctx); 5971902472bSHou Tao else 5981902472bSHou Tao emit(A64_EOR(isdw, tmp2, tmp2, src), ctx); 5991902472bSHou Tao emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); 6001902472bSHou Tao jmp_offset = -3; 6011902472bSHou Tao check_imm19(jmp_offset); 6021902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6031902472bSHou Tao } else if (imm == (BPF_ADD | BPF_FETCH) || 6041902472bSHou Tao imm == (BPF_AND | BPF_FETCH) || 6051902472bSHou Tao imm == (BPF_OR | BPF_FETCH) || 6061902472bSHou Tao imm == (BPF_XOR | BPF_FETCH)) { 6071902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 6081902472bSHou Tao const u8 ax = bpf2a64[BPF_REG_AX]; 6091902472bSHou Tao 6101902472bSHou Tao emit(A64_MOV(isdw, ax, src), ctx); 6111902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 6121902472bSHou Tao if (imm == (BPF_ADD | BPF_FETCH)) 6131902472bSHou Tao emit(A64_ADD(isdw, tmp2, src, ax), ctx); 6141902472bSHou Tao else if (imm == (BPF_AND | BPF_FETCH)) 6151902472bSHou Tao emit(A64_AND(isdw, tmp2, src, ax), ctx); 6161902472bSHou Tao else if (imm == (BPF_OR | BPF_FETCH)) 6171902472bSHou Tao emit(A64_ORR(isdw, tmp2, src, ax), ctx); 6181902472bSHou Tao else 6191902472bSHou Tao emit(A64_EOR(isdw, tmp2, src, ax), ctx); 6201902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 6211902472bSHou Tao jmp_offset = -3; 6221902472bSHou Tao check_imm19(jmp_offset); 6231902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6241902472bSHou Tao emit(A64_DMB_ISH, ctx); 6251902472bSHou Tao } else if (imm == BPF_XCHG) { 6261902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 6271902472bSHou Tao emit(A64_MOV(isdw, tmp2, src), ctx); 6281902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 6291902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 6301902472bSHou Tao jmp_offset = -2; 6311902472bSHou Tao check_imm19(jmp_offset); 6321902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6331902472bSHou Tao emit(A64_DMB_ISH, ctx); 6341902472bSHou Tao } else if (imm == BPF_CMPXCHG) { 6351902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 6361902472bSHou Tao const u8 r0 = bpf2a64[BPF_REG_0]; 6371902472bSHou Tao 6381902472bSHou Tao emit(A64_MOV(isdw, tmp2, r0), ctx); 6391902472bSHou Tao emit(A64_LDXR(isdw, r0, reg), ctx); 6401902472bSHou Tao emit(A64_EOR(isdw, tmp3, r0, tmp2), ctx); 6411902472bSHou Tao jmp_offset = 4; 6421902472bSHou Tao check_imm19(jmp_offset); 6431902472bSHou Tao emit(A64_CBNZ(isdw, tmp3, jmp_offset), ctx); 6441902472bSHou Tao emit(A64_STLXR(isdw, src, reg, tmp3), ctx); 6451902472bSHou Tao jmp_offset = -4; 6461902472bSHou Tao check_imm19(jmp_offset); 6471902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6481902472bSHou Tao emit(A64_DMB_ISH, ctx); 6491902472bSHou Tao } else { 6501902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", imm); 6511902472bSHou Tao return -EINVAL; 6521902472bSHou Tao } 6531902472bSHou Tao 6541902472bSHou Tao return 0; 6551902472bSHou Tao } 6561902472bSHou Tao 657b2ad54e1SXu Kuohai void dummy_tramp(void); 658b2ad54e1SXu Kuohai 659b2ad54e1SXu Kuohai asm ( 660b2ad54e1SXu Kuohai " .pushsection .text, \"ax\", @progbits\n" 66133f32e50SNathan Chancellor " .global dummy_tramp\n" 662b2ad54e1SXu Kuohai " .type dummy_tramp, %function\n" 663b2ad54e1SXu Kuohai "dummy_tramp:" 664b2ad54e1SXu Kuohai #if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) 665b2ad54e1SXu Kuohai " bti j\n" /* dummy_tramp is called via "br x10" */ 666b2ad54e1SXu Kuohai #endif 667339ed900SXu Kuohai " mov x10, x30\n" 668339ed900SXu Kuohai " mov x30, x9\n" 669b2ad54e1SXu Kuohai " ret x10\n" 670b2ad54e1SXu Kuohai " .size dummy_tramp, .-dummy_tramp\n" 671b2ad54e1SXu Kuohai " .popsection\n" 672b2ad54e1SXu Kuohai ); 673b2ad54e1SXu Kuohai 674b2ad54e1SXu Kuohai /* build a plt initialized like this: 675b2ad54e1SXu Kuohai * 676b2ad54e1SXu Kuohai * plt: 677b2ad54e1SXu Kuohai * ldr tmp, target 678b2ad54e1SXu Kuohai * br tmp 679b2ad54e1SXu Kuohai * target: 680b2ad54e1SXu Kuohai * .quad dummy_tramp 681b2ad54e1SXu Kuohai * 682b2ad54e1SXu Kuohai * when a long jump trampoline is attached, target is filled with the 683b2ad54e1SXu Kuohai * trampoline address, and when the trampoline is removed, target is 684b2ad54e1SXu Kuohai * restored to dummy_tramp address. 685b2ad54e1SXu Kuohai */ 686b2ad54e1SXu Kuohai static void build_plt(struct jit_ctx *ctx) 687b2ad54e1SXu Kuohai { 688b2ad54e1SXu Kuohai const u8 tmp = bpf2a64[TMP_REG_1]; 689b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 690b2ad54e1SXu Kuohai 691b2ad54e1SXu Kuohai /* make sure target is 64-bit aligned */ 692b2ad54e1SXu Kuohai if ((ctx->idx + PLT_TARGET_OFFSET / AARCH64_INSN_SIZE) % 2) 693b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 694b2ad54e1SXu Kuohai 695b2ad54e1SXu Kuohai plt = (struct bpf_plt *)(ctx->image + ctx->idx); 696b2ad54e1SXu Kuohai /* plt is called via bl, no BTI needed here */ 697b2ad54e1SXu Kuohai emit(A64_LDR64LIT(tmp, 2 * AARCH64_INSN_SIZE), ctx); 698b2ad54e1SXu Kuohai emit(A64_BR(tmp), ctx); 699b2ad54e1SXu Kuohai 700b2ad54e1SXu Kuohai if (ctx->image) 701b2ad54e1SXu Kuohai plt->target = (u64)&dummy_tramp; 702b2ad54e1SXu Kuohai } 703b2ad54e1SXu Kuohai 70422fc0e80SPuranjay Mohan static void build_epilogue(struct jit_ctx *ctx, bool is_exception_cb) 705e54bcde3SZi Shen Lim { 706e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 707e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 708e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 709e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 710e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 711e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 7125b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 713e54bcde3SZi Shen Lim 714e54bcde3SZi Shen Lim /* We're done with BPF stack */ 715f1c9eed7SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 716e54bcde3SZi Shen Lim 71722fc0e80SPuranjay Mohan /* 71822fc0e80SPuranjay Mohan * Program acting as exception boundary pushes R23 and R24 in addition 71922fc0e80SPuranjay Mohan * to BPF callee-saved registers. Exception callback uses the boundary 72022fc0e80SPuranjay Mohan * program's stack frame, so recover these extra registers in the above 72122fc0e80SPuranjay Mohan * two cases. 72222fc0e80SPuranjay Mohan */ 72322fc0e80SPuranjay Mohan if (ctx->prog->aux->exception_boundary || is_exception_cb) 72422fc0e80SPuranjay Mohan emit(A64_POP(A64_R(23), A64_R(24), A64_SP), ctx); 72522fc0e80SPuranjay Mohan 7265b3d19b9SXu Kuohai /* Restore x27 and x28 */ 7275b3d19b9SXu Kuohai emit(A64_POP(fpb, A64_R(28), A64_SP), ctx); 728ec0738dbSYang Shi /* Restore fs (x25) and x26 */ 729ec0738dbSYang Shi emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 730ec0738dbSYang Shi 731e54bcde3SZi Shen Lim /* Restore callee-saved register */ 732e54bcde3SZi Shen Lim emit(A64_POP(r8, r9, A64_SP), ctx); 733e54bcde3SZi Shen Lim emit(A64_POP(r6, r7, A64_SP), ctx); 734e54bcde3SZi Shen Lim 735ec0738dbSYang Shi /* Restore FP/LR registers */ 736ec0738dbSYang Shi emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 737e54bcde3SZi Shen Lim 738e54bcde3SZi Shen Lim /* Set return value */ 739e54bcde3SZi Shen Lim emit(A64_MOV(1, A64_R(0), r0), ctx); 740e54bcde3SZi Shen Lim 741042152c2SXu Kuohai /* Authenticate lr */ 742042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 743042152c2SXu Kuohai emit(A64_AUTIASP, ctx); 744042152c2SXu Kuohai 745e54bcde3SZi Shen Lim emit(A64_RET(A64_LR), ctx); 746e54bcde3SZi Shen Lim } 747e54bcde3SZi Shen Lim 74880083428SJean-Philippe Brucker #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) 74980083428SJean-Philippe Brucker #define BPF_FIXUP_REG_MASK GENMASK(31, 27) 750339af577SPuranjay Mohan #define DONT_CLEAR 5 /* Unused ARM64 register from BPF's POV */ 75180083428SJean-Philippe Brucker 752d6e2cc56SMark Rutland bool ex_handler_bpf(const struct exception_table_entry *ex, 75380083428SJean-Philippe Brucker struct pt_regs *regs) 75480083428SJean-Philippe Brucker { 75580083428SJean-Philippe Brucker off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup); 75680083428SJean-Philippe Brucker int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup); 75780083428SJean-Philippe Brucker 758339af577SPuranjay Mohan if (dst_reg != DONT_CLEAR) 75980083428SJean-Philippe Brucker regs->regs[dst_reg] = 0; 76080083428SJean-Philippe Brucker regs->pc = (unsigned long)&ex->fixup - offset; 761e8c328d7SMark Rutland return true; 76280083428SJean-Philippe Brucker } 76380083428SJean-Philippe Brucker 76480083428SJean-Philippe Brucker /* For accesses to BTF pointers, add an entry to the exception table */ 76580083428SJean-Philippe Brucker static int add_exception_handler(const struct bpf_insn *insn, 76680083428SJean-Philippe Brucker struct jit_ctx *ctx, 76780083428SJean-Philippe Brucker int dst_reg) 76880083428SJean-Philippe Brucker { 7691dad391dSPuranjay Mohan off_t ins_offset; 7701dad391dSPuranjay Mohan off_t fixup_offset; 77180083428SJean-Philippe Brucker unsigned long pc; 77280083428SJean-Philippe Brucker struct exception_table_entry *ex; 77380083428SJean-Philippe Brucker 77480083428SJean-Philippe Brucker if (!ctx->image) 77580083428SJean-Philippe Brucker /* First pass */ 77680083428SJean-Philippe Brucker return 0; 77780083428SJean-Philippe Brucker 778cc88f540SXu Kuohai if (BPF_MODE(insn->code) != BPF_PROBE_MEM && 779339af577SPuranjay Mohan BPF_MODE(insn->code) != BPF_PROBE_MEMSX && 780339af577SPuranjay Mohan BPF_MODE(insn->code) != BPF_PROBE_MEM32) 78180083428SJean-Philippe Brucker return 0; 78280083428SJean-Philippe Brucker 78380083428SJean-Philippe Brucker if (!ctx->prog->aux->extable || 78480083428SJean-Philippe Brucker WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries)) 78580083428SJean-Philippe Brucker return -EINVAL; 78680083428SJean-Philippe Brucker 78780083428SJean-Philippe Brucker ex = &ctx->prog->aux->extable[ctx->exentry_idx]; 7881dad391dSPuranjay Mohan pc = (unsigned long)&ctx->ro_image[ctx->idx - 1]; 78980083428SJean-Philippe Brucker 7901dad391dSPuranjay Mohan /* 7911dad391dSPuranjay Mohan * This is the relative offset of the instruction that may fault from 7921dad391dSPuranjay Mohan * the exception table itself. This will be written to the exception 7931dad391dSPuranjay Mohan * table and if this instruction faults, the destination register will 7941dad391dSPuranjay Mohan * be set to '0' and the execution will jump to the next instruction. 7951dad391dSPuranjay Mohan */ 7961dad391dSPuranjay Mohan ins_offset = pc - (long)&ex->insn; 7971dad391dSPuranjay Mohan if (WARN_ON_ONCE(ins_offset >= 0 || ins_offset < INT_MIN)) 79880083428SJean-Philippe Brucker return -ERANGE; 79980083428SJean-Philippe Brucker 80080083428SJean-Philippe Brucker /* 80180083428SJean-Philippe Brucker * Since the extable follows the program, the fixup offset is always 80280083428SJean-Philippe Brucker * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value 80380083428SJean-Philippe Brucker * to keep things simple, and put the destination register in the upper 80480083428SJean-Philippe Brucker * bits. We don't need to worry about buildtime or runtime sort 80580083428SJean-Philippe Brucker * modifying the upper bits because the table is already sorted, and 80680083428SJean-Philippe Brucker * isn't part of the main exception table. 8071dad391dSPuranjay Mohan * 8081dad391dSPuranjay Mohan * The fixup_offset is set to the next instruction from the instruction 8091dad391dSPuranjay Mohan * that may fault. The execution will jump to this after handling the 8101dad391dSPuranjay Mohan * fault. 81180083428SJean-Philippe Brucker */ 8121dad391dSPuranjay Mohan fixup_offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE); 8131dad391dSPuranjay Mohan if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, fixup_offset)) 81480083428SJean-Philippe Brucker return -ERANGE; 81580083428SJean-Philippe Brucker 8161dad391dSPuranjay Mohan /* 8171dad391dSPuranjay Mohan * The offsets above have been calculated using the RO buffer but we 8181dad391dSPuranjay Mohan * need to use the R/W buffer for writes. 8191dad391dSPuranjay Mohan * switch ex to rw buffer for writing. 8201dad391dSPuranjay Mohan */ 8211dad391dSPuranjay Mohan ex = (void *)ctx->image + ((void *)ex - (void *)ctx->ro_image); 8221dad391dSPuranjay Mohan 8231dad391dSPuranjay Mohan ex->insn = ins_offset; 8241dad391dSPuranjay Mohan 825339af577SPuranjay Mohan if (BPF_CLASS(insn->code) != BPF_LDX) 826339af577SPuranjay Mohan dst_reg = DONT_CLEAR; 827339af577SPuranjay Mohan 8281dad391dSPuranjay Mohan ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, fixup_offset) | 82980083428SJean-Philippe Brucker FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); 83080083428SJean-Philippe Brucker 831d6e2cc56SMark Rutland ex->type = EX_TYPE_BPF; 832d6e2cc56SMark Rutland 83380083428SJean-Philippe Brucker ctx->exentry_idx++; 83480083428SJean-Philippe Brucker return 0; 83580083428SJean-Philippe Brucker } 83680083428SJean-Philippe Brucker 83730d3d94cSZi Shen Lim /* JITs an eBPF instruction. 83830d3d94cSZi Shen Lim * Returns: 83930d3d94cSZi Shen Lim * 0 - successfully JITed an 8-byte eBPF instruction. 84030d3d94cSZi Shen Lim * >0 - successfully JITed a 16-byte eBPF instruction. 84130d3d94cSZi Shen Lim * <0 - failed to JIT. 84230d3d94cSZi Shen Lim */ 8438c11ea5cSDaniel Borkmann static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, 8448c11ea5cSDaniel Borkmann bool extra_pass) 845e54bcde3SZi Shen Lim { 846e54bcde3SZi Shen Lim const u8 code = insn->code; 847339af577SPuranjay Mohan u8 dst = bpf2a64[insn->dst_reg]; 848339af577SPuranjay Mohan u8 src = bpf2a64[insn->src_reg]; 849e54bcde3SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 850e54bcde3SZi Shen Lim const u8 tmp2 = bpf2a64[TMP_REG_2]; 8515b3d19b9SXu Kuohai const u8 fp = bpf2a64[BPF_REG_FP]; 8525b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 853339af577SPuranjay Mohan const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; 854e54bcde3SZi Shen Lim const s16 off = insn->off; 855e54bcde3SZi Shen Lim const s32 imm = insn->imm; 856e54bcde3SZi Shen Lim const int i = insn - ctx->prog->insnsi; 857654b65a0SJiong Wang const bool is64 = BPF_CLASS(code) == BPF_ALU64 || 858654b65a0SJiong Wang BPF_CLASS(code) == BPF_JMP; 8591902472bSHou Tao u8 jmp_cond; 860e54bcde3SZi Shen Lim s32 jmp_offset; 861fd49591cSLuke Nelson u32 a64_insn; 8625b3d19b9SXu Kuohai u8 src_adj; 8635b3d19b9SXu Kuohai u8 dst_adj; 8645b3d19b9SXu Kuohai int off_adj; 86580083428SJean-Philippe Brucker int ret; 866cc88f540SXu Kuohai bool sign_extend; 867e54bcde3SZi Shen Lim 868e54bcde3SZi Shen Lim switch (code) { 869e54bcde3SZi Shen Lim /* dst = src */ 870e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_X: 871e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_X: 872*4dd31243SPuranjay Mohan if (insn_is_cast_user(insn)) { 873*4dd31243SPuranjay Mohan emit(A64_MOV(0, tmp, src), ctx); // 32-bit mov clears the upper 32 bits 874*4dd31243SPuranjay Mohan emit_a64_mov_i(0, dst, ctx->user_vm_start >> 32, ctx); 875*4dd31243SPuranjay Mohan emit(A64_LSL(1, dst, dst, 32), ctx); 876*4dd31243SPuranjay Mohan emit(A64_CBZ(1, tmp, 2), ctx); 877*4dd31243SPuranjay Mohan emit(A64_ORR(1, tmp, dst, tmp), ctx); 878*4dd31243SPuranjay Mohan emit(A64_MOV(1, dst, tmp), ctx); 879*4dd31243SPuranjay Mohan break; 880*4dd31243SPuranjay Mohan } 881bb0a1d6bSXu Kuohai switch (insn->off) { 882bb0a1d6bSXu Kuohai case 0: 883e54bcde3SZi Shen Lim emit(A64_MOV(is64, dst, src), ctx); 884e54bcde3SZi Shen Lim break; 885bb0a1d6bSXu Kuohai case 8: 886bb0a1d6bSXu Kuohai emit(A64_SXTB(is64, dst, src), ctx); 887bb0a1d6bSXu Kuohai break; 888bb0a1d6bSXu Kuohai case 16: 889bb0a1d6bSXu Kuohai emit(A64_SXTH(is64, dst, src), ctx); 890bb0a1d6bSXu Kuohai break; 891bb0a1d6bSXu Kuohai case 32: 892bb0a1d6bSXu Kuohai emit(A64_SXTW(is64, dst, src), ctx); 893bb0a1d6bSXu Kuohai break; 894bb0a1d6bSXu Kuohai } 895bb0a1d6bSXu Kuohai break; 896e54bcde3SZi Shen Lim /* dst = dst OP src */ 897e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_X: 898e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_X: 899e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, src), ctx); 900e54bcde3SZi Shen Lim break; 901e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_X: 902e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_X: 903e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, src), ctx); 904e54bcde3SZi Shen Lim break; 905e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_X: 906e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_X: 907e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, src), ctx); 908e54bcde3SZi Shen Lim break; 909e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_X: 910e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_X: 911e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, src), ctx); 912e54bcde3SZi Shen Lim break; 913e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_X: 914e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_X: 915e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, src), ctx); 916e54bcde3SZi Shen Lim break; 917e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_X: 918e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_X: 919e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, src), ctx); 920e54bcde3SZi Shen Lim break; 921e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_X: 922e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_X: 92368b18191SXu Kuohai if (!off) 924e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, src), ctx); 92568b18191SXu Kuohai else 92668b18191SXu Kuohai emit(A64_SDIV(is64, dst, dst, src), ctx); 927e54bcde3SZi Shen Lim break; 928119220d8STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 929119220d8STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 93068b18191SXu Kuohai if (!off) 931e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, src), ctx); 93268b18191SXu Kuohai else 93368b18191SXu Kuohai emit(A64_SDIV(is64, tmp, dst, src), ctx); 934504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, src), ctx); 935e54bcde3SZi Shen Lim break; 936d65a634aSZi Shen Lim case BPF_ALU | BPF_LSH | BPF_X: 937d65a634aSZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_X: 938d65a634aSZi Shen Lim emit(A64_LSLV(is64, dst, dst, src), ctx); 939d65a634aSZi Shen Lim break; 940d65a634aSZi Shen Lim case BPF_ALU | BPF_RSH | BPF_X: 941d65a634aSZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_X: 942d65a634aSZi Shen Lim emit(A64_LSRV(is64, dst, dst, src), ctx); 943d65a634aSZi Shen Lim break; 944d65a634aSZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_X: 945d65a634aSZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_X: 946d65a634aSZi Shen Lim emit(A64_ASRV(is64, dst, dst, src), ctx); 947d65a634aSZi Shen Lim break; 948e54bcde3SZi Shen Lim /* dst = -dst */ 949e54bcde3SZi Shen Lim case BPF_ALU | BPF_NEG: 950e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_NEG: 951e54bcde3SZi Shen Lim emit(A64_NEG(is64, dst, dst), ctx); 952e54bcde3SZi Shen Lim break; 953e54bcde3SZi Shen Lim /* dst = BSWAP##imm(dst) */ 954e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_LE: 955e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_BE: 9561104247fSXu Kuohai case BPF_ALU64 | BPF_END | BPF_FROM_LE: 957e54bcde3SZi Shen Lim #ifdef CONFIG_CPU_BIG_ENDIAN 9581104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_BE) 959d63903bbSXi Wang goto emit_bswap_uxt; 960e54bcde3SZi Shen Lim #else /* !CONFIG_CPU_BIG_ENDIAN */ 9611104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_LE) 962d63903bbSXi Wang goto emit_bswap_uxt; 963e54bcde3SZi Shen Lim #endif 964e54bcde3SZi Shen Lim switch (imm) { 965e54bcde3SZi Shen Lim case 16: 966e54bcde3SZi Shen Lim emit(A64_REV16(is64, dst, dst), ctx); 967d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 968d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 969e54bcde3SZi Shen Lim break; 970e54bcde3SZi Shen Lim case 32: 971a51cd6bfSArtem Savkov emit(A64_REV32(0, dst, dst), ctx); 972d63903bbSXi Wang /* upper 32 bits already cleared */ 973e54bcde3SZi Shen Lim break; 974e54bcde3SZi Shen Lim case 64: 975e54bcde3SZi Shen Lim emit(A64_REV64(dst, dst), ctx); 976e54bcde3SZi Shen Lim break; 977e54bcde3SZi Shen Lim } 978e54bcde3SZi Shen Lim break; 979d63903bbSXi Wang emit_bswap_uxt: 980d63903bbSXi Wang switch (imm) { 981d63903bbSXi Wang case 16: 982d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 983d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 984d63903bbSXi Wang break; 985d63903bbSXi Wang case 32: 986d63903bbSXi Wang /* zero-extend 32 bits into 64 bits */ 987d63903bbSXi Wang emit(A64_UXTW(is64, dst, dst), ctx); 988d63903bbSXi Wang break; 989d63903bbSXi Wang case 64: 990d63903bbSXi Wang /* nop */ 991d63903bbSXi Wang break; 992d63903bbSXi Wang } 993d63903bbSXi Wang break; 994e54bcde3SZi Shen Lim /* dst = imm */ 995e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_K: 996e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_K: 997e54bcde3SZi Shen Lim emit_a64_mov_i(is64, dst, imm, ctx); 998e54bcde3SZi Shen Lim break; 999e54bcde3SZi Shen Lim /* dst = dst OP imm */ 1000e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_K: 1001e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_K: 1002fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1003fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, imm), ctx); 1004fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1005fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, -imm), ctx); 1006fd868f14SLuke Nelson } else { 1007e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1008e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, tmp), ctx); 1009fd868f14SLuke Nelson } 1010e54bcde3SZi Shen Lim break; 1011e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_K: 1012e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_K: 1013fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1014fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, imm), ctx); 1015fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1016fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, -imm), ctx); 1017fd868f14SLuke Nelson } else { 1018e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1019e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 1020fd868f14SLuke Nelson } 1021e54bcde3SZi Shen Lim break; 1022e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_K: 1023e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_K: 1024fd49591cSLuke Nelson a64_insn = A64_AND_I(is64, dst, dst, imm); 1025fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1026fd49591cSLuke Nelson emit(a64_insn, ctx); 1027fd49591cSLuke Nelson } else { 1028e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1029e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, tmp), ctx); 1030fd49591cSLuke Nelson } 1031e54bcde3SZi Shen Lim break; 1032e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_K: 1033e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_K: 1034fd49591cSLuke Nelson a64_insn = A64_ORR_I(is64, dst, dst, imm); 1035fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1036fd49591cSLuke Nelson emit(a64_insn, ctx); 1037fd49591cSLuke Nelson } else { 1038e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1039e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, tmp), ctx); 1040fd49591cSLuke Nelson } 1041e54bcde3SZi Shen Lim break; 1042e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_K: 1043e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_K: 1044fd49591cSLuke Nelson a64_insn = A64_EOR_I(is64, dst, dst, imm); 1045fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1046fd49591cSLuke Nelson emit(a64_insn, ctx); 1047fd49591cSLuke Nelson } else { 1048e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1049e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, tmp), ctx); 1050fd49591cSLuke Nelson } 1051e54bcde3SZi Shen Lim break; 1052e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_K: 1053e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_K: 1054e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1055e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, tmp), ctx); 1056e54bcde3SZi Shen Lim break; 1057e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_K: 1058e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_K: 1059e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 106068b18191SXu Kuohai if (!off) 1061e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, tmp), ctx); 106268b18191SXu Kuohai else 106368b18191SXu Kuohai emit(A64_SDIV(is64, dst, dst, tmp), ctx); 1064e54bcde3SZi Shen Lim break; 1065e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_K: 1066e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_K: 1067e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp2, imm, ctx); 106868b18191SXu Kuohai if (!off) 1069e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 107068b18191SXu Kuohai else 107168b18191SXu Kuohai emit(A64_SDIV(is64, tmp, dst, tmp2), ctx); 1072504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, tmp2), ctx); 1073e54bcde3SZi Shen Lim break; 1074e54bcde3SZi Shen Lim case BPF_ALU | BPF_LSH | BPF_K: 1075e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_K: 1076e54bcde3SZi Shen Lim emit(A64_LSL(is64, dst, dst, imm), ctx); 1077e54bcde3SZi Shen Lim break; 1078e54bcde3SZi Shen Lim case BPF_ALU | BPF_RSH | BPF_K: 1079e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_K: 1080e54bcde3SZi Shen Lim emit(A64_LSR(is64, dst, dst, imm), ctx); 1081e54bcde3SZi Shen Lim break; 1082e54bcde3SZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_K: 1083e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_K: 1084e54bcde3SZi Shen Lim emit(A64_ASR(is64, dst, dst, imm), ctx); 1085e54bcde3SZi Shen Lim break; 1086e54bcde3SZi Shen Lim 1087e54bcde3SZi Shen Lim /* JUMP off */ 1088e54bcde3SZi Shen Lim case BPF_JMP | BPF_JA: 1089c32b6ee5SXu Kuohai case BPF_JMP32 | BPF_JA: 1090c32b6ee5SXu Kuohai if (BPF_CLASS(code) == BPF_JMP) 109132f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 1092c32b6ee5SXu Kuohai else 1093c32b6ee5SXu Kuohai jmp_offset = bpf2a64_offset(i, imm, ctx); 1094e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1095e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1096e54bcde3SZi Shen Lim break; 1097e54bcde3SZi Shen Lim /* IF (dst COND src) JUMP off */ 1098e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_X: 1099e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_X: 1100c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_X: 1101e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_X: 1102c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_X: 1103e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_X: 1104e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_X: 1105c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_X: 1106e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_X: 1107c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_X: 1108654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_X: 1109654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_X: 1110654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_X: 1111654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_X: 1112654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_X: 1113654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_X: 1114654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_X: 1115654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_X: 1116654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_X: 1117654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_X: 1118654b65a0SJiong Wang emit(A64_CMP(is64, dst, src), ctx); 1119e54bcde3SZi Shen Lim emit_cond_jmp: 112032f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 1121e54bcde3SZi Shen Lim check_imm19(jmp_offset); 1122e54bcde3SZi Shen Lim switch (BPF_OP(code)) { 1123e54bcde3SZi Shen Lim case BPF_JEQ: 1124e54bcde3SZi Shen Lim jmp_cond = A64_COND_EQ; 1125e54bcde3SZi Shen Lim break; 1126e54bcde3SZi Shen Lim case BPF_JGT: 1127e54bcde3SZi Shen Lim jmp_cond = A64_COND_HI; 1128e54bcde3SZi Shen Lim break; 1129c362b2f3SDaniel Borkmann case BPF_JLT: 1130c362b2f3SDaniel Borkmann jmp_cond = A64_COND_CC; 1131c362b2f3SDaniel Borkmann break; 1132e54bcde3SZi Shen Lim case BPF_JGE: 1133e54bcde3SZi Shen Lim jmp_cond = A64_COND_CS; 1134e54bcde3SZi Shen Lim break; 1135c362b2f3SDaniel Borkmann case BPF_JLE: 1136c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LS; 1137c362b2f3SDaniel Borkmann break; 113898397fc5SZi Shen Lim case BPF_JSET: 1139e54bcde3SZi Shen Lim case BPF_JNE: 1140e54bcde3SZi Shen Lim jmp_cond = A64_COND_NE; 1141e54bcde3SZi Shen Lim break; 1142e54bcde3SZi Shen Lim case BPF_JSGT: 1143e54bcde3SZi Shen Lim jmp_cond = A64_COND_GT; 1144e54bcde3SZi Shen Lim break; 1145c362b2f3SDaniel Borkmann case BPF_JSLT: 1146c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LT; 1147c362b2f3SDaniel Borkmann break; 1148e54bcde3SZi Shen Lim case BPF_JSGE: 1149e54bcde3SZi Shen Lim jmp_cond = A64_COND_GE; 1150e54bcde3SZi Shen Lim break; 1151c362b2f3SDaniel Borkmann case BPF_JSLE: 1152c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LE; 1153c362b2f3SDaniel Borkmann break; 1154e54bcde3SZi Shen Lim default: 1155e54bcde3SZi Shen Lim return -EFAULT; 1156e54bcde3SZi Shen Lim } 1157e54bcde3SZi Shen Lim emit(A64_B_(jmp_cond, jmp_offset), ctx); 1158e54bcde3SZi Shen Lim break; 1159e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_X: 1160654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_X: 1161654b65a0SJiong Wang emit(A64_TST(is64, dst, src), ctx); 1162e54bcde3SZi Shen Lim goto emit_cond_jmp; 1163e54bcde3SZi Shen Lim /* IF (dst COND imm) JUMP off */ 1164e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_K: 1165e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_K: 1166c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_K: 1167e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_K: 1168c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_K: 1169e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_K: 1170e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_K: 1171c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_K: 1172e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_K: 1173c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_K: 1174654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_K: 1175654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_K: 1176654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_K: 1177654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_K: 1178654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_K: 1179654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_K: 1180654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_K: 1181654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_K: 1182654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_K: 1183654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_K: 1184fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1185fd868f14SLuke Nelson emit(A64_CMP_I(is64, dst, imm), ctx); 1186fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1187fd868f14SLuke Nelson emit(A64_CMN_I(is64, dst, -imm), ctx); 1188fd868f14SLuke Nelson } else { 1189654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1190654b65a0SJiong Wang emit(A64_CMP(is64, dst, tmp), ctx); 1191fd868f14SLuke Nelson } 1192e54bcde3SZi Shen Lim goto emit_cond_jmp; 1193e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_K: 1194654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_K: 1195fd49591cSLuke Nelson a64_insn = A64_TST_I(is64, dst, imm); 1196fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1197fd49591cSLuke Nelson emit(a64_insn, ctx); 1198fd49591cSLuke Nelson } else { 1199654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1200654b65a0SJiong Wang emit(A64_TST(is64, dst, tmp), ctx); 1201fd49591cSLuke Nelson } 1202e54bcde3SZi Shen Lim goto emit_cond_jmp; 1203e54bcde3SZi Shen Lim /* function call */ 1204e54bcde3SZi Shen Lim case BPF_JMP | BPF_CALL: 1205e54bcde3SZi Shen Lim { 1206e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 12078c11ea5cSDaniel Borkmann bool func_addr_fixed; 12088c11ea5cSDaniel Borkmann u64 func_addr; 1209e54bcde3SZi Shen Lim 12108c11ea5cSDaniel Borkmann ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 12118c11ea5cSDaniel Borkmann &func_addr, &func_addr_fixed); 12128c11ea5cSDaniel Borkmann if (ret < 0) 12138c11ea5cSDaniel Borkmann return ret; 1214efc9909fSXu Kuohai emit_call(func_addr, ctx); 1215e54bcde3SZi Shen Lim emit(A64_MOV(1, r0, A64_R(0)), ctx); 1216e54bcde3SZi Shen Lim break; 1217e54bcde3SZi Shen Lim } 1218ddb55992SZi Shen Lim /* tail call */ 121971189fa9SAlexei Starovoitov case BPF_JMP | BPF_TAIL_CALL: 1220ddb55992SZi Shen Lim if (emit_bpf_tail_call(ctx)) 1221ddb55992SZi Shen Lim return -EFAULT; 1222ddb55992SZi Shen Lim break; 1223e54bcde3SZi Shen Lim /* function return */ 1224e54bcde3SZi Shen Lim case BPF_JMP | BPF_EXIT: 122551c9fbb1SZi Shen Lim /* Optimization: when last instruction is EXIT, 122651c9fbb1SZi Shen Lim simply fallthrough to epilogue. */ 1227e54bcde3SZi Shen Lim if (i == ctx->prog->len - 1) 1228e54bcde3SZi Shen Lim break; 1229e54bcde3SZi Shen Lim jmp_offset = epilogue_offset(ctx); 1230e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1231e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1232e54bcde3SZi Shen Lim break; 1233e54bcde3SZi Shen Lim 123430d3d94cSZi Shen Lim /* dst = imm64 */ 123530d3d94cSZi Shen Lim case BPF_LD | BPF_IMM | BPF_DW: 123630d3d94cSZi Shen Lim { 123730d3d94cSZi Shen Lim const struct bpf_insn insn1 = insn[1]; 123830d3d94cSZi Shen Lim u64 imm64; 123930d3d94cSZi Shen Lim 12401e4df6b7SXi Wang imm64 = (u64)insn1.imm << 32 | (u32)imm; 1241e4a41c2cSHou Tao if (bpf_pseudo_func(insn)) 1242e4a41c2cSHou Tao emit_addr_mov_i64(dst, imm64, ctx); 1243e4a41c2cSHou Tao else 124430d3d94cSZi Shen Lim emit_a64_mov_i64(dst, imm64, ctx); 124530d3d94cSZi Shen Lim 124630d3d94cSZi Shen Lim return 1; 124730d3d94cSZi Shen Lim } 124830d3d94cSZi Shen Lim 1249cc88f540SXu Kuohai /* LDX: dst = (u64)*(unsigned size *)(src + off) */ 1250e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_W: 1251e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_H: 1252e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_B: 1253e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_DW: 125480083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 125580083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_W: 125680083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_H: 125780083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_B: 1258cc88f540SXu Kuohai /* LDXS: dst_reg = (s64)*(signed size *)(src_reg + off) */ 1259cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_B: 1260cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_H: 1261cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_W: 1262cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_B: 1263cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_H: 1264cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_W: 1265339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_B: 1266339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_H: 1267339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_W: 1268339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_DW: 1269339af577SPuranjay Mohan if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) { 1270339af577SPuranjay Mohan emit(A64_ADD(1, tmp2, src, arena_vm_base), ctx); 1271339af577SPuranjay Mohan src = tmp2; 1272339af577SPuranjay Mohan } 1273339af577SPuranjay Mohan if (ctx->fpb_offset > 0 && src == fp && BPF_MODE(insn->code) != BPF_PROBE_MEM32) { 12745b3d19b9SXu Kuohai src_adj = fpb; 12755b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 12765b3d19b9SXu Kuohai } else { 12775b3d19b9SXu Kuohai src_adj = src; 12785b3d19b9SXu Kuohai off_adj = off; 12795b3d19b9SXu Kuohai } 1280cc88f540SXu Kuohai sign_extend = (BPF_MODE(insn->code) == BPF_MEMSX || 1281cc88f540SXu Kuohai BPF_MODE(insn->code) == BPF_PROBE_MEMSX); 1282e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1283e54bcde3SZi Shen Lim case BPF_W: 12845b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 1285cc88f540SXu Kuohai if (sign_extend) 1286cc88f540SXu Kuohai emit(A64_LDRSWI(dst, src_adj, off_adj), ctx); 1287cc88f540SXu Kuohai else 12885b3d19b9SXu Kuohai emit(A64_LDR32I(dst, src_adj, off_adj), ctx); 12897db6c0f1SXu Kuohai } else { 12907db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1291cc88f540SXu Kuohai if (sign_extend) 1292114b5b3bSPuranjay Mohan emit(A64_LDRSW(dst, src, tmp), ctx); 1293cc88f540SXu Kuohai else 1294e54bcde3SZi Shen Lim emit(A64_LDR32(dst, src, tmp), ctx); 12957db6c0f1SXu Kuohai } 1296e54bcde3SZi Shen Lim break; 1297e54bcde3SZi Shen Lim case BPF_H: 12985b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 1299cc88f540SXu Kuohai if (sign_extend) 1300cc88f540SXu Kuohai emit(A64_LDRSHI(dst, src_adj, off_adj), ctx); 1301cc88f540SXu Kuohai else 13025b3d19b9SXu Kuohai emit(A64_LDRHI(dst, src_adj, off_adj), ctx); 13037db6c0f1SXu Kuohai } else { 13047db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1305cc88f540SXu Kuohai if (sign_extend) 1306cc88f540SXu Kuohai emit(A64_LDRSH(dst, src, tmp), ctx); 1307cc88f540SXu Kuohai else 1308e54bcde3SZi Shen Lim emit(A64_LDRH(dst, src, tmp), ctx); 13097db6c0f1SXu Kuohai } 1310e54bcde3SZi Shen Lim break; 1311e54bcde3SZi Shen Lim case BPF_B: 13125b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 1313cc88f540SXu Kuohai if (sign_extend) 1314cc88f540SXu Kuohai emit(A64_LDRSBI(dst, src_adj, off_adj), ctx); 1315cc88f540SXu Kuohai else 13165b3d19b9SXu Kuohai emit(A64_LDRBI(dst, src_adj, off_adj), ctx); 13177db6c0f1SXu Kuohai } else { 13187db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1319cc88f540SXu Kuohai if (sign_extend) 1320cc88f540SXu Kuohai emit(A64_LDRSB(dst, src, tmp), ctx); 1321cc88f540SXu Kuohai else 1322e54bcde3SZi Shen Lim emit(A64_LDRB(dst, src, tmp), ctx); 13237db6c0f1SXu Kuohai } 1324e54bcde3SZi Shen Lim break; 1325e54bcde3SZi Shen Lim case BPF_DW: 13265b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 13275b3d19b9SXu Kuohai emit(A64_LDR64I(dst, src_adj, off_adj), ctx); 13287db6c0f1SXu Kuohai } else { 13297db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1330e54bcde3SZi Shen Lim emit(A64_LDR64(dst, src, tmp), ctx); 13317db6c0f1SXu Kuohai } 1332e54bcde3SZi Shen Lim break; 1333e54bcde3SZi Shen Lim } 133480083428SJean-Philippe Brucker 133580083428SJean-Philippe Brucker ret = add_exception_handler(insn, ctx, dst); 133680083428SJean-Philippe Brucker if (ret) 133780083428SJean-Philippe Brucker return ret; 1338e54bcde3SZi Shen Lim break; 1339e54bcde3SZi Shen Lim 1340f5e81d11SDaniel Borkmann /* speculation barrier */ 1341f5e81d11SDaniel Borkmann case BPF_ST | BPF_NOSPEC: 1342f5e81d11SDaniel Borkmann /* 1343f5e81d11SDaniel Borkmann * Nothing required here. 1344f5e81d11SDaniel Borkmann * 1345f5e81d11SDaniel Borkmann * In case of arm64, we rely on the firmware mitigation of 1346f5e81d11SDaniel Borkmann * Speculative Store Bypass as controlled via the ssbd kernel 1347f5e81d11SDaniel Borkmann * parameter. Whenever the mitigation is enabled, it works 1348f5e81d11SDaniel Borkmann * for all of the kernel code with no need to provide any 1349f5e81d11SDaniel Borkmann * additional instructions. 1350f5e81d11SDaniel Borkmann */ 1351f5e81d11SDaniel Borkmann break; 1352f5e81d11SDaniel Borkmann 1353e54bcde3SZi Shen Lim /* ST: *(size *)(dst + off) = imm */ 1354e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_W: 1355e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_H: 1356e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_B: 1357e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_DW: 1358339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_B: 1359339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_H: 1360339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_W: 1361339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_DW: 1362339af577SPuranjay Mohan if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) { 1363339af577SPuranjay Mohan emit(A64_ADD(1, tmp2, dst, arena_vm_base), ctx); 1364339af577SPuranjay Mohan dst = tmp2; 1365339af577SPuranjay Mohan } 1366339af577SPuranjay Mohan if (ctx->fpb_offset > 0 && dst == fp && BPF_MODE(insn->code) != BPF_PROBE_MEM32) { 13675b3d19b9SXu Kuohai dst_adj = fpb; 13685b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 13695b3d19b9SXu Kuohai } else { 13705b3d19b9SXu Kuohai dst_adj = dst; 13715b3d19b9SXu Kuohai off_adj = off; 13725b3d19b9SXu Kuohai } 1373df849ba3SYang Shi /* Load imm to a register then store it */ 1374df849ba3SYang Shi emit_a64_mov_i(1, tmp, imm, ctx); 1375df849ba3SYang Shi switch (BPF_SIZE(code)) { 1376df849ba3SYang Shi case BPF_W: 13775b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 13785b3d19b9SXu Kuohai emit(A64_STR32I(tmp, dst_adj, off_adj), ctx); 13797db6c0f1SXu Kuohai } else { 13807db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1381df849ba3SYang Shi emit(A64_STR32(tmp, dst, tmp2), ctx); 13827db6c0f1SXu Kuohai } 1383df849ba3SYang Shi break; 1384df849ba3SYang Shi case BPF_H: 13855b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 13865b3d19b9SXu Kuohai emit(A64_STRHI(tmp, dst_adj, off_adj), ctx); 13877db6c0f1SXu Kuohai } else { 13887db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1389df849ba3SYang Shi emit(A64_STRH(tmp, dst, tmp2), ctx); 13907db6c0f1SXu Kuohai } 1391df849ba3SYang Shi break; 1392df849ba3SYang Shi case BPF_B: 13935b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 13945b3d19b9SXu Kuohai emit(A64_STRBI(tmp, dst_adj, off_adj), ctx); 13957db6c0f1SXu Kuohai } else { 13967db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1397df849ba3SYang Shi emit(A64_STRB(tmp, dst, tmp2), ctx); 13987db6c0f1SXu Kuohai } 1399df849ba3SYang Shi break; 1400df849ba3SYang Shi case BPF_DW: 14015b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 14025b3d19b9SXu Kuohai emit(A64_STR64I(tmp, dst_adj, off_adj), ctx); 14037db6c0f1SXu Kuohai } else { 14047db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1405df849ba3SYang Shi emit(A64_STR64(tmp, dst, tmp2), ctx); 14067db6c0f1SXu Kuohai } 1407df849ba3SYang Shi break; 1408df849ba3SYang Shi } 1409339af577SPuranjay Mohan 1410339af577SPuranjay Mohan ret = add_exception_handler(insn, ctx, dst); 1411339af577SPuranjay Mohan if (ret) 1412339af577SPuranjay Mohan return ret; 1413df849ba3SYang Shi break; 1414e54bcde3SZi Shen Lim 1415e54bcde3SZi Shen Lim /* STX: *(size *)(dst + off) = src */ 1416e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_W: 1417e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_H: 1418e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_B: 1419e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_DW: 1420339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_B: 1421339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_H: 1422339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_W: 1423339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_DW: 1424339af577SPuranjay Mohan if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) { 1425339af577SPuranjay Mohan emit(A64_ADD(1, tmp2, dst, arena_vm_base), ctx); 1426339af577SPuranjay Mohan dst = tmp2; 1427339af577SPuranjay Mohan } 1428339af577SPuranjay Mohan if (ctx->fpb_offset > 0 && dst == fp && BPF_MODE(insn->code) != BPF_PROBE_MEM32) { 14295b3d19b9SXu Kuohai dst_adj = fpb; 14305b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 14315b3d19b9SXu Kuohai } else { 14325b3d19b9SXu Kuohai dst_adj = dst; 14335b3d19b9SXu Kuohai off_adj = off; 14345b3d19b9SXu Kuohai } 1435e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1436e54bcde3SZi Shen Lim case BPF_W: 14375b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 14385b3d19b9SXu Kuohai emit(A64_STR32I(src, dst_adj, off_adj), ctx); 14397db6c0f1SXu Kuohai } else { 14407db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1441e54bcde3SZi Shen Lim emit(A64_STR32(src, dst, tmp), ctx); 14427db6c0f1SXu Kuohai } 1443e54bcde3SZi Shen Lim break; 1444e54bcde3SZi Shen Lim case BPF_H: 14455b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 14465b3d19b9SXu Kuohai emit(A64_STRHI(src, dst_adj, off_adj), ctx); 14477db6c0f1SXu Kuohai } else { 14487db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1449e54bcde3SZi Shen Lim emit(A64_STRH(src, dst, tmp), ctx); 14507db6c0f1SXu Kuohai } 1451e54bcde3SZi Shen Lim break; 1452e54bcde3SZi Shen Lim case BPF_B: 14535b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 14545b3d19b9SXu Kuohai emit(A64_STRBI(src, dst_adj, off_adj), ctx); 14557db6c0f1SXu Kuohai } else { 14567db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1457e54bcde3SZi Shen Lim emit(A64_STRB(src, dst, tmp), ctx); 14587db6c0f1SXu Kuohai } 1459e54bcde3SZi Shen Lim break; 1460e54bcde3SZi Shen Lim case BPF_DW: 14615b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 14625b3d19b9SXu Kuohai emit(A64_STR64I(src, dst_adj, off_adj), ctx); 14637db6c0f1SXu Kuohai } else { 14647db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1465e54bcde3SZi Shen Lim emit(A64_STR64(src, dst, tmp), ctx); 14667db6c0f1SXu Kuohai } 1467e54bcde3SZi Shen Lim break; 1468e54bcde3SZi Shen Lim } 1469339af577SPuranjay Mohan 1470339af577SPuranjay Mohan ret = add_exception_handler(insn, ctx, dst); 1471339af577SPuranjay Mohan if (ret) 1472339af577SPuranjay Mohan return ret; 1473e54bcde3SZi Shen Lim break; 147434b8ab09SDaniel Borkmann 147591c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_W: 147691c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_DW: 14771902472bSHou Tao if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) 14781902472bSHou Tao ret = emit_lse_atomic(insn, ctx); 14791902472bSHou Tao else 14801902472bSHou Tao ret = emit_ll_sc_atomic(insn, ctx); 14811902472bSHou Tao if (ret) 14821902472bSHou Tao return ret; 148385f68fe8SDaniel Borkmann break; 1484e54bcde3SZi Shen Lim 1485e54bcde3SZi Shen Lim default: 1486e54bcde3SZi Shen Lim pr_err_once("unknown opcode %02x\n", code); 1487e54bcde3SZi Shen Lim return -EINVAL; 1488e54bcde3SZi Shen Lim } 1489e54bcde3SZi Shen Lim 1490e54bcde3SZi Shen Lim return 0; 1491e54bcde3SZi Shen Lim } 1492e54bcde3SZi Shen Lim 14935b3d19b9SXu Kuohai /* 14945b3d19b9SXu Kuohai * Return 0 if FP may change at runtime, otherwise find the minimum negative 14955b3d19b9SXu Kuohai * offset to FP, converts it to positive number, and align down to 8 bytes. 14965b3d19b9SXu Kuohai */ 14975b3d19b9SXu Kuohai static int find_fpb_offset(struct bpf_prog *prog) 14985b3d19b9SXu Kuohai { 14995b3d19b9SXu Kuohai int i; 15005b3d19b9SXu Kuohai int offset = 0; 15015b3d19b9SXu Kuohai 15025b3d19b9SXu Kuohai for (i = 0; i < prog->len; i++) { 15035b3d19b9SXu Kuohai const struct bpf_insn *insn = &prog->insnsi[i]; 15045b3d19b9SXu Kuohai const u8 class = BPF_CLASS(insn->code); 15055b3d19b9SXu Kuohai const u8 mode = BPF_MODE(insn->code); 15065b3d19b9SXu Kuohai const u8 src = insn->src_reg; 15075b3d19b9SXu Kuohai const u8 dst = insn->dst_reg; 15085b3d19b9SXu Kuohai const s32 imm = insn->imm; 15095b3d19b9SXu Kuohai const s16 off = insn->off; 15105b3d19b9SXu Kuohai 15115b3d19b9SXu Kuohai switch (class) { 15125b3d19b9SXu Kuohai case BPF_STX: 15135b3d19b9SXu Kuohai case BPF_ST: 15145b3d19b9SXu Kuohai /* fp holds atomic operation result */ 15155b3d19b9SXu Kuohai if (class == BPF_STX && mode == BPF_ATOMIC && 15165b3d19b9SXu Kuohai ((imm == BPF_XCHG || 15175b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_ADD) || 15185b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_AND) || 15195b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_XOR) || 15205b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_OR)) && 15215b3d19b9SXu Kuohai src == BPF_REG_FP)) 15225b3d19b9SXu Kuohai return 0; 15235b3d19b9SXu Kuohai 15245b3d19b9SXu Kuohai if (mode == BPF_MEM && dst == BPF_REG_FP && 15255b3d19b9SXu Kuohai off < offset) 15265b3d19b9SXu Kuohai offset = insn->off; 15275b3d19b9SXu Kuohai break; 15285b3d19b9SXu Kuohai 15295b3d19b9SXu Kuohai case BPF_JMP32: 15305b3d19b9SXu Kuohai case BPF_JMP: 15315b3d19b9SXu Kuohai break; 15325b3d19b9SXu Kuohai 15335b3d19b9SXu Kuohai case BPF_LDX: 15345b3d19b9SXu Kuohai case BPF_LD: 15355b3d19b9SXu Kuohai /* fp holds load result */ 15365b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 15375b3d19b9SXu Kuohai return 0; 15385b3d19b9SXu Kuohai 15395b3d19b9SXu Kuohai if (class == BPF_LDX && mode == BPF_MEM && 15405b3d19b9SXu Kuohai src == BPF_REG_FP && off < offset) 15415b3d19b9SXu Kuohai offset = off; 15425b3d19b9SXu Kuohai break; 15435b3d19b9SXu Kuohai 15445b3d19b9SXu Kuohai case BPF_ALU: 15455b3d19b9SXu Kuohai case BPF_ALU64: 15465b3d19b9SXu Kuohai default: 15475b3d19b9SXu Kuohai /* fp holds ALU result */ 15485b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 15495b3d19b9SXu Kuohai return 0; 15505b3d19b9SXu Kuohai } 15515b3d19b9SXu Kuohai } 15525b3d19b9SXu Kuohai 15535b3d19b9SXu Kuohai if (offset < 0) { 15545b3d19b9SXu Kuohai /* 15555b3d19b9SXu Kuohai * safely be converted to a positive 'int', since insn->off 15565b3d19b9SXu Kuohai * is 's16' 15575b3d19b9SXu Kuohai */ 15585b3d19b9SXu Kuohai offset = -offset; 15595b3d19b9SXu Kuohai /* align down to 8 bytes */ 15605b3d19b9SXu Kuohai offset = ALIGN_DOWN(offset, 8); 15615b3d19b9SXu Kuohai } 15625b3d19b9SXu Kuohai 15635b3d19b9SXu Kuohai return offset; 15645b3d19b9SXu Kuohai } 15655b3d19b9SXu Kuohai 15668c11ea5cSDaniel Borkmann static int build_body(struct jit_ctx *ctx, bool extra_pass) 1567e54bcde3SZi Shen Lim { 1568e54bcde3SZi Shen Lim const struct bpf_prog *prog = ctx->prog; 1569e54bcde3SZi Shen Lim int i; 1570e54bcde3SZi Shen Lim 157132f6865cSIlias Apalodimas /* 157232f6865cSIlias Apalodimas * - offset[0] offset of the end of prologue, 157332f6865cSIlias Apalodimas * start of the 1st instruction. 157432f6865cSIlias Apalodimas * - offset[1] - offset of the end of 1st instruction, 157532f6865cSIlias Apalodimas * start of the 2nd instruction 157632f6865cSIlias Apalodimas * [....] 157732f6865cSIlias Apalodimas * - offset[3] - offset of the end of 3rd instruction, 157832f6865cSIlias Apalodimas * start of 4th instruction 157932f6865cSIlias Apalodimas */ 1580e54bcde3SZi Shen Lim for (i = 0; i < prog->len; i++) { 1581e54bcde3SZi Shen Lim const struct bpf_insn *insn = &prog->insnsi[i]; 1582e54bcde3SZi Shen Lim int ret; 1583e54bcde3SZi Shen Lim 158432f6865cSIlias Apalodimas if (ctx->image == NULL) 158532f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 15868c11ea5cSDaniel Borkmann ret = build_insn(insn, ctx, extra_pass); 158730d3d94cSZi Shen Lim if (ret > 0) { 158830d3d94cSZi Shen Lim i++; 1589ddc665a4SDaniel Borkmann if (ctx->image == NULL) 1590ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 159130d3d94cSZi Shen Lim continue; 159230d3d94cSZi Shen Lim } 1593e54bcde3SZi Shen Lim if (ret) 1594e54bcde3SZi Shen Lim return ret; 1595e54bcde3SZi Shen Lim } 159632f6865cSIlias Apalodimas /* 159732f6865cSIlias Apalodimas * offset is allocated with prog->len + 1 so fill in 159832f6865cSIlias Apalodimas * the last element with the offset after the last 159932f6865cSIlias Apalodimas * instruction (end of program) 160032f6865cSIlias Apalodimas */ 160132f6865cSIlias Apalodimas if (ctx->image == NULL) 160232f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 1603e54bcde3SZi Shen Lim 1604e54bcde3SZi Shen Lim return 0; 1605e54bcde3SZi Shen Lim } 1606e54bcde3SZi Shen Lim 160742ff712bSZi Shen Lim static int validate_code(struct jit_ctx *ctx) 160842ff712bSZi Shen Lim { 160942ff712bSZi Shen Lim int i; 161042ff712bSZi Shen Lim 161142ff712bSZi Shen Lim for (i = 0; i < ctx->idx; i++) { 161242ff712bSZi Shen Lim u32 a64_insn = le32_to_cpu(ctx->image[i]); 161342ff712bSZi Shen Lim 161442ff712bSZi Shen Lim if (a64_insn == AARCH64_BREAK_FAULT) 161542ff712bSZi Shen Lim return -1; 161642ff712bSZi Shen Lim } 1617efc9909fSXu Kuohai return 0; 1618efc9909fSXu Kuohai } 1619efc9909fSXu Kuohai 1620efc9909fSXu Kuohai static int validate_ctx(struct jit_ctx *ctx) 1621efc9909fSXu Kuohai { 1622efc9909fSXu Kuohai if (validate_code(ctx)) 1623efc9909fSXu Kuohai return -1; 162442ff712bSZi Shen Lim 162580083428SJean-Philippe Brucker if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries)) 162680083428SJean-Philippe Brucker return -1; 162780083428SJean-Philippe Brucker 162842ff712bSZi Shen Lim return 0; 162942ff712bSZi Shen Lim } 163042ff712bSZi Shen Lim 1631e54bcde3SZi Shen Lim static inline void bpf_flush_icache(void *start, void *end) 1632e54bcde3SZi Shen Lim { 1633e54bcde3SZi Shen Lim flush_icache_range((unsigned long)start, (unsigned long)end); 1634e54bcde3SZi Shen Lim } 1635e54bcde3SZi Shen Lim 1636db496944SAlexei Starovoitov struct arm64_jit_data { 1637db496944SAlexei Starovoitov struct bpf_binary_header *header; 16381dad391dSPuranjay Mohan u8 *ro_image; 16391dad391dSPuranjay Mohan struct bpf_binary_header *ro_header; 1640db496944SAlexei Starovoitov struct jit_ctx ctx; 1641db496944SAlexei Starovoitov }; 1642db496944SAlexei Starovoitov 1643d1c55ab5SDaniel Borkmann struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 1644e54bcde3SZi Shen Lim { 1645b2ad54e1SXu Kuohai int image_size, prog_size, extable_size, extable_align, extable_offset; 164626eb042eSDaniel Borkmann struct bpf_prog *tmp, *orig_prog = prog; 1647b569c1c6SDaniel Borkmann struct bpf_binary_header *header; 16481dad391dSPuranjay Mohan struct bpf_binary_header *ro_header; 1649db496944SAlexei Starovoitov struct arm64_jit_data *jit_data; 165056ea6a8bSDaniel Borkmann bool was_classic = bpf_prog_was_classic(prog); 165126eb042eSDaniel Borkmann bool tmp_blinded = false; 1652db496944SAlexei Starovoitov bool extra_pass = false; 1653e54bcde3SZi Shen Lim struct jit_ctx ctx; 1654339af577SPuranjay Mohan u64 arena_vm_start; 1655b569c1c6SDaniel Borkmann u8 *image_ptr; 16561dad391dSPuranjay Mohan u8 *ro_image_ptr; 1657e54bcde3SZi Shen Lim 165860b58afcSAlexei Starovoitov if (!prog->jit_requested) 165926eb042eSDaniel Borkmann return orig_prog; 166026eb042eSDaniel Borkmann 166126eb042eSDaniel Borkmann tmp = bpf_jit_blind_constants(prog); 166226eb042eSDaniel Borkmann /* If blinding was requested and we failed during blinding, 166326eb042eSDaniel Borkmann * we must fall back to the interpreter. 166426eb042eSDaniel Borkmann */ 166526eb042eSDaniel Borkmann if (IS_ERR(tmp)) 166626eb042eSDaniel Borkmann return orig_prog; 166726eb042eSDaniel Borkmann if (tmp != prog) { 166826eb042eSDaniel Borkmann tmp_blinded = true; 166926eb042eSDaniel Borkmann prog = tmp; 167026eb042eSDaniel Borkmann } 1671e54bcde3SZi Shen Lim 1672339af577SPuranjay Mohan arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena); 1673db496944SAlexei Starovoitov jit_data = prog->aux->jit_data; 1674db496944SAlexei Starovoitov if (!jit_data) { 1675db496944SAlexei Starovoitov jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 1676db496944SAlexei Starovoitov if (!jit_data) { 1677db496944SAlexei Starovoitov prog = orig_prog; 1678db496944SAlexei Starovoitov goto out; 1679db496944SAlexei Starovoitov } 1680db496944SAlexei Starovoitov prog->aux->jit_data = jit_data; 1681db496944SAlexei Starovoitov } 1682db496944SAlexei Starovoitov if (jit_data->ctx.offset) { 1683db496944SAlexei Starovoitov ctx = jit_data->ctx; 16841dad391dSPuranjay Mohan ro_image_ptr = jit_data->ro_image; 16851dad391dSPuranjay Mohan ro_header = jit_data->ro_header; 1686db496944SAlexei Starovoitov header = jit_data->header; 16871dad391dSPuranjay Mohan image_ptr = (void *)header + ((void *)ro_image_ptr 16881dad391dSPuranjay Mohan - (void *)ro_header); 1689db496944SAlexei Starovoitov extra_pass = true; 169080083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1691db496944SAlexei Starovoitov goto skip_init_ctx; 1692db496944SAlexei Starovoitov } 1693e54bcde3SZi Shen Lim memset(&ctx, 0, sizeof(ctx)); 1694e54bcde3SZi Shen Lim ctx.prog = prog; 1695e54bcde3SZi Shen Lim 169619f68ed6SAijun Sun ctx.offset = kvcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); 169726eb042eSDaniel Borkmann if (ctx.offset == NULL) { 169826eb042eSDaniel Borkmann prog = orig_prog; 1699db496944SAlexei Starovoitov goto out_off; 170026eb042eSDaniel Borkmann } 1701e54bcde3SZi Shen Lim 17025b3d19b9SXu Kuohai ctx.fpb_offset = find_fpb_offset(prog); 1703*4dd31243SPuranjay Mohan ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena); 17045b3d19b9SXu Kuohai 170568e4f238SHou Tao /* 170668e4f238SHou Tao * 1. Initial fake pass to compute ctx->idx and ctx->offset. 170768e4f238SHou Tao * 170868e4f238SHou Tao * BPF line info needs ctx->offset[i] to be the offset of 170968e4f238SHou Tao * instruction[i] in jited image, so build prologue first. 171068e4f238SHou Tao */ 1711339af577SPuranjay Mohan if (build_prologue(&ctx, was_classic, prog->aux->exception_cb, 1712339af577SPuranjay Mohan arena_vm_start)) { 171326eb042eSDaniel Borkmann prog = orig_prog; 171426eb042eSDaniel Borkmann goto out_off; 171526eb042eSDaniel Borkmann } 1716e54bcde3SZi Shen Lim 171768e4f238SHou Tao if (build_body(&ctx, extra_pass)) { 1718ddb55992SZi Shen Lim prog = orig_prog; 1719ddb55992SZi Shen Lim goto out_off; 1720ddb55992SZi Shen Lim } 172151c9fbb1SZi Shen Lim 172251c9fbb1SZi Shen Lim ctx.epilogue_offset = ctx.idx; 172322fc0e80SPuranjay Mohan build_epilogue(&ctx, prog->aux->exception_cb); 1724b2ad54e1SXu Kuohai build_plt(&ctx); 1725e54bcde3SZi Shen Lim 1726b2ad54e1SXu Kuohai extable_align = __alignof__(struct exception_table_entry); 172780083428SJean-Philippe Brucker extable_size = prog->aux->num_exentries * 172880083428SJean-Philippe Brucker sizeof(struct exception_table_entry); 172980083428SJean-Philippe Brucker 1730e54bcde3SZi Shen Lim /* Now we know the actual image size. */ 173180083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1732b2ad54e1SXu Kuohai /* also allocate space for plt target */ 1733b2ad54e1SXu Kuohai extable_offset = round_up(prog_size + PLT_TARGET_SIZE, extable_align); 1734b2ad54e1SXu Kuohai image_size = extable_offset + extable_size; 17351dad391dSPuranjay Mohan ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, 17361dad391dSPuranjay Mohan sizeof(u32), &header, &image_ptr, 17371dad391dSPuranjay Mohan jit_fill_hole); 17381dad391dSPuranjay Mohan if (!ro_header) { 173926eb042eSDaniel Borkmann prog = orig_prog; 174026eb042eSDaniel Borkmann goto out_off; 174126eb042eSDaniel Borkmann } 1742e54bcde3SZi Shen Lim 1743e54bcde3SZi Shen Lim /* 2. Now, the actual pass. */ 1744e54bcde3SZi Shen Lim 17451dad391dSPuranjay Mohan /* 17461dad391dSPuranjay Mohan * Use the image(RW) for writing the JITed instructions. But also save 17471dad391dSPuranjay Mohan * the ro_image(RX) for calculating the offsets in the image. The RW 17481dad391dSPuranjay Mohan * image will be later copied to the RX image from where the program 17491dad391dSPuranjay Mohan * will run. The bpf_jit_binary_pack_finalize() will do this copy in the 17501dad391dSPuranjay Mohan * final step. 17511dad391dSPuranjay Mohan */ 1752425e1ed7SLuc Van Oostenryck ctx.image = (__le32 *)image_ptr; 17531dad391dSPuranjay Mohan ctx.ro_image = (__le32 *)ro_image_ptr; 175480083428SJean-Philippe Brucker if (extable_size) 17551dad391dSPuranjay Mohan prog->aux->extable = (void *)ro_image_ptr + extable_offset; 1756db496944SAlexei Starovoitov skip_init_ctx: 1757e54bcde3SZi Shen Lim ctx.idx = 0; 175880083428SJean-Philippe Brucker ctx.exentry_idx = 0; 1759b569c1c6SDaniel Borkmann 1760339af577SPuranjay Mohan build_prologue(&ctx, was_classic, prog->aux->exception_cb, arena_vm_start); 1761e54bcde3SZi Shen Lim 17628c11ea5cSDaniel Borkmann if (build_body(&ctx, extra_pass)) { 176326eb042eSDaniel Borkmann prog = orig_prog; 17641dad391dSPuranjay Mohan goto out_free_hdr; 176560ef0494SDaniel Borkmann } 1766e54bcde3SZi Shen Lim 176722fc0e80SPuranjay Mohan build_epilogue(&ctx, prog->aux->exception_cb); 1768b2ad54e1SXu Kuohai build_plt(&ctx); 1769e54bcde3SZi Shen Lim 177042ff712bSZi Shen Lim /* 3. Extra pass to validate JITed code. */ 1771efc9909fSXu Kuohai if (validate_ctx(&ctx)) { 177226eb042eSDaniel Borkmann prog = orig_prog; 17731dad391dSPuranjay Mohan goto out_free_hdr; 177442ff712bSZi Shen Lim } 177542ff712bSZi Shen Lim 1776e54bcde3SZi Shen Lim /* And we're done. */ 1777e54bcde3SZi Shen Lim if (bpf_jit_enable > 1) 177880083428SJean-Philippe Brucker bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 1779e54bcde3SZi Shen Lim 1780db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1781db496944SAlexei Starovoitov if (extra_pass && ctx.idx != jit_data->ctx.idx) { 1782db496944SAlexei Starovoitov pr_err_once("multi-func JIT bug %d != %d\n", 1783db496944SAlexei Starovoitov ctx.idx, jit_data->ctx.idx); 1784db496944SAlexei Starovoitov prog->bpf_func = NULL; 1785db496944SAlexei Starovoitov prog->jited = 0; 178610f3b29cSEric Dumazet prog->jited_len = 0; 17871dad391dSPuranjay Mohan goto out_free_hdr; 17881dad391dSPuranjay Mohan } 17891dad391dSPuranjay Mohan if (WARN_ON(bpf_jit_binary_pack_finalize(prog, ro_header, 17901dad391dSPuranjay Mohan header))) { 17911dad391dSPuranjay Mohan /* ro_header has been freed */ 17921dad391dSPuranjay Mohan ro_header = NULL; 17931dad391dSPuranjay Mohan prog = orig_prog; 1794db496944SAlexei Starovoitov goto out_off; 1795db496944SAlexei Starovoitov } 17961dad391dSPuranjay Mohan /* 17971dad391dSPuranjay Mohan * The instructions have now been copied to the ROX region from 17981dad391dSPuranjay Mohan * where they will execute. Now the data cache has to be cleaned to 17991dad391dSPuranjay Mohan * the PoU and the I-cache has to be invalidated for the VAs. 18001dad391dSPuranjay Mohan */ 18011dad391dSPuranjay Mohan bpf_flush_icache(ro_header, ctx.ro_image + ctx.idx); 1802db496944SAlexei Starovoitov } else { 1803db496944SAlexei Starovoitov jit_data->ctx = ctx; 18041dad391dSPuranjay Mohan jit_data->ro_image = ro_image_ptr; 1805db496944SAlexei Starovoitov jit_data->header = header; 18061dad391dSPuranjay Mohan jit_data->ro_header = ro_header; 1807db496944SAlexei Starovoitov } 18081dad391dSPuranjay Mohan 18091dad391dSPuranjay Mohan prog->bpf_func = (void *)ctx.ro_image; 1810a91263d5SDaniel Borkmann prog->jited = 1; 181180083428SJean-Philippe Brucker prog->jited_len = prog_size; 181226eb042eSDaniel Borkmann 1813db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1814dda7596cSHou Tao int i; 1815dda7596cSHou Tao 1816dda7596cSHou Tao /* offset[prog->len] is the size of program */ 1817dda7596cSHou Tao for (i = 0; i <= prog->len; i++) 1818dda7596cSHou Tao ctx.offset[i] *= AARCH64_INSN_SIZE; 181932f6865cSIlias Apalodimas bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 182026eb042eSDaniel Borkmann out_off: 182119f68ed6SAijun Sun kvfree(ctx.offset); 1822db496944SAlexei Starovoitov kfree(jit_data); 1823db496944SAlexei Starovoitov prog->aux->jit_data = NULL; 1824db496944SAlexei Starovoitov } 182526eb042eSDaniel Borkmann out: 182626eb042eSDaniel Borkmann if (tmp_blinded) 182726eb042eSDaniel Borkmann bpf_jit_prog_release_other(prog, prog == orig_prog ? 182826eb042eSDaniel Borkmann tmp : orig_prog); 1829d1c55ab5SDaniel Borkmann return prog; 18301dad391dSPuranjay Mohan 18311dad391dSPuranjay Mohan out_free_hdr: 18321dad391dSPuranjay Mohan if (header) { 18331dad391dSPuranjay Mohan bpf_arch_text_copy(&ro_header->size, &header->size, 18341dad391dSPuranjay Mohan sizeof(header->size)); 18351dad391dSPuranjay Mohan bpf_jit_binary_pack_free(ro_header, header); 18361dad391dSPuranjay Mohan } 18371dad391dSPuranjay Mohan goto out_off; 1838e54bcde3SZi Shen Lim } 183991fc957cSArd Biesheuvel 1840b5e975d2SHou Tao bool bpf_jit_supports_kfunc_call(void) 1841b5e975d2SHou Tao { 1842b5e975d2SHou Tao return true; 1843b5e975d2SHou Tao } 1844b5e975d2SHou Tao 18451dad391dSPuranjay Mohan void *bpf_arch_text_copy(void *dst, void *src, size_t len) 18461dad391dSPuranjay Mohan { 18471dad391dSPuranjay Mohan if (!aarch64_insn_copy(dst, src, len)) 18481dad391dSPuranjay Mohan return ERR_PTR(-EINVAL); 18491dad391dSPuranjay Mohan return dst; 18501dad391dSPuranjay Mohan } 18511dad391dSPuranjay Mohan 18525d63ae90SLorenz Bauer u64 bpf_jit_alloc_exec_limit(void) 18535d63ae90SLorenz Bauer { 1854b89ddf4cSRussell King return VMALLOC_END - VMALLOC_START; 18555d63ae90SLorenz Bauer } 18565d63ae90SLorenz Bauer 185791fc957cSArd Biesheuvel void *bpf_jit_alloc_exec(unsigned long size) 185891fc957cSArd Biesheuvel { 185936c4a73bSAndrey Konovalov /* Memory is intended to be executable, reset the pointer tag. */ 186036c4a73bSAndrey Konovalov return kasan_reset_tag(vmalloc(size)); 186191fc957cSArd Biesheuvel } 186291fc957cSArd Biesheuvel 186391fc957cSArd Biesheuvel void bpf_jit_free_exec(void *addr) 186491fc957cSArd Biesheuvel { 186591fc957cSArd Biesheuvel return vfree(addr); 186691fc957cSArd Biesheuvel } 1867d4609a5dSJakub Sitnicki 1868d4609a5dSJakub Sitnicki /* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */ 1869d4609a5dSJakub Sitnicki bool bpf_jit_supports_subprog_tailcalls(void) 1870d4609a5dSJakub Sitnicki { 1871d4609a5dSJakub Sitnicki return true; 1872d4609a5dSJakub Sitnicki } 1873b2ad54e1SXu Kuohai 1874efc9909fSXu Kuohai static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, 1875efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1876efc9909fSXu Kuohai bool save_ret) 1877efc9909fSXu Kuohai { 1878aada4766SXu Kuohai __le32 *branch; 1879efc9909fSXu Kuohai u64 enter_prog; 1880efc9909fSXu Kuohai u64 exit_prog; 1881efc9909fSXu Kuohai struct bpf_prog *p = l->link.prog; 1882efc9909fSXu Kuohai int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); 1883efc9909fSXu Kuohai 1884271de525SMartin KaFai Lau enter_prog = (u64)bpf_trampoline_enter(p); 1885271de525SMartin KaFai Lau exit_prog = (u64)bpf_trampoline_exit(p); 1886efc9909fSXu Kuohai 1887efc9909fSXu Kuohai if (l->cookie == 0) { 1888efc9909fSXu Kuohai /* if cookie is zero, one instruction is enough to store it */ 1889efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, run_ctx_off + cookie_off), ctx); 1890efc9909fSXu Kuohai } else { 1891efc9909fSXu Kuohai emit_a64_mov_i64(A64_R(10), l->cookie, ctx); 1892efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, run_ctx_off + cookie_off), 1893efc9909fSXu Kuohai ctx); 1894efc9909fSXu Kuohai } 1895efc9909fSXu Kuohai 1896efc9909fSXu Kuohai /* save p to callee saved register x19 to avoid loading p with mov_i64 1897efc9909fSXu Kuohai * each time. 1898efc9909fSXu Kuohai */ 1899efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(19), (const u64)p, ctx); 1900efc9909fSXu Kuohai 1901efc9909fSXu Kuohai /* arg1: prog */ 1902efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1903efc9909fSXu Kuohai /* arg2: &run_ctx */ 1904efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(1), A64_SP, run_ctx_off), ctx); 1905efc9909fSXu Kuohai 1906efc9909fSXu Kuohai emit_call(enter_prog, ctx); 1907efc9909fSXu Kuohai 1908efc9909fSXu Kuohai /* if (__bpf_prog_enter(prog) == 0) 1909efc9909fSXu Kuohai * goto skip_exec_of_prog; 1910efc9909fSXu Kuohai */ 1911efc9909fSXu Kuohai branch = ctx->image + ctx->idx; 1912efc9909fSXu Kuohai emit(A64_NOP, ctx); 1913efc9909fSXu Kuohai 1914efc9909fSXu Kuohai /* save return value to callee saved register x20 */ 1915efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(20), A64_R(0)), ctx); 1916efc9909fSXu Kuohai 1917efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx); 1918efc9909fSXu Kuohai if (!p->jited) 1919efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx); 1920efc9909fSXu Kuohai 1921efc9909fSXu Kuohai emit_call((const u64)p->bpf_func, ctx); 1922efc9909fSXu Kuohai 1923efc9909fSXu Kuohai if (save_ret) 1924efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 1925efc9909fSXu Kuohai 1926efc9909fSXu Kuohai if (ctx->image) { 1927efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branch; 1928aada4766SXu Kuohai *branch = cpu_to_le32(A64_CBZ(1, A64_R(0), offset)); 1929efc9909fSXu Kuohai } 1930efc9909fSXu Kuohai 1931efc9909fSXu Kuohai /* arg1: prog */ 1932efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1933efc9909fSXu Kuohai /* arg2: start time */ 1934efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(1), A64_R(20)), ctx); 1935efc9909fSXu Kuohai /* arg3: &run_ctx */ 1936efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(2), A64_SP, run_ctx_off), ctx); 1937efc9909fSXu Kuohai 1938efc9909fSXu Kuohai emit_call(exit_prog, ctx); 1939efc9909fSXu Kuohai } 1940efc9909fSXu Kuohai 1941efc9909fSXu Kuohai static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, 1942efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1943aada4766SXu Kuohai __le32 **branches) 1944efc9909fSXu Kuohai { 1945efc9909fSXu Kuohai int i; 1946efc9909fSXu Kuohai 1947efc9909fSXu Kuohai /* The first fmod_ret program will receive a garbage return value. 1948efc9909fSXu Kuohai * Set this to 0 to avoid confusing the program. 1949efc9909fSXu Kuohai */ 1950efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx); 1951efc9909fSXu Kuohai for (i = 0; i < tl->nr_links; i++) { 1952efc9909fSXu Kuohai invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off, 1953efc9909fSXu Kuohai run_ctx_off, true); 1954efc9909fSXu Kuohai /* if (*(u64 *)(sp + retval_off) != 0) 1955efc9909fSXu Kuohai * goto do_fexit; 1956efc9909fSXu Kuohai */ 1957efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retval_off), ctx); 1958efc9909fSXu Kuohai /* Save the location of branch, and generate a nop. 1959efc9909fSXu Kuohai * This nop will be replaced with a cbnz later. 1960efc9909fSXu Kuohai */ 1961efc9909fSXu Kuohai branches[i] = ctx->image + ctx->idx; 1962efc9909fSXu Kuohai emit(A64_NOP, ctx); 1963efc9909fSXu Kuohai } 1964efc9909fSXu Kuohai } 1965efc9909fSXu Kuohai 196690564f1eSFlorent Revest static void save_args(struct jit_ctx *ctx, int args_off, int nregs) 1967efc9909fSXu Kuohai { 1968efc9909fSXu Kuohai int i; 1969efc9909fSXu Kuohai 197090564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 1971efc9909fSXu Kuohai emit(A64_STR64I(i, A64_SP, args_off), ctx); 1972efc9909fSXu Kuohai args_off += 8; 1973efc9909fSXu Kuohai } 1974efc9909fSXu Kuohai } 1975efc9909fSXu Kuohai 197690564f1eSFlorent Revest static void restore_args(struct jit_ctx *ctx, int args_off, int nregs) 1977efc9909fSXu Kuohai { 1978efc9909fSXu Kuohai int i; 1979efc9909fSXu Kuohai 198090564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 1981efc9909fSXu Kuohai emit(A64_LDR64I(i, A64_SP, args_off), ctx); 1982efc9909fSXu Kuohai args_off += 8; 1983efc9909fSXu Kuohai } 1984efc9909fSXu Kuohai } 1985efc9909fSXu Kuohai 1986efc9909fSXu Kuohai /* Based on the x86's implementation of arch_prepare_bpf_trampoline(). 1987efc9909fSXu Kuohai * 1988efc9909fSXu Kuohai * bpf prog and function entry before bpf trampoline hooked: 1989efc9909fSXu Kuohai * mov x9, lr 1990efc9909fSXu Kuohai * nop 1991efc9909fSXu Kuohai * 1992efc9909fSXu Kuohai * bpf prog and function entry after bpf trampoline hooked: 1993efc9909fSXu Kuohai * mov x9, lr 1994efc9909fSXu Kuohai * bl <bpf_trampoline or plt> 1995efc9909fSXu Kuohai * 1996efc9909fSXu Kuohai */ 1997efc9909fSXu Kuohai static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, 19987a3d9a15SSong Liu struct bpf_tramp_links *tlinks, void *func_addr, 199990564f1eSFlorent Revest int nregs, u32 flags) 2000efc9909fSXu Kuohai { 2001efc9909fSXu Kuohai int i; 2002efc9909fSXu Kuohai int stack_size; 2003efc9909fSXu Kuohai int retaddr_off; 2004efc9909fSXu Kuohai int regs_off; 2005efc9909fSXu Kuohai int retval_off; 2006efc9909fSXu Kuohai int args_off; 200790564f1eSFlorent Revest int nregs_off; 2008efc9909fSXu Kuohai int ip_off; 2009efc9909fSXu Kuohai int run_ctx_off; 2010efc9909fSXu Kuohai struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; 2011efc9909fSXu Kuohai struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; 2012efc9909fSXu Kuohai struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; 2013efc9909fSXu Kuohai bool save_ret; 2014aada4766SXu Kuohai __le32 **branches = NULL; 2015efc9909fSXu Kuohai 2016efc9909fSXu Kuohai /* trampoline stack layout: 2017efc9909fSXu Kuohai * [ parent ip ] 2018efc9909fSXu Kuohai * [ FP ] 2019efc9909fSXu Kuohai * SP + retaddr_off [ self ip ] 2020efc9909fSXu Kuohai * [ FP ] 2021efc9909fSXu Kuohai * 2022efc9909fSXu Kuohai * [ padding ] align SP to multiples of 16 2023efc9909fSXu Kuohai * 2024efc9909fSXu Kuohai * [ x20 ] callee saved reg x20 2025efc9909fSXu Kuohai * SP + regs_off [ x19 ] callee saved reg x19 2026efc9909fSXu Kuohai * 2027efc9909fSXu Kuohai * SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or 2028efc9909fSXu Kuohai * BPF_TRAMP_F_RET_FENTRY_RET 2029efc9909fSXu Kuohai * 203090564f1eSFlorent Revest * [ arg reg N ] 2031efc9909fSXu Kuohai * [ ... ] 203290564f1eSFlorent Revest * SP + args_off [ arg reg 1 ] 2033efc9909fSXu Kuohai * 203490564f1eSFlorent Revest * SP + nregs_off [ arg regs count ] 2035efc9909fSXu Kuohai * 2036efc9909fSXu Kuohai * SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag 2037efc9909fSXu Kuohai * 2038efc9909fSXu Kuohai * SP + run_ctx_off [ bpf_tramp_run_ctx ] 2039efc9909fSXu Kuohai */ 2040efc9909fSXu Kuohai 2041efc9909fSXu Kuohai stack_size = 0; 2042efc9909fSXu Kuohai run_ctx_off = stack_size; 2043efc9909fSXu Kuohai /* room for bpf_tramp_run_ctx */ 2044efc9909fSXu Kuohai stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); 2045efc9909fSXu Kuohai 2046efc9909fSXu Kuohai ip_off = stack_size; 2047efc9909fSXu Kuohai /* room for IP address argument */ 2048efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) 2049efc9909fSXu Kuohai stack_size += 8; 2050efc9909fSXu Kuohai 205190564f1eSFlorent Revest nregs_off = stack_size; 2052efc9909fSXu Kuohai /* room for args count */ 2053efc9909fSXu Kuohai stack_size += 8; 2054efc9909fSXu Kuohai 2055efc9909fSXu Kuohai args_off = stack_size; 2056efc9909fSXu Kuohai /* room for args */ 205790564f1eSFlorent Revest stack_size += nregs * 8; 2058efc9909fSXu Kuohai 2059efc9909fSXu Kuohai /* room for return value */ 2060efc9909fSXu Kuohai retval_off = stack_size; 2061efc9909fSXu Kuohai save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET); 2062efc9909fSXu Kuohai if (save_ret) 2063efc9909fSXu Kuohai stack_size += 8; 2064efc9909fSXu Kuohai 2065efc9909fSXu Kuohai /* room for callee saved registers, currently x19 and x20 are used */ 2066efc9909fSXu Kuohai regs_off = stack_size; 2067efc9909fSXu Kuohai stack_size += 16; 2068efc9909fSXu Kuohai 2069efc9909fSXu Kuohai /* round up to multiples of 16 to avoid SPAlignmentFault */ 2070efc9909fSXu Kuohai stack_size = round_up(stack_size, 16); 2071efc9909fSXu Kuohai 2072efc9909fSXu Kuohai /* return address locates above FP */ 2073efc9909fSXu Kuohai retaddr_off = stack_size + 8; 2074efc9909fSXu Kuohai 2075efc9909fSXu Kuohai /* bpf trampoline may be invoked by 3 instruction types: 2076efc9909fSXu Kuohai * 1. bl, attached to bpf prog or kernel function via short jump 2077efc9909fSXu Kuohai * 2. br, attached to bpf prog or kernel function via long jump 2078efc9909fSXu Kuohai * 3. blr, working as a function pointer, used by struct_ops. 2079efc9909fSXu Kuohai * So BTI_JC should used here to support both br and blr. 2080efc9909fSXu Kuohai */ 2081efc9909fSXu Kuohai emit_bti(A64_BTI_JC, ctx); 2082efc9909fSXu Kuohai 2083efc9909fSXu Kuohai /* frame for parent function */ 2084efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx); 2085efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 2086efc9909fSXu Kuohai 2087efc9909fSXu Kuohai /* frame for patched function */ 2088efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 2089efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 2090efc9909fSXu Kuohai 2091efc9909fSXu Kuohai /* allocate stack space */ 2092efc9909fSXu Kuohai emit(A64_SUB_I(1, A64_SP, A64_SP, stack_size), ctx); 2093efc9909fSXu Kuohai 2094efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) { 2095efc9909fSXu Kuohai /* save ip address of the traced function */ 20967a3d9a15SSong Liu emit_addr_mov_i64(A64_R(10), (const u64)func_addr, ctx); 2097efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, ip_off), ctx); 2098efc9909fSXu Kuohai } 2099efc9909fSXu Kuohai 210090564f1eSFlorent Revest /* save arg regs count*/ 210190564f1eSFlorent Revest emit(A64_MOVZ(1, A64_R(10), nregs, 0), ctx); 210290564f1eSFlorent Revest emit(A64_STR64I(A64_R(10), A64_SP, nregs_off), ctx); 2103efc9909fSXu Kuohai 210490564f1eSFlorent Revest /* save arg regs */ 210590564f1eSFlorent Revest save_args(ctx, args_off, nregs); 2106efc9909fSXu Kuohai 2107efc9909fSXu Kuohai /* save callee saved registers */ 2108efc9909fSXu Kuohai emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx); 2109efc9909fSXu Kuohai emit(A64_STR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 2110efc9909fSXu Kuohai 2111efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 2112efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 2113efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_enter, ctx); 2114efc9909fSXu Kuohai } 2115efc9909fSXu Kuohai 2116efc9909fSXu Kuohai for (i = 0; i < fentry->nr_links; i++) 2117efc9909fSXu Kuohai invoke_bpf_prog(ctx, fentry->links[i], args_off, 2118efc9909fSXu Kuohai retval_off, run_ctx_off, 2119efc9909fSXu Kuohai flags & BPF_TRAMP_F_RET_FENTRY_RET); 2120efc9909fSXu Kuohai 2121efc9909fSXu Kuohai if (fmod_ret->nr_links) { 2122aada4766SXu Kuohai branches = kcalloc(fmod_ret->nr_links, sizeof(__le32 *), 2123efc9909fSXu Kuohai GFP_KERNEL); 2124efc9909fSXu Kuohai if (!branches) 2125efc9909fSXu Kuohai return -ENOMEM; 2126efc9909fSXu Kuohai 2127efc9909fSXu Kuohai invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off, 2128efc9909fSXu Kuohai run_ctx_off, branches); 2129efc9909fSXu Kuohai } 2130efc9909fSXu Kuohai 2131efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 213290564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 2133efc9909fSXu Kuohai /* call original func */ 2134efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx); 2135738a96c4SXu Kuohai emit(A64_ADR(A64_LR, AARCH64_INSN_SIZE * 2), ctx); 2136738a96c4SXu Kuohai emit(A64_RET(A64_R(10)), ctx); 2137efc9909fSXu Kuohai /* store return value */ 2138efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 2139efc9909fSXu Kuohai /* reserve a nop for bpf_tramp_image_put */ 214096b0f5adSPuranjay Mohan im->ip_after_call = ctx->ro_image + ctx->idx; 2141efc9909fSXu Kuohai emit(A64_NOP, ctx); 2142efc9909fSXu Kuohai } 2143efc9909fSXu Kuohai 2144efc9909fSXu Kuohai /* update the branches saved in invoke_bpf_mod_ret with cbnz */ 2145efc9909fSXu Kuohai for (i = 0; i < fmod_ret->nr_links && ctx->image != NULL; i++) { 2146efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branches[i]; 2147aada4766SXu Kuohai *branches[i] = cpu_to_le32(A64_CBNZ(1, A64_R(10), offset)); 2148efc9909fSXu Kuohai } 2149efc9909fSXu Kuohai 2150efc9909fSXu Kuohai for (i = 0; i < fexit->nr_links; i++) 2151efc9909fSXu Kuohai invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off, 2152efc9909fSXu Kuohai run_ctx_off, false); 2153efc9909fSXu Kuohai 2154efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 215596b0f5adSPuranjay Mohan im->ip_epilogue = ctx->ro_image + ctx->idx; 2156efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 2157efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_exit, ctx); 2158efc9909fSXu Kuohai } 2159efc9909fSXu Kuohai 2160efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_RESTORE_REGS) 216190564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 2162efc9909fSXu Kuohai 2163efc9909fSXu Kuohai /* restore callee saved register x19 and x20 */ 2164efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx); 2165efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 2166efc9909fSXu Kuohai 2167efc9909fSXu Kuohai if (save_ret) 2168efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(0), A64_SP, retval_off), ctx); 2169efc9909fSXu Kuohai 2170efc9909fSXu Kuohai /* reset SP */ 2171efc9909fSXu Kuohai emit(A64_MOV(1, A64_SP, A64_FP), ctx); 2172efc9909fSXu Kuohai 2173efc9909fSXu Kuohai /* pop frames */ 2174efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 2175efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx); 2176efc9909fSXu Kuohai 2177efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_SKIP_FRAME) { 2178efc9909fSXu Kuohai /* skip patched function, return to parent */ 2179efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 2180efc9909fSXu Kuohai emit(A64_RET(A64_R(9)), ctx); 2181efc9909fSXu Kuohai } else { 2182efc9909fSXu Kuohai /* return to patched function */ 2183efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(10), A64_LR), ctx); 2184efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 2185efc9909fSXu Kuohai emit(A64_RET(A64_R(10)), ctx); 2186efc9909fSXu Kuohai } 2187efc9909fSXu Kuohai 2188efc9909fSXu Kuohai kfree(branches); 2189efc9909fSXu Kuohai 2190efc9909fSXu Kuohai return ctx->idx; 2191efc9909fSXu Kuohai } 2192efc9909fSXu Kuohai 219396d1b7c0SSong Liu static int btf_func_model_nregs(const struct btf_func_model *m) 2194efc9909fSXu Kuohai { 219590564f1eSFlorent Revest int nregs = m->nr_args; 219696d1b7c0SSong Liu int i; 2197efc9909fSXu Kuohai 219890564f1eSFlorent Revest /* extra registers needed for struct argument */ 2199eb707ddeSYonghong Song for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) { 220090564f1eSFlorent Revest /* The arg_size is at most 16 bytes, enforced by the verifier. */ 2201eb707ddeSYonghong Song if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) 220290564f1eSFlorent Revest nregs += (m->arg_size[i] + 7) / 8 - 1; 2203eb707ddeSYonghong Song } 2204eb707ddeSYonghong Song 220596d1b7c0SSong Liu return nregs; 220696d1b7c0SSong Liu } 220796d1b7c0SSong Liu 220896d1b7c0SSong Liu int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, 220996d1b7c0SSong Liu struct bpf_tramp_links *tlinks, void *func_addr) 221096d1b7c0SSong Liu { 221196d1b7c0SSong Liu struct jit_ctx ctx = { 221296d1b7c0SSong Liu .image = NULL, 221396d1b7c0SSong Liu .idx = 0, 221496d1b7c0SSong Liu }; 221596d1b7c0SSong Liu struct bpf_tramp_image im; 221696d1b7c0SSong Liu int nregs, ret; 221796d1b7c0SSong Liu 221896d1b7c0SSong Liu nregs = btf_func_model_nregs(m); 221990564f1eSFlorent Revest /* the first 8 registers are used for arguments */ 222090564f1eSFlorent Revest if (nregs > 8) 222190564f1eSFlorent Revest return -ENOTSUPP; 222290564f1eSFlorent Revest 222396d1b7c0SSong Liu ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, nregs, flags); 2224efc9909fSXu Kuohai if (ret < 0) 2225efc9909fSXu Kuohai return ret; 2226efc9909fSXu Kuohai 222796d1b7c0SSong Liu return ret < 0 ? ret : ret * AARCH64_INSN_SIZE; 222896d1b7c0SSong Liu } 2229efc9909fSXu Kuohai 223096b0f5adSPuranjay Mohan void *arch_alloc_bpf_trampoline(unsigned int size) 223196b0f5adSPuranjay Mohan { 223296b0f5adSPuranjay Mohan return bpf_prog_pack_alloc(size, jit_fill_hole); 223396b0f5adSPuranjay Mohan } 223496b0f5adSPuranjay Mohan 223596b0f5adSPuranjay Mohan void arch_free_bpf_trampoline(void *image, unsigned int size) 223696b0f5adSPuranjay Mohan { 223796b0f5adSPuranjay Mohan bpf_prog_pack_free(image, size); 223896b0f5adSPuranjay Mohan } 223996b0f5adSPuranjay Mohan 2240c733239fSChristophe Leroy int arch_protect_bpf_trampoline(void *image, unsigned int size) 224196b0f5adSPuranjay Mohan { 2242c733239fSChristophe Leroy return 0; 224396b0f5adSPuranjay Mohan } 224496b0f5adSPuranjay Mohan 224596b0f5adSPuranjay Mohan int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image, 224696b0f5adSPuranjay Mohan void *ro_image_end, const struct btf_func_model *m, 224796d1b7c0SSong Liu u32 flags, struct bpf_tramp_links *tlinks, 224896d1b7c0SSong Liu void *func_addr) 224996d1b7c0SSong Liu { 225096d1b7c0SSong Liu int ret, nregs; 225196b0f5adSPuranjay Mohan void *image, *tmp; 225296b0f5adSPuranjay Mohan u32 size = ro_image_end - ro_image; 225396b0f5adSPuranjay Mohan 225496b0f5adSPuranjay Mohan /* image doesn't need to be in module memory range, so we can 225596b0f5adSPuranjay Mohan * use kvmalloc. 225696b0f5adSPuranjay Mohan */ 225796b0f5adSPuranjay Mohan image = kvmalloc(size, GFP_KERNEL); 225896b0f5adSPuranjay Mohan if (!image) 225996b0f5adSPuranjay Mohan return -ENOMEM; 226096b0f5adSPuranjay Mohan 226196d1b7c0SSong Liu struct jit_ctx ctx = { 226296d1b7c0SSong Liu .image = image, 226396b0f5adSPuranjay Mohan .ro_image = ro_image, 226496d1b7c0SSong Liu .idx = 0, 226596d1b7c0SSong Liu }; 226696d1b7c0SSong Liu 226796d1b7c0SSong Liu nregs = btf_func_model_nregs(m); 226896d1b7c0SSong Liu /* the first 8 registers are used for arguments */ 226996d1b7c0SSong Liu if (nregs > 8) 227096d1b7c0SSong Liu return -ENOTSUPP; 227196d1b7c0SSong Liu 227296b0f5adSPuranjay Mohan jit_fill_hole(image, (unsigned int)(ro_image_end - ro_image)); 22737a3d9a15SSong Liu ret = prepare_trampoline(&ctx, im, tlinks, func_addr, nregs, flags); 2274efc9909fSXu Kuohai 227596b0f5adSPuranjay Mohan if (ret > 0 && validate_code(&ctx) < 0) { 2276efc9909fSXu Kuohai ret = -EINVAL; 227796b0f5adSPuranjay Mohan goto out; 227896b0f5adSPuranjay Mohan } 2279efc9909fSXu Kuohai 2280efc9909fSXu Kuohai if (ret > 0) 2281efc9909fSXu Kuohai ret *= AARCH64_INSN_SIZE; 2282efc9909fSXu Kuohai 228396b0f5adSPuranjay Mohan tmp = bpf_arch_text_copy(ro_image, image, size); 228496b0f5adSPuranjay Mohan if (IS_ERR(tmp)) { 228596b0f5adSPuranjay Mohan ret = PTR_ERR(tmp); 228696b0f5adSPuranjay Mohan goto out; 228796b0f5adSPuranjay Mohan } 228896b0f5adSPuranjay Mohan 228996b0f5adSPuranjay Mohan bpf_flush_icache(ro_image, ro_image + size); 229096b0f5adSPuranjay Mohan out: 229196b0f5adSPuranjay Mohan kvfree(image); 2292efc9909fSXu Kuohai return ret; 2293efc9909fSXu Kuohai } 2294efc9909fSXu Kuohai 2295b2ad54e1SXu Kuohai static bool is_long_jump(void *ip, void *target) 2296b2ad54e1SXu Kuohai { 2297b2ad54e1SXu Kuohai long offset; 2298b2ad54e1SXu Kuohai 2299b2ad54e1SXu Kuohai /* NULL target means this is a NOP */ 2300b2ad54e1SXu Kuohai if (!target) 2301b2ad54e1SXu Kuohai return false; 2302b2ad54e1SXu Kuohai 2303b2ad54e1SXu Kuohai offset = (long)target - (long)ip; 2304b2ad54e1SXu Kuohai return offset < -SZ_128M || offset >= SZ_128M; 2305b2ad54e1SXu Kuohai } 2306b2ad54e1SXu Kuohai 2307b2ad54e1SXu Kuohai static int gen_branch_or_nop(enum aarch64_insn_branch_type type, void *ip, 2308b2ad54e1SXu Kuohai void *addr, void *plt, u32 *insn) 2309b2ad54e1SXu Kuohai { 2310b2ad54e1SXu Kuohai void *target; 2311b2ad54e1SXu Kuohai 2312b2ad54e1SXu Kuohai if (!addr) { 2313b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_nop(); 2314b2ad54e1SXu Kuohai return 0; 2315b2ad54e1SXu Kuohai } 2316b2ad54e1SXu Kuohai 2317b2ad54e1SXu Kuohai if (is_long_jump(ip, addr)) 2318b2ad54e1SXu Kuohai target = plt; 2319b2ad54e1SXu Kuohai else 2320b2ad54e1SXu Kuohai target = addr; 2321b2ad54e1SXu Kuohai 2322b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_branch_imm((unsigned long)ip, 2323b2ad54e1SXu Kuohai (unsigned long)target, 2324b2ad54e1SXu Kuohai type); 2325b2ad54e1SXu Kuohai 2326b2ad54e1SXu Kuohai return *insn != AARCH64_BREAK_FAULT ? 0 : -EFAULT; 2327b2ad54e1SXu Kuohai } 2328b2ad54e1SXu Kuohai 2329b2ad54e1SXu Kuohai /* Replace the branch instruction from @ip to @old_addr in a bpf prog or a bpf 2330b2ad54e1SXu Kuohai * trampoline with the branch instruction from @ip to @new_addr. If @old_addr 2331b2ad54e1SXu Kuohai * or @new_addr is NULL, the old or new instruction is NOP. 2332b2ad54e1SXu Kuohai * 2333b2ad54e1SXu Kuohai * When @ip is the bpf prog entry, a bpf trampoline is being attached or 2334b2ad54e1SXu Kuohai * detached. Since bpf trampoline and bpf prog are allocated separately with 2335b2ad54e1SXu Kuohai * vmalloc, the address distance may exceed 128MB, the maximum branch range. 2336b2ad54e1SXu Kuohai * So long jump should be handled. 2337b2ad54e1SXu Kuohai * 2338b2ad54e1SXu Kuohai * When a bpf prog is constructed, a plt pointing to empty trampoline 2339b2ad54e1SXu Kuohai * dummy_tramp is placed at the end: 2340b2ad54e1SXu Kuohai * 2341b2ad54e1SXu Kuohai * bpf_prog: 2342b2ad54e1SXu Kuohai * mov x9, lr 2343b2ad54e1SXu Kuohai * nop // patchsite 2344b2ad54e1SXu Kuohai * ... 2345b2ad54e1SXu Kuohai * ret 2346b2ad54e1SXu Kuohai * 2347b2ad54e1SXu Kuohai * plt: 2348b2ad54e1SXu Kuohai * ldr x10, target 2349b2ad54e1SXu Kuohai * br x10 2350b2ad54e1SXu Kuohai * target: 2351b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2352b2ad54e1SXu Kuohai * 2353b2ad54e1SXu Kuohai * This is also the state when no trampoline is attached. 2354b2ad54e1SXu Kuohai * 2355b2ad54e1SXu Kuohai * When a short-jump bpf trampoline is attached, the patchsite is patched 2356b2ad54e1SXu Kuohai * to a bl instruction to the trampoline directly: 2357b2ad54e1SXu Kuohai * 2358b2ad54e1SXu Kuohai * bpf_prog: 2359b2ad54e1SXu Kuohai * mov x9, lr 2360b2ad54e1SXu Kuohai * bl <short-jump bpf trampoline address> // patchsite 2361b2ad54e1SXu Kuohai * ... 2362b2ad54e1SXu Kuohai * ret 2363b2ad54e1SXu Kuohai * 2364b2ad54e1SXu Kuohai * plt: 2365b2ad54e1SXu Kuohai * ldr x10, target 2366b2ad54e1SXu Kuohai * br x10 2367b2ad54e1SXu Kuohai * target: 2368b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2369b2ad54e1SXu Kuohai * 2370b2ad54e1SXu Kuohai * When a long-jump bpf trampoline is attached, the plt target is filled with 2371b2ad54e1SXu Kuohai * the trampoline address and the patchsite is patched to a bl instruction to 2372b2ad54e1SXu Kuohai * the plt: 2373b2ad54e1SXu Kuohai * 2374b2ad54e1SXu Kuohai * bpf_prog: 2375b2ad54e1SXu Kuohai * mov x9, lr 2376b2ad54e1SXu Kuohai * bl plt // patchsite 2377b2ad54e1SXu Kuohai * ... 2378b2ad54e1SXu Kuohai * ret 2379b2ad54e1SXu Kuohai * 2380b2ad54e1SXu Kuohai * plt: 2381b2ad54e1SXu Kuohai * ldr x10, target 2382b2ad54e1SXu Kuohai * br x10 2383b2ad54e1SXu Kuohai * target: 2384b2ad54e1SXu Kuohai * .quad <long-jump bpf trampoline address> // plt target 2385b2ad54e1SXu Kuohai * 2386b2ad54e1SXu Kuohai * The dummy_tramp is used to prevent another CPU from jumping to unknown 2387b2ad54e1SXu Kuohai * locations during the patching process, making the patching process easier. 2388b2ad54e1SXu Kuohai */ 2389b2ad54e1SXu Kuohai int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, 2390b2ad54e1SXu Kuohai void *old_addr, void *new_addr) 2391b2ad54e1SXu Kuohai { 2392b2ad54e1SXu Kuohai int ret; 2393b2ad54e1SXu Kuohai u32 old_insn; 2394b2ad54e1SXu Kuohai u32 new_insn; 2395b2ad54e1SXu Kuohai u32 replaced; 2396b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 2397b2ad54e1SXu Kuohai unsigned long size = 0UL; 2398b2ad54e1SXu Kuohai unsigned long offset = ~0UL; 2399b2ad54e1SXu Kuohai enum aarch64_insn_branch_type branch_type; 2400b2ad54e1SXu Kuohai char namebuf[KSYM_NAME_LEN]; 2401b2ad54e1SXu Kuohai void *image = NULL; 2402b2ad54e1SXu Kuohai u64 plt_target = 0ULL; 2403b2ad54e1SXu Kuohai bool poking_bpf_entry; 2404b2ad54e1SXu Kuohai 2405b2ad54e1SXu Kuohai if (!__bpf_address_lookup((unsigned long)ip, &size, &offset, namebuf)) 2406b2ad54e1SXu Kuohai /* Only poking bpf text is supported. Since kernel function 2407b2ad54e1SXu Kuohai * entry is set up by ftrace, we reply on ftrace to poke kernel 2408b2ad54e1SXu Kuohai * functions. 2409b2ad54e1SXu Kuohai */ 2410b2ad54e1SXu Kuohai return -ENOTSUPP; 2411b2ad54e1SXu Kuohai 2412b2ad54e1SXu Kuohai image = ip - offset; 2413b2ad54e1SXu Kuohai /* zero offset means we're poking bpf prog entry */ 2414b2ad54e1SXu Kuohai poking_bpf_entry = (offset == 0UL); 2415b2ad54e1SXu Kuohai 2416b2ad54e1SXu Kuohai /* bpf prog entry, find plt and the real patchsite */ 2417b2ad54e1SXu Kuohai if (poking_bpf_entry) { 2418b2ad54e1SXu Kuohai /* plt locates at the end of bpf prog */ 2419b2ad54e1SXu Kuohai plt = image + size - PLT_TARGET_OFFSET; 2420b2ad54e1SXu Kuohai 2421b2ad54e1SXu Kuohai /* skip to the nop instruction in bpf prog entry: 2422b2ad54e1SXu Kuohai * bti c // if BTI enabled 2423b2ad54e1SXu Kuohai * mov x9, x30 2424b2ad54e1SXu Kuohai * nop 2425b2ad54e1SXu Kuohai */ 2426b2ad54e1SXu Kuohai ip = image + POKE_OFFSET * AARCH64_INSN_SIZE; 2427b2ad54e1SXu Kuohai } 2428b2ad54e1SXu Kuohai 2429b2ad54e1SXu Kuohai /* long jump is only possible at bpf prog entry */ 2430b2ad54e1SXu Kuohai if (WARN_ON((is_long_jump(ip, new_addr) || is_long_jump(ip, old_addr)) && 2431b2ad54e1SXu Kuohai !poking_bpf_entry)) 2432b2ad54e1SXu Kuohai return -EINVAL; 2433b2ad54e1SXu Kuohai 2434b2ad54e1SXu Kuohai if (poke_type == BPF_MOD_CALL) 2435b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_LINK; 2436b2ad54e1SXu Kuohai else 2437b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_NOLINK; 2438b2ad54e1SXu Kuohai 2439b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, old_addr, plt, &old_insn) < 0) 2440b2ad54e1SXu Kuohai return -EFAULT; 2441b2ad54e1SXu Kuohai 2442b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, new_addr, plt, &new_insn) < 0) 2443b2ad54e1SXu Kuohai return -EFAULT; 2444b2ad54e1SXu Kuohai 2445b2ad54e1SXu Kuohai if (is_long_jump(ip, new_addr)) 2446b2ad54e1SXu Kuohai plt_target = (u64)new_addr; 2447b2ad54e1SXu Kuohai else if (is_long_jump(ip, old_addr)) 2448b2ad54e1SXu Kuohai /* if the old target is a long jump and the new target is not, 2449b2ad54e1SXu Kuohai * restore the plt target to dummy_tramp, so there is always a 2450b2ad54e1SXu Kuohai * legal and harmless address stored in plt target, and we'll 2451b2ad54e1SXu Kuohai * never jump from plt to an unknown place. 2452b2ad54e1SXu Kuohai */ 2453b2ad54e1SXu Kuohai plt_target = (u64)&dummy_tramp; 2454b2ad54e1SXu Kuohai 2455b2ad54e1SXu Kuohai if (plt_target) { 2456b2ad54e1SXu Kuohai /* non-zero plt_target indicates we're patching a bpf prog, 2457b2ad54e1SXu Kuohai * which is read only. 2458b2ad54e1SXu Kuohai */ 2459b2ad54e1SXu Kuohai if (set_memory_rw(PAGE_MASK & ((uintptr_t)&plt->target), 1)) 2460b2ad54e1SXu Kuohai return -EFAULT; 2461b2ad54e1SXu Kuohai WRITE_ONCE(plt->target, plt_target); 2462b2ad54e1SXu Kuohai set_memory_ro(PAGE_MASK & ((uintptr_t)&plt->target), 1); 2463b2ad54e1SXu Kuohai /* since plt target points to either the new trampoline 2464b2ad54e1SXu Kuohai * or dummy_tramp, even if another CPU reads the old plt 2465b2ad54e1SXu Kuohai * target value before fetching the bl instruction to plt, 2466b2ad54e1SXu Kuohai * it will be brought back by dummy_tramp, so no barrier is 2467b2ad54e1SXu Kuohai * required here. 2468b2ad54e1SXu Kuohai */ 2469b2ad54e1SXu Kuohai } 2470b2ad54e1SXu Kuohai 2471b2ad54e1SXu Kuohai /* if the old target and the new target are both long jumps, no 2472b2ad54e1SXu Kuohai * patching is required 2473b2ad54e1SXu Kuohai */ 2474b2ad54e1SXu Kuohai if (old_insn == new_insn) 2475b2ad54e1SXu Kuohai return 0; 2476b2ad54e1SXu Kuohai 2477b2ad54e1SXu Kuohai mutex_lock(&text_mutex); 2478b2ad54e1SXu Kuohai if (aarch64_insn_read(ip, &replaced)) { 2479b2ad54e1SXu Kuohai ret = -EFAULT; 2480b2ad54e1SXu Kuohai goto out; 2481b2ad54e1SXu Kuohai } 2482b2ad54e1SXu Kuohai 2483b2ad54e1SXu Kuohai if (replaced != old_insn) { 2484b2ad54e1SXu Kuohai ret = -EFAULT; 2485b2ad54e1SXu Kuohai goto out; 2486b2ad54e1SXu Kuohai } 2487b2ad54e1SXu Kuohai 2488b2ad54e1SXu Kuohai /* We call aarch64_insn_patch_text_nosync() to replace instruction 2489b2ad54e1SXu Kuohai * atomically, so no other CPUs will fetch a half-new and half-old 2490b2ad54e1SXu Kuohai * instruction. But there is chance that another CPU executes the 2491b2ad54e1SXu Kuohai * old instruction after the patching operation finishes (e.g., 2492b2ad54e1SXu Kuohai * pipeline not flushed, or icache not synchronized yet). 2493b2ad54e1SXu Kuohai * 2494b2ad54e1SXu Kuohai * 1. when a new trampoline is attached, it is not a problem for 2495b2ad54e1SXu Kuohai * different CPUs to jump to different trampolines temporarily. 2496b2ad54e1SXu Kuohai * 2497b2ad54e1SXu Kuohai * 2. when an old trampoline is freed, we should wait for all other 2498b2ad54e1SXu Kuohai * CPUs to exit the trampoline and make sure the trampoline is no 2499b2ad54e1SXu Kuohai * longer reachable, since bpf_tramp_image_put() function already 2500b2ad54e1SXu Kuohai * uses percpu_ref and task-based rcu to do the sync, no need to call 2501b2ad54e1SXu Kuohai * the sync version here, see bpf_tramp_image_put() for details. 2502b2ad54e1SXu Kuohai */ 2503b2ad54e1SXu Kuohai ret = aarch64_insn_patch_text_nosync(ip, new_insn); 2504b2ad54e1SXu Kuohai out: 2505b2ad54e1SXu Kuohai mutex_unlock(&text_mutex); 2506b2ad54e1SXu Kuohai 2507b2ad54e1SXu Kuohai return ret; 2508b2ad54e1SXu Kuohai } 250918a45f12SHou Tao 251018a45f12SHou Tao bool bpf_jit_supports_ptr_xchg(void) 251118a45f12SHou Tao { 251218a45f12SHou Tao return true; 251318a45f12SHou Tao } 251422fc0e80SPuranjay Mohan 251522fc0e80SPuranjay Mohan bool bpf_jit_supports_exceptions(void) 251622fc0e80SPuranjay Mohan { 251722fc0e80SPuranjay Mohan /* We unwind through both kernel frames starting from within bpf_throw 251822fc0e80SPuranjay Mohan * call and BPF frames. Therefore we require FP unwinder to be enabled 251922fc0e80SPuranjay Mohan * to walk kernel frames and reach BPF frames in the stack trace. 252022fc0e80SPuranjay Mohan * ARM64 kernel is aways compiled with CONFIG_FRAME_POINTER=y 252122fc0e80SPuranjay Mohan */ 252222fc0e80SPuranjay Mohan return true; 252322fc0e80SPuranjay Mohan } 25241dad391dSPuranjay Mohan 2525*4dd31243SPuranjay Mohan bool bpf_jit_supports_arena(void) 2526*4dd31243SPuranjay Mohan { 2527*4dd31243SPuranjay Mohan return true; 2528*4dd31243SPuranjay Mohan } 2529*4dd31243SPuranjay Mohan 25301dad391dSPuranjay Mohan void bpf_jit_free(struct bpf_prog *prog) 25311dad391dSPuranjay Mohan { 25321dad391dSPuranjay Mohan if (prog->jited) { 25331dad391dSPuranjay Mohan struct arm64_jit_data *jit_data = prog->aux->jit_data; 25341dad391dSPuranjay Mohan struct bpf_binary_header *hdr; 25351dad391dSPuranjay Mohan 25361dad391dSPuranjay Mohan /* 25371dad391dSPuranjay Mohan * If we fail the final pass of JIT (from jit_subprogs), 25381dad391dSPuranjay Mohan * the program may not be finalized yet. Call finalize here 25391dad391dSPuranjay Mohan * before freeing it. 25401dad391dSPuranjay Mohan */ 25411dad391dSPuranjay Mohan if (jit_data) { 25421dad391dSPuranjay Mohan bpf_arch_text_copy(&jit_data->ro_header->size, &jit_data->header->size, 25431dad391dSPuranjay Mohan sizeof(jit_data->header->size)); 25441dad391dSPuranjay Mohan kfree(jit_data); 25451dad391dSPuranjay Mohan } 25461dad391dSPuranjay Mohan hdr = bpf_jit_binary_pack_hdr(prog); 25471dad391dSPuranjay Mohan bpf_jit_binary_pack_free(hdr, NULL); 25481dad391dSPuranjay Mohan WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog)); 25491dad391dSPuranjay Mohan } 25501dad391dSPuranjay Mohan 25511dad391dSPuranjay Mohan bpf_prog_unlock_free(prog); 25521dad391dSPuranjay Mohan } 2553