1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2e54bcde3SZi Shen Lim /* 3e54bcde3SZi Shen Lim * BPF JIT compiler for ARM64 4e54bcde3SZi Shen Lim * 542ff712bSZi Shen Lim * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com> 6e54bcde3SZi Shen Lim */ 7e54bcde3SZi Shen Lim 8e54bcde3SZi Shen Lim #define pr_fmt(fmt) "bpf_jit: " fmt 9e54bcde3SZi Shen Lim 1080083428SJean-Philippe Brucker #include <linux/bitfield.h> 11ddb55992SZi Shen Lim #include <linux/bpf.h> 12e54bcde3SZi Shen Lim #include <linux/filter.h> 13b2ad54e1SXu Kuohai #include <linux/memory.h> 14e54bcde3SZi Shen Lim #include <linux/printk.h> 15e54bcde3SZi Shen Lim #include <linux/slab.h> 16b569c1c6SDaniel Borkmann 17d6e2cc56SMark Rutland #include <asm/asm-extable.h> 18e54bcde3SZi Shen Lim #include <asm/byteorder.h> 19e54bcde3SZi Shen Lim #include <asm/cacheflush.h> 20b569c1c6SDaniel Borkmann #include <asm/debug-monitors.h> 213e00e39dSMark Rutland #include <asm/insn.h> 22b2ad54e1SXu Kuohai #include <asm/patching.h> 23d4bbc30bSLaura Abbott #include <asm/set_memory.h> 24e54bcde3SZi Shen Lim 25e54bcde3SZi Shen Lim #include "bpf_jit.h" 26e54bcde3SZi Shen Lim 2726eb042eSDaniel Borkmann #define TMP_REG_1 (MAX_BPF_JIT_REG + 0) 2826eb042eSDaniel Borkmann #define TMP_REG_2 (MAX_BPF_JIT_REG + 1) 29ddb55992SZi Shen Lim #define TCALL_CNT (MAX_BPF_JIT_REG + 2) 307005cadeSDaniel Borkmann #define TMP_REG_3 (MAX_BPF_JIT_REG + 3) 315b3d19b9SXu Kuohai #define FP_BOTTOM (MAX_BPF_JIT_REG + 4) 32339af577SPuranjay Mohan #define ARENA_VM_START (MAX_BPF_JIT_REG + 5) 33e54bcde3SZi Shen Lim 341902472bSHou Tao #define check_imm(bits, imm) do { \ 351902472bSHou Tao if ((((imm) > 0) && ((imm) >> (bits))) || \ 361902472bSHou Tao (((imm) < 0) && (~(imm) >> (bits)))) { \ 371902472bSHou Tao pr_info("[%2d] imm=%d(0x%x) out of range\n", \ 381902472bSHou Tao i, imm, imm); \ 391902472bSHou Tao return -EINVAL; \ 401902472bSHou Tao } \ 411902472bSHou Tao } while (0) 421902472bSHou Tao #define check_imm19(imm) check_imm(19, imm) 431902472bSHou Tao #define check_imm26(imm) check_imm(26, imm) 441902472bSHou Tao 45e54bcde3SZi Shen Lim /* Map BPF registers to A64 registers */ 46e54bcde3SZi Shen Lim static const int bpf2a64[] = { 47e54bcde3SZi Shen Lim /* return value from in-kernel function, and exit value from eBPF */ 48e54bcde3SZi Shen Lim [BPF_REG_0] = A64_R(7), 49e54bcde3SZi Shen Lim /* arguments from eBPF program to in-kernel function */ 50e54bcde3SZi Shen Lim [BPF_REG_1] = A64_R(0), 51e54bcde3SZi Shen Lim [BPF_REG_2] = A64_R(1), 52e54bcde3SZi Shen Lim [BPF_REG_3] = A64_R(2), 53e54bcde3SZi Shen Lim [BPF_REG_4] = A64_R(3), 54e54bcde3SZi Shen Lim [BPF_REG_5] = A64_R(4), 55e54bcde3SZi Shen Lim /* callee saved registers that in-kernel function will preserve */ 56e54bcde3SZi Shen Lim [BPF_REG_6] = A64_R(19), 57e54bcde3SZi Shen Lim [BPF_REG_7] = A64_R(20), 58e54bcde3SZi Shen Lim [BPF_REG_8] = A64_R(21), 59e54bcde3SZi Shen Lim [BPF_REG_9] = A64_R(22), 60e54bcde3SZi Shen Lim /* read-only frame pointer to access stack */ 61ec0738dbSYang Shi [BPF_REG_FP] = A64_R(25), 6206edc59cSChristoph Hellwig /* temporary registers for BPF JIT */ 634c1cd4fdSYang Shi [TMP_REG_1] = A64_R(10), 644c1cd4fdSYang Shi [TMP_REG_2] = A64_R(11), 657005cadeSDaniel Borkmann [TMP_REG_3] = A64_R(12), 66ddb55992SZi Shen Lim /* tail_call_cnt */ 67ddb55992SZi Shen Lim [TCALL_CNT] = A64_R(26), 6826eb042eSDaniel Borkmann /* temporary register for blinding constants */ 6926eb042eSDaniel Borkmann [BPF_REG_AX] = A64_R(9), 705b3d19b9SXu Kuohai [FP_BOTTOM] = A64_R(27), 71339af577SPuranjay Mohan /* callee saved register for kern_vm_start address */ 72339af577SPuranjay Mohan [ARENA_VM_START] = A64_R(28), 73e54bcde3SZi Shen Lim }; 74e54bcde3SZi Shen Lim 75e54bcde3SZi Shen Lim struct jit_ctx { 76e54bcde3SZi Shen Lim const struct bpf_prog *prog; 77e54bcde3SZi Shen Lim int idx; 7851c9fbb1SZi Shen Lim int epilogue_offset; 79e54bcde3SZi Shen Lim int *offset; 8080083428SJean-Philippe Brucker int exentry_idx; 81425e1ed7SLuc Van Oostenryck __le32 *image; 821dad391dSPuranjay Mohan __le32 *ro_image; 83f1c9eed7SDaniel Borkmann u32 stack_size; 845b3d19b9SXu Kuohai int fpb_offset; 854dd31243SPuranjay Mohan u64 user_vm_start; 86e54bcde3SZi Shen Lim }; 87e54bcde3SZi Shen Lim 88b2ad54e1SXu Kuohai struct bpf_plt { 89b2ad54e1SXu Kuohai u32 insn_ldr; /* load target */ 90b2ad54e1SXu Kuohai u32 insn_br; /* branch to target */ 91b2ad54e1SXu Kuohai u64 target; /* target value */ 92b2ad54e1SXu Kuohai }; 93b2ad54e1SXu Kuohai 94b2ad54e1SXu Kuohai #define PLT_TARGET_SIZE sizeof_field(struct bpf_plt, target) 95b2ad54e1SXu Kuohai #define PLT_TARGET_OFFSET offsetof(struct bpf_plt, target) 96b2ad54e1SXu Kuohai 97e54bcde3SZi Shen Lim static inline void emit(const u32 insn, struct jit_ctx *ctx) 98e54bcde3SZi Shen Lim { 99e54bcde3SZi Shen Lim if (ctx->image != NULL) 100e54bcde3SZi Shen Lim ctx->image[ctx->idx] = cpu_to_le32(insn); 101e54bcde3SZi Shen Lim 102e54bcde3SZi Shen Lim ctx->idx++; 103e54bcde3SZi Shen Lim } 104e54bcde3SZi Shen Lim 105e54bcde3SZi Shen Lim static inline void emit_a64_mov_i(const int is64, const int reg, 106e54bcde3SZi Shen Lim const s32 val, struct jit_ctx *ctx) 107e54bcde3SZi Shen Lim { 108e54bcde3SZi Shen Lim u16 hi = val >> 16; 109e54bcde3SZi Shen Lim u16 lo = val & 0xffff; 110e54bcde3SZi Shen Lim 111e54bcde3SZi Shen Lim if (hi & 0x8000) { 112e54bcde3SZi Shen Lim if (hi == 0xffff) { 113e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx); 114e54bcde3SZi Shen Lim } else { 115e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx); 1166d2eea6fSDaniel Borkmann if (lo != 0xffff) 117e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, lo, 0), ctx); 118e54bcde3SZi Shen Lim } 119e54bcde3SZi Shen Lim } else { 120e54bcde3SZi Shen Lim emit(A64_MOVZ(is64, reg, lo, 0), ctx); 121e54bcde3SZi Shen Lim if (hi) 122e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, hi, 16), ctx); 123e54bcde3SZi Shen Lim } 124e54bcde3SZi Shen Lim } 125e54bcde3SZi Shen Lim 1266d2eea6fSDaniel Borkmann static int i64_i16_blocks(const u64 val, bool inverse) 1276d2eea6fSDaniel Borkmann { 1286d2eea6fSDaniel Borkmann return (((val >> 0) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1296d2eea6fSDaniel Borkmann (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1306d2eea6fSDaniel Borkmann (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1316d2eea6fSDaniel Borkmann (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000)); 1326d2eea6fSDaniel Borkmann } 1336d2eea6fSDaniel Borkmann 1346d2eea6fSDaniel Borkmann static inline void emit_a64_mov_i64(const int reg, const u64 val, 1356d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1366d2eea6fSDaniel Borkmann { 1376d2eea6fSDaniel Borkmann u64 nrm_tmp = val, rev_tmp = ~val; 1386d2eea6fSDaniel Borkmann bool inverse; 1396d2eea6fSDaniel Borkmann int shift; 1406d2eea6fSDaniel Borkmann 1416d2eea6fSDaniel Borkmann if (!(nrm_tmp >> 32)) 1426d2eea6fSDaniel Borkmann return emit_a64_mov_i(0, reg, (u32)val, ctx); 1436d2eea6fSDaniel Borkmann 1446d2eea6fSDaniel Borkmann inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false); 1456d2eea6fSDaniel Borkmann shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) : 1466d2eea6fSDaniel Borkmann (fls64(nrm_tmp) - 1)), 16), 0); 1476d2eea6fSDaniel Borkmann if (inverse) 1486d2eea6fSDaniel Borkmann emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx); 1496d2eea6fSDaniel Borkmann else 1506d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1516d2eea6fSDaniel Borkmann shift -= 16; 1526d2eea6fSDaniel Borkmann while (shift >= 0) { 1536d2eea6fSDaniel Borkmann if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000)) 1546d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1556d2eea6fSDaniel Borkmann shift -= 16; 1566d2eea6fSDaniel Borkmann } 1576d2eea6fSDaniel Borkmann } 1586d2eea6fSDaniel Borkmann 159b2ad54e1SXu Kuohai static inline void emit_bti(u32 insn, struct jit_ctx *ctx) 160b2ad54e1SXu Kuohai { 161b2ad54e1SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) 162b2ad54e1SXu Kuohai emit(insn, ctx); 163b2ad54e1SXu Kuohai } 164b2ad54e1SXu Kuohai 1656d2eea6fSDaniel Borkmann /* 166cc2b8ed1SArd Biesheuvel * Kernel addresses in the vmalloc space use at most 48 bits, and the 167cc2b8ed1SArd Biesheuvel * remaining bits are guaranteed to be 0x1. So we can compose the address 168cc2b8ed1SArd Biesheuvel * with a fixed length movn/movk/movk sequence. 1696d2eea6fSDaniel Borkmann */ 1706d2eea6fSDaniel Borkmann static inline void emit_addr_mov_i64(const int reg, const u64 val, 1716d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1726d2eea6fSDaniel Borkmann { 1736d2eea6fSDaniel Borkmann u64 tmp = val; 1746d2eea6fSDaniel Borkmann int shift = 0; 1756d2eea6fSDaniel Borkmann 176cc2b8ed1SArd Biesheuvel emit(A64_MOVN(1, reg, ~tmp & 0xffff, shift), ctx); 177cc2b8ed1SArd Biesheuvel while (shift < 32) { 1786d2eea6fSDaniel Borkmann tmp >>= 16; 1796d2eea6fSDaniel Borkmann shift += 16; 1806d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx); 1816d2eea6fSDaniel Borkmann } 1826d2eea6fSDaniel Borkmann } 1836d2eea6fSDaniel Borkmann 184efc9909fSXu Kuohai static inline void emit_call(u64 target, struct jit_ctx *ctx) 185efc9909fSXu Kuohai { 186efc9909fSXu Kuohai u8 tmp = bpf2a64[TMP_REG_1]; 187efc9909fSXu Kuohai 188efc9909fSXu Kuohai emit_addr_mov_i64(tmp, target, ctx); 189efc9909fSXu Kuohai emit(A64_BLR(tmp), ctx); 190efc9909fSXu Kuohai } 191efc9909fSXu Kuohai 19232f6865cSIlias Apalodimas static inline int bpf2a64_offset(int bpf_insn, int off, 193e54bcde3SZi Shen Lim const struct jit_ctx *ctx) 194e54bcde3SZi Shen Lim { 19532f6865cSIlias Apalodimas /* BPF JMP offset is relative to the next instruction */ 19632f6865cSIlias Apalodimas bpf_insn++; 19732f6865cSIlias Apalodimas /* 19832f6865cSIlias Apalodimas * Whereas arm64 branch instructions encode the offset 19932f6865cSIlias Apalodimas * from the branch itself, so we must subtract 1 from the 20032f6865cSIlias Apalodimas * instruction offset. 20132f6865cSIlias Apalodimas */ 20232f6865cSIlias Apalodimas return ctx->offset[bpf_insn + off] - (ctx->offset[bpf_insn] - 1); 203e54bcde3SZi Shen Lim } 204e54bcde3SZi Shen Lim 205b569c1c6SDaniel Borkmann static void jit_fill_hole(void *area, unsigned int size) 206b569c1c6SDaniel Borkmann { 207425e1ed7SLuc Van Oostenryck __le32 *ptr; 208b569c1c6SDaniel Borkmann /* We are guaranteed to have aligned memory. */ 209b569c1c6SDaniel Borkmann for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 210b569c1c6SDaniel Borkmann *ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT); 211b569c1c6SDaniel Borkmann } 212b569c1c6SDaniel Borkmann 2131dad391dSPuranjay Mohan int bpf_arch_text_invalidate(void *dst, size_t len) 2141dad391dSPuranjay Mohan { 2151dad391dSPuranjay Mohan if (!aarch64_insn_set(dst, AARCH64_BREAK_FAULT, len)) 2161dad391dSPuranjay Mohan return -EINVAL; 2171dad391dSPuranjay Mohan 2181dad391dSPuranjay Mohan return 0; 2191dad391dSPuranjay Mohan } 2201dad391dSPuranjay Mohan 221e54bcde3SZi Shen Lim static inline int epilogue_offset(const struct jit_ctx *ctx) 222e54bcde3SZi Shen Lim { 22351c9fbb1SZi Shen Lim int to = ctx->epilogue_offset; 22451c9fbb1SZi Shen Lim int from = ctx->idx; 225e54bcde3SZi Shen Lim 226e54bcde3SZi Shen Lim return to - from; 227e54bcde3SZi Shen Lim } 228e54bcde3SZi Shen Lim 229fd868f14SLuke Nelson static bool is_addsub_imm(u32 imm) 230fd868f14SLuke Nelson { 231fd868f14SLuke Nelson /* Either imm12 or shifted imm12. */ 232fd868f14SLuke Nelson return !(imm & ~0xfff) || !(imm & ~0xfff000); 233fd868f14SLuke Nelson } 234fd868f14SLuke Nelson 2357db6c0f1SXu Kuohai /* 2367db6c0f1SXu Kuohai * There are 3 types of AArch64 LDR/STR (immediate) instruction: 2377db6c0f1SXu Kuohai * Post-index, Pre-index, Unsigned offset. 2387db6c0f1SXu Kuohai * 2397db6c0f1SXu Kuohai * For BPF ldr/str, the "unsigned offset" type is sufficient. 2407db6c0f1SXu Kuohai * 2417db6c0f1SXu Kuohai * "Unsigned offset" type LDR(immediate) format: 2427db6c0f1SXu Kuohai * 2437db6c0f1SXu Kuohai * 3 2 1 0 2447db6c0f1SXu Kuohai * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 2457db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2467db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 1| imm12 | Rn | Rt | 2477db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2487db6c0f1SXu Kuohai * scale 2497db6c0f1SXu Kuohai * 2507db6c0f1SXu Kuohai * "Unsigned offset" type STR(immediate) format: 2517db6c0f1SXu Kuohai * 3 2 1 0 2527db6c0f1SXu Kuohai * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 2537db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2547db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 0| imm12 | Rn | Rt | 2557db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2567db6c0f1SXu Kuohai * scale 2577db6c0f1SXu Kuohai * 2587db6c0f1SXu Kuohai * The offset is calculated from imm12 and scale in the following way: 2597db6c0f1SXu Kuohai * 2607db6c0f1SXu Kuohai * offset = (u64)imm12 << scale 2617db6c0f1SXu Kuohai */ 2625b3d19b9SXu Kuohai static bool is_lsi_offset(int offset, int scale) 2637db6c0f1SXu Kuohai { 2647db6c0f1SXu Kuohai if (offset < 0) 2657db6c0f1SXu Kuohai return false; 2667db6c0f1SXu Kuohai 2677db6c0f1SXu Kuohai if (offset > (0xFFF << scale)) 2687db6c0f1SXu Kuohai return false; 2697db6c0f1SXu Kuohai 2707db6c0f1SXu Kuohai if (offset & ((1 << scale) - 1)) 2717db6c0f1SXu Kuohai return false; 2727db6c0f1SXu Kuohai 2737db6c0f1SXu Kuohai return true; 2747db6c0f1SXu Kuohai } 2757db6c0f1SXu Kuohai 276b2ad54e1SXu Kuohai /* generated prologue: 277b2ad54e1SXu Kuohai * bti c // if CONFIG_ARM64_BTI_KERNEL 278b2ad54e1SXu Kuohai * mov x9, lr 279b2ad54e1SXu Kuohai * nop // POKE_OFFSET 280b2ad54e1SXu Kuohai * paciasp // if CONFIG_ARM64_PTR_AUTH_KERNEL 281b2ad54e1SXu Kuohai * stp x29, lr, [sp, #-16]! 282b2ad54e1SXu Kuohai * mov x29, sp 283b2ad54e1SXu Kuohai * stp x19, x20, [sp, #-16]! 284b2ad54e1SXu Kuohai * stp x21, x22, [sp, #-16]! 285b2ad54e1SXu Kuohai * stp x25, x26, [sp, #-16]! 286b2ad54e1SXu Kuohai * stp x27, x28, [sp, #-16]! 287b2ad54e1SXu Kuohai * mov x25, sp 288b2ad54e1SXu Kuohai * mov tcc, #0 289b2ad54e1SXu Kuohai * // PROLOGUE_OFFSET 290b2ad54e1SXu Kuohai */ 291b2ad54e1SXu Kuohai 292b2ad54e1SXu Kuohai #define BTI_INSNS (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) ? 1 : 0) 293b2ad54e1SXu Kuohai #define PAC_INSNS (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) ? 1 : 0) 294b2ad54e1SXu Kuohai 295b2ad54e1SXu Kuohai /* Offset of nop instruction in bpf prog entry to be poked */ 296b2ad54e1SXu Kuohai #define POKE_OFFSET (BTI_INSNS + 1) 297b2ad54e1SXu Kuohai 298a2284d91SDaniel Borkmann /* Tail call offset to jump into */ 299b2ad54e1SXu Kuohai #define PROLOGUE_OFFSET (BTI_INSNS + 2 + PAC_INSNS + 8) 300ddb55992SZi Shen Lim 30122fc0e80SPuranjay Mohan static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf, 302339af577SPuranjay Mohan bool is_exception_cb, u64 arena_vm_start) 303e54bcde3SZi Shen Lim { 304f1c9eed7SDaniel Borkmann const struct bpf_prog *prog = ctx->prog; 3059af27da6SKumar Kartikeya Dwivedi const bool is_main_prog = !bpf_is_subprog(prog); 306e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 307e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 308e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 309e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 310e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 311ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 3125b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 313339af577SPuranjay Mohan const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; 314ddb55992SZi Shen Lim const int idx0 = ctx->idx; 315ddb55992SZi Shen Lim int cur_offset; 316e54bcde3SZi Shen Lim 317ec0738dbSYang Shi /* 318ec0738dbSYang Shi * BPF prog stack layout 319ec0738dbSYang Shi * 320ec0738dbSYang Shi * high 321ec0738dbSYang Shi * original A64_SP => 0:+-----+ BPF prologue 322ec0738dbSYang Shi * |FP/LR| 323ec0738dbSYang Shi * current A64_FP => -16:+-----+ 324ec0738dbSYang Shi * | ... | callee saved registers 3254c1cd4fdSYang Shi * BPF fp register => -64:+-----+ <= (BPF_FP) 326ec0738dbSYang Shi * | | 327ec0738dbSYang Shi * | ... | BPF prog stack 328ec0738dbSYang Shi * | | 329f1c9eed7SDaniel Borkmann * +-----+ <= (BPF_FP - prog->aux->stack_depth) 33009ece3d0SDaniel Borkmann * |RSVD | padding 331f1c9eed7SDaniel Borkmann * current A64_SP => +-----+ <= (BPF_FP - ctx->stack_size) 332ec0738dbSYang Shi * | | 333ec0738dbSYang Shi * | ... | Function call stack 334ec0738dbSYang Shi * | | 335ec0738dbSYang Shi * +-----+ 336ec0738dbSYang Shi * low 337ec0738dbSYang Shi * 338ec0738dbSYang Shi */ 339ec0738dbSYang Shi 340a3f25d61SAlexander Duyck /* bpf function may be invoked by 3 instruction types: 341a3f25d61SAlexander Duyck * 1. bl, attached via freplace to bpf prog via short jump 342a3f25d61SAlexander Duyck * 2. br, attached via freplace to bpf prog via long jump 343a3f25d61SAlexander Duyck * 3. blr, working as a function pointer, used by emit_call. 344a3f25d61SAlexander Duyck * So BTI_JC should used here to support both br and blr. 345a3f25d61SAlexander Duyck */ 346a3f25d61SAlexander Duyck emit_bti(A64_BTI_JC, ctx); 347b2ad54e1SXu Kuohai 348b2ad54e1SXu Kuohai emit(A64_MOV(1, A64_R(9), A64_LR), ctx); 349b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 350b2ad54e1SXu Kuohai 35122fc0e80SPuranjay Mohan if (!is_exception_cb) { 352042152c2SXu Kuohai /* Sign lr */ 353042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 354042152c2SXu Kuohai emit(A64_PACIASP, ctx); 355ec0738dbSYang Shi /* Save FP and LR registers to stay align with ARM64 AAPCS */ 356ec0738dbSYang Shi emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 357ec0738dbSYang Shi emit(A64_MOV(1, A64_FP, A64_SP), ctx); 358ec0738dbSYang Shi 359ddb55992SZi Shen Lim /* Save callee-saved registers */ 360e54bcde3SZi Shen Lim emit(A64_PUSH(r6, r7, A64_SP), ctx); 361e54bcde3SZi Shen Lim emit(A64_PUSH(r8, r9, A64_SP), ctx); 362ddb55992SZi Shen Lim emit(A64_PUSH(fp, tcc, A64_SP), ctx); 3635b3d19b9SXu Kuohai emit(A64_PUSH(fpb, A64_R(28), A64_SP), ctx); 36422fc0e80SPuranjay Mohan } else { 36522fc0e80SPuranjay Mohan /* 36622fc0e80SPuranjay Mohan * Exception callback receives FP of Main Program as third 36722fc0e80SPuranjay Mohan * parameter 36822fc0e80SPuranjay Mohan */ 36922fc0e80SPuranjay Mohan emit(A64_MOV(1, A64_FP, A64_R(2)), ctx); 37022fc0e80SPuranjay Mohan /* 37122fc0e80SPuranjay Mohan * Main Program already pushed the frame record and the 37222fc0e80SPuranjay Mohan * callee-saved registers. The exception callback will not push 37322fc0e80SPuranjay Mohan * anything and re-use the main program's stack. 37422fc0e80SPuranjay Mohan * 37522fc0e80SPuranjay Mohan * 10 registers are on the stack 37622fc0e80SPuranjay Mohan */ 37722fc0e80SPuranjay Mohan emit(A64_SUB_I(1, A64_SP, A64_FP, 80), ctx); 37822fc0e80SPuranjay Mohan } 379e54bcde3SZi Shen Lim 380ddb55992SZi Shen Lim /* Set up BPF prog stack base register */ 381e54bcde3SZi Shen Lim emit(A64_MOV(1, fp, A64_SP), ctx); 382e54bcde3SZi Shen Lim 383d4609a5dSJakub Sitnicki if (!ebpf_from_cbpf && is_main_prog) { 384ddb55992SZi Shen Lim /* Initialize tail_call_cnt */ 385ddb55992SZi Shen Lim emit(A64_MOVZ(1, tcc, 0, 0), ctx); 386ddb55992SZi Shen Lim 387ddb55992SZi Shen Lim cur_offset = ctx->idx - idx0; 388ddb55992SZi Shen Lim if (cur_offset != PROLOGUE_OFFSET) { 389ddb55992SZi Shen Lim pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 390ddb55992SZi Shen Lim cur_offset, PROLOGUE_OFFSET); 391ddb55992SZi Shen Lim return -1; 392ddb55992SZi Shen Lim } 393fa76cfe6SMark Brown 394fa76cfe6SMark Brown /* BTI landing pad for the tail call, done with a BR */ 395b2ad54e1SXu Kuohai emit_bti(A64_BTI_J, ctx); 39656ea6a8bSDaniel Borkmann } 397a2284d91SDaniel Borkmann 39822fc0e80SPuranjay Mohan /* 39922fc0e80SPuranjay Mohan * Program acting as exception boundary should save all ARM64 40022fc0e80SPuranjay Mohan * Callee-saved registers as the exception callback needs to recover 40122fc0e80SPuranjay Mohan * all ARM64 Callee-saved registers in its epilogue. 40222fc0e80SPuranjay Mohan */ 40322fc0e80SPuranjay Mohan if (prog->aux->exception_boundary) { 40422fc0e80SPuranjay Mohan /* 40522fc0e80SPuranjay Mohan * As we are pushing two more registers, BPF_FP should be moved 40622fc0e80SPuranjay Mohan * 16 bytes 40722fc0e80SPuranjay Mohan */ 40822fc0e80SPuranjay Mohan emit(A64_SUB_I(1, fp, fp, 16), ctx); 40922fc0e80SPuranjay Mohan emit(A64_PUSH(A64_R(23), A64_R(24), A64_SP), ctx); 41022fc0e80SPuranjay Mohan } 41122fc0e80SPuranjay Mohan 4125b3d19b9SXu Kuohai emit(A64_SUB_I(1, fpb, fp, ctx->fpb_offset), ctx); 4135b3d19b9SXu Kuohai 4143f287098STiezhu Yang /* Stack must be multiples of 16B */ 4153f287098STiezhu Yang ctx->stack_size = round_up(prog->aux->stack_depth, 16); 416a2284d91SDaniel Borkmann 417a2284d91SDaniel Borkmann /* Set up function call stack */ 418a2284d91SDaniel Borkmann emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 419339af577SPuranjay Mohan 420339af577SPuranjay Mohan if (arena_vm_start) 421339af577SPuranjay Mohan emit_a64_mov_i64(arena_vm_base, arena_vm_start, ctx); 422339af577SPuranjay Mohan 423ddb55992SZi Shen Lim return 0; 424ddb55992SZi Shen Lim } 425ddb55992SZi Shen Lim 426ddb55992SZi Shen Lim static int out_offset = -1; /* initialized on the first pass of build_body() */ 427ddb55992SZi Shen Lim static int emit_bpf_tail_call(struct jit_ctx *ctx) 428ddb55992SZi Shen Lim { 429ddb55992SZi Shen Lim /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 430ddb55992SZi Shen Lim const u8 r2 = bpf2a64[BPF_REG_2]; 431ddb55992SZi Shen Lim const u8 r3 = bpf2a64[BPF_REG_3]; 432ddb55992SZi Shen Lim 433ddb55992SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 434ddb55992SZi Shen Lim const u8 prg = bpf2a64[TMP_REG_2]; 435ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 436ddb55992SZi Shen Lim const int idx0 = ctx->idx; 437ddb55992SZi Shen Lim #define cur_offset (ctx->idx - idx0) 438ddb55992SZi Shen Lim #define jmp_offset (out_offset - (cur_offset)) 439ddb55992SZi Shen Lim size_t off; 440ddb55992SZi Shen Lim 441ddb55992SZi Shen Lim /* if (index >= array->map.max_entries) 442ddb55992SZi Shen Lim * goto out; 443ddb55992SZi Shen Lim */ 444ddb55992SZi Shen Lim off = offsetof(struct bpf_array, map.max_entries); 445ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 446ddb55992SZi Shen Lim emit(A64_LDR32(tmp, r2, tmp), ctx); 44716338a9bSDaniel Borkmann emit(A64_MOV(0, r3, r3), ctx); 448ddb55992SZi Shen Lim emit(A64_CMP(0, r3, tmp), ctx); 44916338a9bSDaniel Borkmann emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 450ddb55992SZi Shen Lim 451ebf7f6f0STiezhu Yang /* 452ebf7f6f0STiezhu Yang * if (tail_call_cnt >= MAX_TAIL_CALL_CNT) 453ddb55992SZi Shen Lim * goto out; 454ddb55992SZi Shen Lim * tail_call_cnt++; 455ddb55992SZi Shen Lim */ 456ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 457ddb55992SZi Shen Lim emit(A64_CMP(1, tcc, tmp), ctx); 458ebf7f6f0STiezhu Yang emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 459ddb55992SZi Shen Lim emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 460ddb55992SZi Shen Lim 461ddb55992SZi Shen Lim /* prog = array->ptrs[index]; 462ddb55992SZi Shen Lim * if (prog == NULL) 463ddb55992SZi Shen Lim * goto out; 464ddb55992SZi Shen Lim */ 465ddb55992SZi Shen Lim off = offsetof(struct bpf_array, ptrs); 466ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 467d8b54110SDaniel Borkmann emit(A64_ADD(1, tmp, r2, tmp), ctx); 468d8b54110SDaniel Borkmann emit(A64_LSL(1, prg, r3, 3), ctx); 469d8b54110SDaniel Borkmann emit(A64_LDR64(prg, tmp, prg), ctx); 470ddb55992SZi Shen Lim emit(A64_CBZ(1, prg, jmp_offset), ctx); 471ddb55992SZi Shen Lim 472a2284d91SDaniel Borkmann /* goto *(prog->bpf_func + prologue_offset); */ 473ddb55992SZi Shen Lim off = offsetof(struct bpf_prog, bpf_func); 474ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 475ddb55992SZi Shen Lim emit(A64_LDR64(tmp, prg, tmp), ctx); 476ddb55992SZi Shen Lim emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 477a2284d91SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 478ddb55992SZi Shen Lim emit(A64_BR(tmp), ctx); 479ddb55992SZi Shen Lim 480ddb55992SZi Shen Lim /* out: */ 481ddb55992SZi Shen Lim if (out_offset == -1) 482ddb55992SZi Shen Lim out_offset = cur_offset; 483ddb55992SZi Shen Lim if (cur_offset != out_offset) { 484ddb55992SZi Shen Lim pr_err_once("tail_call out_offset = %d, expected %d!\n", 485ddb55992SZi Shen Lim cur_offset, out_offset); 486ddb55992SZi Shen Lim return -1; 487ddb55992SZi Shen Lim } 488ddb55992SZi Shen Lim return 0; 489ddb55992SZi Shen Lim #undef cur_offset 490ddb55992SZi Shen Lim #undef jmp_offset 491e54bcde3SZi Shen Lim } 492e54bcde3SZi Shen Lim 4931902472bSHou Tao #ifdef CONFIG_ARM64_LSE_ATOMICS 4941902472bSHou Tao static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 4951902472bSHou Tao { 4961902472bSHou Tao const u8 code = insn->code; 497e612b5c1SPuranjay Mohan const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; 4981902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 4991902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 5001902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 5011902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 5021902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 503e612b5c1SPuranjay Mohan const bool arena = BPF_MODE(code) == BPF_PROBE_ATOMIC; 5041902472bSHou Tao const s16 off = insn->off; 505e612b5c1SPuranjay Mohan u8 reg = dst; 5061902472bSHou Tao 507e612b5c1SPuranjay Mohan if (off || arena) { 508e612b5c1SPuranjay Mohan if (off) { 5091902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 5101902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 5111902472bSHou Tao reg = tmp; 5121902472bSHou Tao } 513e612b5c1SPuranjay Mohan if (arena) { 514e612b5c1SPuranjay Mohan emit(A64_ADD(1, tmp, reg, arena_vm_base), ctx); 515e612b5c1SPuranjay Mohan reg = tmp; 516e612b5c1SPuranjay Mohan } 517e612b5c1SPuranjay Mohan } 5181902472bSHou Tao 5191902472bSHou Tao switch (insn->imm) { 5201902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 5211902472bSHou Tao case BPF_ADD: 5221902472bSHou Tao emit(A64_STADD(isdw, reg, src), ctx); 5231902472bSHou Tao break; 5241902472bSHou Tao case BPF_AND: 5251902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 5261902472bSHou Tao emit(A64_STCLR(isdw, reg, tmp2), ctx); 5271902472bSHou Tao break; 5281902472bSHou Tao case BPF_OR: 5291902472bSHou Tao emit(A64_STSET(isdw, reg, src), ctx); 5301902472bSHou Tao break; 5311902472bSHou Tao case BPF_XOR: 5321902472bSHou Tao emit(A64_STEOR(isdw, reg, src), ctx); 5331902472bSHou Tao break; 5341902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 5351902472bSHou Tao case BPF_ADD | BPF_FETCH: 5361902472bSHou Tao emit(A64_LDADDAL(isdw, src, reg, src), ctx); 5371902472bSHou Tao break; 5381902472bSHou Tao case BPF_AND | BPF_FETCH: 5391902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 5401902472bSHou Tao emit(A64_LDCLRAL(isdw, src, reg, tmp2), ctx); 5411902472bSHou Tao break; 5421902472bSHou Tao case BPF_OR | BPF_FETCH: 5431902472bSHou Tao emit(A64_LDSETAL(isdw, src, reg, src), ctx); 5441902472bSHou Tao break; 5451902472bSHou Tao case BPF_XOR | BPF_FETCH: 5461902472bSHou Tao emit(A64_LDEORAL(isdw, src, reg, src), ctx); 5471902472bSHou Tao break; 5481902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 5491902472bSHou Tao case BPF_XCHG: 5501902472bSHou Tao emit(A64_SWPAL(isdw, src, reg, src), ctx); 5511902472bSHou Tao break; 5521902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 5531902472bSHou Tao case BPF_CMPXCHG: 5541902472bSHou Tao emit(A64_CASAL(isdw, src, reg, bpf2a64[BPF_REG_0]), ctx); 5551902472bSHou Tao break; 5561902472bSHou Tao default: 5571902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", insn->imm); 5581902472bSHou Tao return -EINVAL; 5591902472bSHou Tao } 5601902472bSHou Tao 5611902472bSHou Tao return 0; 5621902472bSHou Tao } 5631902472bSHou Tao #else 5641902472bSHou Tao static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5651902472bSHou Tao { 5661902472bSHou Tao return -EINVAL; 5671902472bSHou Tao } 5681902472bSHou Tao #endif 5691902472bSHou Tao 5701902472bSHou Tao static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5711902472bSHou Tao { 5721902472bSHou Tao const u8 code = insn->code; 5731902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 5741902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 5751902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 5761902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 5771902472bSHou Tao const u8 tmp3 = bpf2a64[TMP_REG_3]; 5781902472bSHou Tao const int i = insn - ctx->prog->insnsi; 5791902472bSHou Tao const s32 imm = insn->imm; 5801902472bSHou Tao const s16 off = insn->off; 5811902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 5821902472bSHou Tao u8 reg; 5831902472bSHou Tao s32 jmp_offset; 5841902472bSHou Tao 585e612b5c1SPuranjay Mohan if (BPF_MODE(code) == BPF_PROBE_ATOMIC) { 586e612b5c1SPuranjay Mohan /* ll_sc based atomics don't support unsafe pointers yet. */ 587e612b5c1SPuranjay Mohan pr_err_once("unknown atomic opcode %02x\n", code); 588e612b5c1SPuranjay Mohan return -EINVAL; 589e612b5c1SPuranjay Mohan } 590e612b5c1SPuranjay Mohan 5911902472bSHou Tao if (!off) { 5921902472bSHou Tao reg = dst; 5931902472bSHou Tao } else { 5941902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 5951902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 5961902472bSHou Tao reg = tmp; 5971902472bSHou Tao } 5981902472bSHou Tao 5991902472bSHou Tao if (imm == BPF_ADD || imm == BPF_AND || 6001902472bSHou Tao imm == BPF_OR || imm == BPF_XOR) { 6011902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 6021902472bSHou Tao emit(A64_LDXR(isdw, tmp2, reg), ctx); 6031902472bSHou Tao if (imm == BPF_ADD) 6041902472bSHou Tao emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 6051902472bSHou Tao else if (imm == BPF_AND) 6061902472bSHou Tao emit(A64_AND(isdw, tmp2, tmp2, src), ctx); 6071902472bSHou Tao else if (imm == BPF_OR) 6081902472bSHou Tao emit(A64_ORR(isdw, tmp2, tmp2, src), ctx); 6091902472bSHou Tao else 6101902472bSHou Tao emit(A64_EOR(isdw, tmp2, tmp2, src), ctx); 6111902472bSHou Tao emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); 6121902472bSHou Tao jmp_offset = -3; 6131902472bSHou Tao check_imm19(jmp_offset); 6141902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6151902472bSHou Tao } else if (imm == (BPF_ADD | BPF_FETCH) || 6161902472bSHou Tao imm == (BPF_AND | BPF_FETCH) || 6171902472bSHou Tao imm == (BPF_OR | BPF_FETCH) || 6181902472bSHou Tao imm == (BPF_XOR | BPF_FETCH)) { 6191902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 6201902472bSHou Tao const u8 ax = bpf2a64[BPF_REG_AX]; 6211902472bSHou Tao 6221902472bSHou Tao emit(A64_MOV(isdw, ax, src), ctx); 6231902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 6241902472bSHou Tao if (imm == (BPF_ADD | BPF_FETCH)) 6251902472bSHou Tao emit(A64_ADD(isdw, tmp2, src, ax), ctx); 6261902472bSHou Tao else if (imm == (BPF_AND | BPF_FETCH)) 6271902472bSHou Tao emit(A64_AND(isdw, tmp2, src, ax), ctx); 6281902472bSHou Tao else if (imm == (BPF_OR | BPF_FETCH)) 6291902472bSHou Tao emit(A64_ORR(isdw, tmp2, src, ax), ctx); 6301902472bSHou Tao else 6311902472bSHou Tao emit(A64_EOR(isdw, tmp2, src, ax), ctx); 6321902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 6331902472bSHou Tao jmp_offset = -3; 6341902472bSHou Tao check_imm19(jmp_offset); 6351902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6361902472bSHou Tao emit(A64_DMB_ISH, ctx); 6371902472bSHou Tao } else if (imm == BPF_XCHG) { 6381902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 6391902472bSHou Tao emit(A64_MOV(isdw, tmp2, src), ctx); 6401902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 6411902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 6421902472bSHou Tao jmp_offset = -2; 6431902472bSHou Tao check_imm19(jmp_offset); 6441902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6451902472bSHou Tao emit(A64_DMB_ISH, ctx); 6461902472bSHou Tao } else if (imm == BPF_CMPXCHG) { 6471902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 6481902472bSHou Tao const u8 r0 = bpf2a64[BPF_REG_0]; 6491902472bSHou Tao 6501902472bSHou Tao emit(A64_MOV(isdw, tmp2, r0), ctx); 6511902472bSHou Tao emit(A64_LDXR(isdw, r0, reg), ctx); 6521902472bSHou Tao emit(A64_EOR(isdw, tmp3, r0, tmp2), ctx); 6531902472bSHou Tao jmp_offset = 4; 6541902472bSHou Tao check_imm19(jmp_offset); 6551902472bSHou Tao emit(A64_CBNZ(isdw, tmp3, jmp_offset), ctx); 6561902472bSHou Tao emit(A64_STLXR(isdw, src, reg, tmp3), ctx); 6571902472bSHou Tao jmp_offset = -4; 6581902472bSHou Tao check_imm19(jmp_offset); 6591902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6601902472bSHou Tao emit(A64_DMB_ISH, ctx); 6611902472bSHou Tao } else { 6621902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", imm); 6631902472bSHou Tao return -EINVAL; 6641902472bSHou Tao } 6651902472bSHou Tao 6661902472bSHou Tao return 0; 6671902472bSHou Tao } 6681902472bSHou Tao 669b2ad54e1SXu Kuohai void dummy_tramp(void); 670b2ad54e1SXu Kuohai 671b2ad54e1SXu Kuohai asm ( 672b2ad54e1SXu Kuohai " .pushsection .text, \"ax\", @progbits\n" 67333f32e50SNathan Chancellor " .global dummy_tramp\n" 674b2ad54e1SXu Kuohai " .type dummy_tramp, %function\n" 675b2ad54e1SXu Kuohai "dummy_tramp:" 676b2ad54e1SXu Kuohai #if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) 677b2ad54e1SXu Kuohai " bti j\n" /* dummy_tramp is called via "br x10" */ 678b2ad54e1SXu Kuohai #endif 679339ed900SXu Kuohai " mov x10, x30\n" 680339ed900SXu Kuohai " mov x30, x9\n" 681b2ad54e1SXu Kuohai " ret x10\n" 682b2ad54e1SXu Kuohai " .size dummy_tramp, .-dummy_tramp\n" 683b2ad54e1SXu Kuohai " .popsection\n" 684b2ad54e1SXu Kuohai ); 685b2ad54e1SXu Kuohai 686b2ad54e1SXu Kuohai /* build a plt initialized like this: 687b2ad54e1SXu Kuohai * 688b2ad54e1SXu Kuohai * plt: 689b2ad54e1SXu Kuohai * ldr tmp, target 690b2ad54e1SXu Kuohai * br tmp 691b2ad54e1SXu Kuohai * target: 692b2ad54e1SXu Kuohai * .quad dummy_tramp 693b2ad54e1SXu Kuohai * 694b2ad54e1SXu Kuohai * when a long jump trampoline is attached, target is filled with the 695b2ad54e1SXu Kuohai * trampoline address, and when the trampoline is removed, target is 696b2ad54e1SXu Kuohai * restored to dummy_tramp address. 697b2ad54e1SXu Kuohai */ 698b2ad54e1SXu Kuohai static void build_plt(struct jit_ctx *ctx) 699b2ad54e1SXu Kuohai { 700b2ad54e1SXu Kuohai const u8 tmp = bpf2a64[TMP_REG_1]; 701b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 702b2ad54e1SXu Kuohai 703b2ad54e1SXu Kuohai /* make sure target is 64-bit aligned */ 704b2ad54e1SXu Kuohai if ((ctx->idx + PLT_TARGET_OFFSET / AARCH64_INSN_SIZE) % 2) 705b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 706b2ad54e1SXu Kuohai 707b2ad54e1SXu Kuohai plt = (struct bpf_plt *)(ctx->image + ctx->idx); 708b2ad54e1SXu Kuohai /* plt is called via bl, no BTI needed here */ 709b2ad54e1SXu Kuohai emit(A64_LDR64LIT(tmp, 2 * AARCH64_INSN_SIZE), ctx); 710b2ad54e1SXu Kuohai emit(A64_BR(tmp), ctx); 711b2ad54e1SXu Kuohai 712b2ad54e1SXu Kuohai if (ctx->image) 713b2ad54e1SXu Kuohai plt->target = (u64)&dummy_tramp; 714b2ad54e1SXu Kuohai } 715b2ad54e1SXu Kuohai 71622fc0e80SPuranjay Mohan static void build_epilogue(struct jit_ctx *ctx, bool is_exception_cb) 717e54bcde3SZi Shen Lim { 718e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 719e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 720e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 721e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 722e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 723e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 7245b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 725e54bcde3SZi Shen Lim 726e54bcde3SZi Shen Lim /* We're done with BPF stack */ 727f1c9eed7SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 728e54bcde3SZi Shen Lim 72922fc0e80SPuranjay Mohan /* 73022fc0e80SPuranjay Mohan * Program acting as exception boundary pushes R23 and R24 in addition 73122fc0e80SPuranjay Mohan * to BPF callee-saved registers. Exception callback uses the boundary 73222fc0e80SPuranjay Mohan * program's stack frame, so recover these extra registers in the above 73322fc0e80SPuranjay Mohan * two cases. 73422fc0e80SPuranjay Mohan */ 73522fc0e80SPuranjay Mohan if (ctx->prog->aux->exception_boundary || is_exception_cb) 73622fc0e80SPuranjay Mohan emit(A64_POP(A64_R(23), A64_R(24), A64_SP), ctx); 73722fc0e80SPuranjay Mohan 7385b3d19b9SXu Kuohai /* Restore x27 and x28 */ 7395b3d19b9SXu Kuohai emit(A64_POP(fpb, A64_R(28), A64_SP), ctx); 740ec0738dbSYang Shi /* Restore fs (x25) and x26 */ 741ec0738dbSYang Shi emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 742ec0738dbSYang Shi 743e54bcde3SZi Shen Lim /* Restore callee-saved register */ 744e54bcde3SZi Shen Lim emit(A64_POP(r8, r9, A64_SP), ctx); 745e54bcde3SZi Shen Lim emit(A64_POP(r6, r7, A64_SP), ctx); 746e54bcde3SZi Shen Lim 747ec0738dbSYang Shi /* Restore FP/LR registers */ 748ec0738dbSYang Shi emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 749e54bcde3SZi Shen Lim 750e54bcde3SZi Shen Lim /* Set return value */ 751e54bcde3SZi Shen Lim emit(A64_MOV(1, A64_R(0), r0), ctx); 752e54bcde3SZi Shen Lim 753042152c2SXu Kuohai /* Authenticate lr */ 754042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 755042152c2SXu Kuohai emit(A64_AUTIASP, ctx); 756042152c2SXu Kuohai 757e54bcde3SZi Shen Lim emit(A64_RET(A64_LR), ctx); 758e54bcde3SZi Shen Lim } 759e54bcde3SZi Shen Lim 76080083428SJean-Philippe Brucker #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) 76180083428SJean-Philippe Brucker #define BPF_FIXUP_REG_MASK GENMASK(31, 27) 762339af577SPuranjay Mohan #define DONT_CLEAR 5 /* Unused ARM64 register from BPF's POV */ 76380083428SJean-Philippe Brucker 764d6e2cc56SMark Rutland bool ex_handler_bpf(const struct exception_table_entry *ex, 76580083428SJean-Philippe Brucker struct pt_regs *regs) 76680083428SJean-Philippe Brucker { 76780083428SJean-Philippe Brucker off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup); 76880083428SJean-Philippe Brucker int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup); 76980083428SJean-Philippe Brucker 770339af577SPuranjay Mohan if (dst_reg != DONT_CLEAR) 77180083428SJean-Philippe Brucker regs->regs[dst_reg] = 0; 77280083428SJean-Philippe Brucker regs->pc = (unsigned long)&ex->fixup - offset; 773e8c328d7SMark Rutland return true; 77480083428SJean-Philippe Brucker } 77580083428SJean-Philippe Brucker 77680083428SJean-Philippe Brucker /* For accesses to BTF pointers, add an entry to the exception table */ 77780083428SJean-Philippe Brucker static int add_exception_handler(const struct bpf_insn *insn, 77880083428SJean-Philippe Brucker struct jit_ctx *ctx, 77980083428SJean-Philippe Brucker int dst_reg) 78080083428SJean-Philippe Brucker { 7811dad391dSPuranjay Mohan off_t ins_offset; 7821dad391dSPuranjay Mohan off_t fixup_offset; 78380083428SJean-Philippe Brucker unsigned long pc; 78480083428SJean-Philippe Brucker struct exception_table_entry *ex; 78580083428SJean-Philippe Brucker 78680083428SJean-Philippe Brucker if (!ctx->image) 78780083428SJean-Philippe Brucker /* First pass */ 78880083428SJean-Philippe Brucker return 0; 78980083428SJean-Philippe Brucker 790cc88f540SXu Kuohai if (BPF_MODE(insn->code) != BPF_PROBE_MEM && 791339af577SPuranjay Mohan BPF_MODE(insn->code) != BPF_PROBE_MEMSX && 792e612b5c1SPuranjay Mohan BPF_MODE(insn->code) != BPF_PROBE_MEM32 && 793e612b5c1SPuranjay Mohan BPF_MODE(insn->code) != BPF_PROBE_ATOMIC) 79480083428SJean-Philippe Brucker return 0; 79580083428SJean-Philippe Brucker 79680083428SJean-Philippe Brucker if (!ctx->prog->aux->extable || 79780083428SJean-Philippe Brucker WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries)) 79880083428SJean-Philippe Brucker return -EINVAL; 79980083428SJean-Philippe Brucker 80080083428SJean-Philippe Brucker ex = &ctx->prog->aux->extable[ctx->exentry_idx]; 8011dad391dSPuranjay Mohan pc = (unsigned long)&ctx->ro_image[ctx->idx - 1]; 80280083428SJean-Philippe Brucker 8031dad391dSPuranjay Mohan /* 8041dad391dSPuranjay Mohan * This is the relative offset of the instruction that may fault from 8051dad391dSPuranjay Mohan * the exception table itself. This will be written to the exception 8061dad391dSPuranjay Mohan * table and if this instruction faults, the destination register will 8071dad391dSPuranjay Mohan * be set to '0' and the execution will jump to the next instruction. 8081dad391dSPuranjay Mohan */ 8091dad391dSPuranjay Mohan ins_offset = pc - (long)&ex->insn; 8101dad391dSPuranjay Mohan if (WARN_ON_ONCE(ins_offset >= 0 || ins_offset < INT_MIN)) 81180083428SJean-Philippe Brucker return -ERANGE; 81280083428SJean-Philippe Brucker 81380083428SJean-Philippe Brucker /* 81480083428SJean-Philippe Brucker * Since the extable follows the program, the fixup offset is always 81580083428SJean-Philippe Brucker * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value 81680083428SJean-Philippe Brucker * to keep things simple, and put the destination register in the upper 81780083428SJean-Philippe Brucker * bits. We don't need to worry about buildtime or runtime sort 81880083428SJean-Philippe Brucker * modifying the upper bits because the table is already sorted, and 81980083428SJean-Philippe Brucker * isn't part of the main exception table. 8201dad391dSPuranjay Mohan * 8211dad391dSPuranjay Mohan * The fixup_offset is set to the next instruction from the instruction 8221dad391dSPuranjay Mohan * that may fault. The execution will jump to this after handling the 8231dad391dSPuranjay Mohan * fault. 82480083428SJean-Philippe Brucker */ 8251dad391dSPuranjay Mohan fixup_offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE); 8261dad391dSPuranjay Mohan if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, fixup_offset)) 82780083428SJean-Philippe Brucker return -ERANGE; 82880083428SJean-Philippe Brucker 8291dad391dSPuranjay Mohan /* 8301dad391dSPuranjay Mohan * The offsets above have been calculated using the RO buffer but we 8311dad391dSPuranjay Mohan * need to use the R/W buffer for writes. 8321dad391dSPuranjay Mohan * switch ex to rw buffer for writing. 8331dad391dSPuranjay Mohan */ 8341dad391dSPuranjay Mohan ex = (void *)ctx->image + ((void *)ex - (void *)ctx->ro_image); 8351dad391dSPuranjay Mohan 8361dad391dSPuranjay Mohan ex->insn = ins_offset; 8371dad391dSPuranjay Mohan 838339af577SPuranjay Mohan if (BPF_CLASS(insn->code) != BPF_LDX) 839339af577SPuranjay Mohan dst_reg = DONT_CLEAR; 840339af577SPuranjay Mohan 8411dad391dSPuranjay Mohan ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, fixup_offset) | 84280083428SJean-Philippe Brucker FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); 84380083428SJean-Philippe Brucker 844d6e2cc56SMark Rutland ex->type = EX_TYPE_BPF; 845d6e2cc56SMark Rutland 84680083428SJean-Philippe Brucker ctx->exentry_idx++; 84780083428SJean-Philippe Brucker return 0; 84880083428SJean-Philippe Brucker } 84980083428SJean-Philippe Brucker 85030d3d94cSZi Shen Lim /* JITs an eBPF instruction. 85130d3d94cSZi Shen Lim * Returns: 85230d3d94cSZi Shen Lim * 0 - successfully JITed an 8-byte eBPF instruction. 85330d3d94cSZi Shen Lim * >0 - successfully JITed a 16-byte eBPF instruction. 85430d3d94cSZi Shen Lim * <0 - failed to JIT. 85530d3d94cSZi Shen Lim */ 8568c11ea5cSDaniel Borkmann static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, 8578c11ea5cSDaniel Borkmann bool extra_pass) 858e54bcde3SZi Shen Lim { 859e54bcde3SZi Shen Lim const u8 code = insn->code; 860339af577SPuranjay Mohan u8 dst = bpf2a64[insn->dst_reg]; 861339af577SPuranjay Mohan u8 src = bpf2a64[insn->src_reg]; 862e54bcde3SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 863e54bcde3SZi Shen Lim const u8 tmp2 = bpf2a64[TMP_REG_2]; 8645b3d19b9SXu Kuohai const u8 fp = bpf2a64[BPF_REG_FP]; 8655b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 866339af577SPuranjay Mohan const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; 867e54bcde3SZi Shen Lim const s16 off = insn->off; 868e54bcde3SZi Shen Lim const s32 imm = insn->imm; 869e54bcde3SZi Shen Lim const int i = insn - ctx->prog->insnsi; 870654b65a0SJiong Wang const bool is64 = BPF_CLASS(code) == BPF_ALU64 || 871654b65a0SJiong Wang BPF_CLASS(code) == BPF_JMP; 8721902472bSHou Tao u8 jmp_cond; 873e54bcde3SZi Shen Lim s32 jmp_offset; 874fd49591cSLuke Nelson u32 a64_insn; 8755b3d19b9SXu Kuohai u8 src_adj; 8765b3d19b9SXu Kuohai u8 dst_adj; 8775b3d19b9SXu Kuohai int off_adj; 87880083428SJean-Philippe Brucker int ret; 879cc88f540SXu Kuohai bool sign_extend; 880e54bcde3SZi Shen Lim 881e54bcde3SZi Shen Lim switch (code) { 882e54bcde3SZi Shen Lim /* dst = src */ 883e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_X: 884e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_X: 8854dd31243SPuranjay Mohan if (insn_is_cast_user(insn)) { 8864dd31243SPuranjay Mohan emit(A64_MOV(0, tmp, src), ctx); // 32-bit mov clears the upper 32 bits 8874dd31243SPuranjay Mohan emit_a64_mov_i(0, dst, ctx->user_vm_start >> 32, ctx); 8884dd31243SPuranjay Mohan emit(A64_LSL(1, dst, dst, 32), ctx); 8894dd31243SPuranjay Mohan emit(A64_CBZ(1, tmp, 2), ctx); 8904dd31243SPuranjay Mohan emit(A64_ORR(1, tmp, dst, tmp), ctx); 8914dd31243SPuranjay Mohan emit(A64_MOV(1, dst, tmp), ctx); 8924dd31243SPuranjay Mohan break; 8937a4c3222SPuranjay Mohan } else if (insn_is_mov_percpu_addr(insn)) { 8947a4c3222SPuranjay Mohan if (dst != src) 8957a4c3222SPuranjay Mohan emit(A64_MOV(1, dst, src), ctx); 8967a4c3222SPuranjay Mohan if (cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN)) 8977a4c3222SPuranjay Mohan emit(A64_MRS_TPIDR_EL2(tmp), ctx); 8987a4c3222SPuranjay Mohan else 8997a4c3222SPuranjay Mohan emit(A64_MRS_TPIDR_EL1(tmp), ctx); 9007a4c3222SPuranjay Mohan emit(A64_ADD(1, dst, dst, tmp), ctx); 9017a4c3222SPuranjay Mohan break; 9024dd31243SPuranjay Mohan } 903bb0a1d6bSXu Kuohai switch (insn->off) { 904bb0a1d6bSXu Kuohai case 0: 905e54bcde3SZi Shen Lim emit(A64_MOV(is64, dst, src), ctx); 906e54bcde3SZi Shen Lim break; 907bb0a1d6bSXu Kuohai case 8: 908bb0a1d6bSXu Kuohai emit(A64_SXTB(is64, dst, src), ctx); 909bb0a1d6bSXu Kuohai break; 910bb0a1d6bSXu Kuohai case 16: 911bb0a1d6bSXu Kuohai emit(A64_SXTH(is64, dst, src), ctx); 912bb0a1d6bSXu Kuohai break; 913bb0a1d6bSXu Kuohai case 32: 914bb0a1d6bSXu Kuohai emit(A64_SXTW(is64, dst, src), ctx); 915bb0a1d6bSXu Kuohai break; 916bb0a1d6bSXu Kuohai } 917bb0a1d6bSXu Kuohai break; 918e54bcde3SZi Shen Lim /* dst = dst OP src */ 919e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_X: 920e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_X: 921e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, src), ctx); 922e54bcde3SZi Shen Lim break; 923e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_X: 924e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_X: 925e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, src), ctx); 926e54bcde3SZi Shen Lim break; 927e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_X: 928e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_X: 929e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, src), ctx); 930e54bcde3SZi Shen Lim break; 931e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_X: 932e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_X: 933e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, src), ctx); 934e54bcde3SZi Shen Lim break; 935e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_X: 936e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_X: 937e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, src), ctx); 938e54bcde3SZi Shen Lim break; 939e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_X: 940e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_X: 941e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, src), ctx); 942e54bcde3SZi Shen Lim break; 943e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_X: 944e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_X: 94568b18191SXu Kuohai if (!off) 946e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, src), ctx); 94768b18191SXu Kuohai else 94868b18191SXu Kuohai emit(A64_SDIV(is64, dst, dst, src), ctx); 949e54bcde3SZi Shen Lim break; 950119220d8STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 951119220d8STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 95268b18191SXu Kuohai if (!off) 953e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, src), ctx); 95468b18191SXu Kuohai else 95568b18191SXu Kuohai emit(A64_SDIV(is64, tmp, dst, src), ctx); 956504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, src), ctx); 957e54bcde3SZi Shen Lim break; 958d65a634aSZi Shen Lim case BPF_ALU | BPF_LSH | BPF_X: 959d65a634aSZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_X: 960d65a634aSZi Shen Lim emit(A64_LSLV(is64, dst, dst, src), ctx); 961d65a634aSZi Shen Lim break; 962d65a634aSZi Shen Lim case BPF_ALU | BPF_RSH | BPF_X: 963d65a634aSZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_X: 964d65a634aSZi Shen Lim emit(A64_LSRV(is64, dst, dst, src), ctx); 965d65a634aSZi Shen Lim break; 966d65a634aSZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_X: 967d65a634aSZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_X: 968d65a634aSZi Shen Lim emit(A64_ASRV(is64, dst, dst, src), ctx); 969d65a634aSZi Shen Lim break; 970e54bcde3SZi Shen Lim /* dst = -dst */ 971e54bcde3SZi Shen Lim case BPF_ALU | BPF_NEG: 972e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_NEG: 973e54bcde3SZi Shen Lim emit(A64_NEG(is64, dst, dst), ctx); 974e54bcde3SZi Shen Lim break; 975e54bcde3SZi Shen Lim /* dst = BSWAP##imm(dst) */ 976e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_LE: 977e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_BE: 9781104247fSXu Kuohai case BPF_ALU64 | BPF_END | BPF_FROM_LE: 979e54bcde3SZi Shen Lim #ifdef CONFIG_CPU_BIG_ENDIAN 9801104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_BE) 981d63903bbSXi Wang goto emit_bswap_uxt; 982e54bcde3SZi Shen Lim #else /* !CONFIG_CPU_BIG_ENDIAN */ 9831104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_LE) 984d63903bbSXi Wang goto emit_bswap_uxt; 985e54bcde3SZi Shen Lim #endif 986e54bcde3SZi Shen Lim switch (imm) { 987e54bcde3SZi Shen Lim case 16: 988e54bcde3SZi Shen Lim emit(A64_REV16(is64, dst, dst), ctx); 989d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 990d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 991e54bcde3SZi Shen Lim break; 992e54bcde3SZi Shen Lim case 32: 993a51cd6bfSArtem Savkov emit(A64_REV32(0, dst, dst), ctx); 994d63903bbSXi Wang /* upper 32 bits already cleared */ 995e54bcde3SZi Shen Lim break; 996e54bcde3SZi Shen Lim case 64: 997e54bcde3SZi Shen Lim emit(A64_REV64(dst, dst), ctx); 998e54bcde3SZi Shen Lim break; 999e54bcde3SZi Shen Lim } 1000e54bcde3SZi Shen Lim break; 1001d63903bbSXi Wang emit_bswap_uxt: 1002d63903bbSXi Wang switch (imm) { 1003d63903bbSXi Wang case 16: 1004d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 1005d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 1006d63903bbSXi Wang break; 1007d63903bbSXi Wang case 32: 1008d63903bbSXi Wang /* zero-extend 32 bits into 64 bits */ 1009d63903bbSXi Wang emit(A64_UXTW(is64, dst, dst), ctx); 1010d63903bbSXi Wang break; 1011d63903bbSXi Wang case 64: 1012d63903bbSXi Wang /* nop */ 1013d63903bbSXi Wang break; 1014d63903bbSXi Wang } 1015d63903bbSXi Wang break; 1016e54bcde3SZi Shen Lim /* dst = imm */ 1017e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_K: 1018e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_K: 1019e54bcde3SZi Shen Lim emit_a64_mov_i(is64, dst, imm, ctx); 1020e54bcde3SZi Shen Lim break; 1021e54bcde3SZi Shen Lim /* dst = dst OP imm */ 1022e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_K: 1023e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_K: 1024fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1025fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, imm), ctx); 1026fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1027fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, -imm), ctx); 1028fd868f14SLuke Nelson } else { 1029e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1030e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, tmp), ctx); 1031fd868f14SLuke Nelson } 1032e54bcde3SZi Shen Lim break; 1033e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_K: 1034e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_K: 1035fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1036fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, imm), ctx); 1037fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1038fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, -imm), ctx); 1039fd868f14SLuke Nelson } else { 1040e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1041e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 1042fd868f14SLuke Nelson } 1043e54bcde3SZi Shen Lim break; 1044e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_K: 1045e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_K: 1046fd49591cSLuke Nelson a64_insn = A64_AND_I(is64, dst, dst, imm); 1047fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1048fd49591cSLuke Nelson emit(a64_insn, ctx); 1049fd49591cSLuke Nelson } else { 1050e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1051e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, tmp), ctx); 1052fd49591cSLuke Nelson } 1053e54bcde3SZi Shen Lim break; 1054e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_K: 1055e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_K: 1056fd49591cSLuke Nelson a64_insn = A64_ORR_I(is64, dst, dst, imm); 1057fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1058fd49591cSLuke Nelson emit(a64_insn, ctx); 1059fd49591cSLuke Nelson } else { 1060e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1061e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, tmp), ctx); 1062fd49591cSLuke Nelson } 1063e54bcde3SZi Shen Lim break; 1064e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_K: 1065e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_K: 1066fd49591cSLuke Nelson a64_insn = A64_EOR_I(is64, dst, dst, imm); 1067fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1068fd49591cSLuke Nelson emit(a64_insn, ctx); 1069fd49591cSLuke Nelson } else { 1070e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1071e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, tmp), ctx); 1072fd49591cSLuke Nelson } 1073e54bcde3SZi Shen Lim break; 1074e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_K: 1075e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_K: 1076e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1077e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, tmp), ctx); 1078e54bcde3SZi Shen Lim break; 1079e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_K: 1080e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_K: 1081e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 108268b18191SXu Kuohai if (!off) 1083e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, tmp), ctx); 108468b18191SXu Kuohai else 108568b18191SXu Kuohai emit(A64_SDIV(is64, dst, dst, tmp), ctx); 1086e54bcde3SZi Shen Lim break; 1087e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_K: 1088e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_K: 1089e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp2, imm, ctx); 109068b18191SXu Kuohai if (!off) 1091e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 109268b18191SXu Kuohai else 109368b18191SXu Kuohai emit(A64_SDIV(is64, tmp, dst, tmp2), ctx); 1094504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, tmp2), ctx); 1095e54bcde3SZi Shen Lim break; 1096e54bcde3SZi Shen Lim case BPF_ALU | BPF_LSH | BPF_K: 1097e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_K: 1098e54bcde3SZi Shen Lim emit(A64_LSL(is64, dst, dst, imm), ctx); 1099e54bcde3SZi Shen Lim break; 1100e54bcde3SZi Shen Lim case BPF_ALU | BPF_RSH | BPF_K: 1101e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_K: 1102e54bcde3SZi Shen Lim emit(A64_LSR(is64, dst, dst, imm), ctx); 1103e54bcde3SZi Shen Lim break; 1104e54bcde3SZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_K: 1105e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_K: 1106e54bcde3SZi Shen Lim emit(A64_ASR(is64, dst, dst, imm), ctx); 1107e54bcde3SZi Shen Lim break; 1108e54bcde3SZi Shen Lim 1109e54bcde3SZi Shen Lim /* JUMP off */ 1110e54bcde3SZi Shen Lim case BPF_JMP | BPF_JA: 1111c32b6ee5SXu Kuohai case BPF_JMP32 | BPF_JA: 1112c32b6ee5SXu Kuohai if (BPF_CLASS(code) == BPF_JMP) 111332f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 1114c32b6ee5SXu Kuohai else 1115c32b6ee5SXu Kuohai jmp_offset = bpf2a64_offset(i, imm, ctx); 1116e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1117e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1118e54bcde3SZi Shen Lim break; 1119e54bcde3SZi Shen Lim /* IF (dst COND src) JUMP off */ 1120e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_X: 1121e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_X: 1122c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_X: 1123e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_X: 1124c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_X: 1125e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_X: 1126e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_X: 1127c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_X: 1128e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_X: 1129c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_X: 1130654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_X: 1131654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_X: 1132654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_X: 1133654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_X: 1134654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_X: 1135654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_X: 1136654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_X: 1137654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_X: 1138654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_X: 1139654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_X: 1140654b65a0SJiong Wang emit(A64_CMP(is64, dst, src), ctx); 1141e54bcde3SZi Shen Lim emit_cond_jmp: 114232f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 1143e54bcde3SZi Shen Lim check_imm19(jmp_offset); 1144e54bcde3SZi Shen Lim switch (BPF_OP(code)) { 1145e54bcde3SZi Shen Lim case BPF_JEQ: 1146e54bcde3SZi Shen Lim jmp_cond = A64_COND_EQ; 1147e54bcde3SZi Shen Lim break; 1148e54bcde3SZi Shen Lim case BPF_JGT: 1149e54bcde3SZi Shen Lim jmp_cond = A64_COND_HI; 1150e54bcde3SZi Shen Lim break; 1151c362b2f3SDaniel Borkmann case BPF_JLT: 1152c362b2f3SDaniel Borkmann jmp_cond = A64_COND_CC; 1153c362b2f3SDaniel Borkmann break; 1154e54bcde3SZi Shen Lim case BPF_JGE: 1155e54bcde3SZi Shen Lim jmp_cond = A64_COND_CS; 1156e54bcde3SZi Shen Lim break; 1157c362b2f3SDaniel Borkmann case BPF_JLE: 1158c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LS; 1159c362b2f3SDaniel Borkmann break; 116098397fc5SZi Shen Lim case BPF_JSET: 1161e54bcde3SZi Shen Lim case BPF_JNE: 1162e54bcde3SZi Shen Lim jmp_cond = A64_COND_NE; 1163e54bcde3SZi Shen Lim break; 1164e54bcde3SZi Shen Lim case BPF_JSGT: 1165e54bcde3SZi Shen Lim jmp_cond = A64_COND_GT; 1166e54bcde3SZi Shen Lim break; 1167c362b2f3SDaniel Borkmann case BPF_JSLT: 1168c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LT; 1169c362b2f3SDaniel Borkmann break; 1170e54bcde3SZi Shen Lim case BPF_JSGE: 1171e54bcde3SZi Shen Lim jmp_cond = A64_COND_GE; 1172e54bcde3SZi Shen Lim break; 1173c362b2f3SDaniel Borkmann case BPF_JSLE: 1174c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LE; 1175c362b2f3SDaniel Borkmann break; 1176e54bcde3SZi Shen Lim default: 1177e54bcde3SZi Shen Lim return -EFAULT; 1178e54bcde3SZi Shen Lim } 1179e54bcde3SZi Shen Lim emit(A64_B_(jmp_cond, jmp_offset), ctx); 1180e54bcde3SZi Shen Lim break; 1181e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_X: 1182654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_X: 1183654b65a0SJiong Wang emit(A64_TST(is64, dst, src), ctx); 1184e54bcde3SZi Shen Lim goto emit_cond_jmp; 1185e54bcde3SZi Shen Lim /* IF (dst COND imm) JUMP off */ 1186e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_K: 1187e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_K: 1188c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_K: 1189e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_K: 1190c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_K: 1191e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_K: 1192e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_K: 1193c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_K: 1194e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_K: 1195c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_K: 1196654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_K: 1197654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_K: 1198654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_K: 1199654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_K: 1200654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_K: 1201654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_K: 1202654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_K: 1203654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_K: 1204654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_K: 1205654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_K: 1206fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1207fd868f14SLuke Nelson emit(A64_CMP_I(is64, dst, imm), ctx); 1208fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1209fd868f14SLuke Nelson emit(A64_CMN_I(is64, dst, -imm), ctx); 1210fd868f14SLuke Nelson } else { 1211654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1212654b65a0SJiong Wang emit(A64_CMP(is64, dst, tmp), ctx); 1213fd868f14SLuke Nelson } 1214e54bcde3SZi Shen Lim goto emit_cond_jmp; 1215e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_K: 1216654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_K: 1217fd49591cSLuke Nelson a64_insn = A64_TST_I(is64, dst, imm); 1218fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1219fd49591cSLuke Nelson emit(a64_insn, ctx); 1220fd49591cSLuke Nelson } else { 1221654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1222654b65a0SJiong Wang emit(A64_TST(is64, dst, tmp), ctx); 1223fd49591cSLuke Nelson } 1224e54bcde3SZi Shen Lim goto emit_cond_jmp; 1225e54bcde3SZi Shen Lim /* function call */ 1226e54bcde3SZi Shen Lim case BPF_JMP | BPF_CALL: 1227e54bcde3SZi Shen Lim { 1228e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 12298c11ea5cSDaniel Borkmann bool func_addr_fixed; 12308c11ea5cSDaniel Borkmann u64 func_addr; 1231*75fe4c0bSPuranjay Mohan u32 cpu_offset; 1232*75fe4c0bSPuranjay Mohan 1233*75fe4c0bSPuranjay Mohan /* Implement helper call to bpf_get_smp_processor_id() inline */ 1234*75fe4c0bSPuranjay Mohan if (insn->src_reg == 0 && insn->imm == BPF_FUNC_get_smp_processor_id) { 1235*75fe4c0bSPuranjay Mohan cpu_offset = offsetof(struct thread_info, cpu); 1236*75fe4c0bSPuranjay Mohan 1237*75fe4c0bSPuranjay Mohan emit(A64_MRS_SP_EL0(tmp), ctx); 1238*75fe4c0bSPuranjay Mohan if (is_lsi_offset(cpu_offset, 2)) { 1239*75fe4c0bSPuranjay Mohan emit(A64_LDR32I(r0, tmp, cpu_offset), ctx); 1240*75fe4c0bSPuranjay Mohan } else { 1241*75fe4c0bSPuranjay Mohan emit_a64_mov_i(1, tmp2, cpu_offset, ctx); 1242*75fe4c0bSPuranjay Mohan emit(A64_LDR32(r0, tmp, tmp2), ctx); 1243*75fe4c0bSPuranjay Mohan } 1244*75fe4c0bSPuranjay Mohan break; 1245*75fe4c0bSPuranjay Mohan } 1246e54bcde3SZi Shen Lim 12478c11ea5cSDaniel Borkmann ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 12488c11ea5cSDaniel Borkmann &func_addr, &func_addr_fixed); 12498c11ea5cSDaniel Borkmann if (ret < 0) 12508c11ea5cSDaniel Borkmann return ret; 1251efc9909fSXu Kuohai emit_call(func_addr, ctx); 1252e54bcde3SZi Shen Lim emit(A64_MOV(1, r0, A64_R(0)), ctx); 1253e54bcde3SZi Shen Lim break; 1254e54bcde3SZi Shen Lim } 1255ddb55992SZi Shen Lim /* tail call */ 125671189fa9SAlexei Starovoitov case BPF_JMP | BPF_TAIL_CALL: 1257ddb55992SZi Shen Lim if (emit_bpf_tail_call(ctx)) 1258ddb55992SZi Shen Lim return -EFAULT; 1259ddb55992SZi Shen Lim break; 1260e54bcde3SZi Shen Lim /* function return */ 1261e54bcde3SZi Shen Lim case BPF_JMP | BPF_EXIT: 126251c9fbb1SZi Shen Lim /* Optimization: when last instruction is EXIT, 126351c9fbb1SZi Shen Lim simply fallthrough to epilogue. */ 1264e54bcde3SZi Shen Lim if (i == ctx->prog->len - 1) 1265e54bcde3SZi Shen Lim break; 1266e54bcde3SZi Shen Lim jmp_offset = epilogue_offset(ctx); 1267e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1268e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1269e54bcde3SZi Shen Lim break; 1270e54bcde3SZi Shen Lim 127130d3d94cSZi Shen Lim /* dst = imm64 */ 127230d3d94cSZi Shen Lim case BPF_LD | BPF_IMM | BPF_DW: 127330d3d94cSZi Shen Lim { 127430d3d94cSZi Shen Lim const struct bpf_insn insn1 = insn[1]; 127530d3d94cSZi Shen Lim u64 imm64; 127630d3d94cSZi Shen Lim 12771e4df6b7SXi Wang imm64 = (u64)insn1.imm << 32 | (u32)imm; 1278e4a41c2cSHou Tao if (bpf_pseudo_func(insn)) 1279e4a41c2cSHou Tao emit_addr_mov_i64(dst, imm64, ctx); 1280e4a41c2cSHou Tao else 128130d3d94cSZi Shen Lim emit_a64_mov_i64(dst, imm64, ctx); 128230d3d94cSZi Shen Lim 128330d3d94cSZi Shen Lim return 1; 128430d3d94cSZi Shen Lim } 128530d3d94cSZi Shen Lim 1286cc88f540SXu Kuohai /* LDX: dst = (u64)*(unsigned size *)(src + off) */ 1287e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_W: 1288e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_H: 1289e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_B: 1290e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_DW: 129180083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 129280083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_W: 129380083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_H: 129480083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_B: 1295cc88f540SXu Kuohai /* LDXS: dst_reg = (s64)*(signed size *)(src_reg + off) */ 1296cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_B: 1297cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_H: 1298cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_W: 1299cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_B: 1300cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_H: 1301cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_W: 1302339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_B: 1303339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_H: 1304339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_W: 1305339af577SPuranjay Mohan case BPF_LDX | BPF_PROBE_MEM32 | BPF_DW: 1306339af577SPuranjay Mohan if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) { 1307339af577SPuranjay Mohan emit(A64_ADD(1, tmp2, src, arena_vm_base), ctx); 1308339af577SPuranjay Mohan src = tmp2; 1309339af577SPuranjay Mohan } 1310339af577SPuranjay Mohan if (ctx->fpb_offset > 0 && src == fp && BPF_MODE(insn->code) != BPF_PROBE_MEM32) { 13115b3d19b9SXu Kuohai src_adj = fpb; 13125b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 13135b3d19b9SXu Kuohai } else { 13145b3d19b9SXu Kuohai src_adj = src; 13155b3d19b9SXu Kuohai off_adj = off; 13165b3d19b9SXu Kuohai } 1317cc88f540SXu Kuohai sign_extend = (BPF_MODE(insn->code) == BPF_MEMSX || 1318cc88f540SXu Kuohai BPF_MODE(insn->code) == BPF_PROBE_MEMSX); 1319e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1320e54bcde3SZi Shen Lim case BPF_W: 13215b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 1322cc88f540SXu Kuohai if (sign_extend) 1323cc88f540SXu Kuohai emit(A64_LDRSWI(dst, src_adj, off_adj), ctx); 1324cc88f540SXu Kuohai else 13255b3d19b9SXu Kuohai emit(A64_LDR32I(dst, src_adj, off_adj), ctx); 13267db6c0f1SXu Kuohai } else { 13277db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1328cc88f540SXu Kuohai if (sign_extend) 1329114b5b3bSPuranjay Mohan emit(A64_LDRSW(dst, src, tmp), ctx); 1330cc88f540SXu Kuohai else 1331e54bcde3SZi Shen Lim emit(A64_LDR32(dst, src, tmp), ctx); 13327db6c0f1SXu Kuohai } 1333e54bcde3SZi Shen Lim break; 1334e54bcde3SZi Shen Lim case BPF_H: 13355b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 1336cc88f540SXu Kuohai if (sign_extend) 1337cc88f540SXu Kuohai emit(A64_LDRSHI(dst, src_adj, off_adj), ctx); 1338cc88f540SXu Kuohai else 13395b3d19b9SXu Kuohai emit(A64_LDRHI(dst, src_adj, off_adj), ctx); 13407db6c0f1SXu Kuohai } else { 13417db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1342cc88f540SXu Kuohai if (sign_extend) 1343cc88f540SXu Kuohai emit(A64_LDRSH(dst, src, tmp), ctx); 1344cc88f540SXu Kuohai else 1345e54bcde3SZi Shen Lim emit(A64_LDRH(dst, src, tmp), ctx); 13467db6c0f1SXu Kuohai } 1347e54bcde3SZi Shen Lim break; 1348e54bcde3SZi Shen Lim case BPF_B: 13495b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 1350cc88f540SXu Kuohai if (sign_extend) 1351cc88f540SXu Kuohai emit(A64_LDRSBI(dst, src_adj, off_adj), ctx); 1352cc88f540SXu Kuohai else 13535b3d19b9SXu Kuohai emit(A64_LDRBI(dst, src_adj, off_adj), ctx); 13547db6c0f1SXu Kuohai } else { 13557db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1356cc88f540SXu Kuohai if (sign_extend) 1357cc88f540SXu Kuohai emit(A64_LDRSB(dst, src, tmp), ctx); 1358cc88f540SXu Kuohai else 1359e54bcde3SZi Shen Lim emit(A64_LDRB(dst, src, tmp), ctx); 13607db6c0f1SXu Kuohai } 1361e54bcde3SZi Shen Lim break; 1362e54bcde3SZi Shen Lim case BPF_DW: 13635b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 13645b3d19b9SXu Kuohai emit(A64_LDR64I(dst, src_adj, off_adj), ctx); 13657db6c0f1SXu Kuohai } else { 13667db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1367e54bcde3SZi Shen Lim emit(A64_LDR64(dst, src, tmp), ctx); 13687db6c0f1SXu Kuohai } 1369e54bcde3SZi Shen Lim break; 1370e54bcde3SZi Shen Lim } 137180083428SJean-Philippe Brucker 137280083428SJean-Philippe Brucker ret = add_exception_handler(insn, ctx, dst); 137380083428SJean-Philippe Brucker if (ret) 137480083428SJean-Philippe Brucker return ret; 1375e54bcde3SZi Shen Lim break; 1376e54bcde3SZi Shen Lim 1377f5e81d11SDaniel Borkmann /* speculation barrier */ 1378f5e81d11SDaniel Borkmann case BPF_ST | BPF_NOSPEC: 1379f5e81d11SDaniel Borkmann /* 1380f5e81d11SDaniel Borkmann * Nothing required here. 1381f5e81d11SDaniel Borkmann * 1382f5e81d11SDaniel Borkmann * In case of arm64, we rely on the firmware mitigation of 1383f5e81d11SDaniel Borkmann * Speculative Store Bypass as controlled via the ssbd kernel 1384f5e81d11SDaniel Borkmann * parameter. Whenever the mitigation is enabled, it works 1385f5e81d11SDaniel Borkmann * for all of the kernel code with no need to provide any 1386f5e81d11SDaniel Borkmann * additional instructions. 1387f5e81d11SDaniel Borkmann */ 1388f5e81d11SDaniel Borkmann break; 1389f5e81d11SDaniel Borkmann 1390e54bcde3SZi Shen Lim /* ST: *(size *)(dst + off) = imm */ 1391e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_W: 1392e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_H: 1393e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_B: 1394e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_DW: 1395339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_B: 1396339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_H: 1397339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_W: 1398339af577SPuranjay Mohan case BPF_ST | BPF_PROBE_MEM32 | BPF_DW: 1399339af577SPuranjay Mohan if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) { 1400339af577SPuranjay Mohan emit(A64_ADD(1, tmp2, dst, arena_vm_base), ctx); 1401339af577SPuranjay Mohan dst = tmp2; 1402339af577SPuranjay Mohan } 1403339af577SPuranjay Mohan if (ctx->fpb_offset > 0 && dst == fp && BPF_MODE(insn->code) != BPF_PROBE_MEM32) { 14045b3d19b9SXu Kuohai dst_adj = fpb; 14055b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 14065b3d19b9SXu Kuohai } else { 14075b3d19b9SXu Kuohai dst_adj = dst; 14085b3d19b9SXu Kuohai off_adj = off; 14095b3d19b9SXu Kuohai } 1410df849ba3SYang Shi /* Load imm to a register then store it */ 1411df849ba3SYang Shi emit_a64_mov_i(1, tmp, imm, ctx); 1412df849ba3SYang Shi switch (BPF_SIZE(code)) { 1413df849ba3SYang Shi case BPF_W: 14145b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 14155b3d19b9SXu Kuohai emit(A64_STR32I(tmp, dst_adj, off_adj), ctx); 14167db6c0f1SXu Kuohai } else { 14177db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1418df849ba3SYang Shi emit(A64_STR32(tmp, dst, tmp2), ctx); 14197db6c0f1SXu Kuohai } 1420df849ba3SYang Shi break; 1421df849ba3SYang Shi case BPF_H: 14225b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 14235b3d19b9SXu Kuohai emit(A64_STRHI(tmp, dst_adj, off_adj), ctx); 14247db6c0f1SXu Kuohai } else { 14257db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1426df849ba3SYang Shi emit(A64_STRH(tmp, dst, tmp2), ctx); 14277db6c0f1SXu Kuohai } 1428df849ba3SYang Shi break; 1429df849ba3SYang Shi case BPF_B: 14305b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 14315b3d19b9SXu Kuohai emit(A64_STRBI(tmp, dst_adj, off_adj), ctx); 14327db6c0f1SXu Kuohai } else { 14337db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1434df849ba3SYang Shi emit(A64_STRB(tmp, dst, tmp2), ctx); 14357db6c0f1SXu Kuohai } 1436df849ba3SYang Shi break; 1437df849ba3SYang Shi case BPF_DW: 14385b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 14395b3d19b9SXu Kuohai emit(A64_STR64I(tmp, dst_adj, off_adj), ctx); 14407db6c0f1SXu Kuohai } else { 14417db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1442df849ba3SYang Shi emit(A64_STR64(tmp, dst, tmp2), ctx); 14437db6c0f1SXu Kuohai } 1444df849ba3SYang Shi break; 1445df849ba3SYang Shi } 1446339af577SPuranjay Mohan 1447339af577SPuranjay Mohan ret = add_exception_handler(insn, ctx, dst); 1448339af577SPuranjay Mohan if (ret) 1449339af577SPuranjay Mohan return ret; 1450df849ba3SYang Shi break; 1451e54bcde3SZi Shen Lim 1452e54bcde3SZi Shen Lim /* STX: *(size *)(dst + off) = src */ 1453e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_W: 1454e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_H: 1455e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_B: 1456e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_DW: 1457339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_B: 1458339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_H: 1459339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_W: 1460339af577SPuranjay Mohan case BPF_STX | BPF_PROBE_MEM32 | BPF_DW: 1461339af577SPuranjay Mohan if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) { 1462339af577SPuranjay Mohan emit(A64_ADD(1, tmp2, dst, arena_vm_base), ctx); 1463339af577SPuranjay Mohan dst = tmp2; 1464339af577SPuranjay Mohan } 1465339af577SPuranjay Mohan if (ctx->fpb_offset > 0 && dst == fp && BPF_MODE(insn->code) != BPF_PROBE_MEM32) { 14665b3d19b9SXu Kuohai dst_adj = fpb; 14675b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 14685b3d19b9SXu Kuohai } else { 14695b3d19b9SXu Kuohai dst_adj = dst; 14705b3d19b9SXu Kuohai off_adj = off; 14715b3d19b9SXu Kuohai } 1472e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1473e54bcde3SZi Shen Lim case BPF_W: 14745b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 14755b3d19b9SXu Kuohai emit(A64_STR32I(src, dst_adj, off_adj), ctx); 14767db6c0f1SXu Kuohai } else { 14777db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1478e54bcde3SZi Shen Lim emit(A64_STR32(src, dst, tmp), ctx); 14797db6c0f1SXu Kuohai } 1480e54bcde3SZi Shen Lim break; 1481e54bcde3SZi Shen Lim case BPF_H: 14825b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 14835b3d19b9SXu Kuohai emit(A64_STRHI(src, dst_adj, off_adj), ctx); 14847db6c0f1SXu Kuohai } else { 14857db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1486e54bcde3SZi Shen Lim emit(A64_STRH(src, dst, tmp), ctx); 14877db6c0f1SXu Kuohai } 1488e54bcde3SZi Shen Lim break; 1489e54bcde3SZi Shen Lim case BPF_B: 14905b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 14915b3d19b9SXu Kuohai emit(A64_STRBI(src, dst_adj, off_adj), ctx); 14927db6c0f1SXu Kuohai } else { 14937db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1494e54bcde3SZi Shen Lim emit(A64_STRB(src, dst, tmp), ctx); 14957db6c0f1SXu Kuohai } 1496e54bcde3SZi Shen Lim break; 1497e54bcde3SZi Shen Lim case BPF_DW: 14985b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 14995b3d19b9SXu Kuohai emit(A64_STR64I(src, dst_adj, off_adj), ctx); 15007db6c0f1SXu Kuohai } else { 15017db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1502e54bcde3SZi Shen Lim emit(A64_STR64(src, dst, tmp), ctx); 15037db6c0f1SXu Kuohai } 1504e54bcde3SZi Shen Lim break; 1505e54bcde3SZi Shen Lim } 1506339af577SPuranjay Mohan 1507339af577SPuranjay Mohan ret = add_exception_handler(insn, ctx, dst); 1508339af577SPuranjay Mohan if (ret) 1509339af577SPuranjay Mohan return ret; 1510e54bcde3SZi Shen Lim break; 151134b8ab09SDaniel Borkmann 151291c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_W: 151391c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_DW: 1514e612b5c1SPuranjay Mohan case BPF_STX | BPF_PROBE_ATOMIC | BPF_W: 1515e612b5c1SPuranjay Mohan case BPF_STX | BPF_PROBE_ATOMIC | BPF_DW: 15161902472bSHou Tao if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) 15171902472bSHou Tao ret = emit_lse_atomic(insn, ctx); 15181902472bSHou Tao else 15191902472bSHou Tao ret = emit_ll_sc_atomic(insn, ctx); 15201902472bSHou Tao if (ret) 15211902472bSHou Tao return ret; 1522e612b5c1SPuranjay Mohan 1523e612b5c1SPuranjay Mohan ret = add_exception_handler(insn, ctx, dst); 1524e612b5c1SPuranjay Mohan if (ret) 1525e612b5c1SPuranjay Mohan return ret; 152685f68fe8SDaniel Borkmann break; 1527e54bcde3SZi Shen Lim 1528e54bcde3SZi Shen Lim default: 1529e54bcde3SZi Shen Lim pr_err_once("unknown opcode %02x\n", code); 1530e54bcde3SZi Shen Lim return -EINVAL; 1531e54bcde3SZi Shen Lim } 1532e54bcde3SZi Shen Lim 1533e54bcde3SZi Shen Lim return 0; 1534e54bcde3SZi Shen Lim } 1535e54bcde3SZi Shen Lim 15365b3d19b9SXu Kuohai /* 15375b3d19b9SXu Kuohai * Return 0 if FP may change at runtime, otherwise find the minimum negative 15385b3d19b9SXu Kuohai * offset to FP, converts it to positive number, and align down to 8 bytes. 15395b3d19b9SXu Kuohai */ 15405b3d19b9SXu Kuohai static int find_fpb_offset(struct bpf_prog *prog) 15415b3d19b9SXu Kuohai { 15425b3d19b9SXu Kuohai int i; 15435b3d19b9SXu Kuohai int offset = 0; 15445b3d19b9SXu Kuohai 15455b3d19b9SXu Kuohai for (i = 0; i < prog->len; i++) { 15465b3d19b9SXu Kuohai const struct bpf_insn *insn = &prog->insnsi[i]; 15475b3d19b9SXu Kuohai const u8 class = BPF_CLASS(insn->code); 15485b3d19b9SXu Kuohai const u8 mode = BPF_MODE(insn->code); 15495b3d19b9SXu Kuohai const u8 src = insn->src_reg; 15505b3d19b9SXu Kuohai const u8 dst = insn->dst_reg; 15515b3d19b9SXu Kuohai const s32 imm = insn->imm; 15525b3d19b9SXu Kuohai const s16 off = insn->off; 15535b3d19b9SXu Kuohai 15545b3d19b9SXu Kuohai switch (class) { 15555b3d19b9SXu Kuohai case BPF_STX: 15565b3d19b9SXu Kuohai case BPF_ST: 15575b3d19b9SXu Kuohai /* fp holds atomic operation result */ 15585b3d19b9SXu Kuohai if (class == BPF_STX && mode == BPF_ATOMIC && 15595b3d19b9SXu Kuohai ((imm == BPF_XCHG || 15605b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_ADD) || 15615b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_AND) || 15625b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_XOR) || 15635b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_OR)) && 15645b3d19b9SXu Kuohai src == BPF_REG_FP)) 15655b3d19b9SXu Kuohai return 0; 15665b3d19b9SXu Kuohai 15675b3d19b9SXu Kuohai if (mode == BPF_MEM && dst == BPF_REG_FP && 15685b3d19b9SXu Kuohai off < offset) 15695b3d19b9SXu Kuohai offset = insn->off; 15705b3d19b9SXu Kuohai break; 15715b3d19b9SXu Kuohai 15725b3d19b9SXu Kuohai case BPF_JMP32: 15735b3d19b9SXu Kuohai case BPF_JMP: 15745b3d19b9SXu Kuohai break; 15755b3d19b9SXu Kuohai 15765b3d19b9SXu Kuohai case BPF_LDX: 15775b3d19b9SXu Kuohai case BPF_LD: 15785b3d19b9SXu Kuohai /* fp holds load result */ 15795b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 15805b3d19b9SXu Kuohai return 0; 15815b3d19b9SXu Kuohai 15825b3d19b9SXu Kuohai if (class == BPF_LDX && mode == BPF_MEM && 15835b3d19b9SXu Kuohai src == BPF_REG_FP && off < offset) 15845b3d19b9SXu Kuohai offset = off; 15855b3d19b9SXu Kuohai break; 15865b3d19b9SXu Kuohai 15875b3d19b9SXu Kuohai case BPF_ALU: 15885b3d19b9SXu Kuohai case BPF_ALU64: 15895b3d19b9SXu Kuohai default: 15905b3d19b9SXu Kuohai /* fp holds ALU result */ 15915b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 15925b3d19b9SXu Kuohai return 0; 15935b3d19b9SXu Kuohai } 15945b3d19b9SXu Kuohai } 15955b3d19b9SXu Kuohai 15965b3d19b9SXu Kuohai if (offset < 0) { 15975b3d19b9SXu Kuohai /* 15985b3d19b9SXu Kuohai * safely be converted to a positive 'int', since insn->off 15995b3d19b9SXu Kuohai * is 's16' 16005b3d19b9SXu Kuohai */ 16015b3d19b9SXu Kuohai offset = -offset; 16025b3d19b9SXu Kuohai /* align down to 8 bytes */ 16035b3d19b9SXu Kuohai offset = ALIGN_DOWN(offset, 8); 16045b3d19b9SXu Kuohai } 16055b3d19b9SXu Kuohai 16065b3d19b9SXu Kuohai return offset; 16075b3d19b9SXu Kuohai } 16085b3d19b9SXu Kuohai 16098c11ea5cSDaniel Borkmann static int build_body(struct jit_ctx *ctx, bool extra_pass) 1610e54bcde3SZi Shen Lim { 1611e54bcde3SZi Shen Lim const struct bpf_prog *prog = ctx->prog; 1612e54bcde3SZi Shen Lim int i; 1613e54bcde3SZi Shen Lim 161432f6865cSIlias Apalodimas /* 161532f6865cSIlias Apalodimas * - offset[0] offset of the end of prologue, 161632f6865cSIlias Apalodimas * start of the 1st instruction. 161732f6865cSIlias Apalodimas * - offset[1] - offset of the end of 1st instruction, 161832f6865cSIlias Apalodimas * start of the 2nd instruction 161932f6865cSIlias Apalodimas * [....] 162032f6865cSIlias Apalodimas * - offset[3] - offset of the end of 3rd instruction, 162132f6865cSIlias Apalodimas * start of 4th instruction 162232f6865cSIlias Apalodimas */ 1623e54bcde3SZi Shen Lim for (i = 0; i < prog->len; i++) { 1624e54bcde3SZi Shen Lim const struct bpf_insn *insn = &prog->insnsi[i]; 1625e54bcde3SZi Shen Lim int ret; 1626e54bcde3SZi Shen Lim 162732f6865cSIlias Apalodimas if (ctx->image == NULL) 162832f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 16298c11ea5cSDaniel Borkmann ret = build_insn(insn, ctx, extra_pass); 163030d3d94cSZi Shen Lim if (ret > 0) { 163130d3d94cSZi Shen Lim i++; 1632ddc665a4SDaniel Borkmann if (ctx->image == NULL) 1633ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 163430d3d94cSZi Shen Lim continue; 163530d3d94cSZi Shen Lim } 1636e54bcde3SZi Shen Lim if (ret) 1637e54bcde3SZi Shen Lim return ret; 1638e54bcde3SZi Shen Lim } 163932f6865cSIlias Apalodimas /* 164032f6865cSIlias Apalodimas * offset is allocated with prog->len + 1 so fill in 164132f6865cSIlias Apalodimas * the last element with the offset after the last 164232f6865cSIlias Apalodimas * instruction (end of program) 164332f6865cSIlias Apalodimas */ 164432f6865cSIlias Apalodimas if (ctx->image == NULL) 164532f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 1646e54bcde3SZi Shen Lim 1647e54bcde3SZi Shen Lim return 0; 1648e54bcde3SZi Shen Lim } 1649e54bcde3SZi Shen Lim 165042ff712bSZi Shen Lim static int validate_code(struct jit_ctx *ctx) 165142ff712bSZi Shen Lim { 165242ff712bSZi Shen Lim int i; 165342ff712bSZi Shen Lim 165442ff712bSZi Shen Lim for (i = 0; i < ctx->idx; i++) { 165542ff712bSZi Shen Lim u32 a64_insn = le32_to_cpu(ctx->image[i]); 165642ff712bSZi Shen Lim 165742ff712bSZi Shen Lim if (a64_insn == AARCH64_BREAK_FAULT) 165842ff712bSZi Shen Lim return -1; 165942ff712bSZi Shen Lim } 1660efc9909fSXu Kuohai return 0; 1661efc9909fSXu Kuohai } 1662efc9909fSXu Kuohai 1663efc9909fSXu Kuohai static int validate_ctx(struct jit_ctx *ctx) 1664efc9909fSXu Kuohai { 1665efc9909fSXu Kuohai if (validate_code(ctx)) 1666efc9909fSXu Kuohai return -1; 166742ff712bSZi Shen Lim 166880083428SJean-Philippe Brucker if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries)) 166980083428SJean-Philippe Brucker return -1; 167080083428SJean-Philippe Brucker 167142ff712bSZi Shen Lim return 0; 167242ff712bSZi Shen Lim } 167342ff712bSZi Shen Lim 1674e54bcde3SZi Shen Lim static inline void bpf_flush_icache(void *start, void *end) 1675e54bcde3SZi Shen Lim { 1676e54bcde3SZi Shen Lim flush_icache_range((unsigned long)start, (unsigned long)end); 1677e54bcde3SZi Shen Lim } 1678e54bcde3SZi Shen Lim 1679db496944SAlexei Starovoitov struct arm64_jit_data { 1680db496944SAlexei Starovoitov struct bpf_binary_header *header; 16811dad391dSPuranjay Mohan u8 *ro_image; 16821dad391dSPuranjay Mohan struct bpf_binary_header *ro_header; 1683db496944SAlexei Starovoitov struct jit_ctx ctx; 1684db496944SAlexei Starovoitov }; 1685db496944SAlexei Starovoitov 1686d1c55ab5SDaniel Borkmann struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 1687e54bcde3SZi Shen Lim { 1688b2ad54e1SXu Kuohai int image_size, prog_size, extable_size, extable_align, extable_offset; 168926eb042eSDaniel Borkmann struct bpf_prog *tmp, *orig_prog = prog; 1690b569c1c6SDaniel Borkmann struct bpf_binary_header *header; 16911dad391dSPuranjay Mohan struct bpf_binary_header *ro_header; 1692db496944SAlexei Starovoitov struct arm64_jit_data *jit_data; 169356ea6a8bSDaniel Borkmann bool was_classic = bpf_prog_was_classic(prog); 169426eb042eSDaniel Borkmann bool tmp_blinded = false; 1695db496944SAlexei Starovoitov bool extra_pass = false; 1696e54bcde3SZi Shen Lim struct jit_ctx ctx; 1697339af577SPuranjay Mohan u64 arena_vm_start; 1698b569c1c6SDaniel Borkmann u8 *image_ptr; 16991dad391dSPuranjay Mohan u8 *ro_image_ptr; 1700e54bcde3SZi Shen Lim 170160b58afcSAlexei Starovoitov if (!prog->jit_requested) 170226eb042eSDaniel Borkmann return orig_prog; 170326eb042eSDaniel Borkmann 170426eb042eSDaniel Borkmann tmp = bpf_jit_blind_constants(prog); 170526eb042eSDaniel Borkmann /* If blinding was requested and we failed during blinding, 170626eb042eSDaniel Borkmann * we must fall back to the interpreter. 170726eb042eSDaniel Borkmann */ 170826eb042eSDaniel Borkmann if (IS_ERR(tmp)) 170926eb042eSDaniel Borkmann return orig_prog; 171026eb042eSDaniel Borkmann if (tmp != prog) { 171126eb042eSDaniel Borkmann tmp_blinded = true; 171226eb042eSDaniel Borkmann prog = tmp; 171326eb042eSDaniel Borkmann } 1714e54bcde3SZi Shen Lim 1715339af577SPuranjay Mohan arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena); 1716db496944SAlexei Starovoitov jit_data = prog->aux->jit_data; 1717db496944SAlexei Starovoitov if (!jit_data) { 1718db496944SAlexei Starovoitov jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 1719db496944SAlexei Starovoitov if (!jit_data) { 1720db496944SAlexei Starovoitov prog = orig_prog; 1721db496944SAlexei Starovoitov goto out; 1722db496944SAlexei Starovoitov } 1723db496944SAlexei Starovoitov prog->aux->jit_data = jit_data; 1724db496944SAlexei Starovoitov } 1725db496944SAlexei Starovoitov if (jit_data->ctx.offset) { 1726db496944SAlexei Starovoitov ctx = jit_data->ctx; 17271dad391dSPuranjay Mohan ro_image_ptr = jit_data->ro_image; 17281dad391dSPuranjay Mohan ro_header = jit_data->ro_header; 1729db496944SAlexei Starovoitov header = jit_data->header; 17301dad391dSPuranjay Mohan image_ptr = (void *)header + ((void *)ro_image_ptr 17311dad391dSPuranjay Mohan - (void *)ro_header); 1732db496944SAlexei Starovoitov extra_pass = true; 173380083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1734db496944SAlexei Starovoitov goto skip_init_ctx; 1735db496944SAlexei Starovoitov } 1736e54bcde3SZi Shen Lim memset(&ctx, 0, sizeof(ctx)); 1737e54bcde3SZi Shen Lim ctx.prog = prog; 1738e54bcde3SZi Shen Lim 173919f68ed6SAijun Sun ctx.offset = kvcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); 174026eb042eSDaniel Borkmann if (ctx.offset == NULL) { 174126eb042eSDaniel Borkmann prog = orig_prog; 1742db496944SAlexei Starovoitov goto out_off; 174326eb042eSDaniel Borkmann } 1744e54bcde3SZi Shen Lim 17455b3d19b9SXu Kuohai ctx.fpb_offset = find_fpb_offset(prog); 17464dd31243SPuranjay Mohan ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena); 17475b3d19b9SXu Kuohai 174868e4f238SHou Tao /* 174968e4f238SHou Tao * 1. Initial fake pass to compute ctx->idx and ctx->offset. 175068e4f238SHou Tao * 175168e4f238SHou Tao * BPF line info needs ctx->offset[i] to be the offset of 175268e4f238SHou Tao * instruction[i] in jited image, so build prologue first. 175368e4f238SHou Tao */ 1754339af577SPuranjay Mohan if (build_prologue(&ctx, was_classic, prog->aux->exception_cb, 1755339af577SPuranjay Mohan arena_vm_start)) { 175626eb042eSDaniel Borkmann prog = orig_prog; 175726eb042eSDaniel Borkmann goto out_off; 175826eb042eSDaniel Borkmann } 1759e54bcde3SZi Shen Lim 176068e4f238SHou Tao if (build_body(&ctx, extra_pass)) { 1761ddb55992SZi Shen Lim prog = orig_prog; 1762ddb55992SZi Shen Lim goto out_off; 1763ddb55992SZi Shen Lim } 176451c9fbb1SZi Shen Lim 176551c9fbb1SZi Shen Lim ctx.epilogue_offset = ctx.idx; 176622fc0e80SPuranjay Mohan build_epilogue(&ctx, prog->aux->exception_cb); 1767b2ad54e1SXu Kuohai build_plt(&ctx); 1768e54bcde3SZi Shen Lim 1769b2ad54e1SXu Kuohai extable_align = __alignof__(struct exception_table_entry); 177080083428SJean-Philippe Brucker extable_size = prog->aux->num_exentries * 177180083428SJean-Philippe Brucker sizeof(struct exception_table_entry); 177280083428SJean-Philippe Brucker 1773e54bcde3SZi Shen Lim /* Now we know the actual image size. */ 177480083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1775b2ad54e1SXu Kuohai /* also allocate space for plt target */ 1776b2ad54e1SXu Kuohai extable_offset = round_up(prog_size + PLT_TARGET_SIZE, extable_align); 1777b2ad54e1SXu Kuohai image_size = extable_offset + extable_size; 17781dad391dSPuranjay Mohan ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, 17791dad391dSPuranjay Mohan sizeof(u32), &header, &image_ptr, 17801dad391dSPuranjay Mohan jit_fill_hole); 17811dad391dSPuranjay Mohan if (!ro_header) { 178226eb042eSDaniel Borkmann prog = orig_prog; 178326eb042eSDaniel Borkmann goto out_off; 178426eb042eSDaniel Borkmann } 1785e54bcde3SZi Shen Lim 1786e54bcde3SZi Shen Lim /* 2. Now, the actual pass. */ 1787e54bcde3SZi Shen Lim 17881dad391dSPuranjay Mohan /* 17891dad391dSPuranjay Mohan * Use the image(RW) for writing the JITed instructions. But also save 17901dad391dSPuranjay Mohan * the ro_image(RX) for calculating the offsets in the image. The RW 17911dad391dSPuranjay Mohan * image will be later copied to the RX image from where the program 17921dad391dSPuranjay Mohan * will run. The bpf_jit_binary_pack_finalize() will do this copy in the 17931dad391dSPuranjay Mohan * final step. 17941dad391dSPuranjay Mohan */ 1795425e1ed7SLuc Van Oostenryck ctx.image = (__le32 *)image_ptr; 17961dad391dSPuranjay Mohan ctx.ro_image = (__le32 *)ro_image_ptr; 179780083428SJean-Philippe Brucker if (extable_size) 17981dad391dSPuranjay Mohan prog->aux->extable = (void *)ro_image_ptr + extable_offset; 1799db496944SAlexei Starovoitov skip_init_ctx: 1800e54bcde3SZi Shen Lim ctx.idx = 0; 180180083428SJean-Philippe Brucker ctx.exentry_idx = 0; 1802b569c1c6SDaniel Borkmann 1803339af577SPuranjay Mohan build_prologue(&ctx, was_classic, prog->aux->exception_cb, arena_vm_start); 1804e54bcde3SZi Shen Lim 18058c11ea5cSDaniel Borkmann if (build_body(&ctx, extra_pass)) { 180626eb042eSDaniel Borkmann prog = orig_prog; 18071dad391dSPuranjay Mohan goto out_free_hdr; 180860ef0494SDaniel Borkmann } 1809e54bcde3SZi Shen Lim 181022fc0e80SPuranjay Mohan build_epilogue(&ctx, prog->aux->exception_cb); 1811b2ad54e1SXu Kuohai build_plt(&ctx); 1812e54bcde3SZi Shen Lim 181342ff712bSZi Shen Lim /* 3. Extra pass to validate JITed code. */ 1814efc9909fSXu Kuohai if (validate_ctx(&ctx)) { 181526eb042eSDaniel Borkmann prog = orig_prog; 18161dad391dSPuranjay Mohan goto out_free_hdr; 181742ff712bSZi Shen Lim } 181842ff712bSZi Shen Lim 1819e54bcde3SZi Shen Lim /* And we're done. */ 1820e54bcde3SZi Shen Lim if (bpf_jit_enable > 1) 182180083428SJean-Philippe Brucker bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 1822e54bcde3SZi Shen Lim 1823db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1824db496944SAlexei Starovoitov if (extra_pass && ctx.idx != jit_data->ctx.idx) { 1825db496944SAlexei Starovoitov pr_err_once("multi-func JIT bug %d != %d\n", 1826db496944SAlexei Starovoitov ctx.idx, jit_data->ctx.idx); 1827db496944SAlexei Starovoitov prog->bpf_func = NULL; 1828db496944SAlexei Starovoitov prog->jited = 0; 182910f3b29cSEric Dumazet prog->jited_len = 0; 18301dad391dSPuranjay Mohan goto out_free_hdr; 18311dad391dSPuranjay Mohan } 18321dad391dSPuranjay Mohan if (WARN_ON(bpf_jit_binary_pack_finalize(prog, ro_header, 18331dad391dSPuranjay Mohan header))) { 18341dad391dSPuranjay Mohan /* ro_header has been freed */ 18351dad391dSPuranjay Mohan ro_header = NULL; 18361dad391dSPuranjay Mohan prog = orig_prog; 1837db496944SAlexei Starovoitov goto out_off; 1838db496944SAlexei Starovoitov } 18391dad391dSPuranjay Mohan /* 18401dad391dSPuranjay Mohan * The instructions have now been copied to the ROX region from 18411dad391dSPuranjay Mohan * where they will execute. Now the data cache has to be cleaned to 18421dad391dSPuranjay Mohan * the PoU and the I-cache has to be invalidated for the VAs. 18431dad391dSPuranjay Mohan */ 18441dad391dSPuranjay Mohan bpf_flush_icache(ro_header, ctx.ro_image + ctx.idx); 1845db496944SAlexei Starovoitov } else { 1846db496944SAlexei Starovoitov jit_data->ctx = ctx; 18471dad391dSPuranjay Mohan jit_data->ro_image = ro_image_ptr; 1848db496944SAlexei Starovoitov jit_data->header = header; 18491dad391dSPuranjay Mohan jit_data->ro_header = ro_header; 1850db496944SAlexei Starovoitov } 18511dad391dSPuranjay Mohan 18521dad391dSPuranjay Mohan prog->bpf_func = (void *)ctx.ro_image; 1853a91263d5SDaniel Borkmann prog->jited = 1; 185480083428SJean-Philippe Brucker prog->jited_len = prog_size; 185526eb042eSDaniel Borkmann 1856db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1857dda7596cSHou Tao int i; 1858dda7596cSHou Tao 1859dda7596cSHou Tao /* offset[prog->len] is the size of program */ 1860dda7596cSHou Tao for (i = 0; i <= prog->len; i++) 1861dda7596cSHou Tao ctx.offset[i] *= AARCH64_INSN_SIZE; 186232f6865cSIlias Apalodimas bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 186326eb042eSDaniel Borkmann out_off: 186419f68ed6SAijun Sun kvfree(ctx.offset); 1865db496944SAlexei Starovoitov kfree(jit_data); 1866db496944SAlexei Starovoitov prog->aux->jit_data = NULL; 1867db496944SAlexei Starovoitov } 186826eb042eSDaniel Borkmann out: 186926eb042eSDaniel Borkmann if (tmp_blinded) 187026eb042eSDaniel Borkmann bpf_jit_prog_release_other(prog, prog == orig_prog ? 187126eb042eSDaniel Borkmann tmp : orig_prog); 1872d1c55ab5SDaniel Borkmann return prog; 18731dad391dSPuranjay Mohan 18741dad391dSPuranjay Mohan out_free_hdr: 18751dad391dSPuranjay Mohan if (header) { 18761dad391dSPuranjay Mohan bpf_arch_text_copy(&ro_header->size, &header->size, 18771dad391dSPuranjay Mohan sizeof(header->size)); 18781dad391dSPuranjay Mohan bpf_jit_binary_pack_free(ro_header, header); 18791dad391dSPuranjay Mohan } 18801dad391dSPuranjay Mohan goto out_off; 1881e54bcde3SZi Shen Lim } 188291fc957cSArd Biesheuvel 1883b5e975d2SHou Tao bool bpf_jit_supports_kfunc_call(void) 1884b5e975d2SHou Tao { 1885b5e975d2SHou Tao return true; 1886b5e975d2SHou Tao } 1887b5e975d2SHou Tao 18881dad391dSPuranjay Mohan void *bpf_arch_text_copy(void *dst, void *src, size_t len) 18891dad391dSPuranjay Mohan { 18901dad391dSPuranjay Mohan if (!aarch64_insn_copy(dst, src, len)) 18911dad391dSPuranjay Mohan return ERR_PTR(-EINVAL); 18921dad391dSPuranjay Mohan return dst; 18931dad391dSPuranjay Mohan } 18941dad391dSPuranjay Mohan 18955d63ae90SLorenz Bauer u64 bpf_jit_alloc_exec_limit(void) 18965d63ae90SLorenz Bauer { 1897b89ddf4cSRussell King return VMALLOC_END - VMALLOC_START; 18985d63ae90SLorenz Bauer } 18995d63ae90SLorenz Bauer 190091fc957cSArd Biesheuvel void *bpf_jit_alloc_exec(unsigned long size) 190191fc957cSArd Biesheuvel { 190236c4a73bSAndrey Konovalov /* Memory is intended to be executable, reset the pointer tag. */ 190336c4a73bSAndrey Konovalov return kasan_reset_tag(vmalloc(size)); 190491fc957cSArd Biesheuvel } 190591fc957cSArd Biesheuvel 190691fc957cSArd Biesheuvel void bpf_jit_free_exec(void *addr) 190791fc957cSArd Biesheuvel { 190891fc957cSArd Biesheuvel return vfree(addr); 190991fc957cSArd Biesheuvel } 1910d4609a5dSJakub Sitnicki 1911d4609a5dSJakub Sitnicki /* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */ 1912d4609a5dSJakub Sitnicki bool bpf_jit_supports_subprog_tailcalls(void) 1913d4609a5dSJakub Sitnicki { 1914d4609a5dSJakub Sitnicki return true; 1915d4609a5dSJakub Sitnicki } 1916b2ad54e1SXu Kuohai 1917efc9909fSXu Kuohai static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, 1918efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1919efc9909fSXu Kuohai bool save_ret) 1920efc9909fSXu Kuohai { 1921aada4766SXu Kuohai __le32 *branch; 1922efc9909fSXu Kuohai u64 enter_prog; 1923efc9909fSXu Kuohai u64 exit_prog; 1924efc9909fSXu Kuohai struct bpf_prog *p = l->link.prog; 1925efc9909fSXu Kuohai int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); 1926efc9909fSXu Kuohai 1927271de525SMartin KaFai Lau enter_prog = (u64)bpf_trampoline_enter(p); 1928271de525SMartin KaFai Lau exit_prog = (u64)bpf_trampoline_exit(p); 1929efc9909fSXu Kuohai 1930efc9909fSXu Kuohai if (l->cookie == 0) { 1931efc9909fSXu Kuohai /* if cookie is zero, one instruction is enough to store it */ 1932efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, run_ctx_off + cookie_off), ctx); 1933efc9909fSXu Kuohai } else { 1934efc9909fSXu Kuohai emit_a64_mov_i64(A64_R(10), l->cookie, ctx); 1935efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, run_ctx_off + cookie_off), 1936efc9909fSXu Kuohai ctx); 1937efc9909fSXu Kuohai } 1938efc9909fSXu Kuohai 1939efc9909fSXu Kuohai /* save p to callee saved register x19 to avoid loading p with mov_i64 1940efc9909fSXu Kuohai * each time. 1941efc9909fSXu Kuohai */ 1942efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(19), (const u64)p, ctx); 1943efc9909fSXu Kuohai 1944efc9909fSXu Kuohai /* arg1: prog */ 1945efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1946efc9909fSXu Kuohai /* arg2: &run_ctx */ 1947efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(1), A64_SP, run_ctx_off), ctx); 1948efc9909fSXu Kuohai 1949efc9909fSXu Kuohai emit_call(enter_prog, ctx); 1950efc9909fSXu Kuohai 1951efc9909fSXu Kuohai /* if (__bpf_prog_enter(prog) == 0) 1952efc9909fSXu Kuohai * goto skip_exec_of_prog; 1953efc9909fSXu Kuohai */ 1954efc9909fSXu Kuohai branch = ctx->image + ctx->idx; 1955efc9909fSXu Kuohai emit(A64_NOP, ctx); 1956efc9909fSXu Kuohai 1957efc9909fSXu Kuohai /* save return value to callee saved register x20 */ 1958efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(20), A64_R(0)), ctx); 1959efc9909fSXu Kuohai 1960efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx); 1961efc9909fSXu Kuohai if (!p->jited) 1962efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx); 1963efc9909fSXu Kuohai 1964efc9909fSXu Kuohai emit_call((const u64)p->bpf_func, ctx); 1965efc9909fSXu Kuohai 1966efc9909fSXu Kuohai if (save_ret) 1967efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 1968efc9909fSXu Kuohai 1969efc9909fSXu Kuohai if (ctx->image) { 1970efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branch; 1971aada4766SXu Kuohai *branch = cpu_to_le32(A64_CBZ(1, A64_R(0), offset)); 1972efc9909fSXu Kuohai } 1973efc9909fSXu Kuohai 1974efc9909fSXu Kuohai /* arg1: prog */ 1975efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1976efc9909fSXu Kuohai /* arg2: start time */ 1977efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(1), A64_R(20)), ctx); 1978efc9909fSXu Kuohai /* arg3: &run_ctx */ 1979efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(2), A64_SP, run_ctx_off), ctx); 1980efc9909fSXu Kuohai 1981efc9909fSXu Kuohai emit_call(exit_prog, ctx); 1982efc9909fSXu Kuohai } 1983efc9909fSXu Kuohai 1984efc9909fSXu Kuohai static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, 1985efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1986aada4766SXu Kuohai __le32 **branches) 1987efc9909fSXu Kuohai { 1988efc9909fSXu Kuohai int i; 1989efc9909fSXu Kuohai 1990efc9909fSXu Kuohai /* The first fmod_ret program will receive a garbage return value. 1991efc9909fSXu Kuohai * Set this to 0 to avoid confusing the program. 1992efc9909fSXu Kuohai */ 1993efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx); 1994efc9909fSXu Kuohai for (i = 0; i < tl->nr_links; i++) { 1995efc9909fSXu Kuohai invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off, 1996efc9909fSXu Kuohai run_ctx_off, true); 1997efc9909fSXu Kuohai /* if (*(u64 *)(sp + retval_off) != 0) 1998efc9909fSXu Kuohai * goto do_fexit; 1999efc9909fSXu Kuohai */ 2000efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retval_off), ctx); 2001efc9909fSXu Kuohai /* Save the location of branch, and generate a nop. 2002efc9909fSXu Kuohai * This nop will be replaced with a cbnz later. 2003efc9909fSXu Kuohai */ 2004efc9909fSXu Kuohai branches[i] = ctx->image + ctx->idx; 2005efc9909fSXu Kuohai emit(A64_NOP, ctx); 2006efc9909fSXu Kuohai } 2007efc9909fSXu Kuohai } 2008efc9909fSXu Kuohai 200990564f1eSFlorent Revest static void save_args(struct jit_ctx *ctx, int args_off, int nregs) 2010efc9909fSXu Kuohai { 2011efc9909fSXu Kuohai int i; 2012efc9909fSXu Kuohai 201390564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 2014efc9909fSXu Kuohai emit(A64_STR64I(i, A64_SP, args_off), ctx); 2015efc9909fSXu Kuohai args_off += 8; 2016efc9909fSXu Kuohai } 2017efc9909fSXu Kuohai } 2018efc9909fSXu Kuohai 201990564f1eSFlorent Revest static void restore_args(struct jit_ctx *ctx, int args_off, int nregs) 2020efc9909fSXu Kuohai { 2021efc9909fSXu Kuohai int i; 2022efc9909fSXu Kuohai 202390564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 2024efc9909fSXu Kuohai emit(A64_LDR64I(i, A64_SP, args_off), ctx); 2025efc9909fSXu Kuohai args_off += 8; 2026efc9909fSXu Kuohai } 2027efc9909fSXu Kuohai } 2028efc9909fSXu Kuohai 2029efc9909fSXu Kuohai /* Based on the x86's implementation of arch_prepare_bpf_trampoline(). 2030efc9909fSXu Kuohai * 2031efc9909fSXu Kuohai * bpf prog and function entry before bpf trampoline hooked: 2032efc9909fSXu Kuohai * mov x9, lr 2033efc9909fSXu Kuohai * nop 2034efc9909fSXu Kuohai * 2035efc9909fSXu Kuohai * bpf prog and function entry after bpf trampoline hooked: 2036efc9909fSXu Kuohai * mov x9, lr 2037efc9909fSXu Kuohai * bl <bpf_trampoline or plt> 2038efc9909fSXu Kuohai * 2039efc9909fSXu Kuohai */ 2040efc9909fSXu Kuohai static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, 20417a3d9a15SSong Liu struct bpf_tramp_links *tlinks, void *func_addr, 204290564f1eSFlorent Revest int nregs, u32 flags) 2043efc9909fSXu Kuohai { 2044efc9909fSXu Kuohai int i; 2045efc9909fSXu Kuohai int stack_size; 2046efc9909fSXu Kuohai int retaddr_off; 2047efc9909fSXu Kuohai int regs_off; 2048efc9909fSXu Kuohai int retval_off; 2049efc9909fSXu Kuohai int args_off; 205090564f1eSFlorent Revest int nregs_off; 2051efc9909fSXu Kuohai int ip_off; 2052efc9909fSXu Kuohai int run_ctx_off; 2053efc9909fSXu Kuohai struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; 2054efc9909fSXu Kuohai struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; 2055efc9909fSXu Kuohai struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; 2056efc9909fSXu Kuohai bool save_ret; 2057aada4766SXu Kuohai __le32 **branches = NULL; 2058efc9909fSXu Kuohai 2059efc9909fSXu Kuohai /* trampoline stack layout: 2060efc9909fSXu Kuohai * [ parent ip ] 2061efc9909fSXu Kuohai * [ FP ] 2062efc9909fSXu Kuohai * SP + retaddr_off [ self ip ] 2063efc9909fSXu Kuohai * [ FP ] 2064efc9909fSXu Kuohai * 2065efc9909fSXu Kuohai * [ padding ] align SP to multiples of 16 2066efc9909fSXu Kuohai * 2067efc9909fSXu Kuohai * [ x20 ] callee saved reg x20 2068efc9909fSXu Kuohai * SP + regs_off [ x19 ] callee saved reg x19 2069efc9909fSXu Kuohai * 2070efc9909fSXu Kuohai * SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or 2071efc9909fSXu Kuohai * BPF_TRAMP_F_RET_FENTRY_RET 2072efc9909fSXu Kuohai * 207390564f1eSFlorent Revest * [ arg reg N ] 2074efc9909fSXu Kuohai * [ ... ] 207590564f1eSFlorent Revest * SP + args_off [ arg reg 1 ] 2076efc9909fSXu Kuohai * 207790564f1eSFlorent Revest * SP + nregs_off [ arg regs count ] 2078efc9909fSXu Kuohai * 2079efc9909fSXu Kuohai * SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag 2080efc9909fSXu Kuohai * 2081efc9909fSXu Kuohai * SP + run_ctx_off [ bpf_tramp_run_ctx ] 2082efc9909fSXu Kuohai */ 2083efc9909fSXu Kuohai 2084efc9909fSXu Kuohai stack_size = 0; 2085efc9909fSXu Kuohai run_ctx_off = stack_size; 2086efc9909fSXu Kuohai /* room for bpf_tramp_run_ctx */ 2087efc9909fSXu Kuohai stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); 2088efc9909fSXu Kuohai 2089efc9909fSXu Kuohai ip_off = stack_size; 2090efc9909fSXu Kuohai /* room for IP address argument */ 2091efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) 2092efc9909fSXu Kuohai stack_size += 8; 2093efc9909fSXu Kuohai 209490564f1eSFlorent Revest nregs_off = stack_size; 2095efc9909fSXu Kuohai /* room for args count */ 2096efc9909fSXu Kuohai stack_size += 8; 2097efc9909fSXu Kuohai 2098efc9909fSXu Kuohai args_off = stack_size; 2099efc9909fSXu Kuohai /* room for args */ 210090564f1eSFlorent Revest stack_size += nregs * 8; 2101efc9909fSXu Kuohai 2102efc9909fSXu Kuohai /* room for return value */ 2103efc9909fSXu Kuohai retval_off = stack_size; 2104efc9909fSXu Kuohai save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET); 2105efc9909fSXu Kuohai if (save_ret) 2106efc9909fSXu Kuohai stack_size += 8; 2107efc9909fSXu Kuohai 2108efc9909fSXu Kuohai /* room for callee saved registers, currently x19 and x20 are used */ 2109efc9909fSXu Kuohai regs_off = stack_size; 2110efc9909fSXu Kuohai stack_size += 16; 2111efc9909fSXu Kuohai 2112efc9909fSXu Kuohai /* round up to multiples of 16 to avoid SPAlignmentFault */ 2113efc9909fSXu Kuohai stack_size = round_up(stack_size, 16); 2114efc9909fSXu Kuohai 2115efc9909fSXu Kuohai /* return address locates above FP */ 2116efc9909fSXu Kuohai retaddr_off = stack_size + 8; 2117efc9909fSXu Kuohai 2118efc9909fSXu Kuohai /* bpf trampoline may be invoked by 3 instruction types: 2119efc9909fSXu Kuohai * 1. bl, attached to bpf prog or kernel function via short jump 2120efc9909fSXu Kuohai * 2. br, attached to bpf prog or kernel function via long jump 2121efc9909fSXu Kuohai * 3. blr, working as a function pointer, used by struct_ops. 2122efc9909fSXu Kuohai * So BTI_JC should used here to support both br and blr. 2123efc9909fSXu Kuohai */ 2124efc9909fSXu Kuohai emit_bti(A64_BTI_JC, ctx); 2125efc9909fSXu Kuohai 2126efc9909fSXu Kuohai /* frame for parent function */ 2127efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx); 2128efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 2129efc9909fSXu Kuohai 2130efc9909fSXu Kuohai /* frame for patched function */ 2131efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 2132efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 2133efc9909fSXu Kuohai 2134efc9909fSXu Kuohai /* allocate stack space */ 2135efc9909fSXu Kuohai emit(A64_SUB_I(1, A64_SP, A64_SP, stack_size), ctx); 2136efc9909fSXu Kuohai 2137efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) { 2138efc9909fSXu Kuohai /* save ip address of the traced function */ 21397a3d9a15SSong Liu emit_addr_mov_i64(A64_R(10), (const u64)func_addr, ctx); 2140efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, ip_off), ctx); 2141efc9909fSXu Kuohai } 2142efc9909fSXu Kuohai 214390564f1eSFlorent Revest /* save arg regs count*/ 214490564f1eSFlorent Revest emit(A64_MOVZ(1, A64_R(10), nregs, 0), ctx); 214590564f1eSFlorent Revest emit(A64_STR64I(A64_R(10), A64_SP, nregs_off), ctx); 2146efc9909fSXu Kuohai 214790564f1eSFlorent Revest /* save arg regs */ 214890564f1eSFlorent Revest save_args(ctx, args_off, nregs); 2149efc9909fSXu Kuohai 2150efc9909fSXu Kuohai /* save callee saved registers */ 2151efc9909fSXu Kuohai emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx); 2152efc9909fSXu Kuohai emit(A64_STR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 2153efc9909fSXu Kuohai 2154efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 2155efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 2156efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_enter, ctx); 2157efc9909fSXu Kuohai } 2158efc9909fSXu Kuohai 2159efc9909fSXu Kuohai for (i = 0; i < fentry->nr_links; i++) 2160efc9909fSXu Kuohai invoke_bpf_prog(ctx, fentry->links[i], args_off, 2161efc9909fSXu Kuohai retval_off, run_ctx_off, 2162efc9909fSXu Kuohai flags & BPF_TRAMP_F_RET_FENTRY_RET); 2163efc9909fSXu Kuohai 2164efc9909fSXu Kuohai if (fmod_ret->nr_links) { 2165aada4766SXu Kuohai branches = kcalloc(fmod_ret->nr_links, sizeof(__le32 *), 2166efc9909fSXu Kuohai GFP_KERNEL); 2167efc9909fSXu Kuohai if (!branches) 2168efc9909fSXu Kuohai return -ENOMEM; 2169efc9909fSXu Kuohai 2170efc9909fSXu Kuohai invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off, 2171efc9909fSXu Kuohai run_ctx_off, branches); 2172efc9909fSXu Kuohai } 2173efc9909fSXu Kuohai 2174efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 217590564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 2176efc9909fSXu Kuohai /* call original func */ 2177efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx); 2178738a96c4SXu Kuohai emit(A64_ADR(A64_LR, AARCH64_INSN_SIZE * 2), ctx); 2179738a96c4SXu Kuohai emit(A64_RET(A64_R(10)), ctx); 2180efc9909fSXu Kuohai /* store return value */ 2181efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 2182efc9909fSXu Kuohai /* reserve a nop for bpf_tramp_image_put */ 218396b0f5adSPuranjay Mohan im->ip_after_call = ctx->ro_image + ctx->idx; 2184efc9909fSXu Kuohai emit(A64_NOP, ctx); 2185efc9909fSXu Kuohai } 2186efc9909fSXu Kuohai 2187efc9909fSXu Kuohai /* update the branches saved in invoke_bpf_mod_ret with cbnz */ 2188efc9909fSXu Kuohai for (i = 0; i < fmod_ret->nr_links && ctx->image != NULL; i++) { 2189efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branches[i]; 2190aada4766SXu Kuohai *branches[i] = cpu_to_le32(A64_CBNZ(1, A64_R(10), offset)); 2191efc9909fSXu Kuohai } 2192efc9909fSXu Kuohai 2193efc9909fSXu Kuohai for (i = 0; i < fexit->nr_links; i++) 2194efc9909fSXu Kuohai invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off, 2195efc9909fSXu Kuohai run_ctx_off, false); 2196efc9909fSXu Kuohai 2197efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 219896b0f5adSPuranjay Mohan im->ip_epilogue = ctx->ro_image + ctx->idx; 2199efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 2200efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_exit, ctx); 2201efc9909fSXu Kuohai } 2202efc9909fSXu Kuohai 2203efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_RESTORE_REGS) 220490564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 2205efc9909fSXu Kuohai 2206efc9909fSXu Kuohai /* restore callee saved register x19 and x20 */ 2207efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx); 2208efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 2209efc9909fSXu Kuohai 2210efc9909fSXu Kuohai if (save_ret) 2211efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(0), A64_SP, retval_off), ctx); 2212efc9909fSXu Kuohai 2213efc9909fSXu Kuohai /* reset SP */ 2214efc9909fSXu Kuohai emit(A64_MOV(1, A64_SP, A64_FP), ctx); 2215efc9909fSXu Kuohai 2216efc9909fSXu Kuohai /* pop frames */ 2217efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 2218efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx); 2219efc9909fSXu Kuohai 2220efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_SKIP_FRAME) { 2221efc9909fSXu Kuohai /* skip patched function, return to parent */ 2222efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 2223efc9909fSXu Kuohai emit(A64_RET(A64_R(9)), ctx); 2224efc9909fSXu Kuohai } else { 2225efc9909fSXu Kuohai /* return to patched function */ 2226efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(10), A64_LR), ctx); 2227efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 2228efc9909fSXu Kuohai emit(A64_RET(A64_R(10)), ctx); 2229efc9909fSXu Kuohai } 2230efc9909fSXu Kuohai 2231efc9909fSXu Kuohai kfree(branches); 2232efc9909fSXu Kuohai 2233efc9909fSXu Kuohai return ctx->idx; 2234efc9909fSXu Kuohai } 2235efc9909fSXu Kuohai 223696d1b7c0SSong Liu static int btf_func_model_nregs(const struct btf_func_model *m) 2237efc9909fSXu Kuohai { 223890564f1eSFlorent Revest int nregs = m->nr_args; 223996d1b7c0SSong Liu int i; 2240efc9909fSXu Kuohai 224190564f1eSFlorent Revest /* extra registers needed for struct argument */ 2242eb707ddeSYonghong Song for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) { 224390564f1eSFlorent Revest /* The arg_size is at most 16 bytes, enforced by the verifier. */ 2244eb707ddeSYonghong Song if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) 224590564f1eSFlorent Revest nregs += (m->arg_size[i] + 7) / 8 - 1; 2246eb707ddeSYonghong Song } 2247eb707ddeSYonghong Song 224896d1b7c0SSong Liu return nregs; 224996d1b7c0SSong Liu } 225096d1b7c0SSong Liu 225196d1b7c0SSong Liu int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, 225296d1b7c0SSong Liu struct bpf_tramp_links *tlinks, void *func_addr) 225396d1b7c0SSong Liu { 225496d1b7c0SSong Liu struct jit_ctx ctx = { 225596d1b7c0SSong Liu .image = NULL, 225696d1b7c0SSong Liu .idx = 0, 225796d1b7c0SSong Liu }; 225896d1b7c0SSong Liu struct bpf_tramp_image im; 225996d1b7c0SSong Liu int nregs, ret; 226096d1b7c0SSong Liu 226196d1b7c0SSong Liu nregs = btf_func_model_nregs(m); 226290564f1eSFlorent Revest /* the first 8 registers are used for arguments */ 226390564f1eSFlorent Revest if (nregs > 8) 226490564f1eSFlorent Revest return -ENOTSUPP; 226590564f1eSFlorent Revest 226696d1b7c0SSong Liu ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, nregs, flags); 2267efc9909fSXu Kuohai if (ret < 0) 2268efc9909fSXu Kuohai return ret; 2269efc9909fSXu Kuohai 227096d1b7c0SSong Liu return ret < 0 ? ret : ret * AARCH64_INSN_SIZE; 227196d1b7c0SSong Liu } 2272efc9909fSXu Kuohai 227396b0f5adSPuranjay Mohan void *arch_alloc_bpf_trampoline(unsigned int size) 227496b0f5adSPuranjay Mohan { 227596b0f5adSPuranjay Mohan return bpf_prog_pack_alloc(size, jit_fill_hole); 227696b0f5adSPuranjay Mohan } 227796b0f5adSPuranjay Mohan 227896b0f5adSPuranjay Mohan void arch_free_bpf_trampoline(void *image, unsigned int size) 227996b0f5adSPuranjay Mohan { 228096b0f5adSPuranjay Mohan bpf_prog_pack_free(image, size); 228196b0f5adSPuranjay Mohan } 228296b0f5adSPuranjay Mohan 2283c733239fSChristophe Leroy int arch_protect_bpf_trampoline(void *image, unsigned int size) 228496b0f5adSPuranjay Mohan { 2285c733239fSChristophe Leroy return 0; 228696b0f5adSPuranjay Mohan } 228796b0f5adSPuranjay Mohan 228896b0f5adSPuranjay Mohan int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image, 228996b0f5adSPuranjay Mohan void *ro_image_end, const struct btf_func_model *m, 229096d1b7c0SSong Liu u32 flags, struct bpf_tramp_links *tlinks, 229196d1b7c0SSong Liu void *func_addr) 229296d1b7c0SSong Liu { 229396d1b7c0SSong Liu int ret, nregs; 229496b0f5adSPuranjay Mohan void *image, *tmp; 229596b0f5adSPuranjay Mohan u32 size = ro_image_end - ro_image; 229696b0f5adSPuranjay Mohan 229796b0f5adSPuranjay Mohan /* image doesn't need to be in module memory range, so we can 229896b0f5adSPuranjay Mohan * use kvmalloc. 229996b0f5adSPuranjay Mohan */ 230096b0f5adSPuranjay Mohan image = kvmalloc(size, GFP_KERNEL); 230196b0f5adSPuranjay Mohan if (!image) 230296b0f5adSPuranjay Mohan return -ENOMEM; 230396b0f5adSPuranjay Mohan 230496d1b7c0SSong Liu struct jit_ctx ctx = { 230596d1b7c0SSong Liu .image = image, 230696b0f5adSPuranjay Mohan .ro_image = ro_image, 230796d1b7c0SSong Liu .idx = 0, 230896d1b7c0SSong Liu }; 230996d1b7c0SSong Liu 231096d1b7c0SSong Liu nregs = btf_func_model_nregs(m); 231196d1b7c0SSong Liu /* the first 8 registers are used for arguments */ 231296d1b7c0SSong Liu if (nregs > 8) 231396d1b7c0SSong Liu return -ENOTSUPP; 231496d1b7c0SSong Liu 231596b0f5adSPuranjay Mohan jit_fill_hole(image, (unsigned int)(ro_image_end - ro_image)); 23167a3d9a15SSong Liu ret = prepare_trampoline(&ctx, im, tlinks, func_addr, nregs, flags); 2317efc9909fSXu Kuohai 231896b0f5adSPuranjay Mohan if (ret > 0 && validate_code(&ctx) < 0) { 2319efc9909fSXu Kuohai ret = -EINVAL; 232096b0f5adSPuranjay Mohan goto out; 232196b0f5adSPuranjay Mohan } 2322efc9909fSXu Kuohai 2323efc9909fSXu Kuohai if (ret > 0) 2324efc9909fSXu Kuohai ret *= AARCH64_INSN_SIZE; 2325efc9909fSXu Kuohai 232696b0f5adSPuranjay Mohan tmp = bpf_arch_text_copy(ro_image, image, size); 232796b0f5adSPuranjay Mohan if (IS_ERR(tmp)) { 232896b0f5adSPuranjay Mohan ret = PTR_ERR(tmp); 232996b0f5adSPuranjay Mohan goto out; 233096b0f5adSPuranjay Mohan } 233196b0f5adSPuranjay Mohan 233296b0f5adSPuranjay Mohan bpf_flush_icache(ro_image, ro_image + size); 233396b0f5adSPuranjay Mohan out: 233496b0f5adSPuranjay Mohan kvfree(image); 2335efc9909fSXu Kuohai return ret; 2336efc9909fSXu Kuohai } 2337efc9909fSXu Kuohai 2338b2ad54e1SXu Kuohai static bool is_long_jump(void *ip, void *target) 2339b2ad54e1SXu Kuohai { 2340b2ad54e1SXu Kuohai long offset; 2341b2ad54e1SXu Kuohai 2342b2ad54e1SXu Kuohai /* NULL target means this is a NOP */ 2343b2ad54e1SXu Kuohai if (!target) 2344b2ad54e1SXu Kuohai return false; 2345b2ad54e1SXu Kuohai 2346b2ad54e1SXu Kuohai offset = (long)target - (long)ip; 2347b2ad54e1SXu Kuohai return offset < -SZ_128M || offset >= SZ_128M; 2348b2ad54e1SXu Kuohai } 2349b2ad54e1SXu Kuohai 2350b2ad54e1SXu Kuohai static int gen_branch_or_nop(enum aarch64_insn_branch_type type, void *ip, 2351b2ad54e1SXu Kuohai void *addr, void *plt, u32 *insn) 2352b2ad54e1SXu Kuohai { 2353b2ad54e1SXu Kuohai void *target; 2354b2ad54e1SXu Kuohai 2355b2ad54e1SXu Kuohai if (!addr) { 2356b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_nop(); 2357b2ad54e1SXu Kuohai return 0; 2358b2ad54e1SXu Kuohai } 2359b2ad54e1SXu Kuohai 2360b2ad54e1SXu Kuohai if (is_long_jump(ip, addr)) 2361b2ad54e1SXu Kuohai target = plt; 2362b2ad54e1SXu Kuohai else 2363b2ad54e1SXu Kuohai target = addr; 2364b2ad54e1SXu Kuohai 2365b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_branch_imm((unsigned long)ip, 2366b2ad54e1SXu Kuohai (unsigned long)target, 2367b2ad54e1SXu Kuohai type); 2368b2ad54e1SXu Kuohai 2369b2ad54e1SXu Kuohai return *insn != AARCH64_BREAK_FAULT ? 0 : -EFAULT; 2370b2ad54e1SXu Kuohai } 2371b2ad54e1SXu Kuohai 2372b2ad54e1SXu Kuohai /* Replace the branch instruction from @ip to @old_addr in a bpf prog or a bpf 2373b2ad54e1SXu Kuohai * trampoline with the branch instruction from @ip to @new_addr. If @old_addr 2374b2ad54e1SXu Kuohai * or @new_addr is NULL, the old or new instruction is NOP. 2375b2ad54e1SXu Kuohai * 2376b2ad54e1SXu Kuohai * When @ip is the bpf prog entry, a bpf trampoline is being attached or 2377b2ad54e1SXu Kuohai * detached. Since bpf trampoline and bpf prog are allocated separately with 2378b2ad54e1SXu Kuohai * vmalloc, the address distance may exceed 128MB, the maximum branch range. 2379b2ad54e1SXu Kuohai * So long jump should be handled. 2380b2ad54e1SXu Kuohai * 2381b2ad54e1SXu Kuohai * When a bpf prog is constructed, a plt pointing to empty trampoline 2382b2ad54e1SXu Kuohai * dummy_tramp is placed at the end: 2383b2ad54e1SXu Kuohai * 2384b2ad54e1SXu Kuohai * bpf_prog: 2385b2ad54e1SXu Kuohai * mov x9, lr 2386b2ad54e1SXu Kuohai * nop // patchsite 2387b2ad54e1SXu Kuohai * ... 2388b2ad54e1SXu Kuohai * ret 2389b2ad54e1SXu Kuohai * 2390b2ad54e1SXu Kuohai * plt: 2391b2ad54e1SXu Kuohai * ldr x10, target 2392b2ad54e1SXu Kuohai * br x10 2393b2ad54e1SXu Kuohai * target: 2394b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2395b2ad54e1SXu Kuohai * 2396b2ad54e1SXu Kuohai * This is also the state when no trampoline is attached. 2397b2ad54e1SXu Kuohai * 2398b2ad54e1SXu Kuohai * When a short-jump bpf trampoline is attached, the patchsite is patched 2399b2ad54e1SXu Kuohai * to a bl instruction to the trampoline directly: 2400b2ad54e1SXu Kuohai * 2401b2ad54e1SXu Kuohai * bpf_prog: 2402b2ad54e1SXu Kuohai * mov x9, lr 2403b2ad54e1SXu Kuohai * bl <short-jump bpf trampoline address> // patchsite 2404b2ad54e1SXu Kuohai * ... 2405b2ad54e1SXu Kuohai * ret 2406b2ad54e1SXu Kuohai * 2407b2ad54e1SXu Kuohai * plt: 2408b2ad54e1SXu Kuohai * ldr x10, target 2409b2ad54e1SXu Kuohai * br x10 2410b2ad54e1SXu Kuohai * target: 2411b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2412b2ad54e1SXu Kuohai * 2413b2ad54e1SXu Kuohai * When a long-jump bpf trampoline is attached, the plt target is filled with 2414b2ad54e1SXu Kuohai * the trampoline address and the patchsite is patched to a bl instruction to 2415b2ad54e1SXu Kuohai * the plt: 2416b2ad54e1SXu Kuohai * 2417b2ad54e1SXu Kuohai * bpf_prog: 2418b2ad54e1SXu Kuohai * mov x9, lr 2419b2ad54e1SXu Kuohai * bl plt // patchsite 2420b2ad54e1SXu Kuohai * ... 2421b2ad54e1SXu Kuohai * ret 2422b2ad54e1SXu Kuohai * 2423b2ad54e1SXu Kuohai * plt: 2424b2ad54e1SXu Kuohai * ldr x10, target 2425b2ad54e1SXu Kuohai * br x10 2426b2ad54e1SXu Kuohai * target: 2427b2ad54e1SXu Kuohai * .quad <long-jump bpf trampoline address> // plt target 2428b2ad54e1SXu Kuohai * 2429b2ad54e1SXu Kuohai * The dummy_tramp is used to prevent another CPU from jumping to unknown 2430b2ad54e1SXu Kuohai * locations during the patching process, making the patching process easier. 2431b2ad54e1SXu Kuohai */ 2432b2ad54e1SXu Kuohai int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, 2433b2ad54e1SXu Kuohai void *old_addr, void *new_addr) 2434b2ad54e1SXu Kuohai { 2435b2ad54e1SXu Kuohai int ret; 2436b2ad54e1SXu Kuohai u32 old_insn; 2437b2ad54e1SXu Kuohai u32 new_insn; 2438b2ad54e1SXu Kuohai u32 replaced; 2439b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 2440b2ad54e1SXu Kuohai unsigned long size = 0UL; 2441b2ad54e1SXu Kuohai unsigned long offset = ~0UL; 2442b2ad54e1SXu Kuohai enum aarch64_insn_branch_type branch_type; 2443b2ad54e1SXu Kuohai char namebuf[KSYM_NAME_LEN]; 2444b2ad54e1SXu Kuohai void *image = NULL; 2445b2ad54e1SXu Kuohai u64 plt_target = 0ULL; 2446b2ad54e1SXu Kuohai bool poking_bpf_entry; 2447b2ad54e1SXu Kuohai 2448b2ad54e1SXu Kuohai if (!__bpf_address_lookup((unsigned long)ip, &size, &offset, namebuf)) 2449b2ad54e1SXu Kuohai /* Only poking bpf text is supported. Since kernel function 2450b2ad54e1SXu Kuohai * entry is set up by ftrace, we reply on ftrace to poke kernel 2451b2ad54e1SXu Kuohai * functions. 2452b2ad54e1SXu Kuohai */ 2453b2ad54e1SXu Kuohai return -ENOTSUPP; 2454b2ad54e1SXu Kuohai 2455b2ad54e1SXu Kuohai image = ip - offset; 2456b2ad54e1SXu Kuohai /* zero offset means we're poking bpf prog entry */ 2457b2ad54e1SXu Kuohai poking_bpf_entry = (offset == 0UL); 2458b2ad54e1SXu Kuohai 2459b2ad54e1SXu Kuohai /* bpf prog entry, find plt and the real patchsite */ 2460b2ad54e1SXu Kuohai if (poking_bpf_entry) { 2461b2ad54e1SXu Kuohai /* plt locates at the end of bpf prog */ 2462b2ad54e1SXu Kuohai plt = image + size - PLT_TARGET_OFFSET; 2463b2ad54e1SXu Kuohai 2464b2ad54e1SXu Kuohai /* skip to the nop instruction in bpf prog entry: 2465b2ad54e1SXu Kuohai * bti c // if BTI enabled 2466b2ad54e1SXu Kuohai * mov x9, x30 2467b2ad54e1SXu Kuohai * nop 2468b2ad54e1SXu Kuohai */ 2469b2ad54e1SXu Kuohai ip = image + POKE_OFFSET * AARCH64_INSN_SIZE; 2470b2ad54e1SXu Kuohai } 2471b2ad54e1SXu Kuohai 2472b2ad54e1SXu Kuohai /* long jump is only possible at bpf prog entry */ 2473b2ad54e1SXu Kuohai if (WARN_ON((is_long_jump(ip, new_addr) || is_long_jump(ip, old_addr)) && 2474b2ad54e1SXu Kuohai !poking_bpf_entry)) 2475b2ad54e1SXu Kuohai return -EINVAL; 2476b2ad54e1SXu Kuohai 2477b2ad54e1SXu Kuohai if (poke_type == BPF_MOD_CALL) 2478b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_LINK; 2479b2ad54e1SXu Kuohai else 2480b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_NOLINK; 2481b2ad54e1SXu Kuohai 2482b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, old_addr, plt, &old_insn) < 0) 2483b2ad54e1SXu Kuohai return -EFAULT; 2484b2ad54e1SXu Kuohai 2485b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, new_addr, plt, &new_insn) < 0) 2486b2ad54e1SXu Kuohai return -EFAULT; 2487b2ad54e1SXu Kuohai 2488b2ad54e1SXu Kuohai if (is_long_jump(ip, new_addr)) 2489b2ad54e1SXu Kuohai plt_target = (u64)new_addr; 2490b2ad54e1SXu Kuohai else if (is_long_jump(ip, old_addr)) 2491b2ad54e1SXu Kuohai /* if the old target is a long jump and the new target is not, 2492b2ad54e1SXu Kuohai * restore the plt target to dummy_tramp, so there is always a 2493b2ad54e1SXu Kuohai * legal and harmless address stored in plt target, and we'll 2494b2ad54e1SXu Kuohai * never jump from plt to an unknown place. 2495b2ad54e1SXu Kuohai */ 2496b2ad54e1SXu Kuohai plt_target = (u64)&dummy_tramp; 2497b2ad54e1SXu Kuohai 2498b2ad54e1SXu Kuohai if (plt_target) { 2499b2ad54e1SXu Kuohai /* non-zero plt_target indicates we're patching a bpf prog, 2500b2ad54e1SXu Kuohai * which is read only. 2501b2ad54e1SXu Kuohai */ 2502b2ad54e1SXu Kuohai if (set_memory_rw(PAGE_MASK & ((uintptr_t)&plt->target), 1)) 2503b2ad54e1SXu Kuohai return -EFAULT; 2504b2ad54e1SXu Kuohai WRITE_ONCE(plt->target, plt_target); 2505b2ad54e1SXu Kuohai set_memory_ro(PAGE_MASK & ((uintptr_t)&plt->target), 1); 2506b2ad54e1SXu Kuohai /* since plt target points to either the new trampoline 2507b2ad54e1SXu Kuohai * or dummy_tramp, even if another CPU reads the old plt 2508b2ad54e1SXu Kuohai * target value before fetching the bl instruction to plt, 2509b2ad54e1SXu Kuohai * it will be brought back by dummy_tramp, so no barrier is 2510b2ad54e1SXu Kuohai * required here. 2511b2ad54e1SXu Kuohai */ 2512b2ad54e1SXu Kuohai } 2513b2ad54e1SXu Kuohai 2514b2ad54e1SXu Kuohai /* if the old target and the new target are both long jumps, no 2515b2ad54e1SXu Kuohai * patching is required 2516b2ad54e1SXu Kuohai */ 2517b2ad54e1SXu Kuohai if (old_insn == new_insn) 2518b2ad54e1SXu Kuohai return 0; 2519b2ad54e1SXu Kuohai 2520b2ad54e1SXu Kuohai mutex_lock(&text_mutex); 2521b2ad54e1SXu Kuohai if (aarch64_insn_read(ip, &replaced)) { 2522b2ad54e1SXu Kuohai ret = -EFAULT; 2523b2ad54e1SXu Kuohai goto out; 2524b2ad54e1SXu Kuohai } 2525b2ad54e1SXu Kuohai 2526b2ad54e1SXu Kuohai if (replaced != old_insn) { 2527b2ad54e1SXu Kuohai ret = -EFAULT; 2528b2ad54e1SXu Kuohai goto out; 2529b2ad54e1SXu Kuohai } 2530b2ad54e1SXu Kuohai 2531b2ad54e1SXu Kuohai /* We call aarch64_insn_patch_text_nosync() to replace instruction 2532b2ad54e1SXu Kuohai * atomically, so no other CPUs will fetch a half-new and half-old 2533b2ad54e1SXu Kuohai * instruction. But there is chance that another CPU executes the 2534b2ad54e1SXu Kuohai * old instruction after the patching operation finishes (e.g., 2535b2ad54e1SXu Kuohai * pipeline not flushed, or icache not synchronized yet). 2536b2ad54e1SXu Kuohai * 2537b2ad54e1SXu Kuohai * 1. when a new trampoline is attached, it is not a problem for 2538b2ad54e1SXu Kuohai * different CPUs to jump to different trampolines temporarily. 2539b2ad54e1SXu Kuohai * 2540b2ad54e1SXu Kuohai * 2. when an old trampoline is freed, we should wait for all other 2541b2ad54e1SXu Kuohai * CPUs to exit the trampoline and make sure the trampoline is no 2542b2ad54e1SXu Kuohai * longer reachable, since bpf_tramp_image_put() function already 2543b2ad54e1SXu Kuohai * uses percpu_ref and task-based rcu to do the sync, no need to call 2544b2ad54e1SXu Kuohai * the sync version here, see bpf_tramp_image_put() for details. 2545b2ad54e1SXu Kuohai */ 2546b2ad54e1SXu Kuohai ret = aarch64_insn_patch_text_nosync(ip, new_insn); 2547b2ad54e1SXu Kuohai out: 2548b2ad54e1SXu Kuohai mutex_unlock(&text_mutex); 2549b2ad54e1SXu Kuohai 2550b2ad54e1SXu Kuohai return ret; 2551b2ad54e1SXu Kuohai } 255218a45f12SHou Tao 255318a45f12SHou Tao bool bpf_jit_supports_ptr_xchg(void) 255418a45f12SHou Tao { 255518a45f12SHou Tao return true; 255618a45f12SHou Tao } 255722fc0e80SPuranjay Mohan 255822fc0e80SPuranjay Mohan bool bpf_jit_supports_exceptions(void) 255922fc0e80SPuranjay Mohan { 256022fc0e80SPuranjay Mohan /* We unwind through both kernel frames starting from within bpf_throw 256122fc0e80SPuranjay Mohan * call and BPF frames. Therefore we require FP unwinder to be enabled 256222fc0e80SPuranjay Mohan * to walk kernel frames and reach BPF frames in the stack trace. 256322fc0e80SPuranjay Mohan * ARM64 kernel is aways compiled with CONFIG_FRAME_POINTER=y 256422fc0e80SPuranjay Mohan */ 256522fc0e80SPuranjay Mohan return true; 256622fc0e80SPuranjay Mohan } 25671dad391dSPuranjay Mohan 25684dd31243SPuranjay Mohan bool bpf_jit_supports_arena(void) 25694dd31243SPuranjay Mohan { 25704dd31243SPuranjay Mohan return true; 25714dd31243SPuranjay Mohan } 25724dd31243SPuranjay Mohan 2573e612b5c1SPuranjay Mohan bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena) 2574e612b5c1SPuranjay Mohan { 2575e612b5c1SPuranjay Mohan if (!in_arena) 2576e612b5c1SPuranjay Mohan return true; 2577e612b5c1SPuranjay Mohan switch (insn->code) { 2578e612b5c1SPuranjay Mohan case BPF_STX | BPF_ATOMIC | BPF_W: 2579e612b5c1SPuranjay Mohan case BPF_STX | BPF_ATOMIC | BPF_DW: 2580e612b5c1SPuranjay Mohan if (!cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) 2581e612b5c1SPuranjay Mohan return false; 2582e612b5c1SPuranjay Mohan } 2583e612b5c1SPuranjay Mohan return true; 2584e612b5c1SPuranjay Mohan } 2585e612b5c1SPuranjay Mohan 25867a4c3222SPuranjay Mohan bool bpf_jit_supports_percpu_insn(void) 25877a4c3222SPuranjay Mohan { 25887a4c3222SPuranjay Mohan return true; 25897a4c3222SPuranjay Mohan } 25907a4c3222SPuranjay Mohan 2591*75fe4c0bSPuranjay Mohan bool bpf_jit_inlines_helper_call(s32 imm) 2592*75fe4c0bSPuranjay Mohan { 2593*75fe4c0bSPuranjay Mohan switch (imm) { 2594*75fe4c0bSPuranjay Mohan case BPF_FUNC_get_smp_processor_id: 2595*75fe4c0bSPuranjay Mohan return true; 2596*75fe4c0bSPuranjay Mohan default: 2597*75fe4c0bSPuranjay Mohan return false; 2598*75fe4c0bSPuranjay Mohan } 2599*75fe4c0bSPuranjay Mohan } 2600*75fe4c0bSPuranjay Mohan 26011dad391dSPuranjay Mohan void bpf_jit_free(struct bpf_prog *prog) 26021dad391dSPuranjay Mohan { 26031dad391dSPuranjay Mohan if (prog->jited) { 26041dad391dSPuranjay Mohan struct arm64_jit_data *jit_data = prog->aux->jit_data; 26051dad391dSPuranjay Mohan struct bpf_binary_header *hdr; 26061dad391dSPuranjay Mohan 26071dad391dSPuranjay Mohan /* 26081dad391dSPuranjay Mohan * If we fail the final pass of JIT (from jit_subprogs), 26091dad391dSPuranjay Mohan * the program may not be finalized yet. Call finalize here 26101dad391dSPuranjay Mohan * before freeing it. 26111dad391dSPuranjay Mohan */ 26121dad391dSPuranjay Mohan if (jit_data) { 26131dad391dSPuranjay Mohan bpf_arch_text_copy(&jit_data->ro_header->size, &jit_data->header->size, 26141dad391dSPuranjay Mohan sizeof(jit_data->header->size)); 26151dad391dSPuranjay Mohan kfree(jit_data); 26161dad391dSPuranjay Mohan } 26171dad391dSPuranjay Mohan hdr = bpf_jit_binary_pack_hdr(prog); 26181dad391dSPuranjay Mohan bpf_jit_binary_pack_free(hdr, NULL); 26191dad391dSPuranjay Mohan WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog)); 26201dad391dSPuranjay Mohan } 26211dad391dSPuranjay Mohan 26221dad391dSPuranjay Mohan bpf_prog_unlock_free(prog); 26231dad391dSPuranjay Mohan } 2624