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) 32*339af577SPuranjay 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), 71*339af577SPuranjay Mohan /* callee saved register for kern_vm_start address */ 72*339af577SPuranjay 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; 85e54bcde3SZi Shen Lim }; 86e54bcde3SZi Shen Lim 87b2ad54e1SXu Kuohai struct bpf_plt { 88b2ad54e1SXu Kuohai u32 insn_ldr; /* load target */ 89b2ad54e1SXu Kuohai u32 insn_br; /* branch to target */ 90b2ad54e1SXu Kuohai u64 target; /* target value */ 91b2ad54e1SXu Kuohai }; 92b2ad54e1SXu Kuohai 93b2ad54e1SXu Kuohai #define PLT_TARGET_SIZE sizeof_field(struct bpf_plt, target) 94b2ad54e1SXu Kuohai #define PLT_TARGET_OFFSET offsetof(struct bpf_plt, target) 95b2ad54e1SXu Kuohai 96e54bcde3SZi Shen Lim static inline void emit(const u32 insn, struct jit_ctx *ctx) 97e54bcde3SZi Shen Lim { 98e54bcde3SZi Shen Lim if (ctx->image != NULL) 99e54bcde3SZi Shen Lim ctx->image[ctx->idx] = cpu_to_le32(insn); 100e54bcde3SZi Shen Lim 101e54bcde3SZi Shen Lim ctx->idx++; 102e54bcde3SZi Shen Lim } 103e54bcde3SZi Shen Lim 104e54bcde3SZi Shen Lim static inline void emit_a64_mov_i(const int is64, const int reg, 105e54bcde3SZi Shen Lim const s32 val, struct jit_ctx *ctx) 106e54bcde3SZi Shen Lim { 107e54bcde3SZi Shen Lim u16 hi = val >> 16; 108e54bcde3SZi Shen Lim u16 lo = val & 0xffff; 109e54bcde3SZi Shen Lim 110e54bcde3SZi Shen Lim if (hi & 0x8000) { 111e54bcde3SZi Shen Lim if (hi == 0xffff) { 112e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx); 113e54bcde3SZi Shen Lim } else { 114e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx); 1156d2eea6fSDaniel Borkmann if (lo != 0xffff) 116e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, lo, 0), ctx); 117e54bcde3SZi Shen Lim } 118e54bcde3SZi Shen Lim } else { 119e54bcde3SZi Shen Lim emit(A64_MOVZ(is64, reg, lo, 0), ctx); 120e54bcde3SZi Shen Lim if (hi) 121e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, hi, 16), ctx); 122e54bcde3SZi Shen Lim } 123e54bcde3SZi Shen Lim } 124e54bcde3SZi Shen Lim 1256d2eea6fSDaniel Borkmann static int i64_i16_blocks(const u64 val, bool inverse) 1266d2eea6fSDaniel Borkmann { 1276d2eea6fSDaniel Borkmann return (((val >> 0) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1286d2eea6fSDaniel Borkmann (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1296d2eea6fSDaniel Borkmann (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1306d2eea6fSDaniel Borkmann (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000)); 1316d2eea6fSDaniel Borkmann } 1326d2eea6fSDaniel Borkmann 1336d2eea6fSDaniel Borkmann static inline void emit_a64_mov_i64(const int reg, const u64 val, 1346d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1356d2eea6fSDaniel Borkmann { 1366d2eea6fSDaniel Borkmann u64 nrm_tmp = val, rev_tmp = ~val; 1376d2eea6fSDaniel Borkmann bool inverse; 1386d2eea6fSDaniel Borkmann int shift; 1396d2eea6fSDaniel Borkmann 1406d2eea6fSDaniel Borkmann if (!(nrm_tmp >> 32)) 1416d2eea6fSDaniel Borkmann return emit_a64_mov_i(0, reg, (u32)val, ctx); 1426d2eea6fSDaniel Borkmann 1436d2eea6fSDaniel Borkmann inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false); 1446d2eea6fSDaniel Borkmann shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) : 1456d2eea6fSDaniel Borkmann (fls64(nrm_tmp) - 1)), 16), 0); 1466d2eea6fSDaniel Borkmann if (inverse) 1476d2eea6fSDaniel Borkmann emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx); 1486d2eea6fSDaniel Borkmann else 1496d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1506d2eea6fSDaniel Borkmann shift -= 16; 1516d2eea6fSDaniel Borkmann while (shift >= 0) { 1526d2eea6fSDaniel Borkmann if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000)) 1536d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1546d2eea6fSDaniel Borkmann shift -= 16; 1556d2eea6fSDaniel Borkmann } 1566d2eea6fSDaniel Borkmann } 1576d2eea6fSDaniel Borkmann 158b2ad54e1SXu Kuohai static inline void emit_bti(u32 insn, struct jit_ctx *ctx) 159b2ad54e1SXu Kuohai { 160b2ad54e1SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) 161b2ad54e1SXu Kuohai emit(insn, ctx); 162b2ad54e1SXu Kuohai } 163b2ad54e1SXu Kuohai 1646d2eea6fSDaniel Borkmann /* 165cc2b8ed1SArd Biesheuvel * Kernel addresses in the vmalloc space use at most 48 bits, and the 166cc2b8ed1SArd Biesheuvel * remaining bits are guaranteed to be 0x1. So we can compose the address 167cc2b8ed1SArd Biesheuvel * with a fixed length movn/movk/movk sequence. 1686d2eea6fSDaniel Borkmann */ 1696d2eea6fSDaniel Borkmann static inline void emit_addr_mov_i64(const int reg, const u64 val, 1706d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1716d2eea6fSDaniel Borkmann { 1726d2eea6fSDaniel Borkmann u64 tmp = val; 1736d2eea6fSDaniel Borkmann int shift = 0; 1746d2eea6fSDaniel Borkmann 175cc2b8ed1SArd Biesheuvel emit(A64_MOVN(1, reg, ~tmp & 0xffff, shift), ctx); 176cc2b8ed1SArd Biesheuvel while (shift < 32) { 1776d2eea6fSDaniel Borkmann tmp >>= 16; 1786d2eea6fSDaniel Borkmann shift += 16; 1796d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx); 1806d2eea6fSDaniel Borkmann } 1816d2eea6fSDaniel Borkmann } 1826d2eea6fSDaniel Borkmann 183efc9909fSXu Kuohai static inline void emit_call(u64 target, struct jit_ctx *ctx) 184efc9909fSXu Kuohai { 185efc9909fSXu Kuohai u8 tmp = bpf2a64[TMP_REG_1]; 186efc9909fSXu Kuohai 187efc9909fSXu Kuohai emit_addr_mov_i64(tmp, target, ctx); 188efc9909fSXu Kuohai emit(A64_BLR(tmp), ctx); 189efc9909fSXu Kuohai } 190efc9909fSXu Kuohai 19132f6865cSIlias Apalodimas static inline int bpf2a64_offset(int bpf_insn, int off, 192e54bcde3SZi Shen Lim const struct jit_ctx *ctx) 193e54bcde3SZi Shen Lim { 19432f6865cSIlias Apalodimas /* BPF JMP offset is relative to the next instruction */ 19532f6865cSIlias Apalodimas bpf_insn++; 19632f6865cSIlias Apalodimas /* 19732f6865cSIlias Apalodimas * Whereas arm64 branch instructions encode the offset 19832f6865cSIlias Apalodimas * from the branch itself, so we must subtract 1 from the 19932f6865cSIlias Apalodimas * instruction offset. 20032f6865cSIlias Apalodimas */ 20132f6865cSIlias Apalodimas return ctx->offset[bpf_insn + off] - (ctx->offset[bpf_insn] - 1); 202e54bcde3SZi Shen Lim } 203e54bcde3SZi Shen Lim 204b569c1c6SDaniel Borkmann static void jit_fill_hole(void *area, unsigned int size) 205b569c1c6SDaniel Borkmann { 206425e1ed7SLuc Van Oostenryck __le32 *ptr; 207b569c1c6SDaniel Borkmann /* We are guaranteed to have aligned memory. */ 208b569c1c6SDaniel Borkmann for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 209b569c1c6SDaniel Borkmann *ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT); 210b569c1c6SDaniel Borkmann } 211b569c1c6SDaniel Borkmann 2121dad391dSPuranjay Mohan int bpf_arch_text_invalidate(void *dst, size_t len) 2131dad391dSPuranjay Mohan { 2141dad391dSPuranjay Mohan if (!aarch64_insn_set(dst, AARCH64_BREAK_FAULT, len)) 2151dad391dSPuranjay Mohan return -EINVAL; 2161dad391dSPuranjay Mohan 2171dad391dSPuranjay Mohan return 0; 2181dad391dSPuranjay Mohan } 2191dad391dSPuranjay Mohan 220e54bcde3SZi Shen Lim static inline int epilogue_offset(const struct jit_ctx *ctx) 221e54bcde3SZi Shen Lim { 22251c9fbb1SZi Shen Lim int to = ctx->epilogue_offset; 22351c9fbb1SZi Shen Lim int from = ctx->idx; 224e54bcde3SZi Shen Lim 225e54bcde3SZi Shen Lim return to - from; 226e54bcde3SZi Shen Lim } 227e54bcde3SZi Shen Lim 228fd868f14SLuke Nelson static bool is_addsub_imm(u32 imm) 229fd868f14SLuke Nelson { 230fd868f14SLuke Nelson /* Either imm12 or shifted imm12. */ 231fd868f14SLuke Nelson return !(imm & ~0xfff) || !(imm & ~0xfff000); 232fd868f14SLuke Nelson } 233fd868f14SLuke Nelson 2347db6c0f1SXu Kuohai /* 2357db6c0f1SXu Kuohai * There are 3 types of AArch64 LDR/STR (immediate) instruction: 2367db6c0f1SXu Kuohai * Post-index, Pre-index, Unsigned offset. 2377db6c0f1SXu Kuohai * 2387db6c0f1SXu Kuohai * For BPF ldr/str, the "unsigned offset" type is sufficient. 2397db6c0f1SXu Kuohai * 2407db6c0f1SXu Kuohai * "Unsigned offset" type LDR(immediate) format: 2417db6c0f1SXu Kuohai * 2427db6c0f1SXu Kuohai * 3 2 1 0 2437db6c0f1SXu 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 2447db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2457db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 1| imm12 | Rn | Rt | 2467db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2477db6c0f1SXu Kuohai * scale 2487db6c0f1SXu Kuohai * 2497db6c0f1SXu Kuohai * "Unsigned offset" type STR(immediate) format: 2507db6c0f1SXu Kuohai * 3 2 1 0 2517db6c0f1SXu 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 2527db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2537db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 0| imm12 | Rn | Rt | 2547db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2557db6c0f1SXu Kuohai * scale 2567db6c0f1SXu Kuohai * 2577db6c0f1SXu Kuohai * The offset is calculated from imm12 and scale in the following way: 2587db6c0f1SXu Kuohai * 2597db6c0f1SXu Kuohai * offset = (u64)imm12 << scale 2607db6c0f1SXu Kuohai */ 2615b3d19b9SXu Kuohai static bool is_lsi_offset(int offset, int scale) 2627db6c0f1SXu Kuohai { 2637db6c0f1SXu Kuohai if (offset < 0) 2647db6c0f1SXu Kuohai return false; 2657db6c0f1SXu Kuohai 2667db6c0f1SXu Kuohai if (offset > (0xFFF << scale)) 2677db6c0f1SXu Kuohai return false; 2687db6c0f1SXu Kuohai 2697db6c0f1SXu Kuohai if (offset & ((1 << scale) - 1)) 2707db6c0f1SXu Kuohai return false; 2717db6c0f1SXu Kuohai 2727db6c0f1SXu Kuohai return true; 2737db6c0f1SXu Kuohai } 2747db6c0f1SXu Kuohai 275b2ad54e1SXu Kuohai /* generated prologue: 276b2ad54e1SXu Kuohai * bti c // if CONFIG_ARM64_BTI_KERNEL 277b2ad54e1SXu Kuohai * mov x9, lr 278b2ad54e1SXu Kuohai * nop // POKE_OFFSET 279b2ad54e1SXu Kuohai * paciasp // if CONFIG_ARM64_PTR_AUTH_KERNEL 280b2ad54e1SXu Kuohai * stp x29, lr, [sp, #-16]! 281b2ad54e1SXu Kuohai * mov x29, sp 282b2ad54e1SXu Kuohai * stp x19, x20, [sp, #-16]! 283b2ad54e1SXu Kuohai * stp x21, x22, [sp, #-16]! 284b2ad54e1SXu Kuohai * stp x25, x26, [sp, #-16]! 285b2ad54e1SXu Kuohai * stp x27, x28, [sp, #-16]! 286b2ad54e1SXu Kuohai * mov x25, sp 287b2ad54e1SXu Kuohai * mov tcc, #0 288b2ad54e1SXu Kuohai * // PROLOGUE_OFFSET 289b2ad54e1SXu Kuohai */ 290b2ad54e1SXu Kuohai 291b2ad54e1SXu Kuohai #define BTI_INSNS (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) ? 1 : 0) 292b2ad54e1SXu Kuohai #define PAC_INSNS (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) ? 1 : 0) 293b2ad54e1SXu Kuohai 294b2ad54e1SXu Kuohai /* Offset of nop instruction in bpf prog entry to be poked */ 295b2ad54e1SXu Kuohai #define POKE_OFFSET (BTI_INSNS + 1) 296b2ad54e1SXu Kuohai 297a2284d91SDaniel Borkmann /* Tail call offset to jump into */ 298b2ad54e1SXu Kuohai #define PROLOGUE_OFFSET (BTI_INSNS + 2 + PAC_INSNS + 8) 299ddb55992SZi Shen Lim 30022fc0e80SPuranjay Mohan static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf, 301*339af577SPuranjay Mohan bool is_exception_cb, u64 arena_vm_start) 302e54bcde3SZi Shen Lim { 303f1c9eed7SDaniel Borkmann const struct bpf_prog *prog = ctx->prog; 3049af27da6SKumar Kartikeya Dwivedi const bool is_main_prog = !bpf_is_subprog(prog); 305e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 306e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 307e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 308e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 309e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 310ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 3115b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 312*339af577SPuranjay Mohan const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; 313ddb55992SZi Shen Lim const int idx0 = ctx->idx; 314ddb55992SZi Shen Lim int cur_offset; 315e54bcde3SZi Shen Lim 316ec0738dbSYang Shi /* 317ec0738dbSYang Shi * BPF prog stack layout 318ec0738dbSYang Shi * 319ec0738dbSYang Shi * high 320ec0738dbSYang Shi * original A64_SP => 0:+-----+ BPF prologue 321ec0738dbSYang Shi * |FP/LR| 322ec0738dbSYang Shi * current A64_FP => -16:+-----+ 323ec0738dbSYang Shi * | ... | callee saved registers 3244c1cd4fdSYang Shi * BPF fp register => -64:+-----+ <= (BPF_FP) 325ec0738dbSYang Shi * | | 326ec0738dbSYang Shi * | ... | BPF prog stack 327ec0738dbSYang Shi * | | 328f1c9eed7SDaniel Borkmann * +-----+ <= (BPF_FP - prog->aux->stack_depth) 32909ece3d0SDaniel Borkmann * |RSVD | padding 330f1c9eed7SDaniel Borkmann * current A64_SP => +-----+ <= (BPF_FP - ctx->stack_size) 331ec0738dbSYang Shi * | | 332ec0738dbSYang Shi * | ... | Function call stack 333ec0738dbSYang Shi * | | 334ec0738dbSYang Shi * +-----+ 335ec0738dbSYang Shi * low 336ec0738dbSYang Shi * 337ec0738dbSYang Shi */ 338ec0738dbSYang Shi 339a3f25d61SAlexander Duyck /* bpf function may be invoked by 3 instruction types: 340a3f25d61SAlexander Duyck * 1. bl, attached via freplace to bpf prog via short jump 341a3f25d61SAlexander Duyck * 2. br, attached via freplace to bpf prog via long jump 342a3f25d61SAlexander Duyck * 3. blr, working as a function pointer, used by emit_call. 343a3f25d61SAlexander Duyck * So BTI_JC should used here to support both br and blr. 344a3f25d61SAlexander Duyck */ 345a3f25d61SAlexander Duyck emit_bti(A64_BTI_JC, ctx); 346b2ad54e1SXu Kuohai 347b2ad54e1SXu Kuohai emit(A64_MOV(1, A64_R(9), A64_LR), ctx); 348b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 349b2ad54e1SXu Kuohai 35022fc0e80SPuranjay Mohan if (!is_exception_cb) { 351042152c2SXu Kuohai /* Sign lr */ 352042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 353042152c2SXu Kuohai emit(A64_PACIASP, ctx); 354ec0738dbSYang Shi /* Save FP and LR registers to stay align with ARM64 AAPCS */ 355ec0738dbSYang Shi emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 356ec0738dbSYang Shi emit(A64_MOV(1, A64_FP, A64_SP), ctx); 357ec0738dbSYang Shi 358ddb55992SZi Shen Lim /* Save callee-saved registers */ 359e54bcde3SZi Shen Lim emit(A64_PUSH(r6, r7, A64_SP), ctx); 360e54bcde3SZi Shen Lim emit(A64_PUSH(r8, r9, A64_SP), ctx); 361ddb55992SZi Shen Lim emit(A64_PUSH(fp, tcc, A64_SP), ctx); 3625b3d19b9SXu Kuohai emit(A64_PUSH(fpb, A64_R(28), A64_SP), ctx); 36322fc0e80SPuranjay Mohan } else { 36422fc0e80SPuranjay Mohan /* 36522fc0e80SPuranjay Mohan * Exception callback receives FP of Main Program as third 36622fc0e80SPuranjay Mohan * parameter 36722fc0e80SPuranjay Mohan */ 36822fc0e80SPuranjay Mohan emit(A64_MOV(1, A64_FP, A64_R(2)), ctx); 36922fc0e80SPuranjay Mohan /* 37022fc0e80SPuranjay Mohan * Main Program already pushed the frame record and the 37122fc0e80SPuranjay Mohan * callee-saved registers. The exception callback will not push 37222fc0e80SPuranjay Mohan * anything and re-use the main program's stack. 37322fc0e80SPuranjay Mohan * 37422fc0e80SPuranjay Mohan * 10 registers are on the stack 37522fc0e80SPuranjay Mohan */ 37622fc0e80SPuranjay Mohan emit(A64_SUB_I(1, A64_SP, A64_FP, 80), ctx); 37722fc0e80SPuranjay Mohan } 378e54bcde3SZi Shen Lim 379ddb55992SZi Shen Lim /* Set up BPF prog stack base register */ 380e54bcde3SZi Shen Lim emit(A64_MOV(1, fp, A64_SP), ctx); 381e54bcde3SZi Shen Lim 382d4609a5dSJakub Sitnicki if (!ebpf_from_cbpf && is_main_prog) { 383ddb55992SZi Shen Lim /* Initialize tail_call_cnt */ 384ddb55992SZi Shen Lim emit(A64_MOVZ(1, tcc, 0, 0), ctx); 385ddb55992SZi Shen Lim 386ddb55992SZi Shen Lim cur_offset = ctx->idx - idx0; 387ddb55992SZi Shen Lim if (cur_offset != PROLOGUE_OFFSET) { 388ddb55992SZi Shen Lim pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 389ddb55992SZi Shen Lim cur_offset, PROLOGUE_OFFSET); 390ddb55992SZi Shen Lim return -1; 391ddb55992SZi Shen Lim } 392fa76cfe6SMark Brown 393fa76cfe6SMark Brown /* BTI landing pad for the tail call, done with a BR */ 394b2ad54e1SXu Kuohai emit_bti(A64_BTI_J, ctx); 39556ea6a8bSDaniel Borkmann } 396a2284d91SDaniel Borkmann 39722fc0e80SPuranjay Mohan /* 39822fc0e80SPuranjay Mohan * Program acting as exception boundary should save all ARM64 39922fc0e80SPuranjay Mohan * Callee-saved registers as the exception callback needs to recover 40022fc0e80SPuranjay Mohan * all ARM64 Callee-saved registers in its epilogue. 40122fc0e80SPuranjay Mohan */ 40222fc0e80SPuranjay Mohan if (prog->aux->exception_boundary) { 40322fc0e80SPuranjay Mohan /* 40422fc0e80SPuranjay Mohan * As we are pushing two more registers, BPF_FP should be moved 40522fc0e80SPuranjay Mohan * 16 bytes 40622fc0e80SPuranjay Mohan */ 40722fc0e80SPuranjay Mohan emit(A64_SUB_I(1, fp, fp, 16), ctx); 40822fc0e80SPuranjay Mohan emit(A64_PUSH(A64_R(23), A64_R(24), A64_SP), ctx); 40922fc0e80SPuranjay Mohan } 41022fc0e80SPuranjay Mohan 4115b3d19b9SXu Kuohai emit(A64_SUB_I(1, fpb, fp, ctx->fpb_offset), ctx); 4125b3d19b9SXu Kuohai 4133f287098STiezhu Yang /* Stack must be multiples of 16B */ 4143f287098STiezhu Yang ctx->stack_size = round_up(prog->aux->stack_depth, 16); 415a2284d91SDaniel Borkmann 416a2284d91SDaniel Borkmann /* Set up function call stack */ 417a2284d91SDaniel Borkmann emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 418*339af577SPuranjay Mohan 419*339af577SPuranjay Mohan if (arena_vm_start) 420*339af577SPuranjay Mohan emit_a64_mov_i64(arena_vm_base, arena_vm_start, ctx); 421*339af577SPuranjay Mohan 422ddb55992SZi Shen Lim return 0; 423ddb55992SZi Shen Lim } 424ddb55992SZi Shen Lim 425ddb55992SZi Shen Lim static int out_offset = -1; /* initialized on the first pass of build_body() */ 426ddb55992SZi Shen Lim static int emit_bpf_tail_call(struct jit_ctx *ctx) 427ddb55992SZi Shen Lim { 428ddb55992SZi Shen Lim /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 429ddb55992SZi Shen Lim const u8 r2 = bpf2a64[BPF_REG_2]; 430ddb55992SZi Shen Lim const u8 r3 = bpf2a64[BPF_REG_3]; 431ddb55992SZi Shen Lim 432ddb55992SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 433ddb55992SZi Shen Lim const u8 prg = bpf2a64[TMP_REG_2]; 434ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 435ddb55992SZi Shen Lim const int idx0 = ctx->idx; 436ddb55992SZi Shen Lim #define cur_offset (ctx->idx - idx0) 437ddb55992SZi Shen Lim #define jmp_offset (out_offset - (cur_offset)) 438ddb55992SZi Shen Lim size_t off; 439ddb55992SZi Shen Lim 440ddb55992SZi Shen Lim /* if (index >= array->map.max_entries) 441ddb55992SZi Shen Lim * goto out; 442ddb55992SZi Shen Lim */ 443ddb55992SZi Shen Lim off = offsetof(struct bpf_array, map.max_entries); 444ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 445ddb55992SZi Shen Lim emit(A64_LDR32(tmp, r2, tmp), ctx); 44616338a9bSDaniel Borkmann emit(A64_MOV(0, r3, r3), ctx); 447ddb55992SZi Shen Lim emit(A64_CMP(0, r3, tmp), ctx); 44816338a9bSDaniel Borkmann emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 449ddb55992SZi Shen Lim 450ebf7f6f0STiezhu Yang /* 451ebf7f6f0STiezhu Yang * if (tail_call_cnt >= MAX_TAIL_CALL_CNT) 452ddb55992SZi Shen Lim * goto out; 453ddb55992SZi Shen Lim * tail_call_cnt++; 454ddb55992SZi Shen Lim */ 455ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 456ddb55992SZi Shen Lim emit(A64_CMP(1, tcc, tmp), ctx); 457ebf7f6f0STiezhu Yang emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 458ddb55992SZi Shen Lim emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 459ddb55992SZi Shen Lim 460ddb55992SZi Shen Lim /* prog = array->ptrs[index]; 461ddb55992SZi Shen Lim * if (prog == NULL) 462ddb55992SZi Shen Lim * goto out; 463ddb55992SZi Shen Lim */ 464ddb55992SZi Shen Lim off = offsetof(struct bpf_array, ptrs); 465ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 466d8b54110SDaniel Borkmann emit(A64_ADD(1, tmp, r2, tmp), ctx); 467d8b54110SDaniel Borkmann emit(A64_LSL(1, prg, r3, 3), ctx); 468d8b54110SDaniel Borkmann emit(A64_LDR64(prg, tmp, prg), ctx); 469ddb55992SZi Shen Lim emit(A64_CBZ(1, prg, jmp_offset), ctx); 470ddb55992SZi Shen Lim 471a2284d91SDaniel Borkmann /* goto *(prog->bpf_func + prologue_offset); */ 472ddb55992SZi Shen Lim off = offsetof(struct bpf_prog, bpf_func); 473ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 474ddb55992SZi Shen Lim emit(A64_LDR64(tmp, prg, tmp), ctx); 475ddb55992SZi Shen Lim emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 476a2284d91SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 477ddb55992SZi Shen Lim emit(A64_BR(tmp), ctx); 478ddb55992SZi Shen Lim 479ddb55992SZi Shen Lim /* out: */ 480ddb55992SZi Shen Lim if (out_offset == -1) 481ddb55992SZi Shen Lim out_offset = cur_offset; 482ddb55992SZi Shen Lim if (cur_offset != out_offset) { 483ddb55992SZi Shen Lim pr_err_once("tail_call out_offset = %d, expected %d!\n", 484ddb55992SZi Shen Lim cur_offset, out_offset); 485ddb55992SZi Shen Lim return -1; 486ddb55992SZi Shen Lim } 487ddb55992SZi Shen Lim return 0; 488ddb55992SZi Shen Lim #undef cur_offset 489ddb55992SZi Shen Lim #undef jmp_offset 490e54bcde3SZi Shen Lim } 491e54bcde3SZi Shen Lim 4921902472bSHou Tao #ifdef CONFIG_ARM64_LSE_ATOMICS 4931902472bSHou Tao static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 4941902472bSHou Tao { 4951902472bSHou Tao const u8 code = insn->code; 4961902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 4971902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 4981902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 4991902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 5001902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 5011902472bSHou Tao const s16 off = insn->off; 5021902472bSHou Tao u8 reg; 5031902472bSHou Tao 5041902472bSHou Tao if (!off) { 5051902472bSHou Tao reg = dst; 5061902472bSHou Tao } else { 5071902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 5081902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 5091902472bSHou Tao reg = tmp; 5101902472bSHou Tao } 5111902472bSHou Tao 5121902472bSHou Tao switch (insn->imm) { 5131902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 5141902472bSHou Tao case BPF_ADD: 5151902472bSHou Tao emit(A64_STADD(isdw, reg, src), ctx); 5161902472bSHou Tao break; 5171902472bSHou Tao case BPF_AND: 5181902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 5191902472bSHou Tao emit(A64_STCLR(isdw, reg, tmp2), ctx); 5201902472bSHou Tao break; 5211902472bSHou Tao case BPF_OR: 5221902472bSHou Tao emit(A64_STSET(isdw, reg, src), ctx); 5231902472bSHou Tao break; 5241902472bSHou Tao case BPF_XOR: 5251902472bSHou Tao emit(A64_STEOR(isdw, reg, src), ctx); 5261902472bSHou Tao break; 5271902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 5281902472bSHou Tao case BPF_ADD | BPF_FETCH: 5291902472bSHou Tao emit(A64_LDADDAL(isdw, src, reg, src), ctx); 5301902472bSHou Tao break; 5311902472bSHou Tao case BPF_AND | BPF_FETCH: 5321902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 5331902472bSHou Tao emit(A64_LDCLRAL(isdw, src, reg, tmp2), ctx); 5341902472bSHou Tao break; 5351902472bSHou Tao case BPF_OR | BPF_FETCH: 5361902472bSHou Tao emit(A64_LDSETAL(isdw, src, reg, src), ctx); 5371902472bSHou Tao break; 5381902472bSHou Tao case BPF_XOR | BPF_FETCH: 5391902472bSHou Tao emit(A64_LDEORAL(isdw, src, reg, src), ctx); 5401902472bSHou Tao break; 5411902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 5421902472bSHou Tao case BPF_XCHG: 5431902472bSHou Tao emit(A64_SWPAL(isdw, src, reg, src), ctx); 5441902472bSHou Tao break; 5451902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 5461902472bSHou Tao case BPF_CMPXCHG: 5471902472bSHou Tao emit(A64_CASAL(isdw, src, reg, bpf2a64[BPF_REG_0]), ctx); 5481902472bSHou Tao break; 5491902472bSHou Tao default: 5501902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", insn->imm); 5511902472bSHou Tao return -EINVAL; 5521902472bSHou Tao } 5531902472bSHou Tao 5541902472bSHou Tao return 0; 5551902472bSHou Tao } 5561902472bSHou Tao #else 5571902472bSHou Tao static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5581902472bSHou Tao { 5591902472bSHou Tao return -EINVAL; 5601902472bSHou Tao } 5611902472bSHou Tao #endif 5621902472bSHou Tao 5631902472bSHou Tao static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5641902472bSHou Tao { 5651902472bSHou Tao const u8 code = insn->code; 5661902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 5671902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 5681902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 5691902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 5701902472bSHou Tao const u8 tmp3 = bpf2a64[TMP_REG_3]; 5711902472bSHou Tao const int i = insn - ctx->prog->insnsi; 5721902472bSHou Tao const s32 imm = insn->imm; 5731902472bSHou Tao const s16 off = insn->off; 5741902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 5751902472bSHou Tao u8 reg; 5761902472bSHou Tao s32 jmp_offset; 5771902472bSHou Tao 5781902472bSHou Tao if (!off) { 5791902472bSHou Tao reg = dst; 5801902472bSHou Tao } else { 5811902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 5821902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 5831902472bSHou Tao reg = tmp; 5841902472bSHou Tao } 5851902472bSHou Tao 5861902472bSHou Tao if (imm == BPF_ADD || imm == BPF_AND || 5871902472bSHou Tao imm == BPF_OR || imm == BPF_XOR) { 5881902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 5891902472bSHou Tao emit(A64_LDXR(isdw, tmp2, reg), ctx); 5901902472bSHou Tao if (imm == BPF_ADD) 5911902472bSHou Tao emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 5921902472bSHou Tao else if (imm == BPF_AND) 5931902472bSHou Tao emit(A64_AND(isdw, tmp2, tmp2, src), ctx); 5941902472bSHou Tao else if (imm == BPF_OR) 5951902472bSHou Tao emit(A64_ORR(isdw, tmp2, tmp2, src), ctx); 5961902472bSHou Tao else 5971902472bSHou Tao emit(A64_EOR(isdw, tmp2, tmp2, src), ctx); 5981902472bSHou Tao emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); 5991902472bSHou Tao jmp_offset = -3; 6001902472bSHou Tao check_imm19(jmp_offset); 6011902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6021902472bSHou Tao } else if (imm == (BPF_ADD | BPF_FETCH) || 6031902472bSHou Tao imm == (BPF_AND | BPF_FETCH) || 6041902472bSHou Tao imm == (BPF_OR | BPF_FETCH) || 6051902472bSHou Tao imm == (BPF_XOR | BPF_FETCH)) { 6061902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 6071902472bSHou Tao const u8 ax = bpf2a64[BPF_REG_AX]; 6081902472bSHou Tao 6091902472bSHou Tao emit(A64_MOV(isdw, ax, src), ctx); 6101902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 6111902472bSHou Tao if (imm == (BPF_ADD | BPF_FETCH)) 6121902472bSHou Tao emit(A64_ADD(isdw, tmp2, src, ax), ctx); 6131902472bSHou Tao else if (imm == (BPF_AND | BPF_FETCH)) 6141902472bSHou Tao emit(A64_AND(isdw, tmp2, src, ax), ctx); 6151902472bSHou Tao else if (imm == (BPF_OR | BPF_FETCH)) 6161902472bSHou Tao emit(A64_ORR(isdw, tmp2, src, ax), ctx); 6171902472bSHou Tao else 6181902472bSHou Tao emit(A64_EOR(isdw, tmp2, src, ax), ctx); 6191902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 6201902472bSHou Tao jmp_offset = -3; 6211902472bSHou Tao check_imm19(jmp_offset); 6221902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6231902472bSHou Tao emit(A64_DMB_ISH, ctx); 6241902472bSHou Tao } else if (imm == BPF_XCHG) { 6251902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 6261902472bSHou Tao emit(A64_MOV(isdw, tmp2, src), ctx); 6271902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 6281902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 6291902472bSHou Tao jmp_offset = -2; 6301902472bSHou Tao check_imm19(jmp_offset); 6311902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6321902472bSHou Tao emit(A64_DMB_ISH, ctx); 6331902472bSHou Tao } else if (imm == BPF_CMPXCHG) { 6341902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 6351902472bSHou Tao const u8 r0 = bpf2a64[BPF_REG_0]; 6361902472bSHou Tao 6371902472bSHou Tao emit(A64_MOV(isdw, tmp2, r0), ctx); 6381902472bSHou Tao emit(A64_LDXR(isdw, r0, reg), ctx); 6391902472bSHou Tao emit(A64_EOR(isdw, tmp3, r0, tmp2), ctx); 6401902472bSHou Tao jmp_offset = 4; 6411902472bSHou Tao check_imm19(jmp_offset); 6421902472bSHou Tao emit(A64_CBNZ(isdw, tmp3, jmp_offset), ctx); 6431902472bSHou Tao emit(A64_STLXR(isdw, src, reg, tmp3), ctx); 6441902472bSHou Tao jmp_offset = -4; 6451902472bSHou Tao check_imm19(jmp_offset); 6461902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6471902472bSHou Tao emit(A64_DMB_ISH, ctx); 6481902472bSHou Tao } else { 6491902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", imm); 6501902472bSHou Tao return -EINVAL; 6511902472bSHou Tao } 6521902472bSHou Tao 6531902472bSHou Tao return 0; 6541902472bSHou Tao } 6551902472bSHou Tao 656b2ad54e1SXu Kuohai void dummy_tramp(void); 657b2ad54e1SXu Kuohai 658b2ad54e1SXu Kuohai asm ( 659b2ad54e1SXu Kuohai " .pushsection .text, \"ax\", @progbits\n" 66033f32e50SNathan Chancellor " .global dummy_tramp\n" 661b2ad54e1SXu Kuohai " .type dummy_tramp, %function\n" 662b2ad54e1SXu Kuohai "dummy_tramp:" 663b2ad54e1SXu Kuohai #if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) 664b2ad54e1SXu Kuohai " bti j\n" /* dummy_tramp is called via "br x10" */ 665b2ad54e1SXu Kuohai #endif 666339ed900SXu Kuohai " mov x10, x30\n" 667339ed900SXu Kuohai " mov x30, x9\n" 668b2ad54e1SXu Kuohai " ret x10\n" 669b2ad54e1SXu Kuohai " .size dummy_tramp, .-dummy_tramp\n" 670b2ad54e1SXu Kuohai " .popsection\n" 671b2ad54e1SXu Kuohai ); 672b2ad54e1SXu Kuohai 673b2ad54e1SXu Kuohai /* build a plt initialized like this: 674b2ad54e1SXu Kuohai * 675b2ad54e1SXu Kuohai * plt: 676b2ad54e1SXu Kuohai * ldr tmp, target 677b2ad54e1SXu Kuohai * br tmp 678b2ad54e1SXu Kuohai * target: 679b2ad54e1SXu Kuohai * .quad dummy_tramp 680b2ad54e1SXu Kuohai * 681b2ad54e1SXu Kuohai * when a long jump trampoline is attached, target is filled with the 682b2ad54e1SXu Kuohai * trampoline address, and when the trampoline is removed, target is 683b2ad54e1SXu Kuohai * restored to dummy_tramp address. 684b2ad54e1SXu Kuohai */ 685b2ad54e1SXu Kuohai static void build_plt(struct jit_ctx *ctx) 686b2ad54e1SXu Kuohai { 687b2ad54e1SXu Kuohai const u8 tmp = bpf2a64[TMP_REG_1]; 688b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 689b2ad54e1SXu Kuohai 690b2ad54e1SXu Kuohai /* make sure target is 64-bit aligned */ 691b2ad54e1SXu Kuohai if ((ctx->idx + PLT_TARGET_OFFSET / AARCH64_INSN_SIZE) % 2) 692b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 693b2ad54e1SXu Kuohai 694b2ad54e1SXu Kuohai plt = (struct bpf_plt *)(ctx->image + ctx->idx); 695b2ad54e1SXu Kuohai /* plt is called via bl, no BTI needed here */ 696b2ad54e1SXu Kuohai emit(A64_LDR64LIT(tmp, 2 * AARCH64_INSN_SIZE), ctx); 697b2ad54e1SXu Kuohai emit(A64_BR(tmp), ctx); 698b2ad54e1SXu Kuohai 699b2ad54e1SXu Kuohai if (ctx->image) 700b2ad54e1SXu Kuohai plt->target = (u64)&dummy_tramp; 701b2ad54e1SXu Kuohai } 702b2ad54e1SXu Kuohai 70322fc0e80SPuranjay Mohan static void build_epilogue(struct jit_ctx *ctx, bool is_exception_cb) 704e54bcde3SZi Shen Lim { 705e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 706e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 707e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 708e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 709e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 710e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 7115b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 712e54bcde3SZi Shen Lim 713e54bcde3SZi Shen Lim /* We're done with BPF stack */ 714f1c9eed7SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 715e54bcde3SZi Shen Lim 71622fc0e80SPuranjay Mohan /* 71722fc0e80SPuranjay Mohan * Program acting as exception boundary pushes R23 and R24 in addition 71822fc0e80SPuranjay Mohan * to BPF callee-saved registers. Exception callback uses the boundary 71922fc0e80SPuranjay Mohan * program's stack frame, so recover these extra registers in the above 72022fc0e80SPuranjay Mohan * two cases. 72122fc0e80SPuranjay Mohan */ 72222fc0e80SPuranjay Mohan if (ctx->prog->aux->exception_boundary || is_exception_cb) 72322fc0e80SPuranjay Mohan emit(A64_POP(A64_R(23), A64_R(24), A64_SP), ctx); 72422fc0e80SPuranjay Mohan 7255b3d19b9SXu Kuohai /* Restore x27 and x28 */ 7265b3d19b9SXu Kuohai emit(A64_POP(fpb, A64_R(28), A64_SP), ctx); 727ec0738dbSYang Shi /* Restore fs (x25) and x26 */ 728ec0738dbSYang Shi emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 729ec0738dbSYang Shi 730e54bcde3SZi Shen Lim /* Restore callee-saved register */ 731e54bcde3SZi Shen Lim emit(A64_POP(r8, r9, A64_SP), ctx); 732e54bcde3SZi Shen Lim emit(A64_POP(r6, r7, A64_SP), ctx); 733e54bcde3SZi Shen Lim 734ec0738dbSYang Shi /* Restore FP/LR registers */ 735ec0738dbSYang Shi emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 736e54bcde3SZi Shen Lim 737e54bcde3SZi Shen Lim /* Set return value */ 738e54bcde3SZi Shen Lim emit(A64_MOV(1, A64_R(0), r0), ctx); 739e54bcde3SZi Shen Lim 740042152c2SXu Kuohai /* Authenticate lr */ 741042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 742042152c2SXu Kuohai emit(A64_AUTIASP, ctx); 743042152c2SXu Kuohai 744e54bcde3SZi Shen Lim emit(A64_RET(A64_LR), ctx); 745e54bcde3SZi Shen Lim } 746e54bcde3SZi Shen Lim 74780083428SJean-Philippe Brucker #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) 74880083428SJean-Philippe Brucker #define BPF_FIXUP_REG_MASK GENMASK(31, 27) 749*339af577SPuranjay Mohan #define DONT_CLEAR 5 /* Unused ARM64 register from BPF's POV */ 75080083428SJean-Philippe Brucker 751d6e2cc56SMark Rutland bool ex_handler_bpf(const struct exception_table_entry *ex, 75280083428SJean-Philippe Brucker struct pt_regs *regs) 75380083428SJean-Philippe Brucker { 75480083428SJean-Philippe Brucker off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup); 75580083428SJean-Philippe Brucker int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup); 75680083428SJean-Philippe Brucker 757*339af577SPuranjay Mohan if (dst_reg != DONT_CLEAR) 75880083428SJean-Philippe Brucker regs->regs[dst_reg] = 0; 75980083428SJean-Philippe Brucker regs->pc = (unsigned long)&ex->fixup - offset; 760e8c328d7SMark Rutland return true; 76180083428SJean-Philippe Brucker } 76280083428SJean-Philippe Brucker 76380083428SJean-Philippe Brucker /* For accesses to BTF pointers, add an entry to the exception table */ 76480083428SJean-Philippe Brucker static int add_exception_handler(const struct bpf_insn *insn, 76580083428SJean-Philippe Brucker struct jit_ctx *ctx, 76680083428SJean-Philippe Brucker int dst_reg) 76780083428SJean-Philippe Brucker { 7681dad391dSPuranjay Mohan off_t ins_offset; 7691dad391dSPuranjay Mohan off_t fixup_offset; 77080083428SJean-Philippe Brucker unsigned long pc; 77180083428SJean-Philippe Brucker struct exception_table_entry *ex; 77280083428SJean-Philippe Brucker 77380083428SJean-Philippe Brucker if (!ctx->image) 77480083428SJean-Philippe Brucker /* First pass */ 77580083428SJean-Philippe Brucker return 0; 77680083428SJean-Philippe Brucker 777cc88f540SXu Kuohai if (BPF_MODE(insn->code) != BPF_PROBE_MEM && 778*339af577SPuranjay Mohan BPF_MODE(insn->code) != BPF_PROBE_MEMSX && 779*339af577SPuranjay Mohan BPF_MODE(insn->code) != BPF_PROBE_MEM32) 78080083428SJean-Philippe Brucker return 0; 78180083428SJean-Philippe Brucker 78280083428SJean-Philippe Brucker if (!ctx->prog->aux->extable || 78380083428SJean-Philippe Brucker WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries)) 78480083428SJean-Philippe Brucker return -EINVAL; 78580083428SJean-Philippe Brucker 78680083428SJean-Philippe Brucker ex = &ctx->prog->aux->extable[ctx->exentry_idx]; 7871dad391dSPuranjay Mohan pc = (unsigned long)&ctx->ro_image[ctx->idx - 1]; 78880083428SJean-Philippe Brucker 7891dad391dSPuranjay Mohan /* 7901dad391dSPuranjay Mohan * This is the relative offset of the instruction that may fault from 7911dad391dSPuranjay Mohan * the exception table itself. This will be written to the exception 7921dad391dSPuranjay Mohan * table and if this instruction faults, the destination register will 7931dad391dSPuranjay Mohan * be set to '0' and the execution will jump to the next instruction. 7941dad391dSPuranjay Mohan */ 7951dad391dSPuranjay Mohan ins_offset = pc - (long)&ex->insn; 7961dad391dSPuranjay Mohan if (WARN_ON_ONCE(ins_offset >= 0 || ins_offset < INT_MIN)) 79780083428SJean-Philippe Brucker return -ERANGE; 79880083428SJean-Philippe Brucker 79980083428SJean-Philippe Brucker /* 80080083428SJean-Philippe Brucker * Since the extable follows the program, the fixup offset is always 80180083428SJean-Philippe Brucker * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value 80280083428SJean-Philippe Brucker * to keep things simple, and put the destination register in the upper 80380083428SJean-Philippe Brucker * bits. We don't need to worry about buildtime or runtime sort 80480083428SJean-Philippe Brucker * modifying the upper bits because the table is already sorted, and 80580083428SJean-Philippe Brucker * isn't part of the main exception table. 8061dad391dSPuranjay Mohan * 8071dad391dSPuranjay Mohan * The fixup_offset is set to the next instruction from the instruction 8081dad391dSPuranjay Mohan * that may fault. The execution will jump to this after handling the 8091dad391dSPuranjay Mohan * fault. 81080083428SJean-Philippe Brucker */ 8111dad391dSPuranjay Mohan fixup_offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE); 8121dad391dSPuranjay Mohan if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, fixup_offset)) 81380083428SJean-Philippe Brucker return -ERANGE; 81480083428SJean-Philippe Brucker 8151dad391dSPuranjay Mohan /* 8161dad391dSPuranjay Mohan * The offsets above have been calculated using the RO buffer but we 8171dad391dSPuranjay Mohan * need to use the R/W buffer for writes. 8181dad391dSPuranjay Mohan * switch ex to rw buffer for writing. 8191dad391dSPuranjay Mohan */ 8201dad391dSPuranjay Mohan ex = (void *)ctx->image + ((void *)ex - (void *)ctx->ro_image); 8211dad391dSPuranjay Mohan 8221dad391dSPuranjay Mohan ex->insn = ins_offset; 8231dad391dSPuranjay Mohan 824*339af577SPuranjay Mohan if (BPF_CLASS(insn->code) != BPF_LDX) 825*339af577SPuranjay Mohan dst_reg = DONT_CLEAR; 826*339af577SPuranjay Mohan 8271dad391dSPuranjay Mohan ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, fixup_offset) | 82880083428SJean-Philippe Brucker FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); 82980083428SJean-Philippe Brucker 830d6e2cc56SMark Rutland ex->type = EX_TYPE_BPF; 831d6e2cc56SMark Rutland 83280083428SJean-Philippe Brucker ctx->exentry_idx++; 83380083428SJean-Philippe Brucker return 0; 83480083428SJean-Philippe Brucker } 83580083428SJean-Philippe Brucker 83630d3d94cSZi Shen Lim /* JITs an eBPF instruction. 83730d3d94cSZi Shen Lim * Returns: 83830d3d94cSZi Shen Lim * 0 - successfully JITed an 8-byte eBPF instruction. 83930d3d94cSZi Shen Lim * >0 - successfully JITed a 16-byte eBPF instruction. 84030d3d94cSZi Shen Lim * <0 - failed to JIT. 84130d3d94cSZi Shen Lim */ 8428c11ea5cSDaniel Borkmann static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, 8438c11ea5cSDaniel Borkmann bool extra_pass) 844e54bcde3SZi Shen Lim { 845e54bcde3SZi Shen Lim const u8 code = insn->code; 846*339af577SPuranjay Mohan u8 dst = bpf2a64[insn->dst_reg]; 847*339af577SPuranjay Mohan u8 src = bpf2a64[insn->src_reg]; 848e54bcde3SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 849e54bcde3SZi Shen Lim const u8 tmp2 = bpf2a64[TMP_REG_2]; 8505b3d19b9SXu Kuohai const u8 fp = bpf2a64[BPF_REG_FP]; 8515b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 852*339af577SPuranjay Mohan const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; 853e54bcde3SZi Shen Lim const s16 off = insn->off; 854e54bcde3SZi Shen Lim const s32 imm = insn->imm; 855e54bcde3SZi Shen Lim const int i = insn - ctx->prog->insnsi; 856654b65a0SJiong Wang const bool is64 = BPF_CLASS(code) == BPF_ALU64 || 857654b65a0SJiong Wang BPF_CLASS(code) == BPF_JMP; 8581902472bSHou Tao u8 jmp_cond; 859e54bcde3SZi Shen Lim s32 jmp_offset; 860fd49591cSLuke Nelson u32 a64_insn; 8615b3d19b9SXu Kuohai u8 src_adj; 8625b3d19b9SXu Kuohai u8 dst_adj; 8635b3d19b9SXu Kuohai int off_adj; 86480083428SJean-Philippe Brucker int ret; 865cc88f540SXu Kuohai bool sign_extend; 866e54bcde3SZi Shen Lim 867e54bcde3SZi Shen Lim switch (code) { 868e54bcde3SZi Shen Lim /* dst = src */ 869e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_X: 870e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_X: 871bb0a1d6bSXu Kuohai switch (insn->off) { 872bb0a1d6bSXu Kuohai case 0: 873e54bcde3SZi Shen Lim emit(A64_MOV(is64, dst, src), ctx); 874e54bcde3SZi Shen Lim break; 875bb0a1d6bSXu Kuohai case 8: 876bb0a1d6bSXu Kuohai emit(A64_SXTB(is64, dst, src), ctx); 877bb0a1d6bSXu Kuohai break; 878bb0a1d6bSXu Kuohai case 16: 879bb0a1d6bSXu Kuohai emit(A64_SXTH(is64, dst, src), ctx); 880bb0a1d6bSXu Kuohai break; 881bb0a1d6bSXu Kuohai case 32: 882bb0a1d6bSXu Kuohai emit(A64_SXTW(is64, dst, src), ctx); 883bb0a1d6bSXu Kuohai break; 884bb0a1d6bSXu Kuohai } 885bb0a1d6bSXu Kuohai break; 886e54bcde3SZi Shen Lim /* dst = dst OP src */ 887e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_X: 888e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_X: 889e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, src), ctx); 890e54bcde3SZi Shen Lim break; 891e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_X: 892e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_X: 893e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, src), ctx); 894e54bcde3SZi Shen Lim break; 895e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_X: 896e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_X: 897e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, src), ctx); 898e54bcde3SZi Shen Lim break; 899e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_X: 900e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_X: 901e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, src), ctx); 902e54bcde3SZi Shen Lim break; 903e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_X: 904e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_X: 905e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, src), ctx); 906e54bcde3SZi Shen Lim break; 907e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_X: 908e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_X: 909e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, src), ctx); 910e54bcde3SZi Shen Lim break; 911e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_X: 912e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_X: 91368b18191SXu Kuohai if (!off) 914e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, src), ctx); 91568b18191SXu Kuohai else 91668b18191SXu Kuohai emit(A64_SDIV(is64, dst, dst, src), ctx); 917e54bcde3SZi Shen Lim break; 918119220d8STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 919119220d8STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 92068b18191SXu Kuohai if (!off) 921e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, src), ctx); 92268b18191SXu Kuohai else 92368b18191SXu Kuohai emit(A64_SDIV(is64, tmp, dst, src), ctx); 924504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, src), ctx); 925e54bcde3SZi Shen Lim break; 926d65a634aSZi Shen Lim case BPF_ALU | BPF_LSH | BPF_X: 927d65a634aSZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_X: 928d65a634aSZi Shen Lim emit(A64_LSLV(is64, dst, dst, src), ctx); 929d65a634aSZi Shen Lim break; 930d65a634aSZi Shen Lim case BPF_ALU | BPF_RSH | BPF_X: 931d65a634aSZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_X: 932d65a634aSZi Shen Lim emit(A64_LSRV(is64, dst, dst, src), ctx); 933d65a634aSZi Shen Lim break; 934d65a634aSZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_X: 935d65a634aSZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_X: 936d65a634aSZi Shen Lim emit(A64_ASRV(is64, dst, dst, src), ctx); 937d65a634aSZi Shen Lim break; 938e54bcde3SZi Shen Lim /* dst = -dst */ 939e54bcde3SZi Shen Lim case BPF_ALU | BPF_NEG: 940e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_NEG: 941e54bcde3SZi Shen Lim emit(A64_NEG(is64, dst, dst), ctx); 942e54bcde3SZi Shen Lim break; 943e54bcde3SZi Shen Lim /* dst = BSWAP##imm(dst) */ 944e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_LE: 945e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_BE: 9461104247fSXu Kuohai case BPF_ALU64 | BPF_END | BPF_FROM_LE: 947e54bcde3SZi Shen Lim #ifdef CONFIG_CPU_BIG_ENDIAN 9481104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_BE) 949d63903bbSXi Wang goto emit_bswap_uxt; 950e54bcde3SZi Shen Lim #else /* !CONFIG_CPU_BIG_ENDIAN */ 9511104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_LE) 952d63903bbSXi Wang goto emit_bswap_uxt; 953e54bcde3SZi Shen Lim #endif 954e54bcde3SZi Shen Lim switch (imm) { 955e54bcde3SZi Shen Lim case 16: 956e54bcde3SZi Shen Lim emit(A64_REV16(is64, dst, dst), ctx); 957d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 958d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 959e54bcde3SZi Shen Lim break; 960e54bcde3SZi Shen Lim case 32: 961a51cd6bfSArtem Savkov emit(A64_REV32(0, dst, dst), ctx); 962d63903bbSXi Wang /* upper 32 bits already cleared */ 963e54bcde3SZi Shen Lim break; 964e54bcde3SZi Shen Lim case 64: 965e54bcde3SZi Shen Lim emit(A64_REV64(dst, dst), ctx); 966e54bcde3SZi Shen Lim break; 967e54bcde3SZi Shen Lim } 968e54bcde3SZi Shen Lim break; 969d63903bbSXi Wang emit_bswap_uxt: 970d63903bbSXi Wang switch (imm) { 971d63903bbSXi Wang case 16: 972d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 973d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 974d63903bbSXi Wang break; 975d63903bbSXi Wang case 32: 976d63903bbSXi Wang /* zero-extend 32 bits into 64 bits */ 977d63903bbSXi Wang emit(A64_UXTW(is64, dst, dst), ctx); 978d63903bbSXi Wang break; 979d63903bbSXi Wang case 64: 980d63903bbSXi Wang /* nop */ 981d63903bbSXi Wang break; 982d63903bbSXi Wang } 983d63903bbSXi Wang break; 984e54bcde3SZi Shen Lim /* dst = imm */ 985e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_K: 986e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_K: 987e54bcde3SZi Shen Lim emit_a64_mov_i(is64, dst, imm, ctx); 988e54bcde3SZi Shen Lim break; 989e54bcde3SZi Shen Lim /* dst = dst OP imm */ 990e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_K: 991e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_K: 992fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 993fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, imm), ctx); 994fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 995fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, -imm), ctx); 996fd868f14SLuke Nelson } else { 997e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 998e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, tmp), ctx); 999fd868f14SLuke Nelson } 1000e54bcde3SZi Shen Lim break; 1001e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_K: 1002e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_K: 1003fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1004fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, imm), ctx); 1005fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1006fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, -imm), ctx); 1007fd868f14SLuke Nelson } else { 1008e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1009e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 1010fd868f14SLuke Nelson } 1011e54bcde3SZi Shen Lim break; 1012e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_K: 1013e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_K: 1014fd49591cSLuke Nelson a64_insn = A64_AND_I(is64, dst, dst, imm); 1015fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1016fd49591cSLuke Nelson emit(a64_insn, ctx); 1017fd49591cSLuke Nelson } else { 1018e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1019e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, tmp), ctx); 1020fd49591cSLuke Nelson } 1021e54bcde3SZi Shen Lim break; 1022e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_K: 1023e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_K: 1024fd49591cSLuke Nelson a64_insn = A64_ORR_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_ORR(is64, dst, dst, tmp), ctx); 1030fd49591cSLuke Nelson } 1031e54bcde3SZi Shen Lim break; 1032e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_K: 1033e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_K: 1034fd49591cSLuke Nelson a64_insn = A64_EOR_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_EOR(is64, dst, dst, tmp), ctx); 1040fd49591cSLuke Nelson } 1041e54bcde3SZi Shen Lim break; 1042e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_K: 1043e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_K: 1044e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1045e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, tmp), ctx); 1046e54bcde3SZi Shen Lim break; 1047e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_K: 1048e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_K: 1049e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 105068b18191SXu Kuohai if (!off) 1051e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, tmp), ctx); 105268b18191SXu Kuohai else 105368b18191SXu Kuohai emit(A64_SDIV(is64, dst, dst, tmp), ctx); 1054e54bcde3SZi Shen Lim break; 1055e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_K: 1056e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_K: 1057e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp2, imm, ctx); 105868b18191SXu Kuohai if (!off) 1059e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 106068b18191SXu Kuohai else 106168b18191SXu Kuohai emit(A64_SDIV(is64, tmp, dst, tmp2), ctx); 1062504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, tmp2), ctx); 1063e54bcde3SZi Shen Lim break; 1064e54bcde3SZi Shen Lim case BPF_ALU | BPF_LSH | BPF_K: 1065e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_K: 1066e54bcde3SZi Shen Lim emit(A64_LSL(is64, dst, dst, imm), ctx); 1067e54bcde3SZi Shen Lim break; 1068e54bcde3SZi Shen Lim case BPF_ALU | BPF_RSH | BPF_K: 1069e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_K: 1070e54bcde3SZi Shen Lim emit(A64_LSR(is64, dst, dst, imm), ctx); 1071e54bcde3SZi Shen Lim break; 1072e54bcde3SZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_K: 1073e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_K: 1074e54bcde3SZi Shen Lim emit(A64_ASR(is64, dst, dst, imm), ctx); 1075e54bcde3SZi Shen Lim break; 1076e54bcde3SZi Shen Lim 1077e54bcde3SZi Shen Lim /* JUMP off */ 1078e54bcde3SZi Shen Lim case BPF_JMP | BPF_JA: 1079c32b6ee5SXu Kuohai case BPF_JMP32 | BPF_JA: 1080c32b6ee5SXu Kuohai if (BPF_CLASS(code) == BPF_JMP) 108132f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 1082c32b6ee5SXu Kuohai else 1083c32b6ee5SXu Kuohai jmp_offset = bpf2a64_offset(i, imm, ctx); 1084e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1085e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1086e54bcde3SZi Shen Lim break; 1087e54bcde3SZi Shen Lim /* IF (dst COND src) JUMP off */ 1088e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_X: 1089e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_X: 1090c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_X: 1091e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_X: 1092c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_X: 1093e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_X: 1094e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_X: 1095c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_X: 1096e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_X: 1097c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_X: 1098654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_X: 1099654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_X: 1100654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_X: 1101654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_X: 1102654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_X: 1103654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_X: 1104654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_X: 1105654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_X: 1106654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_X: 1107654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_X: 1108654b65a0SJiong Wang emit(A64_CMP(is64, dst, src), ctx); 1109e54bcde3SZi Shen Lim emit_cond_jmp: 111032f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 1111e54bcde3SZi Shen Lim check_imm19(jmp_offset); 1112e54bcde3SZi Shen Lim switch (BPF_OP(code)) { 1113e54bcde3SZi Shen Lim case BPF_JEQ: 1114e54bcde3SZi Shen Lim jmp_cond = A64_COND_EQ; 1115e54bcde3SZi Shen Lim break; 1116e54bcde3SZi Shen Lim case BPF_JGT: 1117e54bcde3SZi Shen Lim jmp_cond = A64_COND_HI; 1118e54bcde3SZi Shen Lim break; 1119c362b2f3SDaniel Borkmann case BPF_JLT: 1120c362b2f3SDaniel Borkmann jmp_cond = A64_COND_CC; 1121c362b2f3SDaniel Borkmann break; 1122e54bcde3SZi Shen Lim case BPF_JGE: 1123e54bcde3SZi Shen Lim jmp_cond = A64_COND_CS; 1124e54bcde3SZi Shen Lim break; 1125c362b2f3SDaniel Borkmann case BPF_JLE: 1126c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LS; 1127c362b2f3SDaniel Borkmann break; 112898397fc5SZi Shen Lim case BPF_JSET: 1129e54bcde3SZi Shen Lim case BPF_JNE: 1130e54bcde3SZi Shen Lim jmp_cond = A64_COND_NE; 1131e54bcde3SZi Shen Lim break; 1132e54bcde3SZi Shen Lim case BPF_JSGT: 1133e54bcde3SZi Shen Lim jmp_cond = A64_COND_GT; 1134e54bcde3SZi Shen Lim break; 1135c362b2f3SDaniel Borkmann case BPF_JSLT: 1136c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LT; 1137c362b2f3SDaniel Borkmann break; 1138e54bcde3SZi Shen Lim case BPF_JSGE: 1139e54bcde3SZi Shen Lim jmp_cond = A64_COND_GE; 1140e54bcde3SZi Shen Lim break; 1141c362b2f3SDaniel Borkmann case BPF_JSLE: 1142c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LE; 1143c362b2f3SDaniel Borkmann break; 1144e54bcde3SZi Shen Lim default: 1145e54bcde3SZi Shen Lim return -EFAULT; 1146e54bcde3SZi Shen Lim } 1147e54bcde3SZi Shen Lim emit(A64_B_(jmp_cond, jmp_offset), ctx); 1148e54bcde3SZi Shen Lim break; 1149e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_X: 1150654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_X: 1151654b65a0SJiong Wang emit(A64_TST(is64, dst, src), ctx); 1152e54bcde3SZi Shen Lim goto emit_cond_jmp; 1153e54bcde3SZi Shen Lim /* IF (dst COND imm) JUMP off */ 1154e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_K: 1155e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_K: 1156c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_K: 1157e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_K: 1158c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_K: 1159e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_K: 1160e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_K: 1161c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_K: 1162e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_K: 1163c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_K: 1164654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_K: 1165654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_K: 1166654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_K: 1167654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_K: 1168654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_K: 1169654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_K: 1170654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_K: 1171654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_K: 1172654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_K: 1173654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_K: 1174fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1175fd868f14SLuke Nelson emit(A64_CMP_I(is64, dst, imm), ctx); 1176fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1177fd868f14SLuke Nelson emit(A64_CMN_I(is64, dst, -imm), ctx); 1178fd868f14SLuke Nelson } else { 1179654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1180654b65a0SJiong Wang emit(A64_CMP(is64, dst, tmp), ctx); 1181fd868f14SLuke Nelson } 1182e54bcde3SZi Shen Lim goto emit_cond_jmp; 1183e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_K: 1184654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_K: 1185fd49591cSLuke Nelson a64_insn = A64_TST_I(is64, dst, imm); 1186fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1187fd49591cSLuke Nelson emit(a64_insn, ctx); 1188fd49591cSLuke Nelson } else { 1189654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1190654b65a0SJiong Wang emit(A64_TST(is64, dst, tmp), ctx); 1191fd49591cSLuke Nelson } 1192e54bcde3SZi Shen Lim goto emit_cond_jmp; 1193e54bcde3SZi Shen Lim /* function call */ 1194e54bcde3SZi Shen Lim case BPF_JMP | BPF_CALL: 1195e54bcde3SZi Shen Lim { 1196e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 11978c11ea5cSDaniel Borkmann bool func_addr_fixed; 11988c11ea5cSDaniel Borkmann u64 func_addr; 1199e54bcde3SZi Shen Lim 12008c11ea5cSDaniel Borkmann ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 12018c11ea5cSDaniel Borkmann &func_addr, &func_addr_fixed); 12028c11ea5cSDaniel Borkmann if (ret < 0) 12038c11ea5cSDaniel Borkmann return ret; 1204efc9909fSXu Kuohai emit_call(func_addr, ctx); 1205e54bcde3SZi Shen Lim emit(A64_MOV(1, r0, A64_R(0)), ctx); 1206e54bcde3SZi Shen Lim break; 1207e54bcde3SZi Shen Lim } 1208ddb55992SZi Shen Lim /* tail call */ 120971189fa9SAlexei Starovoitov case BPF_JMP | BPF_TAIL_CALL: 1210ddb55992SZi Shen Lim if (emit_bpf_tail_call(ctx)) 1211ddb55992SZi Shen Lim return -EFAULT; 1212ddb55992SZi Shen Lim break; 1213e54bcde3SZi Shen Lim /* function return */ 1214e54bcde3SZi Shen Lim case BPF_JMP | BPF_EXIT: 121551c9fbb1SZi Shen Lim /* Optimization: when last instruction is EXIT, 121651c9fbb1SZi Shen Lim simply fallthrough to epilogue. */ 1217e54bcde3SZi Shen Lim if (i == ctx->prog->len - 1) 1218e54bcde3SZi Shen Lim break; 1219e54bcde3SZi Shen Lim jmp_offset = epilogue_offset(ctx); 1220e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1221e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1222e54bcde3SZi Shen Lim break; 1223e54bcde3SZi Shen Lim 122430d3d94cSZi Shen Lim /* dst = imm64 */ 122530d3d94cSZi Shen Lim case BPF_LD | BPF_IMM | BPF_DW: 122630d3d94cSZi Shen Lim { 122730d3d94cSZi Shen Lim const struct bpf_insn insn1 = insn[1]; 122830d3d94cSZi Shen Lim u64 imm64; 122930d3d94cSZi Shen Lim 12301e4df6b7SXi Wang imm64 = (u64)insn1.imm << 32 | (u32)imm; 1231e4a41c2cSHou Tao if (bpf_pseudo_func(insn)) 1232e4a41c2cSHou Tao emit_addr_mov_i64(dst, imm64, ctx); 1233e4a41c2cSHou Tao else 123430d3d94cSZi Shen Lim emit_a64_mov_i64(dst, imm64, ctx); 123530d3d94cSZi Shen Lim 123630d3d94cSZi Shen Lim return 1; 123730d3d94cSZi Shen Lim } 123830d3d94cSZi Shen Lim 1239cc88f540SXu Kuohai /* LDX: dst = (u64)*(unsigned size *)(src + off) */ 1240e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_W: 1241e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_H: 1242e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_B: 1243e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_DW: 124480083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 124580083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_W: 124680083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_H: 124780083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_B: 1248cc88f540SXu Kuohai /* LDXS: dst_reg = (s64)*(signed size *)(src_reg + off) */ 1249cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_B: 1250cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_H: 1251cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_W: 1252cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_B: 1253cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_H: 1254cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_W: 1255*339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_B: 1256*339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_H: 1257*339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_W: 1258*339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_DW: 1259*339af577SPuranjay Mohan if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) { 1260*339af577SPuranjay Mohan emit(A64_ADD(1, tmp2, src, arena_vm_base), ctx); 1261*339af577SPuranjay Mohan src = tmp2; 1262*339af577SPuranjay Mohan } 1263*339af577SPuranjay Mohan if (ctx->fpb_offset > 0 && src == fp && BPF_MODE(insn->code) != BPF_PROBE_MEM32) { 12645b3d19b9SXu Kuohai src_adj = fpb; 12655b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 12665b3d19b9SXu Kuohai } else { 12675b3d19b9SXu Kuohai src_adj = src; 12685b3d19b9SXu Kuohai off_adj = off; 12695b3d19b9SXu Kuohai } 1270cc88f540SXu Kuohai sign_extend = (BPF_MODE(insn->code) == BPF_MEMSX || 1271cc88f540SXu Kuohai BPF_MODE(insn->code) == BPF_PROBE_MEMSX); 1272e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1273e54bcde3SZi Shen Lim case BPF_W: 12745b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 1275cc88f540SXu Kuohai if (sign_extend) 1276cc88f540SXu Kuohai emit(A64_LDRSWI(dst, src_adj, off_adj), ctx); 1277cc88f540SXu Kuohai else 12785b3d19b9SXu Kuohai emit(A64_LDR32I(dst, src_adj, off_adj), ctx); 12797db6c0f1SXu Kuohai } else { 12807db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1281cc88f540SXu Kuohai if (sign_extend) 1282114b5b3bSPuranjay Mohan emit(A64_LDRSW(dst, src, tmp), ctx); 1283cc88f540SXu Kuohai else 1284e54bcde3SZi Shen Lim emit(A64_LDR32(dst, src, tmp), ctx); 12857db6c0f1SXu Kuohai } 1286e54bcde3SZi Shen Lim break; 1287e54bcde3SZi Shen Lim case BPF_H: 12885b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 1289cc88f540SXu Kuohai if (sign_extend) 1290cc88f540SXu Kuohai emit(A64_LDRSHI(dst, src_adj, off_adj), ctx); 1291cc88f540SXu Kuohai else 12925b3d19b9SXu Kuohai emit(A64_LDRHI(dst, src_adj, off_adj), ctx); 12937db6c0f1SXu Kuohai } else { 12947db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1295cc88f540SXu Kuohai if (sign_extend) 1296cc88f540SXu Kuohai emit(A64_LDRSH(dst, src, tmp), ctx); 1297cc88f540SXu Kuohai else 1298e54bcde3SZi Shen Lim emit(A64_LDRH(dst, src, tmp), ctx); 12997db6c0f1SXu Kuohai } 1300e54bcde3SZi Shen Lim break; 1301e54bcde3SZi Shen Lim case BPF_B: 13025b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 1303cc88f540SXu Kuohai if (sign_extend) 1304cc88f540SXu Kuohai emit(A64_LDRSBI(dst, src_adj, off_adj), ctx); 1305cc88f540SXu Kuohai else 13065b3d19b9SXu Kuohai emit(A64_LDRBI(dst, src_adj, off_adj), ctx); 13077db6c0f1SXu Kuohai } else { 13087db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1309cc88f540SXu Kuohai if (sign_extend) 1310cc88f540SXu Kuohai emit(A64_LDRSB(dst, src, tmp), ctx); 1311cc88f540SXu Kuohai else 1312e54bcde3SZi Shen Lim emit(A64_LDRB(dst, src, tmp), ctx); 13137db6c0f1SXu Kuohai } 1314e54bcde3SZi Shen Lim break; 1315e54bcde3SZi Shen Lim case BPF_DW: 13165b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 13175b3d19b9SXu Kuohai emit(A64_LDR64I(dst, src_adj, off_adj), ctx); 13187db6c0f1SXu Kuohai } else { 13197db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1320e54bcde3SZi Shen Lim emit(A64_LDR64(dst, src, tmp), ctx); 13217db6c0f1SXu Kuohai } 1322e54bcde3SZi Shen Lim break; 1323e54bcde3SZi Shen Lim } 132480083428SJean-Philippe Brucker 132580083428SJean-Philippe Brucker ret = add_exception_handler(insn, ctx, dst); 132680083428SJean-Philippe Brucker if (ret) 132780083428SJean-Philippe Brucker return ret; 1328e54bcde3SZi Shen Lim break; 1329e54bcde3SZi Shen Lim 1330f5e81d11SDaniel Borkmann /* speculation barrier */ 1331f5e81d11SDaniel Borkmann case BPF_ST | BPF_NOSPEC: 1332f5e81d11SDaniel Borkmann /* 1333f5e81d11SDaniel Borkmann * Nothing required here. 1334f5e81d11SDaniel Borkmann * 1335f5e81d11SDaniel Borkmann * In case of arm64, we rely on the firmware mitigation of 1336f5e81d11SDaniel Borkmann * Speculative Store Bypass as controlled via the ssbd kernel 1337f5e81d11SDaniel Borkmann * parameter. Whenever the mitigation is enabled, it works 1338f5e81d11SDaniel Borkmann * for all of the kernel code with no need to provide any 1339f5e81d11SDaniel Borkmann * additional instructions. 1340f5e81d11SDaniel Borkmann */ 1341f5e81d11SDaniel Borkmann break; 1342f5e81d11SDaniel Borkmann 1343e54bcde3SZi Shen Lim /* ST: *(size *)(dst + off) = imm */ 1344e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_W: 1345e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_H: 1346e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_B: 1347e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_DW: 1348*339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_B: 1349*339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_H: 1350*339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_W: 1351*339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_DW: 1352*339af577SPuranjay Mohan if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) { 1353*339af577SPuranjay Mohan emit(A64_ADD(1, tmp2, dst, arena_vm_base), ctx); 1354*339af577SPuranjay Mohan dst = tmp2; 1355*339af577SPuranjay Mohan } 1356*339af577SPuranjay Mohan if (ctx->fpb_offset > 0 && dst == fp && BPF_MODE(insn->code) != BPF_PROBE_MEM32) { 13575b3d19b9SXu Kuohai dst_adj = fpb; 13585b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 13595b3d19b9SXu Kuohai } else { 13605b3d19b9SXu Kuohai dst_adj = dst; 13615b3d19b9SXu Kuohai off_adj = off; 13625b3d19b9SXu Kuohai } 1363df849ba3SYang Shi /* Load imm to a register then store it */ 1364df849ba3SYang Shi emit_a64_mov_i(1, tmp, imm, ctx); 1365df849ba3SYang Shi switch (BPF_SIZE(code)) { 1366df849ba3SYang Shi case BPF_W: 13675b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 13685b3d19b9SXu Kuohai emit(A64_STR32I(tmp, dst_adj, off_adj), ctx); 13697db6c0f1SXu Kuohai } else { 13707db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1371df849ba3SYang Shi emit(A64_STR32(tmp, dst, tmp2), ctx); 13727db6c0f1SXu Kuohai } 1373df849ba3SYang Shi break; 1374df849ba3SYang Shi case BPF_H: 13755b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 13765b3d19b9SXu Kuohai emit(A64_STRHI(tmp, dst_adj, off_adj), ctx); 13777db6c0f1SXu Kuohai } else { 13787db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1379df849ba3SYang Shi emit(A64_STRH(tmp, dst, tmp2), ctx); 13807db6c0f1SXu Kuohai } 1381df849ba3SYang Shi break; 1382df849ba3SYang Shi case BPF_B: 13835b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 13845b3d19b9SXu Kuohai emit(A64_STRBI(tmp, dst_adj, off_adj), ctx); 13857db6c0f1SXu Kuohai } else { 13867db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1387df849ba3SYang Shi emit(A64_STRB(tmp, dst, tmp2), ctx); 13887db6c0f1SXu Kuohai } 1389df849ba3SYang Shi break; 1390df849ba3SYang Shi case BPF_DW: 13915b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 13925b3d19b9SXu Kuohai emit(A64_STR64I(tmp, dst_adj, off_adj), ctx); 13937db6c0f1SXu Kuohai } else { 13947db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1395df849ba3SYang Shi emit(A64_STR64(tmp, dst, tmp2), ctx); 13967db6c0f1SXu Kuohai } 1397df849ba3SYang Shi break; 1398df849ba3SYang Shi } 1399*339af577SPuranjay Mohan 1400*339af577SPuranjay Mohan ret = add_exception_handler(insn, ctx, dst); 1401*339af577SPuranjay Mohan if (ret) 1402*339af577SPuranjay Mohan return ret; 1403df849ba3SYang Shi break; 1404e54bcde3SZi Shen Lim 1405e54bcde3SZi Shen Lim /* STX: *(size *)(dst + off) = src */ 1406e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_W: 1407e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_H: 1408e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_B: 1409e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_DW: 1410*339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_B: 1411*339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_H: 1412*339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_W: 1413*339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_DW: 1414*339af577SPuranjay Mohan if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) { 1415*339af577SPuranjay Mohan emit(A64_ADD(1, tmp2, dst, arena_vm_base), ctx); 1416*339af577SPuranjay Mohan dst = tmp2; 1417*339af577SPuranjay Mohan } 1418*339af577SPuranjay Mohan if (ctx->fpb_offset > 0 && dst == fp && BPF_MODE(insn->code) != BPF_PROBE_MEM32) { 14195b3d19b9SXu Kuohai dst_adj = fpb; 14205b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 14215b3d19b9SXu Kuohai } else { 14225b3d19b9SXu Kuohai dst_adj = dst; 14235b3d19b9SXu Kuohai off_adj = off; 14245b3d19b9SXu Kuohai } 1425e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1426e54bcde3SZi Shen Lim case BPF_W: 14275b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 14285b3d19b9SXu Kuohai emit(A64_STR32I(src, dst_adj, off_adj), ctx); 14297db6c0f1SXu Kuohai } else { 14307db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1431e54bcde3SZi Shen Lim emit(A64_STR32(src, dst, tmp), ctx); 14327db6c0f1SXu Kuohai } 1433e54bcde3SZi Shen Lim break; 1434e54bcde3SZi Shen Lim case BPF_H: 14355b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 14365b3d19b9SXu Kuohai emit(A64_STRHI(src, dst_adj, off_adj), ctx); 14377db6c0f1SXu Kuohai } else { 14387db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1439e54bcde3SZi Shen Lim emit(A64_STRH(src, dst, tmp), ctx); 14407db6c0f1SXu Kuohai } 1441e54bcde3SZi Shen Lim break; 1442e54bcde3SZi Shen Lim case BPF_B: 14435b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 14445b3d19b9SXu Kuohai emit(A64_STRBI(src, dst_adj, off_adj), ctx); 14457db6c0f1SXu Kuohai } else { 14467db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1447e54bcde3SZi Shen Lim emit(A64_STRB(src, dst, tmp), ctx); 14487db6c0f1SXu Kuohai } 1449e54bcde3SZi Shen Lim break; 1450e54bcde3SZi Shen Lim case BPF_DW: 14515b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 14525b3d19b9SXu Kuohai emit(A64_STR64I(src, dst_adj, off_adj), ctx); 14537db6c0f1SXu Kuohai } else { 14547db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1455e54bcde3SZi Shen Lim emit(A64_STR64(src, dst, tmp), ctx); 14567db6c0f1SXu Kuohai } 1457e54bcde3SZi Shen Lim break; 1458e54bcde3SZi Shen Lim } 1459*339af577SPuranjay Mohan 1460*339af577SPuranjay Mohan ret = add_exception_handler(insn, ctx, dst); 1461*339af577SPuranjay Mohan if (ret) 1462*339af577SPuranjay Mohan return ret; 1463e54bcde3SZi Shen Lim break; 146434b8ab09SDaniel Borkmann 146591c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_W: 146691c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_DW: 14671902472bSHou Tao if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) 14681902472bSHou Tao ret = emit_lse_atomic(insn, ctx); 14691902472bSHou Tao else 14701902472bSHou Tao ret = emit_ll_sc_atomic(insn, ctx); 14711902472bSHou Tao if (ret) 14721902472bSHou Tao return ret; 147385f68fe8SDaniel Borkmann break; 1474e54bcde3SZi Shen Lim 1475e54bcde3SZi Shen Lim default: 1476e54bcde3SZi Shen Lim pr_err_once("unknown opcode %02x\n", code); 1477e54bcde3SZi Shen Lim return -EINVAL; 1478e54bcde3SZi Shen Lim } 1479e54bcde3SZi Shen Lim 1480e54bcde3SZi Shen Lim return 0; 1481e54bcde3SZi Shen Lim } 1482e54bcde3SZi Shen Lim 14835b3d19b9SXu Kuohai /* 14845b3d19b9SXu Kuohai * Return 0 if FP may change at runtime, otherwise find the minimum negative 14855b3d19b9SXu Kuohai * offset to FP, converts it to positive number, and align down to 8 bytes. 14865b3d19b9SXu Kuohai */ 14875b3d19b9SXu Kuohai static int find_fpb_offset(struct bpf_prog *prog) 14885b3d19b9SXu Kuohai { 14895b3d19b9SXu Kuohai int i; 14905b3d19b9SXu Kuohai int offset = 0; 14915b3d19b9SXu Kuohai 14925b3d19b9SXu Kuohai for (i = 0; i < prog->len; i++) { 14935b3d19b9SXu Kuohai const struct bpf_insn *insn = &prog->insnsi[i]; 14945b3d19b9SXu Kuohai const u8 class = BPF_CLASS(insn->code); 14955b3d19b9SXu Kuohai const u8 mode = BPF_MODE(insn->code); 14965b3d19b9SXu Kuohai const u8 src = insn->src_reg; 14975b3d19b9SXu Kuohai const u8 dst = insn->dst_reg; 14985b3d19b9SXu Kuohai const s32 imm = insn->imm; 14995b3d19b9SXu Kuohai const s16 off = insn->off; 15005b3d19b9SXu Kuohai 15015b3d19b9SXu Kuohai switch (class) { 15025b3d19b9SXu Kuohai case BPF_STX: 15035b3d19b9SXu Kuohai case BPF_ST: 15045b3d19b9SXu Kuohai /* fp holds atomic operation result */ 15055b3d19b9SXu Kuohai if (class == BPF_STX && mode == BPF_ATOMIC && 15065b3d19b9SXu Kuohai ((imm == BPF_XCHG || 15075b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_ADD) || 15085b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_AND) || 15095b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_XOR) || 15105b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_OR)) && 15115b3d19b9SXu Kuohai src == BPF_REG_FP)) 15125b3d19b9SXu Kuohai return 0; 15135b3d19b9SXu Kuohai 15145b3d19b9SXu Kuohai if (mode == BPF_MEM && dst == BPF_REG_FP && 15155b3d19b9SXu Kuohai off < offset) 15165b3d19b9SXu Kuohai offset = insn->off; 15175b3d19b9SXu Kuohai break; 15185b3d19b9SXu Kuohai 15195b3d19b9SXu Kuohai case BPF_JMP32: 15205b3d19b9SXu Kuohai case BPF_JMP: 15215b3d19b9SXu Kuohai break; 15225b3d19b9SXu Kuohai 15235b3d19b9SXu Kuohai case BPF_LDX: 15245b3d19b9SXu Kuohai case BPF_LD: 15255b3d19b9SXu Kuohai /* fp holds load result */ 15265b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 15275b3d19b9SXu Kuohai return 0; 15285b3d19b9SXu Kuohai 15295b3d19b9SXu Kuohai if (class == BPF_LDX && mode == BPF_MEM && 15305b3d19b9SXu Kuohai src == BPF_REG_FP && off < offset) 15315b3d19b9SXu Kuohai offset = off; 15325b3d19b9SXu Kuohai break; 15335b3d19b9SXu Kuohai 15345b3d19b9SXu Kuohai case BPF_ALU: 15355b3d19b9SXu Kuohai case BPF_ALU64: 15365b3d19b9SXu Kuohai default: 15375b3d19b9SXu Kuohai /* fp holds ALU result */ 15385b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 15395b3d19b9SXu Kuohai return 0; 15405b3d19b9SXu Kuohai } 15415b3d19b9SXu Kuohai } 15425b3d19b9SXu Kuohai 15435b3d19b9SXu Kuohai if (offset < 0) { 15445b3d19b9SXu Kuohai /* 15455b3d19b9SXu Kuohai * safely be converted to a positive 'int', since insn->off 15465b3d19b9SXu Kuohai * is 's16' 15475b3d19b9SXu Kuohai */ 15485b3d19b9SXu Kuohai offset = -offset; 15495b3d19b9SXu Kuohai /* align down to 8 bytes */ 15505b3d19b9SXu Kuohai offset = ALIGN_DOWN(offset, 8); 15515b3d19b9SXu Kuohai } 15525b3d19b9SXu Kuohai 15535b3d19b9SXu Kuohai return offset; 15545b3d19b9SXu Kuohai } 15555b3d19b9SXu Kuohai 15568c11ea5cSDaniel Borkmann static int build_body(struct jit_ctx *ctx, bool extra_pass) 1557e54bcde3SZi Shen Lim { 1558e54bcde3SZi Shen Lim const struct bpf_prog *prog = ctx->prog; 1559e54bcde3SZi Shen Lim int i; 1560e54bcde3SZi Shen Lim 156132f6865cSIlias Apalodimas /* 156232f6865cSIlias Apalodimas * - offset[0] offset of the end of prologue, 156332f6865cSIlias Apalodimas * start of the 1st instruction. 156432f6865cSIlias Apalodimas * - offset[1] - offset of the end of 1st instruction, 156532f6865cSIlias Apalodimas * start of the 2nd instruction 156632f6865cSIlias Apalodimas * [....] 156732f6865cSIlias Apalodimas * - offset[3] - offset of the end of 3rd instruction, 156832f6865cSIlias Apalodimas * start of 4th instruction 156932f6865cSIlias Apalodimas */ 1570e54bcde3SZi Shen Lim for (i = 0; i < prog->len; i++) { 1571e54bcde3SZi Shen Lim const struct bpf_insn *insn = &prog->insnsi[i]; 1572e54bcde3SZi Shen Lim int ret; 1573e54bcde3SZi Shen Lim 157432f6865cSIlias Apalodimas if (ctx->image == NULL) 157532f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 15768c11ea5cSDaniel Borkmann ret = build_insn(insn, ctx, extra_pass); 157730d3d94cSZi Shen Lim if (ret > 0) { 157830d3d94cSZi Shen Lim i++; 1579ddc665a4SDaniel Borkmann if (ctx->image == NULL) 1580ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 158130d3d94cSZi Shen Lim continue; 158230d3d94cSZi Shen Lim } 1583e54bcde3SZi Shen Lim if (ret) 1584e54bcde3SZi Shen Lim return ret; 1585e54bcde3SZi Shen Lim } 158632f6865cSIlias Apalodimas /* 158732f6865cSIlias Apalodimas * offset is allocated with prog->len + 1 so fill in 158832f6865cSIlias Apalodimas * the last element with the offset after the last 158932f6865cSIlias Apalodimas * instruction (end of program) 159032f6865cSIlias Apalodimas */ 159132f6865cSIlias Apalodimas if (ctx->image == NULL) 159232f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 1593e54bcde3SZi Shen Lim 1594e54bcde3SZi Shen Lim return 0; 1595e54bcde3SZi Shen Lim } 1596e54bcde3SZi Shen Lim 159742ff712bSZi Shen Lim static int validate_code(struct jit_ctx *ctx) 159842ff712bSZi Shen Lim { 159942ff712bSZi Shen Lim int i; 160042ff712bSZi Shen Lim 160142ff712bSZi Shen Lim for (i = 0; i < ctx->idx; i++) { 160242ff712bSZi Shen Lim u32 a64_insn = le32_to_cpu(ctx->image[i]); 160342ff712bSZi Shen Lim 160442ff712bSZi Shen Lim if (a64_insn == AARCH64_BREAK_FAULT) 160542ff712bSZi Shen Lim return -1; 160642ff712bSZi Shen Lim } 1607efc9909fSXu Kuohai return 0; 1608efc9909fSXu Kuohai } 1609efc9909fSXu Kuohai 1610efc9909fSXu Kuohai static int validate_ctx(struct jit_ctx *ctx) 1611efc9909fSXu Kuohai { 1612efc9909fSXu Kuohai if (validate_code(ctx)) 1613efc9909fSXu Kuohai return -1; 161442ff712bSZi Shen Lim 161580083428SJean-Philippe Brucker if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries)) 161680083428SJean-Philippe Brucker return -1; 161780083428SJean-Philippe Brucker 161842ff712bSZi Shen Lim return 0; 161942ff712bSZi Shen Lim } 162042ff712bSZi Shen Lim 1621e54bcde3SZi Shen Lim static inline void bpf_flush_icache(void *start, void *end) 1622e54bcde3SZi Shen Lim { 1623e54bcde3SZi Shen Lim flush_icache_range((unsigned long)start, (unsigned long)end); 1624e54bcde3SZi Shen Lim } 1625e54bcde3SZi Shen Lim 1626db496944SAlexei Starovoitov struct arm64_jit_data { 1627db496944SAlexei Starovoitov struct bpf_binary_header *header; 16281dad391dSPuranjay Mohan u8 *ro_image; 16291dad391dSPuranjay Mohan struct bpf_binary_header *ro_header; 1630db496944SAlexei Starovoitov struct jit_ctx ctx; 1631db496944SAlexei Starovoitov }; 1632db496944SAlexei Starovoitov 1633d1c55ab5SDaniel Borkmann struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 1634e54bcde3SZi Shen Lim { 1635b2ad54e1SXu Kuohai int image_size, prog_size, extable_size, extable_align, extable_offset; 163626eb042eSDaniel Borkmann struct bpf_prog *tmp, *orig_prog = prog; 1637b569c1c6SDaniel Borkmann struct bpf_binary_header *header; 16381dad391dSPuranjay Mohan struct bpf_binary_header *ro_header; 1639db496944SAlexei Starovoitov struct arm64_jit_data *jit_data; 164056ea6a8bSDaniel Borkmann bool was_classic = bpf_prog_was_classic(prog); 164126eb042eSDaniel Borkmann bool tmp_blinded = false; 1642db496944SAlexei Starovoitov bool extra_pass = false; 1643e54bcde3SZi Shen Lim struct jit_ctx ctx; 1644*339af577SPuranjay Mohan u64 arena_vm_start; 1645b569c1c6SDaniel Borkmann u8 *image_ptr; 16461dad391dSPuranjay Mohan u8 *ro_image_ptr; 1647e54bcde3SZi Shen Lim 164860b58afcSAlexei Starovoitov if (!prog->jit_requested) 164926eb042eSDaniel Borkmann return orig_prog; 165026eb042eSDaniel Borkmann 165126eb042eSDaniel Borkmann tmp = bpf_jit_blind_constants(prog); 165226eb042eSDaniel Borkmann /* If blinding was requested and we failed during blinding, 165326eb042eSDaniel Borkmann * we must fall back to the interpreter. 165426eb042eSDaniel Borkmann */ 165526eb042eSDaniel Borkmann if (IS_ERR(tmp)) 165626eb042eSDaniel Borkmann return orig_prog; 165726eb042eSDaniel Borkmann if (tmp != prog) { 165826eb042eSDaniel Borkmann tmp_blinded = true; 165926eb042eSDaniel Borkmann prog = tmp; 166026eb042eSDaniel Borkmann } 1661e54bcde3SZi Shen Lim 1662*339af577SPuranjay Mohan arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena); 1663db496944SAlexei Starovoitov jit_data = prog->aux->jit_data; 1664db496944SAlexei Starovoitov if (!jit_data) { 1665db496944SAlexei Starovoitov jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 1666db496944SAlexei Starovoitov if (!jit_data) { 1667db496944SAlexei Starovoitov prog = orig_prog; 1668db496944SAlexei Starovoitov goto out; 1669db496944SAlexei Starovoitov } 1670db496944SAlexei Starovoitov prog->aux->jit_data = jit_data; 1671db496944SAlexei Starovoitov } 1672db496944SAlexei Starovoitov if (jit_data->ctx.offset) { 1673db496944SAlexei Starovoitov ctx = jit_data->ctx; 16741dad391dSPuranjay Mohan ro_image_ptr = jit_data->ro_image; 16751dad391dSPuranjay Mohan ro_header = jit_data->ro_header; 1676db496944SAlexei Starovoitov header = jit_data->header; 16771dad391dSPuranjay Mohan image_ptr = (void *)header + ((void *)ro_image_ptr 16781dad391dSPuranjay Mohan - (void *)ro_header); 1679db496944SAlexei Starovoitov extra_pass = true; 168080083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1681db496944SAlexei Starovoitov goto skip_init_ctx; 1682db496944SAlexei Starovoitov } 1683e54bcde3SZi Shen Lim memset(&ctx, 0, sizeof(ctx)); 1684e54bcde3SZi Shen Lim ctx.prog = prog; 1685e54bcde3SZi Shen Lim 168619f68ed6SAijun Sun ctx.offset = kvcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); 168726eb042eSDaniel Borkmann if (ctx.offset == NULL) { 168826eb042eSDaniel Borkmann prog = orig_prog; 1689db496944SAlexei Starovoitov goto out_off; 169026eb042eSDaniel Borkmann } 1691e54bcde3SZi Shen Lim 16925b3d19b9SXu Kuohai ctx.fpb_offset = find_fpb_offset(prog); 16935b3d19b9SXu Kuohai 169468e4f238SHou Tao /* 169568e4f238SHou Tao * 1. Initial fake pass to compute ctx->idx and ctx->offset. 169668e4f238SHou Tao * 169768e4f238SHou Tao * BPF line info needs ctx->offset[i] to be the offset of 169868e4f238SHou Tao * instruction[i] in jited image, so build prologue first. 169968e4f238SHou Tao */ 1700*339af577SPuranjay Mohan if (build_prologue(&ctx, was_classic, prog->aux->exception_cb, 1701*339af577SPuranjay Mohan arena_vm_start)) { 170226eb042eSDaniel Borkmann prog = orig_prog; 170326eb042eSDaniel Borkmann goto out_off; 170426eb042eSDaniel Borkmann } 1705e54bcde3SZi Shen Lim 170668e4f238SHou Tao if (build_body(&ctx, extra_pass)) { 1707ddb55992SZi Shen Lim prog = orig_prog; 1708ddb55992SZi Shen Lim goto out_off; 1709ddb55992SZi Shen Lim } 171051c9fbb1SZi Shen Lim 171151c9fbb1SZi Shen Lim ctx.epilogue_offset = ctx.idx; 171222fc0e80SPuranjay Mohan build_epilogue(&ctx, prog->aux->exception_cb); 1713b2ad54e1SXu Kuohai build_plt(&ctx); 1714e54bcde3SZi Shen Lim 1715b2ad54e1SXu Kuohai extable_align = __alignof__(struct exception_table_entry); 171680083428SJean-Philippe Brucker extable_size = prog->aux->num_exentries * 171780083428SJean-Philippe Brucker sizeof(struct exception_table_entry); 171880083428SJean-Philippe Brucker 1719e54bcde3SZi Shen Lim /* Now we know the actual image size. */ 172080083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1721b2ad54e1SXu Kuohai /* also allocate space for plt target */ 1722b2ad54e1SXu Kuohai extable_offset = round_up(prog_size + PLT_TARGET_SIZE, extable_align); 1723b2ad54e1SXu Kuohai image_size = extable_offset + extable_size; 17241dad391dSPuranjay Mohan ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, 17251dad391dSPuranjay Mohan sizeof(u32), &header, &image_ptr, 17261dad391dSPuranjay Mohan jit_fill_hole); 17271dad391dSPuranjay Mohan if (!ro_header) { 172826eb042eSDaniel Borkmann prog = orig_prog; 172926eb042eSDaniel Borkmann goto out_off; 173026eb042eSDaniel Borkmann } 1731e54bcde3SZi Shen Lim 1732e54bcde3SZi Shen Lim /* 2. Now, the actual pass. */ 1733e54bcde3SZi Shen Lim 17341dad391dSPuranjay Mohan /* 17351dad391dSPuranjay Mohan * Use the image(RW) for writing the JITed instructions. But also save 17361dad391dSPuranjay Mohan * the ro_image(RX) for calculating the offsets in the image. The RW 17371dad391dSPuranjay Mohan * image will be later copied to the RX image from where the program 17381dad391dSPuranjay Mohan * will run. The bpf_jit_binary_pack_finalize() will do this copy in the 17391dad391dSPuranjay Mohan * final step. 17401dad391dSPuranjay Mohan */ 1741425e1ed7SLuc Van Oostenryck ctx.image = (__le32 *)image_ptr; 17421dad391dSPuranjay Mohan ctx.ro_image = (__le32 *)ro_image_ptr; 174380083428SJean-Philippe Brucker if (extable_size) 17441dad391dSPuranjay Mohan prog->aux->extable = (void *)ro_image_ptr + extable_offset; 1745db496944SAlexei Starovoitov skip_init_ctx: 1746e54bcde3SZi Shen Lim ctx.idx = 0; 174780083428SJean-Philippe Brucker ctx.exentry_idx = 0; 1748b569c1c6SDaniel Borkmann 1749*339af577SPuranjay Mohan build_prologue(&ctx, was_classic, prog->aux->exception_cb, arena_vm_start); 1750e54bcde3SZi Shen Lim 17518c11ea5cSDaniel Borkmann if (build_body(&ctx, extra_pass)) { 175226eb042eSDaniel Borkmann prog = orig_prog; 17531dad391dSPuranjay Mohan goto out_free_hdr; 175460ef0494SDaniel Borkmann } 1755e54bcde3SZi Shen Lim 175622fc0e80SPuranjay Mohan build_epilogue(&ctx, prog->aux->exception_cb); 1757b2ad54e1SXu Kuohai build_plt(&ctx); 1758e54bcde3SZi Shen Lim 175942ff712bSZi Shen Lim /* 3. Extra pass to validate JITed code. */ 1760efc9909fSXu Kuohai if (validate_ctx(&ctx)) { 176126eb042eSDaniel Borkmann prog = orig_prog; 17621dad391dSPuranjay Mohan goto out_free_hdr; 176342ff712bSZi Shen Lim } 176442ff712bSZi Shen Lim 1765e54bcde3SZi Shen Lim /* And we're done. */ 1766e54bcde3SZi Shen Lim if (bpf_jit_enable > 1) 176780083428SJean-Philippe Brucker bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 1768e54bcde3SZi Shen Lim 1769db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1770db496944SAlexei Starovoitov if (extra_pass && ctx.idx != jit_data->ctx.idx) { 1771db496944SAlexei Starovoitov pr_err_once("multi-func JIT bug %d != %d\n", 1772db496944SAlexei Starovoitov ctx.idx, jit_data->ctx.idx); 1773db496944SAlexei Starovoitov prog->bpf_func = NULL; 1774db496944SAlexei Starovoitov prog->jited = 0; 177510f3b29cSEric Dumazet prog->jited_len = 0; 17761dad391dSPuranjay Mohan goto out_free_hdr; 17771dad391dSPuranjay Mohan } 17781dad391dSPuranjay Mohan if (WARN_ON(bpf_jit_binary_pack_finalize(prog, ro_header, 17791dad391dSPuranjay Mohan header))) { 17801dad391dSPuranjay Mohan /* ro_header has been freed */ 17811dad391dSPuranjay Mohan ro_header = NULL; 17821dad391dSPuranjay Mohan prog = orig_prog; 1783db496944SAlexei Starovoitov goto out_off; 1784db496944SAlexei Starovoitov } 17851dad391dSPuranjay Mohan /* 17861dad391dSPuranjay Mohan * The instructions have now been copied to the ROX region from 17871dad391dSPuranjay Mohan * where they will execute. Now the data cache has to be cleaned to 17881dad391dSPuranjay Mohan * the PoU and the I-cache has to be invalidated for the VAs. 17891dad391dSPuranjay Mohan */ 17901dad391dSPuranjay Mohan bpf_flush_icache(ro_header, ctx.ro_image + ctx.idx); 1791db496944SAlexei Starovoitov } else { 1792db496944SAlexei Starovoitov jit_data->ctx = ctx; 17931dad391dSPuranjay Mohan jit_data->ro_image = ro_image_ptr; 1794db496944SAlexei Starovoitov jit_data->header = header; 17951dad391dSPuranjay Mohan jit_data->ro_header = ro_header; 1796db496944SAlexei Starovoitov } 17971dad391dSPuranjay Mohan 17981dad391dSPuranjay Mohan prog->bpf_func = (void *)ctx.ro_image; 1799a91263d5SDaniel Borkmann prog->jited = 1; 180080083428SJean-Philippe Brucker prog->jited_len = prog_size; 180126eb042eSDaniel Borkmann 1802db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1803dda7596cSHou Tao int i; 1804dda7596cSHou Tao 1805dda7596cSHou Tao /* offset[prog->len] is the size of program */ 1806dda7596cSHou Tao for (i = 0; i <= prog->len; i++) 1807dda7596cSHou Tao ctx.offset[i] *= AARCH64_INSN_SIZE; 180832f6865cSIlias Apalodimas bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 180926eb042eSDaniel Borkmann out_off: 181019f68ed6SAijun Sun kvfree(ctx.offset); 1811db496944SAlexei Starovoitov kfree(jit_data); 1812db496944SAlexei Starovoitov prog->aux->jit_data = NULL; 1813db496944SAlexei Starovoitov } 181426eb042eSDaniel Borkmann out: 181526eb042eSDaniel Borkmann if (tmp_blinded) 181626eb042eSDaniel Borkmann bpf_jit_prog_release_other(prog, prog == orig_prog ? 181726eb042eSDaniel Borkmann tmp : orig_prog); 1818d1c55ab5SDaniel Borkmann return prog; 18191dad391dSPuranjay Mohan 18201dad391dSPuranjay Mohan out_free_hdr: 18211dad391dSPuranjay Mohan if (header) { 18221dad391dSPuranjay Mohan bpf_arch_text_copy(&ro_header->size, &header->size, 18231dad391dSPuranjay Mohan sizeof(header->size)); 18241dad391dSPuranjay Mohan bpf_jit_binary_pack_free(ro_header, header); 18251dad391dSPuranjay Mohan } 18261dad391dSPuranjay Mohan goto out_off; 1827e54bcde3SZi Shen Lim } 182891fc957cSArd Biesheuvel 1829b5e975d2SHou Tao bool bpf_jit_supports_kfunc_call(void) 1830b5e975d2SHou Tao { 1831b5e975d2SHou Tao return true; 1832b5e975d2SHou Tao } 1833b5e975d2SHou Tao 18341dad391dSPuranjay Mohan void *bpf_arch_text_copy(void *dst, void *src, size_t len) 18351dad391dSPuranjay Mohan { 18361dad391dSPuranjay Mohan if (!aarch64_insn_copy(dst, src, len)) 18371dad391dSPuranjay Mohan return ERR_PTR(-EINVAL); 18381dad391dSPuranjay Mohan return dst; 18391dad391dSPuranjay Mohan } 18401dad391dSPuranjay Mohan 18415d63ae90SLorenz Bauer u64 bpf_jit_alloc_exec_limit(void) 18425d63ae90SLorenz Bauer { 1843b89ddf4cSRussell King return VMALLOC_END - VMALLOC_START; 18445d63ae90SLorenz Bauer } 18455d63ae90SLorenz Bauer 184691fc957cSArd Biesheuvel void *bpf_jit_alloc_exec(unsigned long size) 184791fc957cSArd Biesheuvel { 184836c4a73bSAndrey Konovalov /* Memory is intended to be executable, reset the pointer tag. */ 184936c4a73bSAndrey Konovalov return kasan_reset_tag(vmalloc(size)); 185091fc957cSArd Biesheuvel } 185191fc957cSArd Biesheuvel 185291fc957cSArd Biesheuvel void bpf_jit_free_exec(void *addr) 185391fc957cSArd Biesheuvel { 185491fc957cSArd Biesheuvel return vfree(addr); 185591fc957cSArd Biesheuvel } 1856d4609a5dSJakub Sitnicki 1857d4609a5dSJakub Sitnicki /* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */ 1858d4609a5dSJakub Sitnicki bool bpf_jit_supports_subprog_tailcalls(void) 1859d4609a5dSJakub Sitnicki { 1860d4609a5dSJakub Sitnicki return true; 1861d4609a5dSJakub Sitnicki } 1862b2ad54e1SXu Kuohai 1863efc9909fSXu Kuohai static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, 1864efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1865efc9909fSXu Kuohai bool save_ret) 1866efc9909fSXu Kuohai { 1867aada4766SXu Kuohai __le32 *branch; 1868efc9909fSXu Kuohai u64 enter_prog; 1869efc9909fSXu Kuohai u64 exit_prog; 1870efc9909fSXu Kuohai struct bpf_prog *p = l->link.prog; 1871efc9909fSXu Kuohai int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); 1872efc9909fSXu Kuohai 1873271de525SMartin KaFai Lau enter_prog = (u64)bpf_trampoline_enter(p); 1874271de525SMartin KaFai Lau exit_prog = (u64)bpf_trampoline_exit(p); 1875efc9909fSXu Kuohai 1876efc9909fSXu Kuohai if (l->cookie == 0) { 1877efc9909fSXu Kuohai /* if cookie is zero, one instruction is enough to store it */ 1878efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, run_ctx_off + cookie_off), ctx); 1879efc9909fSXu Kuohai } else { 1880efc9909fSXu Kuohai emit_a64_mov_i64(A64_R(10), l->cookie, ctx); 1881efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, run_ctx_off + cookie_off), 1882efc9909fSXu Kuohai ctx); 1883efc9909fSXu Kuohai } 1884efc9909fSXu Kuohai 1885efc9909fSXu Kuohai /* save p to callee saved register x19 to avoid loading p with mov_i64 1886efc9909fSXu Kuohai * each time. 1887efc9909fSXu Kuohai */ 1888efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(19), (const u64)p, ctx); 1889efc9909fSXu Kuohai 1890efc9909fSXu Kuohai /* arg1: prog */ 1891efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1892efc9909fSXu Kuohai /* arg2: &run_ctx */ 1893efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(1), A64_SP, run_ctx_off), ctx); 1894efc9909fSXu Kuohai 1895efc9909fSXu Kuohai emit_call(enter_prog, ctx); 1896efc9909fSXu Kuohai 1897efc9909fSXu Kuohai /* if (__bpf_prog_enter(prog) == 0) 1898efc9909fSXu Kuohai * goto skip_exec_of_prog; 1899efc9909fSXu Kuohai */ 1900efc9909fSXu Kuohai branch = ctx->image + ctx->idx; 1901efc9909fSXu Kuohai emit(A64_NOP, ctx); 1902efc9909fSXu Kuohai 1903efc9909fSXu Kuohai /* save return value to callee saved register x20 */ 1904efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(20), A64_R(0)), ctx); 1905efc9909fSXu Kuohai 1906efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx); 1907efc9909fSXu Kuohai if (!p->jited) 1908efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx); 1909efc9909fSXu Kuohai 1910efc9909fSXu Kuohai emit_call((const u64)p->bpf_func, ctx); 1911efc9909fSXu Kuohai 1912efc9909fSXu Kuohai if (save_ret) 1913efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 1914efc9909fSXu Kuohai 1915efc9909fSXu Kuohai if (ctx->image) { 1916efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branch; 1917aada4766SXu Kuohai *branch = cpu_to_le32(A64_CBZ(1, A64_R(0), offset)); 1918efc9909fSXu Kuohai } 1919efc9909fSXu Kuohai 1920efc9909fSXu Kuohai /* arg1: prog */ 1921efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1922efc9909fSXu Kuohai /* arg2: start time */ 1923efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(1), A64_R(20)), ctx); 1924efc9909fSXu Kuohai /* arg3: &run_ctx */ 1925efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(2), A64_SP, run_ctx_off), ctx); 1926efc9909fSXu Kuohai 1927efc9909fSXu Kuohai emit_call(exit_prog, ctx); 1928efc9909fSXu Kuohai } 1929efc9909fSXu Kuohai 1930efc9909fSXu Kuohai static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, 1931efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1932aada4766SXu Kuohai __le32 **branches) 1933efc9909fSXu Kuohai { 1934efc9909fSXu Kuohai int i; 1935efc9909fSXu Kuohai 1936efc9909fSXu Kuohai /* The first fmod_ret program will receive a garbage return value. 1937efc9909fSXu Kuohai * Set this to 0 to avoid confusing the program. 1938efc9909fSXu Kuohai */ 1939efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx); 1940efc9909fSXu Kuohai for (i = 0; i < tl->nr_links; i++) { 1941efc9909fSXu Kuohai invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off, 1942efc9909fSXu Kuohai run_ctx_off, true); 1943efc9909fSXu Kuohai /* if (*(u64 *)(sp + retval_off) != 0) 1944efc9909fSXu Kuohai * goto do_fexit; 1945efc9909fSXu Kuohai */ 1946efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retval_off), ctx); 1947efc9909fSXu Kuohai /* Save the location of branch, and generate a nop. 1948efc9909fSXu Kuohai * This nop will be replaced with a cbnz later. 1949efc9909fSXu Kuohai */ 1950efc9909fSXu Kuohai branches[i] = ctx->image + ctx->idx; 1951efc9909fSXu Kuohai emit(A64_NOP, ctx); 1952efc9909fSXu Kuohai } 1953efc9909fSXu Kuohai } 1954efc9909fSXu Kuohai 195590564f1eSFlorent Revest static void save_args(struct jit_ctx *ctx, int args_off, int nregs) 1956efc9909fSXu Kuohai { 1957efc9909fSXu Kuohai int i; 1958efc9909fSXu Kuohai 195990564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 1960efc9909fSXu Kuohai emit(A64_STR64I(i, A64_SP, args_off), ctx); 1961efc9909fSXu Kuohai args_off += 8; 1962efc9909fSXu Kuohai } 1963efc9909fSXu Kuohai } 1964efc9909fSXu Kuohai 196590564f1eSFlorent Revest static void restore_args(struct jit_ctx *ctx, int args_off, int nregs) 1966efc9909fSXu Kuohai { 1967efc9909fSXu Kuohai int i; 1968efc9909fSXu Kuohai 196990564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 1970efc9909fSXu Kuohai emit(A64_LDR64I(i, A64_SP, args_off), ctx); 1971efc9909fSXu Kuohai args_off += 8; 1972efc9909fSXu Kuohai } 1973efc9909fSXu Kuohai } 1974efc9909fSXu Kuohai 1975efc9909fSXu Kuohai /* Based on the x86's implementation of arch_prepare_bpf_trampoline(). 1976efc9909fSXu Kuohai * 1977efc9909fSXu Kuohai * bpf prog and function entry before bpf trampoline hooked: 1978efc9909fSXu Kuohai * mov x9, lr 1979efc9909fSXu Kuohai * nop 1980efc9909fSXu Kuohai * 1981efc9909fSXu Kuohai * bpf prog and function entry after bpf trampoline hooked: 1982efc9909fSXu Kuohai * mov x9, lr 1983efc9909fSXu Kuohai * bl <bpf_trampoline or plt> 1984efc9909fSXu Kuohai * 1985efc9909fSXu Kuohai */ 1986efc9909fSXu Kuohai static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, 19877a3d9a15SSong Liu struct bpf_tramp_links *tlinks, void *func_addr, 198890564f1eSFlorent Revest int nregs, u32 flags) 1989efc9909fSXu Kuohai { 1990efc9909fSXu Kuohai int i; 1991efc9909fSXu Kuohai int stack_size; 1992efc9909fSXu Kuohai int retaddr_off; 1993efc9909fSXu Kuohai int regs_off; 1994efc9909fSXu Kuohai int retval_off; 1995efc9909fSXu Kuohai int args_off; 199690564f1eSFlorent Revest int nregs_off; 1997efc9909fSXu Kuohai int ip_off; 1998efc9909fSXu Kuohai int run_ctx_off; 1999efc9909fSXu Kuohai struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; 2000efc9909fSXu Kuohai struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; 2001efc9909fSXu Kuohai struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; 2002efc9909fSXu Kuohai bool save_ret; 2003aada4766SXu Kuohai __le32 **branches = NULL; 2004efc9909fSXu Kuohai 2005efc9909fSXu Kuohai /* trampoline stack layout: 2006efc9909fSXu Kuohai * [ parent ip ] 2007efc9909fSXu Kuohai * [ FP ] 2008efc9909fSXu Kuohai * SP + retaddr_off [ self ip ] 2009efc9909fSXu Kuohai * [ FP ] 2010efc9909fSXu Kuohai * 2011efc9909fSXu Kuohai * [ padding ] align SP to multiples of 16 2012efc9909fSXu Kuohai * 2013efc9909fSXu Kuohai * [ x20 ] callee saved reg x20 2014efc9909fSXu Kuohai * SP + regs_off [ x19 ] callee saved reg x19 2015efc9909fSXu Kuohai * 2016efc9909fSXu Kuohai * SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or 2017efc9909fSXu Kuohai * BPF_TRAMP_F_RET_FENTRY_RET 2018efc9909fSXu Kuohai * 201990564f1eSFlorent Revest * [ arg reg N ] 2020efc9909fSXu Kuohai * [ ... ] 202190564f1eSFlorent Revest * SP + args_off [ arg reg 1 ] 2022efc9909fSXu Kuohai * 202390564f1eSFlorent Revest * SP + nregs_off [ arg regs count ] 2024efc9909fSXu Kuohai * 2025efc9909fSXu Kuohai * SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag 2026efc9909fSXu Kuohai * 2027efc9909fSXu Kuohai * SP + run_ctx_off [ bpf_tramp_run_ctx ] 2028efc9909fSXu Kuohai */ 2029efc9909fSXu Kuohai 2030efc9909fSXu Kuohai stack_size = 0; 2031efc9909fSXu Kuohai run_ctx_off = stack_size; 2032efc9909fSXu Kuohai /* room for bpf_tramp_run_ctx */ 2033efc9909fSXu Kuohai stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); 2034efc9909fSXu Kuohai 2035efc9909fSXu Kuohai ip_off = stack_size; 2036efc9909fSXu Kuohai /* room for IP address argument */ 2037efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) 2038efc9909fSXu Kuohai stack_size += 8; 2039efc9909fSXu Kuohai 204090564f1eSFlorent Revest nregs_off = stack_size; 2041efc9909fSXu Kuohai /* room for args count */ 2042efc9909fSXu Kuohai stack_size += 8; 2043efc9909fSXu Kuohai 2044efc9909fSXu Kuohai args_off = stack_size; 2045efc9909fSXu Kuohai /* room for args */ 204690564f1eSFlorent Revest stack_size += nregs * 8; 2047efc9909fSXu Kuohai 2048efc9909fSXu Kuohai /* room for return value */ 2049efc9909fSXu Kuohai retval_off = stack_size; 2050efc9909fSXu Kuohai save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET); 2051efc9909fSXu Kuohai if (save_ret) 2052efc9909fSXu Kuohai stack_size += 8; 2053efc9909fSXu Kuohai 2054efc9909fSXu Kuohai /* room for callee saved registers, currently x19 and x20 are used */ 2055efc9909fSXu Kuohai regs_off = stack_size; 2056efc9909fSXu Kuohai stack_size += 16; 2057efc9909fSXu Kuohai 2058efc9909fSXu Kuohai /* round up to multiples of 16 to avoid SPAlignmentFault */ 2059efc9909fSXu Kuohai stack_size = round_up(stack_size, 16); 2060efc9909fSXu Kuohai 2061efc9909fSXu Kuohai /* return address locates above FP */ 2062efc9909fSXu Kuohai retaddr_off = stack_size + 8; 2063efc9909fSXu Kuohai 2064efc9909fSXu Kuohai /* bpf trampoline may be invoked by 3 instruction types: 2065efc9909fSXu Kuohai * 1. bl, attached to bpf prog or kernel function via short jump 2066efc9909fSXu Kuohai * 2. br, attached to bpf prog or kernel function via long jump 2067efc9909fSXu Kuohai * 3. blr, working as a function pointer, used by struct_ops. 2068efc9909fSXu Kuohai * So BTI_JC should used here to support both br and blr. 2069efc9909fSXu Kuohai */ 2070efc9909fSXu Kuohai emit_bti(A64_BTI_JC, ctx); 2071efc9909fSXu Kuohai 2072efc9909fSXu Kuohai /* frame for parent function */ 2073efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx); 2074efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 2075efc9909fSXu Kuohai 2076efc9909fSXu Kuohai /* frame for patched function */ 2077efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 2078efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 2079efc9909fSXu Kuohai 2080efc9909fSXu Kuohai /* allocate stack space */ 2081efc9909fSXu Kuohai emit(A64_SUB_I(1, A64_SP, A64_SP, stack_size), ctx); 2082efc9909fSXu Kuohai 2083efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) { 2084efc9909fSXu Kuohai /* save ip address of the traced function */ 20857a3d9a15SSong Liu emit_addr_mov_i64(A64_R(10), (const u64)func_addr, ctx); 2086efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, ip_off), ctx); 2087efc9909fSXu Kuohai } 2088efc9909fSXu Kuohai 208990564f1eSFlorent Revest /* save arg regs count*/ 209090564f1eSFlorent Revest emit(A64_MOVZ(1, A64_R(10), nregs, 0), ctx); 209190564f1eSFlorent Revest emit(A64_STR64I(A64_R(10), A64_SP, nregs_off), ctx); 2092efc9909fSXu Kuohai 209390564f1eSFlorent Revest /* save arg regs */ 209490564f1eSFlorent Revest save_args(ctx, args_off, nregs); 2095efc9909fSXu Kuohai 2096efc9909fSXu Kuohai /* save callee saved registers */ 2097efc9909fSXu Kuohai emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx); 2098efc9909fSXu Kuohai emit(A64_STR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 2099efc9909fSXu Kuohai 2100efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 2101efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 2102efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_enter, ctx); 2103efc9909fSXu Kuohai } 2104efc9909fSXu Kuohai 2105efc9909fSXu Kuohai for (i = 0; i < fentry->nr_links; i++) 2106efc9909fSXu Kuohai invoke_bpf_prog(ctx, fentry->links[i], args_off, 2107efc9909fSXu Kuohai retval_off, run_ctx_off, 2108efc9909fSXu Kuohai flags & BPF_TRAMP_F_RET_FENTRY_RET); 2109efc9909fSXu Kuohai 2110efc9909fSXu Kuohai if (fmod_ret->nr_links) { 2111aada4766SXu Kuohai branches = kcalloc(fmod_ret->nr_links, sizeof(__le32 *), 2112efc9909fSXu Kuohai GFP_KERNEL); 2113efc9909fSXu Kuohai if (!branches) 2114efc9909fSXu Kuohai return -ENOMEM; 2115efc9909fSXu Kuohai 2116efc9909fSXu Kuohai invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off, 2117efc9909fSXu Kuohai run_ctx_off, branches); 2118efc9909fSXu Kuohai } 2119efc9909fSXu Kuohai 2120efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 212190564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 2122efc9909fSXu Kuohai /* call original func */ 2123efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx); 2124738a96c4SXu Kuohai emit(A64_ADR(A64_LR, AARCH64_INSN_SIZE * 2), ctx); 2125738a96c4SXu Kuohai emit(A64_RET(A64_R(10)), ctx); 2126efc9909fSXu Kuohai /* store return value */ 2127efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 2128efc9909fSXu Kuohai /* reserve a nop for bpf_tramp_image_put */ 212996b0f5adSPuranjay Mohan im->ip_after_call = ctx->ro_image + ctx->idx; 2130efc9909fSXu Kuohai emit(A64_NOP, ctx); 2131efc9909fSXu Kuohai } 2132efc9909fSXu Kuohai 2133efc9909fSXu Kuohai /* update the branches saved in invoke_bpf_mod_ret with cbnz */ 2134efc9909fSXu Kuohai for (i = 0; i < fmod_ret->nr_links && ctx->image != NULL; i++) { 2135efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branches[i]; 2136aada4766SXu Kuohai *branches[i] = cpu_to_le32(A64_CBNZ(1, A64_R(10), offset)); 2137efc9909fSXu Kuohai } 2138efc9909fSXu Kuohai 2139efc9909fSXu Kuohai for (i = 0; i < fexit->nr_links; i++) 2140efc9909fSXu Kuohai invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off, 2141efc9909fSXu Kuohai run_ctx_off, false); 2142efc9909fSXu Kuohai 2143efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 214496b0f5adSPuranjay Mohan im->ip_epilogue = ctx->ro_image + ctx->idx; 2145efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 2146efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_exit, ctx); 2147efc9909fSXu Kuohai } 2148efc9909fSXu Kuohai 2149efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_RESTORE_REGS) 215090564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 2151efc9909fSXu Kuohai 2152efc9909fSXu Kuohai /* restore callee saved register x19 and x20 */ 2153efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx); 2154efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 2155efc9909fSXu Kuohai 2156efc9909fSXu Kuohai if (save_ret) 2157efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(0), A64_SP, retval_off), ctx); 2158efc9909fSXu Kuohai 2159efc9909fSXu Kuohai /* reset SP */ 2160efc9909fSXu Kuohai emit(A64_MOV(1, A64_SP, A64_FP), ctx); 2161efc9909fSXu Kuohai 2162efc9909fSXu Kuohai /* pop frames */ 2163efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 2164efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx); 2165efc9909fSXu Kuohai 2166efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_SKIP_FRAME) { 2167efc9909fSXu Kuohai /* skip patched function, return to parent */ 2168efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 2169efc9909fSXu Kuohai emit(A64_RET(A64_R(9)), ctx); 2170efc9909fSXu Kuohai } else { 2171efc9909fSXu Kuohai /* return to patched function */ 2172efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(10), A64_LR), ctx); 2173efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 2174efc9909fSXu Kuohai emit(A64_RET(A64_R(10)), ctx); 2175efc9909fSXu Kuohai } 2176efc9909fSXu Kuohai 2177efc9909fSXu Kuohai kfree(branches); 2178efc9909fSXu Kuohai 2179efc9909fSXu Kuohai return ctx->idx; 2180efc9909fSXu Kuohai } 2181efc9909fSXu Kuohai 218296d1b7c0SSong Liu static int btf_func_model_nregs(const struct btf_func_model *m) 2183efc9909fSXu Kuohai { 218490564f1eSFlorent Revest int nregs = m->nr_args; 218596d1b7c0SSong Liu int i; 2186efc9909fSXu Kuohai 218790564f1eSFlorent Revest /* extra registers needed for struct argument */ 2188eb707ddeSYonghong Song for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) { 218990564f1eSFlorent Revest /* The arg_size is at most 16 bytes, enforced by the verifier. */ 2190eb707ddeSYonghong Song if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) 219190564f1eSFlorent Revest nregs += (m->arg_size[i] + 7) / 8 - 1; 2192eb707ddeSYonghong Song } 2193eb707ddeSYonghong Song 219496d1b7c0SSong Liu return nregs; 219596d1b7c0SSong Liu } 219696d1b7c0SSong Liu 219796d1b7c0SSong Liu int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, 219896d1b7c0SSong Liu struct bpf_tramp_links *tlinks, void *func_addr) 219996d1b7c0SSong Liu { 220096d1b7c0SSong Liu struct jit_ctx ctx = { 220196d1b7c0SSong Liu .image = NULL, 220296d1b7c0SSong Liu .idx = 0, 220396d1b7c0SSong Liu }; 220496d1b7c0SSong Liu struct bpf_tramp_image im; 220596d1b7c0SSong Liu int nregs, ret; 220696d1b7c0SSong Liu 220796d1b7c0SSong Liu nregs = btf_func_model_nregs(m); 220890564f1eSFlorent Revest /* the first 8 registers are used for arguments */ 220990564f1eSFlorent Revest if (nregs > 8) 221090564f1eSFlorent Revest return -ENOTSUPP; 221190564f1eSFlorent Revest 221296d1b7c0SSong Liu ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, nregs, flags); 2213efc9909fSXu Kuohai if (ret < 0) 2214efc9909fSXu Kuohai return ret; 2215efc9909fSXu Kuohai 221696d1b7c0SSong Liu return ret < 0 ? ret : ret * AARCH64_INSN_SIZE; 221796d1b7c0SSong Liu } 2218efc9909fSXu Kuohai 221996b0f5adSPuranjay Mohan void *arch_alloc_bpf_trampoline(unsigned int size) 222096b0f5adSPuranjay Mohan { 222196b0f5adSPuranjay Mohan return bpf_prog_pack_alloc(size, jit_fill_hole); 222296b0f5adSPuranjay Mohan } 222396b0f5adSPuranjay Mohan 222496b0f5adSPuranjay Mohan void arch_free_bpf_trampoline(void *image, unsigned int size) 222596b0f5adSPuranjay Mohan { 222696b0f5adSPuranjay Mohan bpf_prog_pack_free(image, size); 222796b0f5adSPuranjay Mohan } 222896b0f5adSPuranjay Mohan 2229c733239fSChristophe Leroy int arch_protect_bpf_trampoline(void *image, unsigned int size) 223096b0f5adSPuranjay Mohan { 2231c733239fSChristophe Leroy return 0; 223296b0f5adSPuranjay Mohan } 223396b0f5adSPuranjay Mohan 223496b0f5adSPuranjay Mohan int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image, 223596b0f5adSPuranjay Mohan void *ro_image_end, const struct btf_func_model *m, 223696d1b7c0SSong Liu u32 flags, struct bpf_tramp_links *tlinks, 223796d1b7c0SSong Liu void *func_addr) 223896d1b7c0SSong Liu { 223996d1b7c0SSong Liu int ret, nregs; 224096b0f5adSPuranjay Mohan void *image, *tmp; 224196b0f5adSPuranjay Mohan u32 size = ro_image_end - ro_image; 224296b0f5adSPuranjay Mohan 224396b0f5adSPuranjay Mohan /* image doesn't need to be in module memory range, so we can 224496b0f5adSPuranjay Mohan * use kvmalloc. 224596b0f5adSPuranjay Mohan */ 224696b0f5adSPuranjay Mohan image = kvmalloc(size, GFP_KERNEL); 224796b0f5adSPuranjay Mohan if (!image) 224896b0f5adSPuranjay Mohan return -ENOMEM; 224996b0f5adSPuranjay Mohan 225096d1b7c0SSong Liu struct jit_ctx ctx = { 225196d1b7c0SSong Liu .image = image, 225296b0f5adSPuranjay Mohan .ro_image = ro_image, 225396d1b7c0SSong Liu .idx = 0, 225496d1b7c0SSong Liu }; 225596d1b7c0SSong Liu 225696d1b7c0SSong Liu nregs = btf_func_model_nregs(m); 225796d1b7c0SSong Liu /* the first 8 registers are used for arguments */ 225896d1b7c0SSong Liu if (nregs > 8) 225996d1b7c0SSong Liu return -ENOTSUPP; 226096d1b7c0SSong Liu 226196b0f5adSPuranjay Mohan jit_fill_hole(image, (unsigned int)(ro_image_end - ro_image)); 22627a3d9a15SSong Liu ret = prepare_trampoline(&ctx, im, tlinks, func_addr, nregs, flags); 2263efc9909fSXu Kuohai 226496b0f5adSPuranjay Mohan if (ret > 0 && validate_code(&ctx) < 0) { 2265efc9909fSXu Kuohai ret = -EINVAL; 226696b0f5adSPuranjay Mohan goto out; 226796b0f5adSPuranjay Mohan } 2268efc9909fSXu Kuohai 2269efc9909fSXu Kuohai if (ret > 0) 2270efc9909fSXu Kuohai ret *= AARCH64_INSN_SIZE; 2271efc9909fSXu Kuohai 227296b0f5adSPuranjay Mohan tmp = bpf_arch_text_copy(ro_image, image, size); 227396b0f5adSPuranjay Mohan if (IS_ERR(tmp)) { 227496b0f5adSPuranjay Mohan ret = PTR_ERR(tmp); 227596b0f5adSPuranjay Mohan goto out; 227696b0f5adSPuranjay Mohan } 227796b0f5adSPuranjay Mohan 227896b0f5adSPuranjay Mohan bpf_flush_icache(ro_image, ro_image + size); 227996b0f5adSPuranjay Mohan out: 228096b0f5adSPuranjay Mohan kvfree(image); 2281efc9909fSXu Kuohai return ret; 2282efc9909fSXu Kuohai } 2283efc9909fSXu Kuohai 2284b2ad54e1SXu Kuohai static bool is_long_jump(void *ip, void *target) 2285b2ad54e1SXu Kuohai { 2286b2ad54e1SXu Kuohai long offset; 2287b2ad54e1SXu Kuohai 2288b2ad54e1SXu Kuohai /* NULL target means this is a NOP */ 2289b2ad54e1SXu Kuohai if (!target) 2290b2ad54e1SXu Kuohai return false; 2291b2ad54e1SXu Kuohai 2292b2ad54e1SXu Kuohai offset = (long)target - (long)ip; 2293b2ad54e1SXu Kuohai return offset < -SZ_128M || offset >= SZ_128M; 2294b2ad54e1SXu Kuohai } 2295b2ad54e1SXu Kuohai 2296b2ad54e1SXu Kuohai static int gen_branch_or_nop(enum aarch64_insn_branch_type type, void *ip, 2297b2ad54e1SXu Kuohai void *addr, void *plt, u32 *insn) 2298b2ad54e1SXu Kuohai { 2299b2ad54e1SXu Kuohai void *target; 2300b2ad54e1SXu Kuohai 2301b2ad54e1SXu Kuohai if (!addr) { 2302b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_nop(); 2303b2ad54e1SXu Kuohai return 0; 2304b2ad54e1SXu Kuohai } 2305b2ad54e1SXu Kuohai 2306b2ad54e1SXu Kuohai if (is_long_jump(ip, addr)) 2307b2ad54e1SXu Kuohai target = plt; 2308b2ad54e1SXu Kuohai else 2309b2ad54e1SXu Kuohai target = addr; 2310b2ad54e1SXu Kuohai 2311b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_branch_imm((unsigned long)ip, 2312b2ad54e1SXu Kuohai (unsigned long)target, 2313b2ad54e1SXu Kuohai type); 2314b2ad54e1SXu Kuohai 2315b2ad54e1SXu Kuohai return *insn != AARCH64_BREAK_FAULT ? 0 : -EFAULT; 2316b2ad54e1SXu Kuohai } 2317b2ad54e1SXu Kuohai 2318b2ad54e1SXu Kuohai /* Replace the branch instruction from @ip to @old_addr in a bpf prog or a bpf 2319b2ad54e1SXu Kuohai * trampoline with the branch instruction from @ip to @new_addr. If @old_addr 2320b2ad54e1SXu Kuohai * or @new_addr is NULL, the old or new instruction is NOP. 2321b2ad54e1SXu Kuohai * 2322b2ad54e1SXu Kuohai * When @ip is the bpf prog entry, a bpf trampoline is being attached or 2323b2ad54e1SXu Kuohai * detached. Since bpf trampoline and bpf prog are allocated separately with 2324b2ad54e1SXu Kuohai * vmalloc, the address distance may exceed 128MB, the maximum branch range. 2325b2ad54e1SXu Kuohai * So long jump should be handled. 2326b2ad54e1SXu Kuohai * 2327b2ad54e1SXu Kuohai * When a bpf prog is constructed, a plt pointing to empty trampoline 2328b2ad54e1SXu Kuohai * dummy_tramp is placed at the end: 2329b2ad54e1SXu Kuohai * 2330b2ad54e1SXu Kuohai * bpf_prog: 2331b2ad54e1SXu Kuohai * mov x9, lr 2332b2ad54e1SXu Kuohai * nop // patchsite 2333b2ad54e1SXu Kuohai * ... 2334b2ad54e1SXu Kuohai * ret 2335b2ad54e1SXu Kuohai * 2336b2ad54e1SXu Kuohai * plt: 2337b2ad54e1SXu Kuohai * ldr x10, target 2338b2ad54e1SXu Kuohai * br x10 2339b2ad54e1SXu Kuohai * target: 2340b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2341b2ad54e1SXu Kuohai * 2342b2ad54e1SXu Kuohai * This is also the state when no trampoline is attached. 2343b2ad54e1SXu Kuohai * 2344b2ad54e1SXu Kuohai * When a short-jump bpf trampoline is attached, the patchsite is patched 2345b2ad54e1SXu Kuohai * to a bl instruction to the trampoline directly: 2346b2ad54e1SXu Kuohai * 2347b2ad54e1SXu Kuohai * bpf_prog: 2348b2ad54e1SXu Kuohai * mov x9, lr 2349b2ad54e1SXu Kuohai * bl <short-jump bpf trampoline address> // patchsite 2350b2ad54e1SXu Kuohai * ... 2351b2ad54e1SXu Kuohai * ret 2352b2ad54e1SXu Kuohai * 2353b2ad54e1SXu Kuohai * plt: 2354b2ad54e1SXu Kuohai * ldr x10, target 2355b2ad54e1SXu Kuohai * br x10 2356b2ad54e1SXu Kuohai * target: 2357b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2358b2ad54e1SXu Kuohai * 2359b2ad54e1SXu Kuohai * When a long-jump bpf trampoline is attached, the plt target is filled with 2360b2ad54e1SXu Kuohai * the trampoline address and the patchsite is patched to a bl instruction to 2361b2ad54e1SXu Kuohai * the plt: 2362b2ad54e1SXu Kuohai * 2363b2ad54e1SXu Kuohai * bpf_prog: 2364b2ad54e1SXu Kuohai * mov x9, lr 2365b2ad54e1SXu Kuohai * bl plt // patchsite 2366b2ad54e1SXu Kuohai * ... 2367b2ad54e1SXu Kuohai * ret 2368b2ad54e1SXu Kuohai * 2369b2ad54e1SXu Kuohai * plt: 2370b2ad54e1SXu Kuohai * ldr x10, target 2371b2ad54e1SXu Kuohai * br x10 2372b2ad54e1SXu Kuohai * target: 2373b2ad54e1SXu Kuohai * .quad <long-jump bpf trampoline address> // plt target 2374b2ad54e1SXu Kuohai * 2375b2ad54e1SXu Kuohai * The dummy_tramp is used to prevent another CPU from jumping to unknown 2376b2ad54e1SXu Kuohai * locations during the patching process, making the patching process easier. 2377b2ad54e1SXu Kuohai */ 2378b2ad54e1SXu Kuohai int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, 2379b2ad54e1SXu Kuohai void *old_addr, void *new_addr) 2380b2ad54e1SXu Kuohai { 2381b2ad54e1SXu Kuohai int ret; 2382b2ad54e1SXu Kuohai u32 old_insn; 2383b2ad54e1SXu Kuohai u32 new_insn; 2384b2ad54e1SXu Kuohai u32 replaced; 2385b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 2386b2ad54e1SXu Kuohai unsigned long size = 0UL; 2387b2ad54e1SXu Kuohai unsigned long offset = ~0UL; 2388b2ad54e1SXu Kuohai enum aarch64_insn_branch_type branch_type; 2389b2ad54e1SXu Kuohai char namebuf[KSYM_NAME_LEN]; 2390b2ad54e1SXu Kuohai void *image = NULL; 2391b2ad54e1SXu Kuohai u64 plt_target = 0ULL; 2392b2ad54e1SXu Kuohai bool poking_bpf_entry; 2393b2ad54e1SXu Kuohai 2394b2ad54e1SXu Kuohai if (!__bpf_address_lookup((unsigned long)ip, &size, &offset, namebuf)) 2395b2ad54e1SXu Kuohai /* Only poking bpf text is supported. Since kernel function 2396b2ad54e1SXu Kuohai * entry is set up by ftrace, we reply on ftrace to poke kernel 2397b2ad54e1SXu Kuohai * functions. 2398b2ad54e1SXu Kuohai */ 2399b2ad54e1SXu Kuohai return -ENOTSUPP; 2400b2ad54e1SXu Kuohai 2401b2ad54e1SXu Kuohai image = ip - offset; 2402b2ad54e1SXu Kuohai /* zero offset means we're poking bpf prog entry */ 2403b2ad54e1SXu Kuohai poking_bpf_entry = (offset == 0UL); 2404b2ad54e1SXu Kuohai 2405b2ad54e1SXu Kuohai /* bpf prog entry, find plt and the real patchsite */ 2406b2ad54e1SXu Kuohai if (poking_bpf_entry) { 2407b2ad54e1SXu Kuohai /* plt locates at the end of bpf prog */ 2408b2ad54e1SXu Kuohai plt = image + size - PLT_TARGET_OFFSET; 2409b2ad54e1SXu Kuohai 2410b2ad54e1SXu Kuohai /* skip to the nop instruction in bpf prog entry: 2411b2ad54e1SXu Kuohai * bti c // if BTI enabled 2412b2ad54e1SXu Kuohai * mov x9, x30 2413b2ad54e1SXu Kuohai * nop 2414b2ad54e1SXu Kuohai */ 2415b2ad54e1SXu Kuohai ip = image + POKE_OFFSET * AARCH64_INSN_SIZE; 2416b2ad54e1SXu Kuohai } 2417b2ad54e1SXu Kuohai 2418b2ad54e1SXu Kuohai /* long jump is only possible at bpf prog entry */ 2419b2ad54e1SXu Kuohai if (WARN_ON((is_long_jump(ip, new_addr) || is_long_jump(ip, old_addr)) && 2420b2ad54e1SXu Kuohai !poking_bpf_entry)) 2421b2ad54e1SXu Kuohai return -EINVAL; 2422b2ad54e1SXu Kuohai 2423b2ad54e1SXu Kuohai if (poke_type == BPF_MOD_CALL) 2424b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_LINK; 2425b2ad54e1SXu Kuohai else 2426b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_NOLINK; 2427b2ad54e1SXu Kuohai 2428b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, old_addr, plt, &old_insn) < 0) 2429b2ad54e1SXu Kuohai return -EFAULT; 2430b2ad54e1SXu Kuohai 2431b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, new_addr, plt, &new_insn) < 0) 2432b2ad54e1SXu Kuohai return -EFAULT; 2433b2ad54e1SXu Kuohai 2434b2ad54e1SXu Kuohai if (is_long_jump(ip, new_addr)) 2435b2ad54e1SXu Kuohai plt_target = (u64)new_addr; 2436b2ad54e1SXu Kuohai else if (is_long_jump(ip, old_addr)) 2437b2ad54e1SXu Kuohai /* if the old target is a long jump and the new target is not, 2438b2ad54e1SXu Kuohai * restore the plt target to dummy_tramp, so there is always a 2439b2ad54e1SXu Kuohai * legal and harmless address stored in plt target, and we'll 2440b2ad54e1SXu Kuohai * never jump from plt to an unknown place. 2441b2ad54e1SXu Kuohai */ 2442b2ad54e1SXu Kuohai plt_target = (u64)&dummy_tramp; 2443b2ad54e1SXu Kuohai 2444b2ad54e1SXu Kuohai if (plt_target) { 2445b2ad54e1SXu Kuohai /* non-zero plt_target indicates we're patching a bpf prog, 2446b2ad54e1SXu Kuohai * which is read only. 2447b2ad54e1SXu Kuohai */ 2448b2ad54e1SXu Kuohai if (set_memory_rw(PAGE_MASK & ((uintptr_t)&plt->target), 1)) 2449b2ad54e1SXu Kuohai return -EFAULT; 2450b2ad54e1SXu Kuohai WRITE_ONCE(plt->target, plt_target); 2451b2ad54e1SXu Kuohai set_memory_ro(PAGE_MASK & ((uintptr_t)&plt->target), 1); 2452b2ad54e1SXu Kuohai /* since plt target points to either the new trampoline 2453b2ad54e1SXu Kuohai * or dummy_tramp, even if another CPU reads the old plt 2454b2ad54e1SXu Kuohai * target value before fetching the bl instruction to plt, 2455b2ad54e1SXu Kuohai * it will be brought back by dummy_tramp, so no barrier is 2456b2ad54e1SXu Kuohai * required here. 2457b2ad54e1SXu Kuohai */ 2458b2ad54e1SXu Kuohai } 2459b2ad54e1SXu Kuohai 2460b2ad54e1SXu Kuohai /* if the old target and the new target are both long jumps, no 2461b2ad54e1SXu Kuohai * patching is required 2462b2ad54e1SXu Kuohai */ 2463b2ad54e1SXu Kuohai if (old_insn == new_insn) 2464b2ad54e1SXu Kuohai return 0; 2465b2ad54e1SXu Kuohai 2466b2ad54e1SXu Kuohai mutex_lock(&text_mutex); 2467b2ad54e1SXu Kuohai if (aarch64_insn_read(ip, &replaced)) { 2468b2ad54e1SXu Kuohai ret = -EFAULT; 2469b2ad54e1SXu Kuohai goto out; 2470b2ad54e1SXu Kuohai } 2471b2ad54e1SXu Kuohai 2472b2ad54e1SXu Kuohai if (replaced != old_insn) { 2473b2ad54e1SXu Kuohai ret = -EFAULT; 2474b2ad54e1SXu Kuohai goto out; 2475b2ad54e1SXu Kuohai } 2476b2ad54e1SXu Kuohai 2477b2ad54e1SXu Kuohai /* We call aarch64_insn_patch_text_nosync() to replace instruction 2478b2ad54e1SXu Kuohai * atomically, so no other CPUs will fetch a half-new and half-old 2479b2ad54e1SXu Kuohai * instruction. But there is chance that another CPU executes the 2480b2ad54e1SXu Kuohai * old instruction after the patching operation finishes (e.g., 2481b2ad54e1SXu Kuohai * pipeline not flushed, or icache not synchronized yet). 2482b2ad54e1SXu Kuohai * 2483b2ad54e1SXu Kuohai * 1. when a new trampoline is attached, it is not a problem for 2484b2ad54e1SXu Kuohai * different CPUs to jump to different trampolines temporarily. 2485b2ad54e1SXu Kuohai * 2486b2ad54e1SXu Kuohai * 2. when an old trampoline is freed, we should wait for all other 2487b2ad54e1SXu Kuohai * CPUs to exit the trampoline and make sure the trampoline is no 2488b2ad54e1SXu Kuohai * longer reachable, since bpf_tramp_image_put() function already 2489b2ad54e1SXu Kuohai * uses percpu_ref and task-based rcu to do the sync, no need to call 2490b2ad54e1SXu Kuohai * the sync version here, see bpf_tramp_image_put() for details. 2491b2ad54e1SXu Kuohai */ 2492b2ad54e1SXu Kuohai ret = aarch64_insn_patch_text_nosync(ip, new_insn); 2493b2ad54e1SXu Kuohai out: 2494b2ad54e1SXu Kuohai mutex_unlock(&text_mutex); 2495b2ad54e1SXu Kuohai 2496b2ad54e1SXu Kuohai return ret; 2497b2ad54e1SXu Kuohai } 249818a45f12SHou Tao 249918a45f12SHou Tao bool bpf_jit_supports_ptr_xchg(void) 250018a45f12SHou Tao { 250118a45f12SHou Tao return true; 250218a45f12SHou Tao } 250322fc0e80SPuranjay Mohan 250422fc0e80SPuranjay Mohan bool bpf_jit_supports_exceptions(void) 250522fc0e80SPuranjay Mohan { 250622fc0e80SPuranjay Mohan /* We unwind through both kernel frames starting from within bpf_throw 250722fc0e80SPuranjay Mohan * call and BPF frames. Therefore we require FP unwinder to be enabled 250822fc0e80SPuranjay Mohan * to walk kernel frames and reach BPF frames in the stack trace. 250922fc0e80SPuranjay Mohan * ARM64 kernel is aways compiled with CONFIG_FRAME_POINTER=y 251022fc0e80SPuranjay Mohan */ 251122fc0e80SPuranjay Mohan return true; 251222fc0e80SPuranjay Mohan } 25131dad391dSPuranjay Mohan 25141dad391dSPuranjay Mohan void bpf_jit_free(struct bpf_prog *prog) 25151dad391dSPuranjay Mohan { 25161dad391dSPuranjay Mohan if (prog->jited) { 25171dad391dSPuranjay Mohan struct arm64_jit_data *jit_data = prog->aux->jit_data; 25181dad391dSPuranjay Mohan struct bpf_binary_header *hdr; 25191dad391dSPuranjay Mohan 25201dad391dSPuranjay Mohan /* 25211dad391dSPuranjay Mohan * If we fail the final pass of JIT (from jit_subprogs), 25221dad391dSPuranjay Mohan * the program may not be finalized yet. Call finalize here 25231dad391dSPuranjay Mohan * before freeing it. 25241dad391dSPuranjay Mohan */ 25251dad391dSPuranjay Mohan if (jit_data) { 25261dad391dSPuranjay Mohan bpf_arch_text_copy(&jit_data->ro_header->size, &jit_data->header->size, 25271dad391dSPuranjay Mohan sizeof(jit_data->header->size)); 25281dad391dSPuranjay Mohan kfree(jit_data); 25291dad391dSPuranjay Mohan } 25301dad391dSPuranjay Mohan hdr = bpf_jit_binary_pack_hdr(prog); 25311dad391dSPuranjay Mohan bpf_jit_binary_pack_free(hdr, NULL); 25321dad391dSPuranjay Mohan WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog)); 25331dad391dSPuranjay Mohan } 25341dad391dSPuranjay Mohan 25351dad391dSPuranjay Mohan bpf_prog_unlock_free(prog); 25361dad391dSPuranjay Mohan } 2537