1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2e54bcde3SZi Shen Lim /* 3e54bcde3SZi Shen Lim * BPF JIT compiler for ARM64 4e54bcde3SZi Shen Lim * 542ff712bSZi Shen Lim * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com> 6e54bcde3SZi Shen Lim */ 7e54bcde3SZi Shen Lim 8e54bcde3SZi Shen Lim #define pr_fmt(fmt) "bpf_jit: " fmt 9e54bcde3SZi Shen Lim 1080083428SJean-Philippe Brucker #include <linux/bitfield.h> 11ddb55992SZi Shen Lim #include <linux/bpf.h> 12e54bcde3SZi Shen Lim #include <linux/filter.h> 13b2ad54e1SXu Kuohai #include <linux/memory.h> 14e54bcde3SZi Shen Lim #include <linux/printk.h> 15e54bcde3SZi Shen Lim #include <linux/slab.h> 16b569c1c6SDaniel Borkmann 17d6e2cc56SMark Rutland #include <asm/asm-extable.h> 18e54bcde3SZi Shen Lim #include <asm/byteorder.h> 19e54bcde3SZi Shen Lim #include <asm/cacheflush.h> 20b569c1c6SDaniel Borkmann #include <asm/debug-monitors.h> 213e00e39dSMark Rutland #include <asm/insn.h> 22b2ad54e1SXu Kuohai #include <asm/patching.h> 23d4bbc30bSLaura Abbott #include <asm/set_memory.h> 24e54bcde3SZi Shen Lim 25e54bcde3SZi Shen Lim #include "bpf_jit.h" 26e54bcde3SZi Shen Lim 2726eb042eSDaniel Borkmann #define TMP_REG_1 (MAX_BPF_JIT_REG + 0) 2826eb042eSDaniel Borkmann #define TMP_REG_2 (MAX_BPF_JIT_REG + 1) 29ddb55992SZi Shen Lim #define TCALL_CNT (MAX_BPF_JIT_REG + 2) 307005cadeSDaniel Borkmann #define TMP_REG_3 (MAX_BPF_JIT_REG + 3) 315b3d19b9SXu Kuohai #define FP_BOTTOM (MAX_BPF_JIT_REG + 4) 32e54bcde3SZi Shen Lim 331902472bSHou Tao #define check_imm(bits, imm) do { \ 341902472bSHou Tao if ((((imm) > 0) && ((imm) >> (bits))) || \ 351902472bSHou Tao (((imm) < 0) && (~(imm) >> (bits)))) { \ 361902472bSHou Tao pr_info("[%2d] imm=%d(0x%x) out of range\n", \ 371902472bSHou Tao i, imm, imm); \ 381902472bSHou Tao return -EINVAL; \ 391902472bSHou Tao } \ 401902472bSHou Tao } while (0) 411902472bSHou Tao #define check_imm19(imm) check_imm(19, imm) 421902472bSHou Tao #define check_imm26(imm) check_imm(26, imm) 431902472bSHou Tao 44e54bcde3SZi Shen Lim /* Map BPF registers to A64 registers */ 45e54bcde3SZi Shen Lim static const int bpf2a64[] = { 46e54bcde3SZi Shen Lim /* return value from in-kernel function, and exit value from eBPF */ 47e54bcde3SZi Shen Lim [BPF_REG_0] = A64_R(7), 48e54bcde3SZi Shen Lim /* arguments from eBPF program to in-kernel function */ 49e54bcde3SZi Shen Lim [BPF_REG_1] = A64_R(0), 50e54bcde3SZi Shen Lim [BPF_REG_2] = A64_R(1), 51e54bcde3SZi Shen Lim [BPF_REG_3] = A64_R(2), 52e54bcde3SZi Shen Lim [BPF_REG_4] = A64_R(3), 53e54bcde3SZi Shen Lim [BPF_REG_5] = A64_R(4), 54e54bcde3SZi Shen Lim /* callee saved registers that in-kernel function will preserve */ 55e54bcde3SZi Shen Lim [BPF_REG_6] = A64_R(19), 56e54bcde3SZi Shen Lim [BPF_REG_7] = A64_R(20), 57e54bcde3SZi Shen Lim [BPF_REG_8] = A64_R(21), 58e54bcde3SZi Shen Lim [BPF_REG_9] = A64_R(22), 59e54bcde3SZi Shen Lim /* read-only frame pointer to access stack */ 60ec0738dbSYang Shi [BPF_REG_FP] = A64_R(25), 6106edc59cSChristoph Hellwig /* temporary registers for BPF JIT */ 624c1cd4fdSYang Shi [TMP_REG_1] = A64_R(10), 634c1cd4fdSYang Shi [TMP_REG_2] = A64_R(11), 647005cadeSDaniel Borkmann [TMP_REG_3] = A64_R(12), 65ddb55992SZi Shen Lim /* tail_call_cnt */ 66ddb55992SZi Shen Lim [TCALL_CNT] = A64_R(26), 6726eb042eSDaniel Borkmann /* temporary register for blinding constants */ 6826eb042eSDaniel Borkmann [BPF_REG_AX] = A64_R(9), 695b3d19b9SXu Kuohai [FP_BOTTOM] = A64_R(27), 70e54bcde3SZi Shen Lim }; 71e54bcde3SZi Shen Lim 72e54bcde3SZi Shen Lim struct jit_ctx { 73e54bcde3SZi Shen Lim const struct bpf_prog *prog; 74e54bcde3SZi Shen Lim int idx; 7551c9fbb1SZi Shen Lim int epilogue_offset; 76e54bcde3SZi Shen Lim int *offset; 7780083428SJean-Philippe Brucker int exentry_idx; 78425e1ed7SLuc Van Oostenryck __le32 *image; 79f1c9eed7SDaniel Borkmann u32 stack_size; 805b3d19b9SXu Kuohai int fpb_offset; 81e54bcde3SZi Shen Lim }; 82e54bcde3SZi Shen Lim 83b2ad54e1SXu Kuohai struct bpf_plt { 84b2ad54e1SXu Kuohai u32 insn_ldr; /* load target */ 85b2ad54e1SXu Kuohai u32 insn_br; /* branch to target */ 86b2ad54e1SXu Kuohai u64 target; /* target value */ 87b2ad54e1SXu Kuohai }; 88b2ad54e1SXu Kuohai 89b2ad54e1SXu Kuohai #define PLT_TARGET_SIZE sizeof_field(struct bpf_plt, target) 90b2ad54e1SXu Kuohai #define PLT_TARGET_OFFSET offsetof(struct bpf_plt, target) 91b2ad54e1SXu Kuohai 92e54bcde3SZi Shen Lim static inline void emit(const u32 insn, struct jit_ctx *ctx) 93e54bcde3SZi Shen Lim { 94e54bcde3SZi Shen Lim if (ctx->image != NULL) 95e54bcde3SZi Shen Lim ctx->image[ctx->idx] = cpu_to_le32(insn); 96e54bcde3SZi Shen Lim 97e54bcde3SZi Shen Lim ctx->idx++; 98e54bcde3SZi Shen Lim } 99e54bcde3SZi Shen Lim 100e54bcde3SZi Shen Lim static inline void emit_a64_mov_i(const int is64, const int reg, 101e54bcde3SZi Shen Lim const s32 val, struct jit_ctx *ctx) 102e54bcde3SZi Shen Lim { 103e54bcde3SZi Shen Lim u16 hi = val >> 16; 104e54bcde3SZi Shen Lim u16 lo = val & 0xffff; 105e54bcde3SZi Shen Lim 106e54bcde3SZi Shen Lim if (hi & 0x8000) { 107e54bcde3SZi Shen Lim if (hi == 0xffff) { 108e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx); 109e54bcde3SZi Shen Lim } else { 110e54bcde3SZi Shen Lim emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx); 1116d2eea6fSDaniel Borkmann if (lo != 0xffff) 112e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, lo, 0), ctx); 113e54bcde3SZi Shen Lim } 114e54bcde3SZi Shen Lim } else { 115e54bcde3SZi Shen Lim emit(A64_MOVZ(is64, reg, lo, 0), ctx); 116e54bcde3SZi Shen Lim if (hi) 117e54bcde3SZi Shen Lim emit(A64_MOVK(is64, reg, hi, 16), ctx); 118e54bcde3SZi Shen Lim } 119e54bcde3SZi Shen Lim } 120e54bcde3SZi Shen Lim 1216d2eea6fSDaniel Borkmann static int i64_i16_blocks(const u64 val, bool inverse) 1226d2eea6fSDaniel Borkmann { 1236d2eea6fSDaniel Borkmann return (((val >> 0) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1246d2eea6fSDaniel Borkmann (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1256d2eea6fSDaniel Borkmann (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 1266d2eea6fSDaniel Borkmann (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000)); 1276d2eea6fSDaniel Borkmann } 1286d2eea6fSDaniel Borkmann 1296d2eea6fSDaniel Borkmann static inline void emit_a64_mov_i64(const int reg, const u64 val, 1306d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1316d2eea6fSDaniel Borkmann { 1326d2eea6fSDaniel Borkmann u64 nrm_tmp = val, rev_tmp = ~val; 1336d2eea6fSDaniel Borkmann bool inverse; 1346d2eea6fSDaniel Borkmann int shift; 1356d2eea6fSDaniel Borkmann 1366d2eea6fSDaniel Borkmann if (!(nrm_tmp >> 32)) 1376d2eea6fSDaniel Borkmann return emit_a64_mov_i(0, reg, (u32)val, ctx); 1386d2eea6fSDaniel Borkmann 1396d2eea6fSDaniel Borkmann inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false); 1406d2eea6fSDaniel Borkmann shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) : 1416d2eea6fSDaniel Borkmann (fls64(nrm_tmp) - 1)), 16), 0); 1426d2eea6fSDaniel Borkmann if (inverse) 1436d2eea6fSDaniel Borkmann emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx); 1446d2eea6fSDaniel Borkmann else 1456d2eea6fSDaniel Borkmann emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1466d2eea6fSDaniel Borkmann shift -= 16; 1476d2eea6fSDaniel Borkmann while (shift >= 0) { 1486d2eea6fSDaniel Borkmann if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000)) 1496d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1506d2eea6fSDaniel Borkmann shift -= 16; 1516d2eea6fSDaniel Borkmann } 1526d2eea6fSDaniel Borkmann } 1536d2eea6fSDaniel Borkmann 154b2ad54e1SXu Kuohai static inline void emit_bti(u32 insn, struct jit_ctx *ctx) 155b2ad54e1SXu Kuohai { 156b2ad54e1SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) 157b2ad54e1SXu Kuohai emit(insn, ctx); 158b2ad54e1SXu Kuohai } 159b2ad54e1SXu Kuohai 1606d2eea6fSDaniel Borkmann /* 161cc2b8ed1SArd Biesheuvel * Kernel addresses in the vmalloc space use at most 48 bits, and the 162cc2b8ed1SArd Biesheuvel * remaining bits are guaranteed to be 0x1. So we can compose the address 163cc2b8ed1SArd Biesheuvel * with a fixed length movn/movk/movk sequence. 1646d2eea6fSDaniel Borkmann */ 1656d2eea6fSDaniel Borkmann static inline void emit_addr_mov_i64(const int reg, const u64 val, 1666d2eea6fSDaniel Borkmann struct jit_ctx *ctx) 1676d2eea6fSDaniel Borkmann { 1686d2eea6fSDaniel Borkmann u64 tmp = val; 1696d2eea6fSDaniel Borkmann int shift = 0; 1706d2eea6fSDaniel Borkmann 171cc2b8ed1SArd Biesheuvel emit(A64_MOVN(1, reg, ~tmp & 0xffff, shift), ctx); 172cc2b8ed1SArd Biesheuvel while (shift < 32) { 1736d2eea6fSDaniel Borkmann tmp >>= 16; 1746d2eea6fSDaniel Borkmann shift += 16; 1756d2eea6fSDaniel Borkmann emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx); 1766d2eea6fSDaniel Borkmann } 1776d2eea6fSDaniel Borkmann } 1786d2eea6fSDaniel Borkmann 179efc9909fSXu Kuohai static inline void emit_call(u64 target, struct jit_ctx *ctx) 180efc9909fSXu Kuohai { 181efc9909fSXu Kuohai u8 tmp = bpf2a64[TMP_REG_1]; 182efc9909fSXu Kuohai 183efc9909fSXu Kuohai emit_addr_mov_i64(tmp, target, ctx); 184efc9909fSXu Kuohai emit(A64_BLR(tmp), ctx); 185efc9909fSXu Kuohai } 186efc9909fSXu Kuohai 18732f6865cSIlias Apalodimas static inline int bpf2a64_offset(int bpf_insn, int off, 188e54bcde3SZi Shen Lim const struct jit_ctx *ctx) 189e54bcde3SZi Shen Lim { 19032f6865cSIlias Apalodimas /* BPF JMP offset is relative to the next instruction */ 19132f6865cSIlias Apalodimas bpf_insn++; 19232f6865cSIlias Apalodimas /* 19332f6865cSIlias Apalodimas * Whereas arm64 branch instructions encode the offset 19432f6865cSIlias Apalodimas * from the branch itself, so we must subtract 1 from the 19532f6865cSIlias Apalodimas * instruction offset. 19632f6865cSIlias Apalodimas */ 19732f6865cSIlias Apalodimas return ctx->offset[bpf_insn + off] - (ctx->offset[bpf_insn] - 1); 198e54bcde3SZi Shen Lim } 199e54bcde3SZi Shen Lim 200b569c1c6SDaniel Borkmann static void jit_fill_hole(void *area, unsigned int size) 201b569c1c6SDaniel Borkmann { 202425e1ed7SLuc Van Oostenryck __le32 *ptr; 203b569c1c6SDaniel Borkmann /* We are guaranteed to have aligned memory. */ 204b569c1c6SDaniel Borkmann for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 205b569c1c6SDaniel Borkmann *ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT); 206b569c1c6SDaniel Borkmann } 207b569c1c6SDaniel Borkmann 208e54bcde3SZi Shen Lim static inline int epilogue_offset(const struct jit_ctx *ctx) 209e54bcde3SZi Shen Lim { 21051c9fbb1SZi Shen Lim int to = ctx->epilogue_offset; 21151c9fbb1SZi Shen Lim int from = ctx->idx; 212e54bcde3SZi Shen Lim 213e54bcde3SZi Shen Lim return to - from; 214e54bcde3SZi Shen Lim } 215e54bcde3SZi Shen Lim 216fd868f14SLuke Nelson static bool is_addsub_imm(u32 imm) 217fd868f14SLuke Nelson { 218fd868f14SLuke Nelson /* Either imm12 or shifted imm12. */ 219fd868f14SLuke Nelson return !(imm & ~0xfff) || !(imm & ~0xfff000); 220fd868f14SLuke Nelson } 221fd868f14SLuke Nelson 2227db6c0f1SXu Kuohai /* 2237db6c0f1SXu Kuohai * There are 3 types of AArch64 LDR/STR (immediate) instruction: 2247db6c0f1SXu Kuohai * Post-index, Pre-index, Unsigned offset. 2257db6c0f1SXu Kuohai * 2267db6c0f1SXu Kuohai * For BPF ldr/str, the "unsigned offset" type is sufficient. 2277db6c0f1SXu Kuohai * 2287db6c0f1SXu Kuohai * "Unsigned offset" type LDR(immediate) format: 2297db6c0f1SXu Kuohai * 2307db6c0f1SXu Kuohai * 3 2 1 0 2317db6c0f1SXu Kuohai * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 2327db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2337db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 1| imm12 | Rn | Rt | 2347db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2357db6c0f1SXu Kuohai * scale 2367db6c0f1SXu Kuohai * 2377db6c0f1SXu Kuohai * "Unsigned offset" type STR(immediate) format: 2387db6c0f1SXu Kuohai * 3 2 1 0 2397db6c0f1SXu Kuohai * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 2407db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2417db6c0f1SXu Kuohai * |x x|1 1 1 0 0 1 0 0| imm12 | Rn | Rt | 2427db6c0f1SXu Kuohai * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 2437db6c0f1SXu Kuohai * scale 2447db6c0f1SXu Kuohai * 2457db6c0f1SXu Kuohai * The offset is calculated from imm12 and scale in the following way: 2467db6c0f1SXu Kuohai * 2477db6c0f1SXu Kuohai * offset = (u64)imm12 << scale 2487db6c0f1SXu Kuohai */ 2495b3d19b9SXu Kuohai static bool is_lsi_offset(int offset, int scale) 2507db6c0f1SXu Kuohai { 2517db6c0f1SXu Kuohai if (offset < 0) 2527db6c0f1SXu Kuohai return false; 2537db6c0f1SXu Kuohai 2547db6c0f1SXu Kuohai if (offset > (0xFFF << scale)) 2557db6c0f1SXu Kuohai return false; 2567db6c0f1SXu Kuohai 2577db6c0f1SXu Kuohai if (offset & ((1 << scale) - 1)) 2587db6c0f1SXu Kuohai return false; 2597db6c0f1SXu Kuohai 2607db6c0f1SXu Kuohai return true; 2617db6c0f1SXu Kuohai } 2627db6c0f1SXu Kuohai 263b2ad54e1SXu Kuohai /* generated prologue: 264b2ad54e1SXu Kuohai * bti c // if CONFIG_ARM64_BTI_KERNEL 265b2ad54e1SXu Kuohai * mov x9, lr 266b2ad54e1SXu Kuohai * nop // POKE_OFFSET 267b2ad54e1SXu Kuohai * paciasp // if CONFIG_ARM64_PTR_AUTH_KERNEL 268b2ad54e1SXu Kuohai * stp x29, lr, [sp, #-16]! 269b2ad54e1SXu Kuohai * mov x29, sp 270b2ad54e1SXu Kuohai * stp x19, x20, [sp, #-16]! 271b2ad54e1SXu Kuohai * stp x21, x22, [sp, #-16]! 272b2ad54e1SXu Kuohai * stp x25, x26, [sp, #-16]! 273b2ad54e1SXu Kuohai * stp x27, x28, [sp, #-16]! 274b2ad54e1SXu Kuohai * mov x25, sp 275b2ad54e1SXu Kuohai * mov tcc, #0 276b2ad54e1SXu Kuohai * // PROLOGUE_OFFSET 277b2ad54e1SXu Kuohai */ 278b2ad54e1SXu Kuohai 279b2ad54e1SXu Kuohai #define BTI_INSNS (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) ? 1 : 0) 280b2ad54e1SXu Kuohai #define PAC_INSNS (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) ? 1 : 0) 281b2ad54e1SXu Kuohai 282b2ad54e1SXu Kuohai /* Offset of nop instruction in bpf prog entry to be poked */ 283b2ad54e1SXu Kuohai #define POKE_OFFSET (BTI_INSNS + 1) 284b2ad54e1SXu Kuohai 285a2284d91SDaniel Borkmann /* Tail call offset to jump into */ 286b2ad54e1SXu Kuohai #define PROLOGUE_OFFSET (BTI_INSNS + 2 + PAC_INSNS + 8) 287ddb55992SZi Shen Lim 288*22fc0e80SPuranjay Mohan static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf, 289*22fc0e80SPuranjay Mohan bool is_exception_cb) 290e54bcde3SZi Shen Lim { 291f1c9eed7SDaniel Borkmann const struct bpf_prog *prog = ctx->prog; 2929af27da6SKumar Kartikeya Dwivedi const bool is_main_prog = !bpf_is_subprog(prog); 293e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 294e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 295e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 296e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 297e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 298ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 2995b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 300ddb55992SZi Shen Lim const int idx0 = ctx->idx; 301ddb55992SZi Shen Lim int cur_offset; 302e54bcde3SZi Shen Lim 303ec0738dbSYang Shi /* 304ec0738dbSYang Shi * BPF prog stack layout 305ec0738dbSYang Shi * 306ec0738dbSYang Shi * high 307ec0738dbSYang Shi * original A64_SP => 0:+-----+ BPF prologue 308ec0738dbSYang Shi * |FP/LR| 309ec0738dbSYang Shi * current A64_FP => -16:+-----+ 310ec0738dbSYang Shi * | ... | callee saved registers 3114c1cd4fdSYang Shi * BPF fp register => -64:+-----+ <= (BPF_FP) 312ec0738dbSYang Shi * | | 313ec0738dbSYang Shi * | ... | BPF prog stack 314ec0738dbSYang Shi * | | 315f1c9eed7SDaniel Borkmann * +-----+ <= (BPF_FP - prog->aux->stack_depth) 31609ece3d0SDaniel Borkmann * |RSVD | padding 317f1c9eed7SDaniel Borkmann * current A64_SP => +-----+ <= (BPF_FP - ctx->stack_size) 318ec0738dbSYang Shi * | | 319ec0738dbSYang Shi * | ... | Function call stack 320ec0738dbSYang Shi * | | 321ec0738dbSYang Shi * +-----+ 322ec0738dbSYang Shi * low 323ec0738dbSYang Shi * 324ec0738dbSYang Shi */ 325ec0738dbSYang Shi 326a3f25d61SAlexander Duyck /* bpf function may be invoked by 3 instruction types: 327a3f25d61SAlexander Duyck * 1. bl, attached via freplace to bpf prog via short jump 328a3f25d61SAlexander Duyck * 2. br, attached via freplace to bpf prog via long jump 329a3f25d61SAlexander Duyck * 3. blr, working as a function pointer, used by emit_call. 330a3f25d61SAlexander Duyck * So BTI_JC should used here to support both br and blr. 331a3f25d61SAlexander Duyck */ 332a3f25d61SAlexander Duyck emit_bti(A64_BTI_JC, ctx); 333b2ad54e1SXu Kuohai 334b2ad54e1SXu Kuohai emit(A64_MOV(1, A64_R(9), A64_LR), ctx); 335b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 336b2ad54e1SXu Kuohai 337*22fc0e80SPuranjay Mohan if (!is_exception_cb) { 338042152c2SXu Kuohai /* Sign lr */ 339042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 340042152c2SXu Kuohai emit(A64_PACIASP, ctx); 341ec0738dbSYang Shi /* Save FP and LR registers to stay align with ARM64 AAPCS */ 342ec0738dbSYang Shi emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 343ec0738dbSYang Shi emit(A64_MOV(1, A64_FP, A64_SP), ctx); 344ec0738dbSYang Shi 345ddb55992SZi Shen Lim /* Save callee-saved registers */ 346e54bcde3SZi Shen Lim emit(A64_PUSH(r6, r7, A64_SP), ctx); 347e54bcde3SZi Shen Lim emit(A64_PUSH(r8, r9, A64_SP), ctx); 348ddb55992SZi Shen Lim emit(A64_PUSH(fp, tcc, A64_SP), ctx); 3495b3d19b9SXu Kuohai emit(A64_PUSH(fpb, A64_R(28), A64_SP), ctx); 350*22fc0e80SPuranjay Mohan } else { 351*22fc0e80SPuranjay Mohan /* 352*22fc0e80SPuranjay Mohan * Exception callback receives FP of Main Program as third 353*22fc0e80SPuranjay Mohan * parameter 354*22fc0e80SPuranjay Mohan */ 355*22fc0e80SPuranjay Mohan emit(A64_MOV(1, A64_FP, A64_R(2)), ctx); 356*22fc0e80SPuranjay Mohan /* 357*22fc0e80SPuranjay Mohan * Main Program already pushed the frame record and the 358*22fc0e80SPuranjay Mohan * callee-saved registers. The exception callback will not push 359*22fc0e80SPuranjay Mohan * anything and re-use the main program's stack. 360*22fc0e80SPuranjay Mohan * 361*22fc0e80SPuranjay Mohan * 10 registers are on the stack 362*22fc0e80SPuranjay Mohan */ 363*22fc0e80SPuranjay Mohan emit(A64_SUB_I(1, A64_SP, A64_FP, 80), ctx); 364*22fc0e80SPuranjay Mohan } 365e54bcde3SZi Shen Lim 366ddb55992SZi Shen Lim /* Set up BPF prog stack base register */ 367e54bcde3SZi Shen Lim emit(A64_MOV(1, fp, A64_SP), ctx); 368e54bcde3SZi Shen Lim 369d4609a5dSJakub Sitnicki if (!ebpf_from_cbpf && is_main_prog) { 370ddb55992SZi Shen Lim /* Initialize tail_call_cnt */ 371ddb55992SZi Shen Lim emit(A64_MOVZ(1, tcc, 0, 0), ctx); 372ddb55992SZi Shen Lim 373ddb55992SZi Shen Lim cur_offset = ctx->idx - idx0; 374ddb55992SZi Shen Lim if (cur_offset != PROLOGUE_OFFSET) { 375ddb55992SZi Shen Lim pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 376ddb55992SZi Shen Lim cur_offset, PROLOGUE_OFFSET); 377ddb55992SZi Shen Lim return -1; 378ddb55992SZi Shen Lim } 379fa76cfe6SMark Brown 380fa76cfe6SMark Brown /* BTI landing pad for the tail call, done with a BR */ 381b2ad54e1SXu Kuohai emit_bti(A64_BTI_J, ctx); 38256ea6a8bSDaniel Borkmann } 383a2284d91SDaniel Borkmann 384*22fc0e80SPuranjay Mohan /* 385*22fc0e80SPuranjay Mohan * Program acting as exception boundary should save all ARM64 386*22fc0e80SPuranjay Mohan * Callee-saved registers as the exception callback needs to recover 387*22fc0e80SPuranjay Mohan * all ARM64 Callee-saved registers in its epilogue. 388*22fc0e80SPuranjay Mohan */ 389*22fc0e80SPuranjay Mohan if (prog->aux->exception_boundary) { 390*22fc0e80SPuranjay Mohan /* 391*22fc0e80SPuranjay Mohan * As we are pushing two more registers, BPF_FP should be moved 392*22fc0e80SPuranjay Mohan * 16 bytes 393*22fc0e80SPuranjay Mohan */ 394*22fc0e80SPuranjay Mohan emit(A64_SUB_I(1, fp, fp, 16), ctx); 395*22fc0e80SPuranjay Mohan emit(A64_PUSH(A64_R(23), A64_R(24), A64_SP), ctx); 396*22fc0e80SPuranjay Mohan } 397*22fc0e80SPuranjay Mohan 3985b3d19b9SXu Kuohai emit(A64_SUB_I(1, fpb, fp, ctx->fpb_offset), ctx); 3995b3d19b9SXu Kuohai 4003f287098STiezhu Yang /* Stack must be multiples of 16B */ 4013f287098STiezhu Yang ctx->stack_size = round_up(prog->aux->stack_depth, 16); 402a2284d91SDaniel Borkmann 403a2284d91SDaniel Borkmann /* Set up function call stack */ 404a2284d91SDaniel Borkmann emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 405ddb55992SZi Shen Lim return 0; 406ddb55992SZi Shen Lim } 407ddb55992SZi Shen Lim 408ddb55992SZi Shen Lim static int out_offset = -1; /* initialized on the first pass of build_body() */ 409ddb55992SZi Shen Lim static int emit_bpf_tail_call(struct jit_ctx *ctx) 410ddb55992SZi Shen Lim { 411ddb55992SZi Shen Lim /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 412ddb55992SZi Shen Lim const u8 r2 = bpf2a64[BPF_REG_2]; 413ddb55992SZi Shen Lim const u8 r3 = bpf2a64[BPF_REG_3]; 414ddb55992SZi Shen Lim 415ddb55992SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 416ddb55992SZi Shen Lim const u8 prg = bpf2a64[TMP_REG_2]; 417ddb55992SZi Shen Lim const u8 tcc = bpf2a64[TCALL_CNT]; 418ddb55992SZi Shen Lim const int idx0 = ctx->idx; 419ddb55992SZi Shen Lim #define cur_offset (ctx->idx - idx0) 420ddb55992SZi Shen Lim #define jmp_offset (out_offset - (cur_offset)) 421ddb55992SZi Shen Lim size_t off; 422ddb55992SZi Shen Lim 423ddb55992SZi Shen Lim /* if (index >= array->map.max_entries) 424ddb55992SZi Shen Lim * goto out; 425ddb55992SZi Shen Lim */ 426ddb55992SZi Shen Lim off = offsetof(struct bpf_array, map.max_entries); 427ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 428ddb55992SZi Shen Lim emit(A64_LDR32(tmp, r2, tmp), ctx); 42916338a9bSDaniel Borkmann emit(A64_MOV(0, r3, r3), ctx); 430ddb55992SZi Shen Lim emit(A64_CMP(0, r3, tmp), ctx); 43116338a9bSDaniel Borkmann emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 432ddb55992SZi Shen Lim 433ebf7f6f0STiezhu Yang /* 434ebf7f6f0STiezhu Yang * if (tail_call_cnt >= MAX_TAIL_CALL_CNT) 435ddb55992SZi Shen Lim * goto out; 436ddb55992SZi Shen Lim * tail_call_cnt++; 437ddb55992SZi Shen Lim */ 438ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 439ddb55992SZi Shen Lim emit(A64_CMP(1, tcc, tmp), ctx); 440ebf7f6f0STiezhu Yang emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 441ddb55992SZi Shen Lim emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 442ddb55992SZi Shen Lim 443ddb55992SZi Shen Lim /* prog = array->ptrs[index]; 444ddb55992SZi Shen Lim * if (prog == NULL) 445ddb55992SZi Shen Lim * goto out; 446ddb55992SZi Shen Lim */ 447ddb55992SZi Shen Lim off = offsetof(struct bpf_array, ptrs); 448ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 449d8b54110SDaniel Borkmann emit(A64_ADD(1, tmp, r2, tmp), ctx); 450d8b54110SDaniel Borkmann emit(A64_LSL(1, prg, r3, 3), ctx); 451d8b54110SDaniel Borkmann emit(A64_LDR64(prg, tmp, prg), ctx); 452ddb55992SZi Shen Lim emit(A64_CBZ(1, prg, jmp_offset), ctx); 453ddb55992SZi Shen Lim 454a2284d91SDaniel Borkmann /* goto *(prog->bpf_func + prologue_offset); */ 455ddb55992SZi Shen Lim off = offsetof(struct bpf_prog, bpf_func); 456ddb55992SZi Shen Lim emit_a64_mov_i64(tmp, off, ctx); 457ddb55992SZi Shen Lim emit(A64_LDR64(tmp, prg, tmp), ctx); 458ddb55992SZi Shen Lim emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 459a2284d91SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 460ddb55992SZi Shen Lim emit(A64_BR(tmp), ctx); 461ddb55992SZi Shen Lim 462ddb55992SZi Shen Lim /* out: */ 463ddb55992SZi Shen Lim if (out_offset == -1) 464ddb55992SZi Shen Lim out_offset = cur_offset; 465ddb55992SZi Shen Lim if (cur_offset != out_offset) { 466ddb55992SZi Shen Lim pr_err_once("tail_call out_offset = %d, expected %d!\n", 467ddb55992SZi Shen Lim cur_offset, out_offset); 468ddb55992SZi Shen Lim return -1; 469ddb55992SZi Shen Lim } 470ddb55992SZi Shen Lim return 0; 471ddb55992SZi Shen Lim #undef cur_offset 472ddb55992SZi Shen Lim #undef jmp_offset 473e54bcde3SZi Shen Lim } 474e54bcde3SZi Shen Lim 4751902472bSHou Tao #ifdef CONFIG_ARM64_LSE_ATOMICS 4761902472bSHou Tao static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 4771902472bSHou Tao { 4781902472bSHou Tao const u8 code = insn->code; 4791902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 4801902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 4811902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 4821902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 4831902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 4841902472bSHou Tao const s16 off = insn->off; 4851902472bSHou Tao u8 reg; 4861902472bSHou Tao 4871902472bSHou Tao if (!off) { 4881902472bSHou Tao reg = dst; 4891902472bSHou Tao } else { 4901902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 4911902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 4921902472bSHou Tao reg = tmp; 4931902472bSHou Tao } 4941902472bSHou Tao 4951902472bSHou Tao switch (insn->imm) { 4961902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 4971902472bSHou Tao case BPF_ADD: 4981902472bSHou Tao emit(A64_STADD(isdw, reg, src), ctx); 4991902472bSHou Tao break; 5001902472bSHou Tao case BPF_AND: 5011902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 5021902472bSHou Tao emit(A64_STCLR(isdw, reg, tmp2), ctx); 5031902472bSHou Tao break; 5041902472bSHou Tao case BPF_OR: 5051902472bSHou Tao emit(A64_STSET(isdw, reg, src), ctx); 5061902472bSHou Tao break; 5071902472bSHou Tao case BPF_XOR: 5081902472bSHou Tao emit(A64_STEOR(isdw, reg, src), ctx); 5091902472bSHou Tao break; 5101902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 5111902472bSHou Tao case BPF_ADD | BPF_FETCH: 5121902472bSHou Tao emit(A64_LDADDAL(isdw, src, reg, src), ctx); 5131902472bSHou Tao break; 5141902472bSHou Tao case BPF_AND | BPF_FETCH: 5151902472bSHou Tao emit(A64_MVN(isdw, tmp2, src), ctx); 5161902472bSHou Tao emit(A64_LDCLRAL(isdw, src, reg, tmp2), ctx); 5171902472bSHou Tao break; 5181902472bSHou Tao case BPF_OR | BPF_FETCH: 5191902472bSHou Tao emit(A64_LDSETAL(isdw, src, reg, src), ctx); 5201902472bSHou Tao break; 5211902472bSHou Tao case BPF_XOR | BPF_FETCH: 5221902472bSHou Tao emit(A64_LDEORAL(isdw, src, reg, src), ctx); 5231902472bSHou Tao break; 5241902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 5251902472bSHou Tao case BPF_XCHG: 5261902472bSHou Tao emit(A64_SWPAL(isdw, src, reg, src), ctx); 5271902472bSHou Tao break; 5281902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 5291902472bSHou Tao case BPF_CMPXCHG: 5301902472bSHou Tao emit(A64_CASAL(isdw, src, reg, bpf2a64[BPF_REG_0]), ctx); 5311902472bSHou Tao break; 5321902472bSHou Tao default: 5331902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", insn->imm); 5341902472bSHou Tao return -EINVAL; 5351902472bSHou Tao } 5361902472bSHou Tao 5371902472bSHou Tao return 0; 5381902472bSHou Tao } 5391902472bSHou Tao #else 5401902472bSHou Tao static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5411902472bSHou Tao { 5421902472bSHou Tao return -EINVAL; 5431902472bSHou Tao } 5441902472bSHou Tao #endif 5451902472bSHou Tao 5461902472bSHou Tao static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 5471902472bSHou Tao { 5481902472bSHou Tao const u8 code = insn->code; 5491902472bSHou Tao const u8 dst = bpf2a64[insn->dst_reg]; 5501902472bSHou Tao const u8 src = bpf2a64[insn->src_reg]; 5511902472bSHou Tao const u8 tmp = bpf2a64[TMP_REG_1]; 5521902472bSHou Tao const u8 tmp2 = bpf2a64[TMP_REG_2]; 5531902472bSHou Tao const u8 tmp3 = bpf2a64[TMP_REG_3]; 5541902472bSHou Tao const int i = insn - ctx->prog->insnsi; 5551902472bSHou Tao const s32 imm = insn->imm; 5561902472bSHou Tao const s16 off = insn->off; 5571902472bSHou Tao const bool isdw = BPF_SIZE(code) == BPF_DW; 5581902472bSHou Tao u8 reg; 5591902472bSHou Tao s32 jmp_offset; 5601902472bSHou Tao 5611902472bSHou Tao if (!off) { 5621902472bSHou Tao reg = dst; 5631902472bSHou Tao } else { 5641902472bSHou Tao emit_a64_mov_i(1, tmp, off, ctx); 5651902472bSHou Tao emit(A64_ADD(1, tmp, tmp, dst), ctx); 5661902472bSHou Tao reg = tmp; 5671902472bSHou Tao } 5681902472bSHou Tao 5691902472bSHou Tao if (imm == BPF_ADD || imm == BPF_AND || 5701902472bSHou Tao imm == BPF_OR || imm == BPF_XOR) { 5711902472bSHou Tao /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ 5721902472bSHou Tao emit(A64_LDXR(isdw, tmp2, reg), ctx); 5731902472bSHou Tao if (imm == BPF_ADD) 5741902472bSHou Tao emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 5751902472bSHou Tao else if (imm == BPF_AND) 5761902472bSHou Tao emit(A64_AND(isdw, tmp2, tmp2, src), ctx); 5771902472bSHou Tao else if (imm == BPF_OR) 5781902472bSHou Tao emit(A64_ORR(isdw, tmp2, tmp2, src), ctx); 5791902472bSHou Tao else 5801902472bSHou Tao emit(A64_EOR(isdw, tmp2, tmp2, src), ctx); 5811902472bSHou Tao emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); 5821902472bSHou Tao jmp_offset = -3; 5831902472bSHou Tao check_imm19(jmp_offset); 5841902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 5851902472bSHou Tao } else if (imm == (BPF_ADD | BPF_FETCH) || 5861902472bSHou Tao imm == (BPF_AND | BPF_FETCH) || 5871902472bSHou Tao imm == (BPF_OR | BPF_FETCH) || 5881902472bSHou Tao imm == (BPF_XOR | BPF_FETCH)) { 5891902472bSHou Tao /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ 5901902472bSHou Tao const u8 ax = bpf2a64[BPF_REG_AX]; 5911902472bSHou Tao 5921902472bSHou Tao emit(A64_MOV(isdw, ax, src), ctx); 5931902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 5941902472bSHou Tao if (imm == (BPF_ADD | BPF_FETCH)) 5951902472bSHou Tao emit(A64_ADD(isdw, tmp2, src, ax), ctx); 5961902472bSHou Tao else if (imm == (BPF_AND | BPF_FETCH)) 5971902472bSHou Tao emit(A64_AND(isdw, tmp2, src, ax), ctx); 5981902472bSHou Tao else if (imm == (BPF_OR | BPF_FETCH)) 5991902472bSHou Tao emit(A64_ORR(isdw, tmp2, src, ax), ctx); 6001902472bSHou Tao else 6011902472bSHou Tao emit(A64_EOR(isdw, tmp2, src, ax), ctx); 6021902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 6031902472bSHou Tao jmp_offset = -3; 6041902472bSHou Tao check_imm19(jmp_offset); 6051902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6061902472bSHou Tao emit(A64_DMB_ISH, ctx); 6071902472bSHou Tao } else if (imm == BPF_XCHG) { 6081902472bSHou Tao /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ 6091902472bSHou Tao emit(A64_MOV(isdw, tmp2, src), ctx); 6101902472bSHou Tao emit(A64_LDXR(isdw, src, reg), ctx); 6111902472bSHou Tao emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); 6121902472bSHou Tao jmp_offset = -2; 6131902472bSHou Tao check_imm19(jmp_offset); 6141902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6151902472bSHou Tao emit(A64_DMB_ISH, ctx); 6161902472bSHou Tao } else if (imm == BPF_CMPXCHG) { 6171902472bSHou Tao /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ 6181902472bSHou Tao const u8 r0 = bpf2a64[BPF_REG_0]; 6191902472bSHou Tao 6201902472bSHou Tao emit(A64_MOV(isdw, tmp2, r0), ctx); 6211902472bSHou Tao emit(A64_LDXR(isdw, r0, reg), ctx); 6221902472bSHou Tao emit(A64_EOR(isdw, tmp3, r0, tmp2), ctx); 6231902472bSHou Tao jmp_offset = 4; 6241902472bSHou Tao check_imm19(jmp_offset); 6251902472bSHou Tao emit(A64_CBNZ(isdw, tmp3, jmp_offset), ctx); 6261902472bSHou Tao emit(A64_STLXR(isdw, src, reg, tmp3), ctx); 6271902472bSHou Tao jmp_offset = -4; 6281902472bSHou Tao check_imm19(jmp_offset); 6291902472bSHou Tao emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 6301902472bSHou Tao emit(A64_DMB_ISH, ctx); 6311902472bSHou Tao } else { 6321902472bSHou Tao pr_err_once("unknown atomic op code %02x\n", imm); 6331902472bSHou Tao return -EINVAL; 6341902472bSHou Tao } 6351902472bSHou Tao 6361902472bSHou Tao return 0; 6371902472bSHou Tao } 6381902472bSHou Tao 639b2ad54e1SXu Kuohai void dummy_tramp(void); 640b2ad54e1SXu Kuohai 641b2ad54e1SXu Kuohai asm ( 642b2ad54e1SXu Kuohai " .pushsection .text, \"ax\", @progbits\n" 64333f32e50SNathan Chancellor " .global dummy_tramp\n" 644b2ad54e1SXu Kuohai " .type dummy_tramp, %function\n" 645b2ad54e1SXu Kuohai "dummy_tramp:" 646b2ad54e1SXu Kuohai #if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) 647b2ad54e1SXu Kuohai " bti j\n" /* dummy_tramp is called via "br x10" */ 648b2ad54e1SXu Kuohai #endif 649339ed900SXu Kuohai " mov x10, x30\n" 650339ed900SXu Kuohai " mov x30, x9\n" 651b2ad54e1SXu Kuohai " ret x10\n" 652b2ad54e1SXu Kuohai " .size dummy_tramp, .-dummy_tramp\n" 653b2ad54e1SXu Kuohai " .popsection\n" 654b2ad54e1SXu Kuohai ); 655b2ad54e1SXu Kuohai 656b2ad54e1SXu Kuohai /* build a plt initialized like this: 657b2ad54e1SXu Kuohai * 658b2ad54e1SXu Kuohai * plt: 659b2ad54e1SXu Kuohai * ldr tmp, target 660b2ad54e1SXu Kuohai * br tmp 661b2ad54e1SXu Kuohai * target: 662b2ad54e1SXu Kuohai * .quad dummy_tramp 663b2ad54e1SXu Kuohai * 664b2ad54e1SXu Kuohai * when a long jump trampoline is attached, target is filled with the 665b2ad54e1SXu Kuohai * trampoline address, and when the trampoline is removed, target is 666b2ad54e1SXu Kuohai * restored to dummy_tramp address. 667b2ad54e1SXu Kuohai */ 668b2ad54e1SXu Kuohai static void build_plt(struct jit_ctx *ctx) 669b2ad54e1SXu Kuohai { 670b2ad54e1SXu Kuohai const u8 tmp = bpf2a64[TMP_REG_1]; 671b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 672b2ad54e1SXu Kuohai 673b2ad54e1SXu Kuohai /* make sure target is 64-bit aligned */ 674b2ad54e1SXu Kuohai if ((ctx->idx + PLT_TARGET_OFFSET / AARCH64_INSN_SIZE) % 2) 675b2ad54e1SXu Kuohai emit(A64_NOP, ctx); 676b2ad54e1SXu Kuohai 677b2ad54e1SXu Kuohai plt = (struct bpf_plt *)(ctx->image + ctx->idx); 678b2ad54e1SXu Kuohai /* plt is called via bl, no BTI needed here */ 679b2ad54e1SXu Kuohai emit(A64_LDR64LIT(tmp, 2 * AARCH64_INSN_SIZE), ctx); 680b2ad54e1SXu Kuohai emit(A64_BR(tmp), ctx); 681b2ad54e1SXu Kuohai 682b2ad54e1SXu Kuohai if (ctx->image) 683b2ad54e1SXu Kuohai plt->target = (u64)&dummy_tramp; 684b2ad54e1SXu Kuohai } 685b2ad54e1SXu Kuohai 686*22fc0e80SPuranjay Mohan static void build_epilogue(struct jit_ctx *ctx, bool is_exception_cb) 687e54bcde3SZi Shen Lim { 688e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 689e54bcde3SZi Shen Lim const u8 r6 = bpf2a64[BPF_REG_6]; 690e54bcde3SZi Shen Lim const u8 r7 = bpf2a64[BPF_REG_7]; 691e54bcde3SZi Shen Lim const u8 r8 = bpf2a64[BPF_REG_8]; 692e54bcde3SZi Shen Lim const u8 r9 = bpf2a64[BPF_REG_9]; 693e54bcde3SZi Shen Lim const u8 fp = bpf2a64[BPF_REG_FP]; 6945b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 695e54bcde3SZi Shen Lim 696e54bcde3SZi Shen Lim /* We're done with BPF stack */ 697f1c9eed7SDaniel Borkmann emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 698e54bcde3SZi Shen Lim 699*22fc0e80SPuranjay Mohan /* 700*22fc0e80SPuranjay Mohan * Program acting as exception boundary pushes R23 and R24 in addition 701*22fc0e80SPuranjay Mohan * to BPF callee-saved registers. Exception callback uses the boundary 702*22fc0e80SPuranjay Mohan * program's stack frame, so recover these extra registers in the above 703*22fc0e80SPuranjay Mohan * two cases. 704*22fc0e80SPuranjay Mohan */ 705*22fc0e80SPuranjay Mohan if (ctx->prog->aux->exception_boundary || is_exception_cb) 706*22fc0e80SPuranjay Mohan emit(A64_POP(A64_R(23), A64_R(24), A64_SP), ctx); 707*22fc0e80SPuranjay Mohan 7085b3d19b9SXu Kuohai /* Restore x27 and x28 */ 7095b3d19b9SXu Kuohai emit(A64_POP(fpb, A64_R(28), A64_SP), ctx); 710ec0738dbSYang Shi /* Restore fs (x25) and x26 */ 711ec0738dbSYang Shi emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 712ec0738dbSYang Shi 713e54bcde3SZi Shen Lim /* Restore callee-saved register */ 714e54bcde3SZi Shen Lim emit(A64_POP(r8, r9, A64_SP), ctx); 715e54bcde3SZi Shen Lim emit(A64_POP(r6, r7, A64_SP), ctx); 716e54bcde3SZi Shen Lim 717ec0738dbSYang Shi /* Restore FP/LR registers */ 718ec0738dbSYang Shi emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 719e54bcde3SZi Shen Lim 720e54bcde3SZi Shen Lim /* Set return value */ 721e54bcde3SZi Shen Lim emit(A64_MOV(1, A64_R(0), r0), ctx); 722e54bcde3SZi Shen Lim 723042152c2SXu Kuohai /* Authenticate lr */ 724042152c2SXu Kuohai if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 725042152c2SXu Kuohai emit(A64_AUTIASP, ctx); 726042152c2SXu Kuohai 727e54bcde3SZi Shen Lim emit(A64_RET(A64_LR), ctx); 728e54bcde3SZi Shen Lim } 729e54bcde3SZi Shen Lim 73080083428SJean-Philippe Brucker #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) 73180083428SJean-Philippe Brucker #define BPF_FIXUP_REG_MASK GENMASK(31, 27) 73280083428SJean-Philippe Brucker 733d6e2cc56SMark Rutland bool ex_handler_bpf(const struct exception_table_entry *ex, 73480083428SJean-Philippe Brucker struct pt_regs *regs) 73580083428SJean-Philippe Brucker { 73680083428SJean-Philippe Brucker off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup); 73780083428SJean-Philippe Brucker int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup); 73880083428SJean-Philippe Brucker 73980083428SJean-Philippe Brucker regs->regs[dst_reg] = 0; 74080083428SJean-Philippe Brucker regs->pc = (unsigned long)&ex->fixup - offset; 741e8c328d7SMark Rutland return true; 74280083428SJean-Philippe Brucker } 74380083428SJean-Philippe Brucker 74480083428SJean-Philippe Brucker /* For accesses to BTF pointers, add an entry to the exception table */ 74580083428SJean-Philippe Brucker static int add_exception_handler(const struct bpf_insn *insn, 74680083428SJean-Philippe Brucker struct jit_ctx *ctx, 74780083428SJean-Philippe Brucker int dst_reg) 74880083428SJean-Philippe Brucker { 74980083428SJean-Philippe Brucker off_t offset; 75080083428SJean-Philippe Brucker unsigned long pc; 75180083428SJean-Philippe Brucker struct exception_table_entry *ex; 75280083428SJean-Philippe Brucker 75380083428SJean-Philippe Brucker if (!ctx->image) 75480083428SJean-Philippe Brucker /* First pass */ 75580083428SJean-Philippe Brucker return 0; 75680083428SJean-Philippe Brucker 757cc88f540SXu Kuohai if (BPF_MODE(insn->code) != BPF_PROBE_MEM && 758cc88f540SXu Kuohai BPF_MODE(insn->code) != BPF_PROBE_MEMSX) 75980083428SJean-Philippe Brucker return 0; 76080083428SJean-Philippe Brucker 76180083428SJean-Philippe Brucker if (!ctx->prog->aux->extable || 76280083428SJean-Philippe Brucker WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries)) 76380083428SJean-Philippe Brucker return -EINVAL; 76480083428SJean-Philippe Brucker 76580083428SJean-Philippe Brucker ex = &ctx->prog->aux->extable[ctx->exentry_idx]; 76680083428SJean-Philippe Brucker pc = (unsigned long)&ctx->image[ctx->idx - 1]; 76780083428SJean-Philippe Brucker 76880083428SJean-Philippe Brucker offset = pc - (long)&ex->insn; 76980083428SJean-Philippe Brucker if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN)) 77080083428SJean-Philippe Brucker return -ERANGE; 77180083428SJean-Philippe Brucker ex->insn = offset; 77280083428SJean-Philippe Brucker 77380083428SJean-Philippe Brucker /* 77480083428SJean-Philippe Brucker * Since the extable follows the program, the fixup offset is always 77580083428SJean-Philippe Brucker * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value 77680083428SJean-Philippe Brucker * to keep things simple, and put the destination register in the upper 77780083428SJean-Philippe Brucker * bits. We don't need to worry about buildtime or runtime sort 77880083428SJean-Philippe Brucker * modifying the upper bits because the table is already sorted, and 77980083428SJean-Philippe Brucker * isn't part of the main exception table. 78080083428SJean-Philippe Brucker */ 78180083428SJean-Philippe Brucker offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE); 78280083428SJean-Philippe Brucker if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset)) 78380083428SJean-Philippe Brucker return -ERANGE; 78480083428SJean-Philippe Brucker 78580083428SJean-Philippe Brucker ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) | 78680083428SJean-Philippe Brucker FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); 78780083428SJean-Philippe Brucker 788d6e2cc56SMark Rutland ex->type = EX_TYPE_BPF; 789d6e2cc56SMark Rutland 79080083428SJean-Philippe Brucker ctx->exentry_idx++; 79180083428SJean-Philippe Brucker return 0; 79280083428SJean-Philippe Brucker } 79380083428SJean-Philippe Brucker 79430d3d94cSZi Shen Lim /* JITs an eBPF instruction. 79530d3d94cSZi Shen Lim * Returns: 79630d3d94cSZi Shen Lim * 0 - successfully JITed an 8-byte eBPF instruction. 79730d3d94cSZi Shen Lim * >0 - successfully JITed a 16-byte eBPF instruction. 79830d3d94cSZi Shen Lim * <0 - failed to JIT. 79930d3d94cSZi Shen Lim */ 8008c11ea5cSDaniel Borkmann static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, 8018c11ea5cSDaniel Borkmann bool extra_pass) 802e54bcde3SZi Shen Lim { 803e54bcde3SZi Shen Lim const u8 code = insn->code; 804e54bcde3SZi Shen Lim const u8 dst = bpf2a64[insn->dst_reg]; 805e54bcde3SZi Shen Lim const u8 src = bpf2a64[insn->src_reg]; 806e54bcde3SZi Shen Lim const u8 tmp = bpf2a64[TMP_REG_1]; 807e54bcde3SZi Shen Lim const u8 tmp2 = bpf2a64[TMP_REG_2]; 8085b3d19b9SXu Kuohai const u8 fp = bpf2a64[BPF_REG_FP]; 8095b3d19b9SXu Kuohai const u8 fpb = bpf2a64[FP_BOTTOM]; 810e54bcde3SZi Shen Lim const s16 off = insn->off; 811e54bcde3SZi Shen Lim const s32 imm = insn->imm; 812e54bcde3SZi Shen Lim const int i = insn - ctx->prog->insnsi; 813654b65a0SJiong Wang const bool is64 = BPF_CLASS(code) == BPF_ALU64 || 814654b65a0SJiong Wang BPF_CLASS(code) == BPF_JMP; 8151902472bSHou Tao u8 jmp_cond; 816e54bcde3SZi Shen Lim s32 jmp_offset; 817fd49591cSLuke Nelson u32 a64_insn; 8185b3d19b9SXu Kuohai u8 src_adj; 8195b3d19b9SXu Kuohai u8 dst_adj; 8205b3d19b9SXu Kuohai int off_adj; 82180083428SJean-Philippe Brucker int ret; 822cc88f540SXu Kuohai bool sign_extend; 823e54bcde3SZi Shen Lim 824e54bcde3SZi Shen Lim switch (code) { 825e54bcde3SZi Shen Lim /* dst = src */ 826e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_X: 827e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_X: 828bb0a1d6bSXu Kuohai switch (insn->off) { 829bb0a1d6bSXu Kuohai case 0: 830e54bcde3SZi Shen Lim emit(A64_MOV(is64, dst, src), ctx); 831e54bcde3SZi Shen Lim break; 832bb0a1d6bSXu Kuohai case 8: 833bb0a1d6bSXu Kuohai emit(A64_SXTB(is64, dst, src), ctx); 834bb0a1d6bSXu Kuohai break; 835bb0a1d6bSXu Kuohai case 16: 836bb0a1d6bSXu Kuohai emit(A64_SXTH(is64, dst, src), ctx); 837bb0a1d6bSXu Kuohai break; 838bb0a1d6bSXu Kuohai case 32: 839bb0a1d6bSXu Kuohai emit(A64_SXTW(is64, dst, src), ctx); 840bb0a1d6bSXu Kuohai break; 841bb0a1d6bSXu Kuohai } 842bb0a1d6bSXu Kuohai break; 843e54bcde3SZi Shen Lim /* dst = dst OP src */ 844e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_X: 845e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_X: 846e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, src), ctx); 847e54bcde3SZi Shen Lim break; 848e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_X: 849e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_X: 850e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, src), ctx); 851e54bcde3SZi Shen Lim break; 852e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_X: 853e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_X: 854e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, src), ctx); 855e54bcde3SZi Shen Lim break; 856e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_X: 857e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_X: 858e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, src), ctx); 859e54bcde3SZi Shen Lim break; 860e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_X: 861e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_X: 862e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, src), ctx); 863e54bcde3SZi Shen Lim break; 864e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_X: 865e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_X: 866e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, src), ctx); 867e54bcde3SZi Shen Lim break; 868e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_X: 869e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_X: 87068b18191SXu Kuohai if (!off) 871e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, src), ctx); 87268b18191SXu Kuohai else 87368b18191SXu Kuohai emit(A64_SDIV(is64, dst, dst, src), ctx); 874e54bcde3SZi Shen Lim break; 875119220d8STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 876119220d8STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 87768b18191SXu Kuohai if (!off) 878e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, src), ctx); 87968b18191SXu Kuohai else 88068b18191SXu Kuohai emit(A64_SDIV(is64, tmp, dst, src), ctx); 881504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, src), ctx); 882e54bcde3SZi Shen Lim break; 883d65a634aSZi Shen Lim case BPF_ALU | BPF_LSH | BPF_X: 884d65a634aSZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_X: 885d65a634aSZi Shen Lim emit(A64_LSLV(is64, dst, dst, src), ctx); 886d65a634aSZi Shen Lim break; 887d65a634aSZi Shen Lim case BPF_ALU | BPF_RSH | BPF_X: 888d65a634aSZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_X: 889d65a634aSZi Shen Lim emit(A64_LSRV(is64, dst, dst, src), ctx); 890d65a634aSZi Shen Lim break; 891d65a634aSZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_X: 892d65a634aSZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_X: 893d65a634aSZi Shen Lim emit(A64_ASRV(is64, dst, dst, src), ctx); 894d65a634aSZi Shen Lim break; 895e54bcde3SZi Shen Lim /* dst = -dst */ 896e54bcde3SZi Shen Lim case BPF_ALU | BPF_NEG: 897e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_NEG: 898e54bcde3SZi Shen Lim emit(A64_NEG(is64, dst, dst), ctx); 899e54bcde3SZi Shen Lim break; 900e54bcde3SZi Shen Lim /* dst = BSWAP##imm(dst) */ 901e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_LE: 902e54bcde3SZi Shen Lim case BPF_ALU | BPF_END | BPF_FROM_BE: 9031104247fSXu Kuohai case BPF_ALU64 | BPF_END | BPF_FROM_LE: 904e54bcde3SZi Shen Lim #ifdef CONFIG_CPU_BIG_ENDIAN 9051104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_BE) 906d63903bbSXi Wang goto emit_bswap_uxt; 907e54bcde3SZi Shen Lim #else /* !CONFIG_CPU_BIG_ENDIAN */ 9081104247fSXu Kuohai if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_LE) 909d63903bbSXi Wang goto emit_bswap_uxt; 910e54bcde3SZi Shen Lim #endif 911e54bcde3SZi Shen Lim switch (imm) { 912e54bcde3SZi Shen Lim case 16: 913e54bcde3SZi Shen Lim emit(A64_REV16(is64, dst, dst), ctx); 914d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 915d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 916e54bcde3SZi Shen Lim break; 917e54bcde3SZi Shen Lim case 32: 918e54bcde3SZi Shen Lim emit(A64_REV32(is64, dst, dst), ctx); 919d63903bbSXi Wang /* upper 32 bits already cleared */ 920e54bcde3SZi Shen Lim break; 921e54bcde3SZi Shen Lim case 64: 922e54bcde3SZi Shen Lim emit(A64_REV64(dst, dst), ctx); 923e54bcde3SZi Shen Lim break; 924e54bcde3SZi Shen Lim } 925e54bcde3SZi Shen Lim break; 926d63903bbSXi Wang emit_bswap_uxt: 927d63903bbSXi Wang switch (imm) { 928d63903bbSXi Wang case 16: 929d63903bbSXi Wang /* zero-extend 16 bits into 64 bits */ 930d63903bbSXi Wang emit(A64_UXTH(is64, dst, dst), ctx); 931d63903bbSXi Wang break; 932d63903bbSXi Wang case 32: 933d63903bbSXi Wang /* zero-extend 32 bits into 64 bits */ 934d63903bbSXi Wang emit(A64_UXTW(is64, dst, dst), ctx); 935d63903bbSXi Wang break; 936d63903bbSXi Wang case 64: 937d63903bbSXi Wang /* nop */ 938d63903bbSXi Wang break; 939d63903bbSXi Wang } 940d63903bbSXi Wang break; 941e54bcde3SZi Shen Lim /* dst = imm */ 942e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOV | BPF_K: 943e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOV | BPF_K: 944e54bcde3SZi Shen Lim emit_a64_mov_i(is64, dst, imm, ctx); 945e54bcde3SZi Shen Lim break; 946e54bcde3SZi Shen Lim /* dst = dst OP imm */ 947e54bcde3SZi Shen Lim case BPF_ALU | BPF_ADD | BPF_K: 948e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ADD | BPF_K: 949fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 950fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, imm), ctx); 951fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 952fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, -imm), ctx); 953fd868f14SLuke Nelson } else { 954e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 955e54bcde3SZi Shen Lim emit(A64_ADD(is64, dst, dst, tmp), ctx); 956fd868f14SLuke Nelson } 957e54bcde3SZi Shen Lim break; 958e54bcde3SZi Shen Lim case BPF_ALU | BPF_SUB | BPF_K: 959e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_SUB | BPF_K: 960fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 961fd868f14SLuke Nelson emit(A64_SUB_I(is64, dst, dst, imm), ctx); 962fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 963fd868f14SLuke Nelson emit(A64_ADD_I(is64, dst, dst, -imm), ctx); 964fd868f14SLuke Nelson } else { 965e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 966e54bcde3SZi Shen Lim emit(A64_SUB(is64, dst, dst, tmp), ctx); 967fd868f14SLuke Nelson } 968e54bcde3SZi Shen Lim break; 969e54bcde3SZi Shen Lim case BPF_ALU | BPF_AND | BPF_K: 970e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_AND | BPF_K: 971fd49591cSLuke Nelson a64_insn = A64_AND_I(is64, dst, dst, imm); 972fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 973fd49591cSLuke Nelson emit(a64_insn, ctx); 974fd49591cSLuke Nelson } else { 975e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 976e54bcde3SZi Shen Lim emit(A64_AND(is64, dst, dst, tmp), ctx); 977fd49591cSLuke Nelson } 978e54bcde3SZi Shen Lim break; 979e54bcde3SZi Shen Lim case BPF_ALU | BPF_OR | BPF_K: 980e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_OR | BPF_K: 981fd49591cSLuke Nelson a64_insn = A64_ORR_I(is64, dst, dst, imm); 982fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 983fd49591cSLuke Nelson emit(a64_insn, ctx); 984fd49591cSLuke Nelson } else { 985e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 986e54bcde3SZi Shen Lim emit(A64_ORR(is64, dst, dst, tmp), ctx); 987fd49591cSLuke Nelson } 988e54bcde3SZi Shen Lim break; 989e54bcde3SZi Shen Lim case BPF_ALU | BPF_XOR | BPF_K: 990e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_XOR | BPF_K: 991fd49591cSLuke Nelson a64_insn = A64_EOR_I(is64, dst, dst, imm); 992fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 993fd49591cSLuke Nelson emit(a64_insn, ctx); 994fd49591cSLuke Nelson } else { 995e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 996e54bcde3SZi Shen Lim emit(A64_EOR(is64, dst, dst, tmp), ctx); 997fd49591cSLuke Nelson } 998e54bcde3SZi Shen Lim break; 999e54bcde3SZi Shen Lim case BPF_ALU | BPF_MUL | BPF_K: 1000e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MUL | BPF_K: 1001e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 1002e54bcde3SZi Shen Lim emit(A64_MUL(is64, dst, dst, tmp), ctx); 1003e54bcde3SZi Shen Lim break; 1004e54bcde3SZi Shen Lim case BPF_ALU | BPF_DIV | BPF_K: 1005e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_DIV | BPF_K: 1006e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp, imm, ctx); 100768b18191SXu Kuohai if (!off) 1008e54bcde3SZi Shen Lim emit(A64_UDIV(is64, dst, dst, tmp), ctx); 100968b18191SXu Kuohai else 101068b18191SXu Kuohai emit(A64_SDIV(is64, dst, dst, tmp), ctx); 1011e54bcde3SZi Shen Lim break; 1012e54bcde3SZi Shen Lim case BPF_ALU | BPF_MOD | BPF_K: 1013e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_MOD | BPF_K: 1014e54bcde3SZi Shen Lim emit_a64_mov_i(is64, tmp2, imm, ctx); 101568b18191SXu Kuohai if (!off) 1016e54bcde3SZi Shen Lim emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 101768b18191SXu Kuohai else 101868b18191SXu Kuohai emit(A64_SDIV(is64, tmp, dst, tmp2), ctx); 1019504792e0SJerin Jacob emit(A64_MSUB(is64, dst, dst, tmp, tmp2), ctx); 1020e54bcde3SZi Shen Lim break; 1021e54bcde3SZi Shen Lim case BPF_ALU | BPF_LSH | BPF_K: 1022e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_LSH | BPF_K: 1023e54bcde3SZi Shen Lim emit(A64_LSL(is64, dst, dst, imm), ctx); 1024e54bcde3SZi Shen Lim break; 1025e54bcde3SZi Shen Lim case BPF_ALU | BPF_RSH | BPF_K: 1026e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_RSH | BPF_K: 1027e54bcde3SZi Shen Lim emit(A64_LSR(is64, dst, dst, imm), ctx); 1028e54bcde3SZi Shen Lim break; 1029e54bcde3SZi Shen Lim case BPF_ALU | BPF_ARSH | BPF_K: 1030e54bcde3SZi Shen Lim case BPF_ALU64 | BPF_ARSH | BPF_K: 1031e54bcde3SZi Shen Lim emit(A64_ASR(is64, dst, dst, imm), ctx); 1032e54bcde3SZi Shen Lim break; 1033e54bcde3SZi Shen Lim 1034e54bcde3SZi Shen Lim /* JUMP off */ 1035e54bcde3SZi Shen Lim case BPF_JMP | BPF_JA: 1036c32b6ee5SXu Kuohai case BPF_JMP32 | BPF_JA: 1037c32b6ee5SXu Kuohai if (BPF_CLASS(code) == BPF_JMP) 103832f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 1039c32b6ee5SXu Kuohai else 1040c32b6ee5SXu Kuohai jmp_offset = bpf2a64_offset(i, imm, ctx); 1041e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1042e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1043e54bcde3SZi Shen Lim break; 1044e54bcde3SZi Shen Lim /* IF (dst COND src) JUMP off */ 1045e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_X: 1046e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_X: 1047c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_X: 1048e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_X: 1049c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_X: 1050e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_X: 1051e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_X: 1052c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_X: 1053e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_X: 1054c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_X: 1055654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_X: 1056654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_X: 1057654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_X: 1058654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_X: 1059654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_X: 1060654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_X: 1061654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_X: 1062654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_X: 1063654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_X: 1064654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_X: 1065654b65a0SJiong Wang emit(A64_CMP(is64, dst, src), ctx); 1066e54bcde3SZi Shen Lim emit_cond_jmp: 106732f6865cSIlias Apalodimas jmp_offset = bpf2a64_offset(i, off, ctx); 1068e54bcde3SZi Shen Lim check_imm19(jmp_offset); 1069e54bcde3SZi Shen Lim switch (BPF_OP(code)) { 1070e54bcde3SZi Shen Lim case BPF_JEQ: 1071e54bcde3SZi Shen Lim jmp_cond = A64_COND_EQ; 1072e54bcde3SZi Shen Lim break; 1073e54bcde3SZi Shen Lim case BPF_JGT: 1074e54bcde3SZi Shen Lim jmp_cond = A64_COND_HI; 1075e54bcde3SZi Shen Lim break; 1076c362b2f3SDaniel Borkmann case BPF_JLT: 1077c362b2f3SDaniel Borkmann jmp_cond = A64_COND_CC; 1078c362b2f3SDaniel Borkmann break; 1079e54bcde3SZi Shen Lim case BPF_JGE: 1080e54bcde3SZi Shen Lim jmp_cond = A64_COND_CS; 1081e54bcde3SZi Shen Lim break; 1082c362b2f3SDaniel Borkmann case BPF_JLE: 1083c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LS; 1084c362b2f3SDaniel Borkmann break; 108598397fc5SZi Shen Lim case BPF_JSET: 1086e54bcde3SZi Shen Lim case BPF_JNE: 1087e54bcde3SZi Shen Lim jmp_cond = A64_COND_NE; 1088e54bcde3SZi Shen Lim break; 1089e54bcde3SZi Shen Lim case BPF_JSGT: 1090e54bcde3SZi Shen Lim jmp_cond = A64_COND_GT; 1091e54bcde3SZi Shen Lim break; 1092c362b2f3SDaniel Borkmann case BPF_JSLT: 1093c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LT; 1094c362b2f3SDaniel Borkmann break; 1095e54bcde3SZi Shen Lim case BPF_JSGE: 1096e54bcde3SZi Shen Lim jmp_cond = A64_COND_GE; 1097e54bcde3SZi Shen Lim break; 1098c362b2f3SDaniel Borkmann case BPF_JSLE: 1099c362b2f3SDaniel Borkmann jmp_cond = A64_COND_LE; 1100c362b2f3SDaniel Borkmann break; 1101e54bcde3SZi Shen Lim default: 1102e54bcde3SZi Shen Lim return -EFAULT; 1103e54bcde3SZi Shen Lim } 1104e54bcde3SZi Shen Lim emit(A64_B_(jmp_cond, jmp_offset), ctx); 1105e54bcde3SZi Shen Lim break; 1106e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_X: 1107654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_X: 1108654b65a0SJiong Wang emit(A64_TST(is64, dst, src), ctx); 1109e54bcde3SZi Shen Lim goto emit_cond_jmp; 1110e54bcde3SZi Shen Lim /* IF (dst COND imm) JUMP off */ 1111e54bcde3SZi Shen Lim case BPF_JMP | BPF_JEQ | BPF_K: 1112e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGT | BPF_K: 1113c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLT | BPF_K: 1114e54bcde3SZi Shen Lim case BPF_JMP | BPF_JGE | BPF_K: 1115c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JLE | BPF_K: 1116e54bcde3SZi Shen Lim case BPF_JMP | BPF_JNE | BPF_K: 1117e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGT | BPF_K: 1118c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLT | BPF_K: 1119e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSGE | BPF_K: 1120c362b2f3SDaniel Borkmann case BPF_JMP | BPF_JSLE | BPF_K: 1121654b65a0SJiong Wang case BPF_JMP32 | BPF_JEQ | BPF_K: 1122654b65a0SJiong Wang case BPF_JMP32 | BPF_JGT | BPF_K: 1123654b65a0SJiong Wang case BPF_JMP32 | BPF_JLT | BPF_K: 1124654b65a0SJiong Wang case BPF_JMP32 | BPF_JGE | BPF_K: 1125654b65a0SJiong Wang case BPF_JMP32 | BPF_JLE | BPF_K: 1126654b65a0SJiong Wang case BPF_JMP32 | BPF_JNE | BPF_K: 1127654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGT | BPF_K: 1128654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLT | BPF_K: 1129654b65a0SJiong Wang case BPF_JMP32 | BPF_JSGE | BPF_K: 1130654b65a0SJiong Wang case BPF_JMP32 | BPF_JSLE | BPF_K: 1131fd868f14SLuke Nelson if (is_addsub_imm(imm)) { 1132fd868f14SLuke Nelson emit(A64_CMP_I(is64, dst, imm), ctx); 1133fd868f14SLuke Nelson } else if (is_addsub_imm(-imm)) { 1134fd868f14SLuke Nelson emit(A64_CMN_I(is64, dst, -imm), ctx); 1135fd868f14SLuke Nelson } else { 1136654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1137654b65a0SJiong Wang emit(A64_CMP(is64, dst, tmp), ctx); 1138fd868f14SLuke Nelson } 1139e54bcde3SZi Shen Lim goto emit_cond_jmp; 1140e54bcde3SZi Shen Lim case BPF_JMP | BPF_JSET | BPF_K: 1141654b65a0SJiong Wang case BPF_JMP32 | BPF_JSET | BPF_K: 1142fd49591cSLuke Nelson a64_insn = A64_TST_I(is64, dst, imm); 1143fd49591cSLuke Nelson if (a64_insn != AARCH64_BREAK_FAULT) { 1144fd49591cSLuke Nelson emit(a64_insn, ctx); 1145fd49591cSLuke Nelson } else { 1146654b65a0SJiong Wang emit_a64_mov_i(is64, tmp, imm, ctx); 1147654b65a0SJiong Wang emit(A64_TST(is64, dst, tmp), ctx); 1148fd49591cSLuke Nelson } 1149e54bcde3SZi Shen Lim goto emit_cond_jmp; 1150e54bcde3SZi Shen Lim /* function call */ 1151e54bcde3SZi Shen Lim case BPF_JMP | BPF_CALL: 1152e54bcde3SZi Shen Lim { 1153e54bcde3SZi Shen Lim const u8 r0 = bpf2a64[BPF_REG_0]; 11548c11ea5cSDaniel Borkmann bool func_addr_fixed; 11558c11ea5cSDaniel Borkmann u64 func_addr; 1156e54bcde3SZi Shen Lim 11578c11ea5cSDaniel Borkmann ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 11588c11ea5cSDaniel Borkmann &func_addr, &func_addr_fixed); 11598c11ea5cSDaniel Borkmann if (ret < 0) 11608c11ea5cSDaniel Borkmann return ret; 1161efc9909fSXu Kuohai emit_call(func_addr, ctx); 1162e54bcde3SZi Shen Lim emit(A64_MOV(1, r0, A64_R(0)), ctx); 1163e54bcde3SZi Shen Lim break; 1164e54bcde3SZi Shen Lim } 1165ddb55992SZi Shen Lim /* tail call */ 116671189fa9SAlexei Starovoitov case BPF_JMP | BPF_TAIL_CALL: 1167ddb55992SZi Shen Lim if (emit_bpf_tail_call(ctx)) 1168ddb55992SZi Shen Lim return -EFAULT; 1169ddb55992SZi Shen Lim break; 1170e54bcde3SZi Shen Lim /* function return */ 1171e54bcde3SZi Shen Lim case BPF_JMP | BPF_EXIT: 117251c9fbb1SZi Shen Lim /* Optimization: when last instruction is EXIT, 117351c9fbb1SZi Shen Lim simply fallthrough to epilogue. */ 1174e54bcde3SZi Shen Lim if (i == ctx->prog->len - 1) 1175e54bcde3SZi Shen Lim break; 1176e54bcde3SZi Shen Lim jmp_offset = epilogue_offset(ctx); 1177e54bcde3SZi Shen Lim check_imm26(jmp_offset); 1178e54bcde3SZi Shen Lim emit(A64_B(jmp_offset), ctx); 1179e54bcde3SZi Shen Lim break; 1180e54bcde3SZi Shen Lim 118130d3d94cSZi Shen Lim /* dst = imm64 */ 118230d3d94cSZi Shen Lim case BPF_LD | BPF_IMM | BPF_DW: 118330d3d94cSZi Shen Lim { 118430d3d94cSZi Shen Lim const struct bpf_insn insn1 = insn[1]; 118530d3d94cSZi Shen Lim u64 imm64; 118630d3d94cSZi Shen Lim 11871e4df6b7SXi Wang imm64 = (u64)insn1.imm << 32 | (u32)imm; 1188e4a41c2cSHou Tao if (bpf_pseudo_func(insn)) 1189e4a41c2cSHou Tao emit_addr_mov_i64(dst, imm64, ctx); 1190e4a41c2cSHou Tao else 119130d3d94cSZi Shen Lim emit_a64_mov_i64(dst, imm64, ctx); 119230d3d94cSZi Shen Lim 119330d3d94cSZi Shen Lim return 1; 119430d3d94cSZi Shen Lim } 119530d3d94cSZi Shen Lim 1196cc88f540SXu Kuohai /* LDX: dst = (u64)*(unsigned size *)(src + off) */ 1197e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_W: 1198e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_H: 1199e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_B: 1200e54bcde3SZi Shen Lim case BPF_LDX | BPF_MEM | BPF_DW: 120180083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 120280083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_W: 120380083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_H: 120480083428SJean-Philippe Brucker case BPF_LDX | BPF_PROBE_MEM | BPF_B: 1205cc88f540SXu Kuohai /* LDXS: dst_reg = (s64)*(signed size *)(src_reg + off) */ 1206cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_B: 1207cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_H: 1208cc88f540SXu Kuohai case BPF_LDX | BPF_MEMSX | BPF_W: 1209cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_B: 1210cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_H: 1211cc88f540SXu Kuohai case BPF_LDX | BPF_PROBE_MEMSX | BPF_W: 12125b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && src == fp) { 12135b3d19b9SXu Kuohai src_adj = fpb; 12145b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 12155b3d19b9SXu Kuohai } else { 12165b3d19b9SXu Kuohai src_adj = src; 12175b3d19b9SXu Kuohai off_adj = off; 12185b3d19b9SXu Kuohai } 1219cc88f540SXu Kuohai sign_extend = (BPF_MODE(insn->code) == BPF_MEMSX || 1220cc88f540SXu Kuohai BPF_MODE(insn->code) == BPF_PROBE_MEMSX); 1221e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1222e54bcde3SZi Shen Lim case BPF_W: 12235b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 1224cc88f540SXu Kuohai if (sign_extend) 1225cc88f540SXu Kuohai emit(A64_LDRSWI(dst, src_adj, off_adj), ctx); 1226cc88f540SXu Kuohai else 12275b3d19b9SXu Kuohai emit(A64_LDR32I(dst, src_adj, off_adj), ctx); 12287db6c0f1SXu Kuohai } else { 12297db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1230cc88f540SXu Kuohai if (sign_extend) 1231cc88f540SXu Kuohai emit(A64_LDRSW(dst, src_adj, off_adj), ctx); 1232cc88f540SXu Kuohai else 1233e54bcde3SZi Shen Lim emit(A64_LDR32(dst, src, tmp), ctx); 12347db6c0f1SXu Kuohai } 1235e54bcde3SZi Shen Lim break; 1236e54bcde3SZi Shen Lim case BPF_H: 12375b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 1238cc88f540SXu Kuohai if (sign_extend) 1239cc88f540SXu Kuohai emit(A64_LDRSHI(dst, src_adj, off_adj), ctx); 1240cc88f540SXu Kuohai else 12415b3d19b9SXu Kuohai emit(A64_LDRHI(dst, src_adj, off_adj), ctx); 12427db6c0f1SXu Kuohai } else { 12437db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1244cc88f540SXu Kuohai if (sign_extend) 1245cc88f540SXu Kuohai emit(A64_LDRSH(dst, src, tmp), ctx); 1246cc88f540SXu Kuohai else 1247e54bcde3SZi Shen Lim emit(A64_LDRH(dst, src, tmp), ctx); 12487db6c0f1SXu Kuohai } 1249e54bcde3SZi Shen Lim break; 1250e54bcde3SZi Shen Lim case BPF_B: 12515b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 1252cc88f540SXu Kuohai if (sign_extend) 1253cc88f540SXu Kuohai emit(A64_LDRSBI(dst, src_adj, off_adj), ctx); 1254cc88f540SXu Kuohai else 12555b3d19b9SXu Kuohai emit(A64_LDRBI(dst, src_adj, off_adj), ctx); 12567db6c0f1SXu Kuohai } else { 12577db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1258cc88f540SXu Kuohai if (sign_extend) 1259cc88f540SXu Kuohai emit(A64_LDRSB(dst, src, tmp), ctx); 1260cc88f540SXu Kuohai else 1261e54bcde3SZi Shen Lim emit(A64_LDRB(dst, src, tmp), ctx); 12627db6c0f1SXu Kuohai } 1263e54bcde3SZi Shen Lim break; 1264e54bcde3SZi Shen Lim case BPF_DW: 12655b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 12665b3d19b9SXu Kuohai emit(A64_LDR64I(dst, src_adj, off_adj), ctx); 12677db6c0f1SXu Kuohai } else { 12687db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1269e54bcde3SZi Shen Lim emit(A64_LDR64(dst, src, tmp), ctx); 12707db6c0f1SXu Kuohai } 1271e54bcde3SZi Shen Lim break; 1272e54bcde3SZi Shen Lim } 127380083428SJean-Philippe Brucker 127480083428SJean-Philippe Brucker ret = add_exception_handler(insn, ctx, dst); 127580083428SJean-Philippe Brucker if (ret) 127680083428SJean-Philippe Brucker return ret; 1277e54bcde3SZi Shen Lim break; 1278e54bcde3SZi Shen Lim 1279f5e81d11SDaniel Borkmann /* speculation barrier */ 1280f5e81d11SDaniel Borkmann case BPF_ST | BPF_NOSPEC: 1281f5e81d11SDaniel Borkmann /* 1282f5e81d11SDaniel Borkmann * Nothing required here. 1283f5e81d11SDaniel Borkmann * 1284f5e81d11SDaniel Borkmann * In case of arm64, we rely on the firmware mitigation of 1285f5e81d11SDaniel Borkmann * Speculative Store Bypass as controlled via the ssbd kernel 1286f5e81d11SDaniel Borkmann * parameter. Whenever the mitigation is enabled, it works 1287f5e81d11SDaniel Borkmann * for all of the kernel code with no need to provide any 1288f5e81d11SDaniel Borkmann * additional instructions. 1289f5e81d11SDaniel Borkmann */ 1290f5e81d11SDaniel Borkmann break; 1291f5e81d11SDaniel Borkmann 1292e54bcde3SZi Shen Lim /* ST: *(size *)(dst + off) = imm */ 1293e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_W: 1294e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_H: 1295e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_B: 1296e54bcde3SZi Shen Lim case BPF_ST | BPF_MEM | BPF_DW: 12975b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && dst == fp) { 12985b3d19b9SXu Kuohai dst_adj = fpb; 12995b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 13005b3d19b9SXu Kuohai } else { 13015b3d19b9SXu Kuohai dst_adj = dst; 13025b3d19b9SXu Kuohai off_adj = off; 13035b3d19b9SXu Kuohai } 1304df849ba3SYang Shi /* Load imm to a register then store it */ 1305df849ba3SYang Shi emit_a64_mov_i(1, tmp, imm, ctx); 1306df849ba3SYang Shi switch (BPF_SIZE(code)) { 1307df849ba3SYang Shi case BPF_W: 13085b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 13095b3d19b9SXu Kuohai emit(A64_STR32I(tmp, dst_adj, off_adj), ctx); 13107db6c0f1SXu Kuohai } else { 13117db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1312df849ba3SYang Shi emit(A64_STR32(tmp, dst, tmp2), ctx); 13137db6c0f1SXu Kuohai } 1314df849ba3SYang Shi break; 1315df849ba3SYang Shi case BPF_H: 13165b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 13175b3d19b9SXu Kuohai emit(A64_STRHI(tmp, dst_adj, off_adj), ctx); 13187db6c0f1SXu Kuohai } else { 13197db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1320df849ba3SYang Shi emit(A64_STRH(tmp, dst, tmp2), ctx); 13217db6c0f1SXu Kuohai } 1322df849ba3SYang Shi break; 1323df849ba3SYang Shi case BPF_B: 13245b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 13255b3d19b9SXu Kuohai emit(A64_STRBI(tmp, dst_adj, off_adj), ctx); 13267db6c0f1SXu Kuohai } else { 13277db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1328df849ba3SYang Shi emit(A64_STRB(tmp, dst, tmp2), ctx); 13297db6c0f1SXu Kuohai } 1330df849ba3SYang Shi break; 1331df849ba3SYang Shi case BPF_DW: 13325b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 13335b3d19b9SXu Kuohai emit(A64_STR64I(tmp, dst_adj, off_adj), ctx); 13347db6c0f1SXu Kuohai } else { 13357db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp2, off, ctx); 1336df849ba3SYang Shi emit(A64_STR64(tmp, dst, tmp2), ctx); 13377db6c0f1SXu Kuohai } 1338df849ba3SYang Shi break; 1339df849ba3SYang Shi } 1340df849ba3SYang Shi break; 1341e54bcde3SZi Shen Lim 1342e54bcde3SZi Shen Lim /* STX: *(size *)(dst + off) = src */ 1343e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_W: 1344e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_H: 1345e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_B: 1346e54bcde3SZi Shen Lim case BPF_STX | BPF_MEM | BPF_DW: 13475b3d19b9SXu Kuohai if (ctx->fpb_offset > 0 && dst == fp) { 13485b3d19b9SXu Kuohai dst_adj = fpb; 13495b3d19b9SXu Kuohai off_adj = off + ctx->fpb_offset; 13505b3d19b9SXu Kuohai } else { 13515b3d19b9SXu Kuohai dst_adj = dst; 13525b3d19b9SXu Kuohai off_adj = off; 13535b3d19b9SXu Kuohai } 1354e54bcde3SZi Shen Lim switch (BPF_SIZE(code)) { 1355e54bcde3SZi Shen Lim case BPF_W: 13565b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 2)) { 13575b3d19b9SXu Kuohai emit(A64_STR32I(src, dst_adj, off_adj), ctx); 13587db6c0f1SXu Kuohai } else { 13597db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1360e54bcde3SZi Shen Lim emit(A64_STR32(src, dst, tmp), ctx); 13617db6c0f1SXu Kuohai } 1362e54bcde3SZi Shen Lim break; 1363e54bcde3SZi Shen Lim case BPF_H: 13645b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 1)) { 13655b3d19b9SXu Kuohai emit(A64_STRHI(src, dst_adj, off_adj), ctx); 13667db6c0f1SXu Kuohai } else { 13677db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1368e54bcde3SZi Shen Lim emit(A64_STRH(src, dst, tmp), ctx); 13697db6c0f1SXu Kuohai } 1370e54bcde3SZi Shen Lim break; 1371e54bcde3SZi Shen Lim case BPF_B: 13725b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 0)) { 13735b3d19b9SXu Kuohai emit(A64_STRBI(src, dst_adj, off_adj), ctx); 13747db6c0f1SXu Kuohai } else { 13757db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1376e54bcde3SZi Shen Lim emit(A64_STRB(src, dst, tmp), ctx); 13777db6c0f1SXu Kuohai } 1378e54bcde3SZi Shen Lim break; 1379e54bcde3SZi Shen Lim case BPF_DW: 13805b3d19b9SXu Kuohai if (is_lsi_offset(off_adj, 3)) { 13815b3d19b9SXu Kuohai emit(A64_STR64I(src, dst_adj, off_adj), ctx); 13827db6c0f1SXu Kuohai } else { 13837db6c0f1SXu Kuohai emit_a64_mov_i(1, tmp, off, ctx); 1384e54bcde3SZi Shen Lim emit(A64_STR64(src, dst, tmp), ctx); 13857db6c0f1SXu Kuohai } 1386e54bcde3SZi Shen Lim break; 1387e54bcde3SZi Shen Lim } 1388e54bcde3SZi Shen Lim break; 138934b8ab09SDaniel Borkmann 139091c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_W: 139191c960b0SBrendan Jackman case BPF_STX | BPF_ATOMIC | BPF_DW: 13921902472bSHou Tao if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) 13931902472bSHou Tao ret = emit_lse_atomic(insn, ctx); 13941902472bSHou Tao else 13951902472bSHou Tao ret = emit_ll_sc_atomic(insn, ctx); 13961902472bSHou Tao if (ret) 13971902472bSHou Tao return ret; 139885f68fe8SDaniel Borkmann break; 1399e54bcde3SZi Shen Lim 1400e54bcde3SZi Shen Lim default: 1401e54bcde3SZi Shen Lim pr_err_once("unknown opcode %02x\n", code); 1402e54bcde3SZi Shen Lim return -EINVAL; 1403e54bcde3SZi Shen Lim } 1404e54bcde3SZi Shen Lim 1405e54bcde3SZi Shen Lim return 0; 1406e54bcde3SZi Shen Lim } 1407e54bcde3SZi Shen Lim 14085b3d19b9SXu Kuohai /* 14095b3d19b9SXu Kuohai * Return 0 if FP may change at runtime, otherwise find the minimum negative 14105b3d19b9SXu Kuohai * offset to FP, converts it to positive number, and align down to 8 bytes. 14115b3d19b9SXu Kuohai */ 14125b3d19b9SXu Kuohai static int find_fpb_offset(struct bpf_prog *prog) 14135b3d19b9SXu Kuohai { 14145b3d19b9SXu Kuohai int i; 14155b3d19b9SXu Kuohai int offset = 0; 14165b3d19b9SXu Kuohai 14175b3d19b9SXu Kuohai for (i = 0; i < prog->len; i++) { 14185b3d19b9SXu Kuohai const struct bpf_insn *insn = &prog->insnsi[i]; 14195b3d19b9SXu Kuohai const u8 class = BPF_CLASS(insn->code); 14205b3d19b9SXu Kuohai const u8 mode = BPF_MODE(insn->code); 14215b3d19b9SXu Kuohai const u8 src = insn->src_reg; 14225b3d19b9SXu Kuohai const u8 dst = insn->dst_reg; 14235b3d19b9SXu Kuohai const s32 imm = insn->imm; 14245b3d19b9SXu Kuohai const s16 off = insn->off; 14255b3d19b9SXu Kuohai 14265b3d19b9SXu Kuohai switch (class) { 14275b3d19b9SXu Kuohai case BPF_STX: 14285b3d19b9SXu Kuohai case BPF_ST: 14295b3d19b9SXu Kuohai /* fp holds atomic operation result */ 14305b3d19b9SXu Kuohai if (class == BPF_STX && mode == BPF_ATOMIC && 14315b3d19b9SXu Kuohai ((imm == BPF_XCHG || 14325b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_ADD) || 14335b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_AND) || 14345b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_XOR) || 14355b3d19b9SXu Kuohai imm == (BPF_FETCH | BPF_OR)) && 14365b3d19b9SXu Kuohai src == BPF_REG_FP)) 14375b3d19b9SXu Kuohai return 0; 14385b3d19b9SXu Kuohai 14395b3d19b9SXu Kuohai if (mode == BPF_MEM && dst == BPF_REG_FP && 14405b3d19b9SXu Kuohai off < offset) 14415b3d19b9SXu Kuohai offset = insn->off; 14425b3d19b9SXu Kuohai break; 14435b3d19b9SXu Kuohai 14445b3d19b9SXu Kuohai case BPF_JMP32: 14455b3d19b9SXu Kuohai case BPF_JMP: 14465b3d19b9SXu Kuohai break; 14475b3d19b9SXu Kuohai 14485b3d19b9SXu Kuohai case BPF_LDX: 14495b3d19b9SXu Kuohai case BPF_LD: 14505b3d19b9SXu Kuohai /* fp holds load result */ 14515b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 14525b3d19b9SXu Kuohai return 0; 14535b3d19b9SXu Kuohai 14545b3d19b9SXu Kuohai if (class == BPF_LDX && mode == BPF_MEM && 14555b3d19b9SXu Kuohai src == BPF_REG_FP && off < offset) 14565b3d19b9SXu Kuohai offset = off; 14575b3d19b9SXu Kuohai break; 14585b3d19b9SXu Kuohai 14595b3d19b9SXu Kuohai case BPF_ALU: 14605b3d19b9SXu Kuohai case BPF_ALU64: 14615b3d19b9SXu Kuohai default: 14625b3d19b9SXu Kuohai /* fp holds ALU result */ 14635b3d19b9SXu Kuohai if (dst == BPF_REG_FP) 14645b3d19b9SXu Kuohai return 0; 14655b3d19b9SXu Kuohai } 14665b3d19b9SXu Kuohai } 14675b3d19b9SXu Kuohai 14685b3d19b9SXu Kuohai if (offset < 0) { 14695b3d19b9SXu Kuohai /* 14705b3d19b9SXu Kuohai * safely be converted to a positive 'int', since insn->off 14715b3d19b9SXu Kuohai * is 's16' 14725b3d19b9SXu Kuohai */ 14735b3d19b9SXu Kuohai offset = -offset; 14745b3d19b9SXu Kuohai /* align down to 8 bytes */ 14755b3d19b9SXu Kuohai offset = ALIGN_DOWN(offset, 8); 14765b3d19b9SXu Kuohai } 14775b3d19b9SXu Kuohai 14785b3d19b9SXu Kuohai return offset; 14795b3d19b9SXu Kuohai } 14805b3d19b9SXu Kuohai 14818c11ea5cSDaniel Borkmann static int build_body(struct jit_ctx *ctx, bool extra_pass) 1482e54bcde3SZi Shen Lim { 1483e54bcde3SZi Shen Lim const struct bpf_prog *prog = ctx->prog; 1484e54bcde3SZi Shen Lim int i; 1485e54bcde3SZi Shen Lim 148632f6865cSIlias Apalodimas /* 148732f6865cSIlias Apalodimas * - offset[0] offset of the end of prologue, 148832f6865cSIlias Apalodimas * start of the 1st instruction. 148932f6865cSIlias Apalodimas * - offset[1] - offset of the end of 1st instruction, 149032f6865cSIlias Apalodimas * start of the 2nd instruction 149132f6865cSIlias Apalodimas * [....] 149232f6865cSIlias Apalodimas * - offset[3] - offset of the end of 3rd instruction, 149332f6865cSIlias Apalodimas * start of 4th instruction 149432f6865cSIlias Apalodimas */ 1495e54bcde3SZi Shen Lim for (i = 0; i < prog->len; i++) { 1496e54bcde3SZi Shen Lim const struct bpf_insn *insn = &prog->insnsi[i]; 1497e54bcde3SZi Shen Lim int ret; 1498e54bcde3SZi Shen Lim 149932f6865cSIlias Apalodimas if (ctx->image == NULL) 150032f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 15018c11ea5cSDaniel Borkmann ret = build_insn(insn, ctx, extra_pass); 150230d3d94cSZi Shen Lim if (ret > 0) { 150330d3d94cSZi Shen Lim i++; 1504ddc665a4SDaniel Borkmann if (ctx->image == NULL) 1505ddc665a4SDaniel Borkmann ctx->offset[i] = ctx->idx; 150630d3d94cSZi Shen Lim continue; 150730d3d94cSZi Shen Lim } 1508e54bcde3SZi Shen Lim if (ret) 1509e54bcde3SZi Shen Lim return ret; 1510e54bcde3SZi Shen Lim } 151132f6865cSIlias Apalodimas /* 151232f6865cSIlias Apalodimas * offset is allocated with prog->len + 1 so fill in 151332f6865cSIlias Apalodimas * the last element with the offset after the last 151432f6865cSIlias Apalodimas * instruction (end of program) 151532f6865cSIlias Apalodimas */ 151632f6865cSIlias Apalodimas if (ctx->image == NULL) 151732f6865cSIlias Apalodimas ctx->offset[i] = ctx->idx; 1518e54bcde3SZi Shen Lim 1519e54bcde3SZi Shen Lim return 0; 1520e54bcde3SZi Shen Lim } 1521e54bcde3SZi Shen Lim 152242ff712bSZi Shen Lim static int validate_code(struct jit_ctx *ctx) 152342ff712bSZi Shen Lim { 152442ff712bSZi Shen Lim int i; 152542ff712bSZi Shen Lim 152642ff712bSZi Shen Lim for (i = 0; i < ctx->idx; i++) { 152742ff712bSZi Shen Lim u32 a64_insn = le32_to_cpu(ctx->image[i]); 152842ff712bSZi Shen Lim 152942ff712bSZi Shen Lim if (a64_insn == AARCH64_BREAK_FAULT) 153042ff712bSZi Shen Lim return -1; 153142ff712bSZi Shen Lim } 1532efc9909fSXu Kuohai return 0; 1533efc9909fSXu Kuohai } 1534efc9909fSXu Kuohai 1535efc9909fSXu Kuohai static int validate_ctx(struct jit_ctx *ctx) 1536efc9909fSXu Kuohai { 1537efc9909fSXu Kuohai if (validate_code(ctx)) 1538efc9909fSXu Kuohai return -1; 153942ff712bSZi Shen Lim 154080083428SJean-Philippe Brucker if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries)) 154180083428SJean-Philippe Brucker return -1; 154280083428SJean-Philippe Brucker 154342ff712bSZi Shen Lim return 0; 154442ff712bSZi Shen Lim } 154542ff712bSZi Shen Lim 1546e54bcde3SZi Shen Lim static inline void bpf_flush_icache(void *start, void *end) 1547e54bcde3SZi Shen Lim { 1548e54bcde3SZi Shen Lim flush_icache_range((unsigned long)start, (unsigned long)end); 1549e54bcde3SZi Shen Lim } 1550e54bcde3SZi Shen Lim 1551db496944SAlexei Starovoitov struct arm64_jit_data { 1552db496944SAlexei Starovoitov struct bpf_binary_header *header; 1553db496944SAlexei Starovoitov u8 *image; 1554db496944SAlexei Starovoitov struct jit_ctx ctx; 1555db496944SAlexei Starovoitov }; 1556db496944SAlexei Starovoitov 1557d1c55ab5SDaniel Borkmann struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 1558e54bcde3SZi Shen Lim { 1559b2ad54e1SXu Kuohai int image_size, prog_size, extable_size, extable_align, extable_offset; 156026eb042eSDaniel Borkmann struct bpf_prog *tmp, *orig_prog = prog; 1561b569c1c6SDaniel Borkmann struct bpf_binary_header *header; 1562db496944SAlexei Starovoitov struct arm64_jit_data *jit_data; 156356ea6a8bSDaniel Borkmann bool was_classic = bpf_prog_was_classic(prog); 156426eb042eSDaniel Borkmann bool tmp_blinded = false; 1565db496944SAlexei Starovoitov bool extra_pass = false; 1566e54bcde3SZi Shen Lim struct jit_ctx ctx; 1567b569c1c6SDaniel Borkmann u8 *image_ptr; 1568e54bcde3SZi Shen Lim 156960b58afcSAlexei Starovoitov if (!prog->jit_requested) 157026eb042eSDaniel Borkmann return orig_prog; 157126eb042eSDaniel Borkmann 157226eb042eSDaniel Borkmann tmp = bpf_jit_blind_constants(prog); 157326eb042eSDaniel Borkmann /* If blinding was requested and we failed during blinding, 157426eb042eSDaniel Borkmann * we must fall back to the interpreter. 157526eb042eSDaniel Borkmann */ 157626eb042eSDaniel Borkmann if (IS_ERR(tmp)) 157726eb042eSDaniel Borkmann return orig_prog; 157826eb042eSDaniel Borkmann if (tmp != prog) { 157926eb042eSDaniel Borkmann tmp_blinded = true; 158026eb042eSDaniel Borkmann prog = tmp; 158126eb042eSDaniel Borkmann } 1582e54bcde3SZi Shen Lim 1583db496944SAlexei Starovoitov jit_data = prog->aux->jit_data; 1584db496944SAlexei Starovoitov if (!jit_data) { 1585db496944SAlexei Starovoitov jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 1586db496944SAlexei Starovoitov if (!jit_data) { 1587db496944SAlexei Starovoitov prog = orig_prog; 1588db496944SAlexei Starovoitov goto out; 1589db496944SAlexei Starovoitov } 1590db496944SAlexei Starovoitov prog->aux->jit_data = jit_data; 1591db496944SAlexei Starovoitov } 1592db496944SAlexei Starovoitov if (jit_data->ctx.offset) { 1593db496944SAlexei Starovoitov ctx = jit_data->ctx; 1594db496944SAlexei Starovoitov image_ptr = jit_data->image; 1595db496944SAlexei Starovoitov header = jit_data->header; 1596db496944SAlexei Starovoitov extra_pass = true; 159780083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1598db496944SAlexei Starovoitov goto skip_init_ctx; 1599db496944SAlexei Starovoitov } 1600e54bcde3SZi Shen Lim memset(&ctx, 0, sizeof(ctx)); 1601e54bcde3SZi Shen Lim ctx.prog = prog; 1602e54bcde3SZi Shen Lim 160319f68ed6SAijun Sun ctx.offset = kvcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); 160426eb042eSDaniel Borkmann if (ctx.offset == NULL) { 160526eb042eSDaniel Borkmann prog = orig_prog; 1606db496944SAlexei Starovoitov goto out_off; 160726eb042eSDaniel Borkmann } 1608e54bcde3SZi Shen Lim 16095b3d19b9SXu Kuohai ctx.fpb_offset = find_fpb_offset(prog); 16105b3d19b9SXu Kuohai 161168e4f238SHou Tao /* 161268e4f238SHou Tao * 1. Initial fake pass to compute ctx->idx and ctx->offset. 161368e4f238SHou Tao * 161468e4f238SHou Tao * BPF line info needs ctx->offset[i] to be the offset of 161568e4f238SHou Tao * instruction[i] in jited image, so build prologue first. 161668e4f238SHou Tao */ 1617*22fc0e80SPuranjay Mohan if (build_prologue(&ctx, was_classic, prog->aux->exception_cb)) { 161826eb042eSDaniel Borkmann prog = orig_prog; 161926eb042eSDaniel Borkmann goto out_off; 162026eb042eSDaniel Borkmann } 1621e54bcde3SZi Shen Lim 162268e4f238SHou Tao if (build_body(&ctx, extra_pass)) { 1623ddb55992SZi Shen Lim prog = orig_prog; 1624ddb55992SZi Shen Lim goto out_off; 1625ddb55992SZi Shen Lim } 162651c9fbb1SZi Shen Lim 162751c9fbb1SZi Shen Lim ctx.epilogue_offset = ctx.idx; 1628*22fc0e80SPuranjay Mohan build_epilogue(&ctx, prog->aux->exception_cb); 1629b2ad54e1SXu Kuohai build_plt(&ctx); 1630e54bcde3SZi Shen Lim 1631b2ad54e1SXu Kuohai extable_align = __alignof__(struct exception_table_entry); 163280083428SJean-Philippe Brucker extable_size = prog->aux->num_exentries * 163380083428SJean-Philippe Brucker sizeof(struct exception_table_entry); 163480083428SJean-Philippe Brucker 1635e54bcde3SZi Shen Lim /* Now we know the actual image size. */ 163680083428SJean-Philippe Brucker prog_size = sizeof(u32) * ctx.idx; 1637b2ad54e1SXu Kuohai /* also allocate space for plt target */ 1638b2ad54e1SXu Kuohai extable_offset = round_up(prog_size + PLT_TARGET_SIZE, extable_align); 1639b2ad54e1SXu Kuohai image_size = extable_offset + extable_size; 1640b569c1c6SDaniel Borkmann header = bpf_jit_binary_alloc(image_size, &image_ptr, 1641b569c1c6SDaniel Borkmann sizeof(u32), jit_fill_hole); 164226eb042eSDaniel Borkmann if (header == NULL) { 164326eb042eSDaniel Borkmann prog = orig_prog; 164426eb042eSDaniel Borkmann goto out_off; 164526eb042eSDaniel Borkmann } 1646e54bcde3SZi Shen Lim 1647e54bcde3SZi Shen Lim /* 2. Now, the actual pass. */ 1648e54bcde3SZi Shen Lim 1649425e1ed7SLuc Van Oostenryck ctx.image = (__le32 *)image_ptr; 165080083428SJean-Philippe Brucker if (extable_size) 1651b2ad54e1SXu Kuohai prog->aux->extable = (void *)image_ptr + extable_offset; 1652db496944SAlexei Starovoitov skip_init_ctx: 1653e54bcde3SZi Shen Lim ctx.idx = 0; 165480083428SJean-Philippe Brucker ctx.exentry_idx = 0; 1655b569c1c6SDaniel Borkmann 1656*22fc0e80SPuranjay Mohan build_prologue(&ctx, was_classic, prog->aux->exception_cb); 1657e54bcde3SZi Shen Lim 16588c11ea5cSDaniel Borkmann if (build_body(&ctx, extra_pass)) { 1659b569c1c6SDaniel Borkmann bpf_jit_binary_free(header); 166026eb042eSDaniel Borkmann prog = orig_prog; 166126eb042eSDaniel Borkmann goto out_off; 166260ef0494SDaniel Borkmann } 1663e54bcde3SZi Shen Lim 1664*22fc0e80SPuranjay Mohan build_epilogue(&ctx, prog->aux->exception_cb); 1665b2ad54e1SXu Kuohai build_plt(&ctx); 1666e54bcde3SZi Shen Lim 166742ff712bSZi Shen Lim /* 3. Extra pass to validate JITed code. */ 1668efc9909fSXu Kuohai if (validate_ctx(&ctx)) { 166942ff712bSZi Shen Lim bpf_jit_binary_free(header); 167026eb042eSDaniel Borkmann prog = orig_prog; 167126eb042eSDaniel Borkmann goto out_off; 167242ff712bSZi Shen Lim } 167342ff712bSZi Shen Lim 1674e54bcde3SZi Shen Lim /* And we're done. */ 1675e54bcde3SZi Shen Lim if (bpf_jit_enable > 1) 167680083428SJean-Philippe Brucker bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 1677e54bcde3SZi Shen Lim 1678c3d4c682SDaniel Borkmann bpf_flush_icache(header, ctx.image + ctx.idx); 1679b569c1c6SDaniel Borkmann 1680db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1681db496944SAlexei Starovoitov if (extra_pass && ctx.idx != jit_data->ctx.idx) { 1682db496944SAlexei Starovoitov pr_err_once("multi-func JIT bug %d != %d\n", 1683db496944SAlexei Starovoitov ctx.idx, jit_data->ctx.idx); 1684db496944SAlexei Starovoitov bpf_jit_binary_free(header); 1685db496944SAlexei Starovoitov prog->bpf_func = NULL; 1686db496944SAlexei Starovoitov prog->jited = 0; 168710f3b29cSEric Dumazet prog->jited_len = 0; 1688db496944SAlexei Starovoitov goto out_off; 1689db496944SAlexei Starovoitov } 16909d876e79SDaniel Borkmann bpf_jit_binary_lock_ro(header); 1691db496944SAlexei Starovoitov } else { 1692db496944SAlexei Starovoitov jit_data->ctx = ctx; 1693db496944SAlexei Starovoitov jit_data->image = image_ptr; 1694db496944SAlexei Starovoitov jit_data->header = header; 1695db496944SAlexei Starovoitov } 1696e54bcde3SZi Shen Lim prog->bpf_func = (void *)ctx.image; 1697a91263d5SDaniel Borkmann prog->jited = 1; 169880083428SJean-Philippe Brucker prog->jited_len = prog_size; 169926eb042eSDaniel Borkmann 1700db496944SAlexei Starovoitov if (!prog->is_func || extra_pass) { 1701dda7596cSHou Tao int i; 1702dda7596cSHou Tao 1703dda7596cSHou Tao /* offset[prog->len] is the size of program */ 1704dda7596cSHou Tao for (i = 0; i <= prog->len; i++) 1705dda7596cSHou Tao ctx.offset[i] *= AARCH64_INSN_SIZE; 170632f6865cSIlias Apalodimas bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 170726eb042eSDaniel Borkmann out_off: 170819f68ed6SAijun Sun kvfree(ctx.offset); 1709db496944SAlexei Starovoitov kfree(jit_data); 1710db496944SAlexei Starovoitov prog->aux->jit_data = NULL; 1711db496944SAlexei Starovoitov } 171226eb042eSDaniel Borkmann out: 171326eb042eSDaniel Borkmann if (tmp_blinded) 171426eb042eSDaniel Borkmann bpf_jit_prog_release_other(prog, prog == orig_prog ? 171526eb042eSDaniel Borkmann tmp : orig_prog); 1716d1c55ab5SDaniel Borkmann return prog; 1717e54bcde3SZi Shen Lim } 171891fc957cSArd Biesheuvel 1719b5e975d2SHou Tao bool bpf_jit_supports_kfunc_call(void) 1720b5e975d2SHou Tao { 1721b5e975d2SHou Tao return true; 1722b5e975d2SHou Tao } 1723b5e975d2SHou Tao 17245d63ae90SLorenz Bauer u64 bpf_jit_alloc_exec_limit(void) 17255d63ae90SLorenz Bauer { 1726b89ddf4cSRussell King return VMALLOC_END - VMALLOC_START; 17275d63ae90SLorenz Bauer } 17285d63ae90SLorenz Bauer 172991fc957cSArd Biesheuvel void *bpf_jit_alloc_exec(unsigned long size) 173091fc957cSArd Biesheuvel { 173136c4a73bSAndrey Konovalov /* Memory is intended to be executable, reset the pointer tag. */ 173236c4a73bSAndrey Konovalov return kasan_reset_tag(vmalloc(size)); 173391fc957cSArd Biesheuvel } 173491fc957cSArd Biesheuvel 173591fc957cSArd Biesheuvel void bpf_jit_free_exec(void *addr) 173691fc957cSArd Biesheuvel { 173791fc957cSArd Biesheuvel return vfree(addr); 173891fc957cSArd Biesheuvel } 1739d4609a5dSJakub Sitnicki 1740d4609a5dSJakub Sitnicki /* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */ 1741d4609a5dSJakub Sitnicki bool bpf_jit_supports_subprog_tailcalls(void) 1742d4609a5dSJakub Sitnicki { 1743d4609a5dSJakub Sitnicki return true; 1744d4609a5dSJakub Sitnicki } 1745b2ad54e1SXu Kuohai 1746efc9909fSXu Kuohai static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, 1747efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1748efc9909fSXu Kuohai bool save_ret) 1749efc9909fSXu Kuohai { 1750aada4766SXu Kuohai __le32 *branch; 1751efc9909fSXu Kuohai u64 enter_prog; 1752efc9909fSXu Kuohai u64 exit_prog; 1753efc9909fSXu Kuohai struct bpf_prog *p = l->link.prog; 1754efc9909fSXu Kuohai int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); 1755efc9909fSXu Kuohai 1756271de525SMartin KaFai Lau enter_prog = (u64)bpf_trampoline_enter(p); 1757271de525SMartin KaFai Lau exit_prog = (u64)bpf_trampoline_exit(p); 1758efc9909fSXu Kuohai 1759efc9909fSXu Kuohai if (l->cookie == 0) { 1760efc9909fSXu Kuohai /* if cookie is zero, one instruction is enough to store it */ 1761efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, run_ctx_off + cookie_off), ctx); 1762efc9909fSXu Kuohai } else { 1763efc9909fSXu Kuohai emit_a64_mov_i64(A64_R(10), l->cookie, ctx); 1764efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, run_ctx_off + cookie_off), 1765efc9909fSXu Kuohai ctx); 1766efc9909fSXu Kuohai } 1767efc9909fSXu Kuohai 1768efc9909fSXu Kuohai /* save p to callee saved register x19 to avoid loading p with mov_i64 1769efc9909fSXu Kuohai * each time. 1770efc9909fSXu Kuohai */ 1771efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(19), (const u64)p, ctx); 1772efc9909fSXu Kuohai 1773efc9909fSXu Kuohai /* arg1: prog */ 1774efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1775efc9909fSXu Kuohai /* arg2: &run_ctx */ 1776efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(1), A64_SP, run_ctx_off), ctx); 1777efc9909fSXu Kuohai 1778efc9909fSXu Kuohai emit_call(enter_prog, ctx); 1779efc9909fSXu Kuohai 1780efc9909fSXu Kuohai /* if (__bpf_prog_enter(prog) == 0) 1781efc9909fSXu Kuohai * goto skip_exec_of_prog; 1782efc9909fSXu Kuohai */ 1783efc9909fSXu Kuohai branch = ctx->image + ctx->idx; 1784efc9909fSXu Kuohai emit(A64_NOP, ctx); 1785efc9909fSXu Kuohai 1786efc9909fSXu Kuohai /* save return value to callee saved register x20 */ 1787efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(20), A64_R(0)), ctx); 1788efc9909fSXu Kuohai 1789efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx); 1790efc9909fSXu Kuohai if (!p->jited) 1791efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx); 1792efc9909fSXu Kuohai 1793efc9909fSXu Kuohai emit_call((const u64)p->bpf_func, ctx); 1794efc9909fSXu Kuohai 1795efc9909fSXu Kuohai if (save_ret) 1796efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 1797efc9909fSXu Kuohai 1798efc9909fSXu Kuohai if (ctx->image) { 1799efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branch; 1800aada4766SXu Kuohai *branch = cpu_to_le32(A64_CBZ(1, A64_R(0), offset)); 1801efc9909fSXu Kuohai } 1802efc9909fSXu Kuohai 1803efc9909fSXu Kuohai /* arg1: prog */ 1804efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(0), A64_R(19)), ctx); 1805efc9909fSXu Kuohai /* arg2: start time */ 1806efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(1), A64_R(20)), ctx); 1807efc9909fSXu Kuohai /* arg3: &run_ctx */ 1808efc9909fSXu Kuohai emit(A64_ADD_I(1, A64_R(2), A64_SP, run_ctx_off), ctx); 1809efc9909fSXu Kuohai 1810efc9909fSXu Kuohai emit_call(exit_prog, ctx); 1811efc9909fSXu Kuohai } 1812efc9909fSXu Kuohai 1813efc9909fSXu Kuohai static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, 1814efc9909fSXu Kuohai int args_off, int retval_off, int run_ctx_off, 1815aada4766SXu Kuohai __le32 **branches) 1816efc9909fSXu Kuohai { 1817efc9909fSXu Kuohai int i; 1818efc9909fSXu Kuohai 1819efc9909fSXu Kuohai /* The first fmod_ret program will receive a garbage return value. 1820efc9909fSXu Kuohai * Set this to 0 to avoid confusing the program. 1821efc9909fSXu Kuohai */ 1822efc9909fSXu Kuohai emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx); 1823efc9909fSXu Kuohai for (i = 0; i < tl->nr_links; i++) { 1824efc9909fSXu Kuohai invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off, 1825efc9909fSXu Kuohai run_ctx_off, true); 1826efc9909fSXu Kuohai /* if (*(u64 *)(sp + retval_off) != 0) 1827efc9909fSXu Kuohai * goto do_fexit; 1828efc9909fSXu Kuohai */ 1829efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retval_off), ctx); 1830efc9909fSXu Kuohai /* Save the location of branch, and generate a nop. 1831efc9909fSXu Kuohai * This nop will be replaced with a cbnz later. 1832efc9909fSXu Kuohai */ 1833efc9909fSXu Kuohai branches[i] = ctx->image + ctx->idx; 1834efc9909fSXu Kuohai emit(A64_NOP, ctx); 1835efc9909fSXu Kuohai } 1836efc9909fSXu Kuohai } 1837efc9909fSXu Kuohai 183890564f1eSFlorent Revest static void save_args(struct jit_ctx *ctx, int args_off, int nregs) 1839efc9909fSXu Kuohai { 1840efc9909fSXu Kuohai int i; 1841efc9909fSXu Kuohai 184290564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 1843efc9909fSXu Kuohai emit(A64_STR64I(i, A64_SP, args_off), ctx); 1844efc9909fSXu Kuohai args_off += 8; 1845efc9909fSXu Kuohai } 1846efc9909fSXu Kuohai } 1847efc9909fSXu Kuohai 184890564f1eSFlorent Revest static void restore_args(struct jit_ctx *ctx, int args_off, int nregs) 1849efc9909fSXu Kuohai { 1850efc9909fSXu Kuohai int i; 1851efc9909fSXu Kuohai 185290564f1eSFlorent Revest for (i = 0; i < nregs; i++) { 1853efc9909fSXu Kuohai emit(A64_LDR64I(i, A64_SP, args_off), ctx); 1854efc9909fSXu Kuohai args_off += 8; 1855efc9909fSXu Kuohai } 1856efc9909fSXu Kuohai } 1857efc9909fSXu Kuohai 1858efc9909fSXu Kuohai /* Based on the x86's implementation of arch_prepare_bpf_trampoline(). 1859efc9909fSXu Kuohai * 1860efc9909fSXu Kuohai * bpf prog and function entry before bpf trampoline hooked: 1861efc9909fSXu Kuohai * mov x9, lr 1862efc9909fSXu Kuohai * nop 1863efc9909fSXu Kuohai * 1864efc9909fSXu Kuohai * bpf prog and function entry after bpf trampoline hooked: 1865efc9909fSXu Kuohai * mov x9, lr 1866efc9909fSXu Kuohai * bl <bpf_trampoline or plt> 1867efc9909fSXu Kuohai * 1868efc9909fSXu Kuohai */ 1869efc9909fSXu Kuohai static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, 18707a3d9a15SSong Liu struct bpf_tramp_links *tlinks, void *func_addr, 187190564f1eSFlorent Revest int nregs, u32 flags) 1872efc9909fSXu Kuohai { 1873efc9909fSXu Kuohai int i; 1874efc9909fSXu Kuohai int stack_size; 1875efc9909fSXu Kuohai int retaddr_off; 1876efc9909fSXu Kuohai int regs_off; 1877efc9909fSXu Kuohai int retval_off; 1878efc9909fSXu Kuohai int args_off; 187990564f1eSFlorent Revest int nregs_off; 1880efc9909fSXu Kuohai int ip_off; 1881efc9909fSXu Kuohai int run_ctx_off; 1882efc9909fSXu Kuohai struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; 1883efc9909fSXu Kuohai struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; 1884efc9909fSXu Kuohai struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; 1885efc9909fSXu Kuohai bool save_ret; 1886aada4766SXu Kuohai __le32 **branches = NULL; 1887efc9909fSXu Kuohai 1888efc9909fSXu Kuohai /* trampoline stack layout: 1889efc9909fSXu Kuohai * [ parent ip ] 1890efc9909fSXu Kuohai * [ FP ] 1891efc9909fSXu Kuohai * SP + retaddr_off [ self ip ] 1892efc9909fSXu Kuohai * [ FP ] 1893efc9909fSXu Kuohai * 1894efc9909fSXu Kuohai * [ padding ] align SP to multiples of 16 1895efc9909fSXu Kuohai * 1896efc9909fSXu Kuohai * [ x20 ] callee saved reg x20 1897efc9909fSXu Kuohai * SP + regs_off [ x19 ] callee saved reg x19 1898efc9909fSXu Kuohai * 1899efc9909fSXu Kuohai * SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or 1900efc9909fSXu Kuohai * BPF_TRAMP_F_RET_FENTRY_RET 1901efc9909fSXu Kuohai * 190290564f1eSFlorent Revest * [ arg reg N ] 1903efc9909fSXu Kuohai * [ ... ] 190490564f1eSFlorent Revest * SP + args_off [ arg reg 1 ] 1905efc9909fSXu Kuohai * 190690564f1eSFlorent Revest * SP + nregs_off [ arg regs count ] 1907efc9909fSXu Kuohai * 1908efc9909fSXu Kuohai * SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag 1909efc9909fSXu Kuohai * 1910efc9909fSXu Kuohai * SP + run_ctx_off [ bpf_tramp_run_ctx ] 1911efc9909fSXu Kuohai */ 1912efc9909fSXu Kuohai 1913efc9909fSXu Kuohai stack_size = 0; 1914efc9909fSXu Kuohai run_ctx_off = stack_size; 1915efc9909fSXu Kuohai /* room for bpf_tramp_run_ctx */ 1916efc9909fSXu Kuohai stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); 1917efc9909fSXu Kuohai 1918efc9909fSXu Kuohai ip_off = stack_size; 1919efc9909fSXu Kuohai /* room for IP address argument */ 1920efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) 1921efc9909fSXu Kuohai stack_size += 8; 1922efc9909fSXu Kuohai 192390564f1eSFlorent Revest nregs_off = stack_size; 1924efc9909fSXu Kuohai /* room for args count */ 1925efc9909fSXu Kuohai stack_size += 8; 1926efc9909fSXu Kuohai 1927efc9909fSXu Kuohai args_off = stack_size; 1928efc9909fSXu Kuohai /* room for args */ 192990564f1eSFlorent Revest stack_size += nregs * 8; 1930efc9909fSXu Kuohai 1931efc9909fSXu Kuohai /* room for return value */ 1932efc9909fSXu Kuohai retval_off = stack_size; 1933efc9909fSXu Kuohai save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET); 1934efc9909fSXu Kuohai if (save_ret) 1935efc9909fSXu Kuohai stack_size += 8; 1936efc9909fSXu Kuohai 1937efc9909fSXu Kuohai /* room for callee saved registers, currently x19 and x20 are used */ 1938efc9909fSXu Kuohai regs_off = stack_size; 1939efc9909fSXu Kuohai stack_size += 16; 1940efc9909fSXu Kuohai 1941efc9909fSXu Kuohai /* round up to multiples of 16 to avoid SPAlignmentFault */ 1942efc9909fSXu Kuohai stack_size = round_up(stack_size, 16); 1943efc9909fSXu Kuohai 1944efc9909fSXu Kuohai /* return address locates above FP */ 1945efc9909fSXu Kuohai retaddr_off = stack_size + 8; 1946efc9909fSXu Kuohai 1947efc9909fSXu Kuohai /* bpf trampoline may be invoked by 3 instruction types: 1948efc9909fSXu Kuohai * 1. bl, attached to bpf prog or kernel function via short jump 1949efc9909fSXu Kuohai * 2. br, attached to bpf prog or kernel function via long jump 1950efc9909fSXu Kuohai * 3. blr, working as a function pointer, used by struct_ops. 1951efc9909fSXu Kuohai * So BTI_JC should used here to support both br and blr. 1952efc9909fSXu Kuohai */ 1953efc9909fSXu Kuohai emit_bti(A64_BTI_JC, ctx); 1954efc9909fSXu Kuohai 1955efc9909fSXu Kuohai /* frame for parent function */ 1956efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx); 1957efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 1958efc9909fSXu Kuohai 1959efc9909fSXu Kuohai /* frame for patched function */ 1960efc9909fSXu Kuohai emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 1961efc9909fSXu Kuohai emit(A64_MOV(1, A64_FP, A64_SP), ctx); 1962efc9909fSXu Kuohai 1963efc9909fSXu Kuohai /* allocate stack space */ 1964efc9909fSXu Kuohai emit(A64_SUB_I(1, A64_SP, A64_SP, stack_size), ctx); 1965efc9909fSXu Kuohai 1966efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_IP_ARG) { 1967efc9909fSXu Kuohai /* save ip address of the traced function */ 19687a3d9a15SSong Liu emit_addr_mov_i64(A64_R(10), (const u64)func_addr, ctx); 1969efc9909fSXu Kuohai emit(A64_STR64I(A64_R(10), A64_SP, ip_off), ctx); 1970efc9909fSXu Kuohai } 1971efc9909fSXu Kuohai 197290564f1eSFlorent Revest /* save arg regs count*/ 197390564f1eSFlorent Revest emit(A64_MOVZ(1, A64_R(10), nregs, 0), ctx); 197490564f1eSFlorent Revest emit(A64_STR64I(A64_R(10), A64_SP, nregs_off), ctx); 1975efc9909fSXu Kuohai 197690564f1eSFlorent Revest /* save arg regs */ 197790564f1eSFlorent Revest save_args(ctx, args_off, nregs); 1978efc9909fSXu Kuohai 1979efc9909fSXu Kuohai /* save callee saved registers */ 1980efc9909fSXu Kuohai emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx); 1981efc9909fSXu Kuohai emit(A64_STR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 1982efc9909fSXu Kuohai 1983efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 1984efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 1985efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_enter, ctx); 1986efc9909fSXu Kuohai } 1987efc9909fSXu Kuohai 1988efc9909fSXu Kuohai for (i = 0; i < fentry->nr_links; i++) 1989efc9909fSXu Kuohai invoke_bpf_prog(ctx, fentry->links[i], args_off, 1990efc9909fSXu Kuohai retval_off, run_ctx_off, 1991efc9909fSXu Kuohai flags & BPF_TRAMP_F_RET_FENTRY_RET); 1992efc9909fSXu Kuohai 1993efc9909fSXu Kuohai if (fmod_ret->nr_links) { 1994aada4766SXu Kuohai branches = kcalloc(fmod_ret->nr_links, sizeof(__le32 *), 1995efc9909fSXu Kuohai GFP_KERNEL); 1996efc9909fSXu Kuohai if (!branches) 1997efc9909fSXu Kuohai return -ENOMEM; 1998efc9909fSXu Kuohai 1999efc9909fSXu Kuohai invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off, 2000efc9909fSXu Kuohai run_ctx_off, branches); 2001efc9909fSXu Kuohai } 2002efc9909fSXu Kuohai 2003efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 200490564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 2005efc9909fSXu Kuohai /* call original func */ 2006efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx); 2007738a96c4SXu Kuohai emit(A64_ADR(A64_LR, AARCH64_INSN_SIZE * 2), ctx); 2008738a96c4SXu Kuohai emit(A64_RET(A64_R(10)), ctx); 2009efc9909fSXu Kuohai /* store return value */ 2010efc9909fSXu Kuohai emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); 2011efc9909fSXu Kuohai /* reserve a nop for bpf_tramp_image_put */ 2012efc9909fSXu Kuohai im->ip_after_call = ctx->image + ctx->idx; 2013efc9909fSXu Kuohai emit(A64_NOP, ctx); 2014efc9909fSXu Kuohai } 2015efc9909fSXu Kuohai 2016efc9909fSXu Kuohai /* update the branches saved in invoke_bpf_mod_ret with cbnz */ 2017efc9909fSXu Kuohai for (i = 0; i < fmod_ret->nr_links && ctx->image != NULL; i++) { 2018efc9909fSXu Kuohai int offset = &ctx->image[ctx->idx] - branches[i]; 2019aada4766SXu Kuohai *branches[i] = cpu_to_le32(A64_CBNZ(1, A64_R(10), offset)); 2020efc9909fSXu Kuohai } 2021efc9909fSXu Kuohai 2022efc9909fSXu Kuohai for (i = 0; i < fexit->nr_links; i++) 2023efc9909fSXu Kuohai invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off, 2024efc9909fSXu Kuohai run_ctx_off, false); 2025efc9909fSXu Kuohai 2026efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_CALL_ORIG) { 2027efc9909fSXu Kuohai im->ip_epilogue = ctx->image + ctx->idx; 2028efc9909fSXu Kuohai emit_addr_mov_i64(A64_R(0), (const u64)im, ctx); 2029efc9909fSXu Kuohai emit_call((const u64)__bpf_tramp_exit, ctx); 2030efc9909fSXu Kuohai } 2031efc9909fSXu Kuohai 2032efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_RESTORE_REGS) 203390564f1eSFlorent Revest restore_args(ctx, args_off, nregs); 2034efc9909fSXu Kuohai 2035efc9909fSXu Kuohai /* restore callee saved register x19 and x20 */ 2036efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx); 2037efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(20), A64_SP, regs_off + 8), ctx); 2038efc9909fSXu Kuohai 2039efc9909fSXu Kuohai if (save_ret) 2040efc9909fSXu Kuohai emit(A64_LDR64I(A64_R(0), A64_SP, retval_off), ctx); 2041efc9909fSXu Kuohai 2042efc9909fSXu Kuohai /* reset SP */ 2043efc9909fSXu Kuohai emit(A64_MOV(1, A64_SP, A64_FP), ctx); 2044efc9909fSXu Kuohai 2045efc9909fSXu Kuohai /* pop frames */ 2046efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 2047efc9909fSXu Kuohai emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx); 2048efc9909fSXu Kuohai 2049efc9909fSXu Kuohai if (flags & BPF_TRAMP_F_SKIP_FRAME) { 2050efc9909fSXu Kuohai /* skip patched function, return to parent */ 2051efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 2052efc9909fSXu Kuohai emit(A64_RET(A64_R(9)), ctx); 2053efc9909fSXu Kuohai } else { 2054efc9909fSXu Kuohai /* return to patched function */ 2055efc9909fSXu Kuohai emit(A64_MOV(1, A64_R(10), A64_LR), ctx); 2056efc9909fSXu Kuohai emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); 2057efc9909fSXu Kuohai emit(A64_RET(A64_R(10)), ctx); 2058efc9909fSXu Kuohai } 2059efc9909fSXu Kuohai 2060efc9909fSXu Kuohai if (ctx->image) 2061efc9909fSXu Kuohai bpf_flush_icache(ctx->image, ctx->image + ctx->idx); 2062efc9909fSXu Kuohai 2063efc9909fSXu Kuohai kfree(branches); 2064efc9909fSXu Kuohai 2065efc9909fSXu Kuohai return ctx->idx; 2066efc9909fSXu Kuohai } 2067efc9909fSXu Kuohai 206896d1b7c0SSong Liu static int btf_func_model_nregs(const struct btf_func_model *m) 2069efc9909fSXu Kuohai { 207090564f1eSFlorent Revest int nregs = m->nr_args; 207196d1b7c0SSong Liu int i; 2072efc9909fSXu Kuohai 207390564f1eSFlorent Revest /* extra registers needed for struct argument */ 2074eb707ddeSYonghong Song for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) { 207590564f1eSFlorent Revest /* The arg_size is at most 16 bytes, enforced by the verifier. */ 2076eb707ddeSYonghong Song if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) 207790564f1eSFlorent Revest nregs += (m->arg_size[i] + 7) / 8 - 1; 2078eb707ddeSYonghong Song } 2079eb707ddeSYonghong Song 208096d1b7c0SSong Liu return nregs; 208196d1b7c0SSong Liu } 208296d1b7c0SSong Liu 208396d1b7c0SSong Liu int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, 208496d1b7c0SSong Liu struct bpf_tramp_links *tlinks, void *func_addr) 208596d1b7c0SSong Liu { 208696d1b7c0SSong Liu struct jit_ctx ctx = { 208796d1b7c0SSong Liu .image = NULL, 208896d1b7c0SSong Liu .idx = 0, 208996d1b7c0SSong Liu }; 209096d1b7c0SSong Liu struct bpf_tramp_image im; 209196d1b7c0SSong Liu int nregs, ret; 209296d1b7c0SSong Liu 209396d1b7c0SSong Liu nregs = btf_func_model_nregs(m); 209490564f1eSFlorent Revest /* the first 8 registers are used for arguments */ 209590564f1eSFlorent Revest if (nregs > 8) 209690564f1eSFlorent Revest return -ENOTSUPP; 209790564f1eSFlorent Revest 209896d1b7c0SSong Liu ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, nregs, flags); 2099efc9909fSXu Kuohai if (ret < 0) 2100efc9909fSXu Kuohai return ret; 2101efc9909fSXu Kuohai 210296d1b7c0SSong Liu return ret < 0 ? ret : ret * AARCH64_INSN_SIZE; 210396d1b7c0SSong Liu } 2104efc9909fSXu Kuohai 210596d1b7c0SSong Liu int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, 210696d1b7c0SSong Liu void *image_end, const struct btf_func_model *m, 210796d1b7c0SSong Liu u32 flags, struct bpf_tramp_links *tlinks, 210896d1b7c0SSong Liu void *func_addr) 210996d1b7c0SSong Liu { 211096d1b7c0SSong Liu int ret, nregs; 211196d1b7c0SSong Liu struct jit_ctx ctx = { 211296d1b7c0SSong Liu .image = image, 211396d1b7c0SSong Liu .idx = 0, 211496d1b7c0SSong Liu }; 211596d1b7c0SSong Liu 211696d1b7c0SSong Liu nregs = btf_func_model_nregs(m); 211796d1b7c0SSong Liu /* the first 8 registers are used for arguments */ 211896d1b7c0SSong Liu if (nregs > 8) 211996d1b7c0SSong Liu return -ENOTSUPP; 212096d1b7c0SSong Liu 2121efc9909fSXu Kuohai jit_fill_hole(image, (unsigned int)(image_end - image)); 21227a3d9a15SSong Liu ret = prepare_trampoline(&ctx, im, tlinks, func_addr, nregs, flags); 2123efc9909fSXu Kuohai 2124efc9909fSXu Kuohai if (ret > 0 && validate_code(&ctx) < 0) 2125efc9909fSXu Kuohai ret = -EINVAL; 2126efc9909fSXu Kuohai 2127efc9909fSXu Kuohai if (ret > 0) 2128efc9909fSXu Kuohai ret *= AARCH64_INSN_SIZE; 2129efc9909fSXu Kuohai 2130efc9909fSXu Kuohai return ret; 2131efc9909fSXu Kuohai } 2132efc9909fSXu Kuohai 2133b2ad54e1SXu Kuohai static bool is_long_jump(void *ip, void *target) 2134b2ad54e1SXu Kuohai { 2135b2ad54e1SXu Kuohai long offset; 2136b2ad54e1SXu Kuohai 2137b2ad54e1SXu Kuohai /* NULL target means this is a NOP */ 2138b2ad54e1SXu Kuohai if (!target) 2139b2ad54e1SXu Kuohai return false; 2140b2ad54e1SXu Kuohai 2141b2ad54e1SXu Kuohai offset = (long)target - (long)ip; 2142b2ad54e1SXu Kuohai return offset < -SZ_128M || offset >= SZ_128M; 2143b2ad54e1SXu Kuohai } 2144b2ad54e1SXu Kuohai 2145b2ad54e1SXu Kuohai static int gen_branch_or_nop(enum aarch64_insn_branch_type type, void *ip, 2146b2ad54e1SXu Kuohai void *addr, void *plt, u32 *insn) 2147b2ad54e1SXu Kuohai { 2148b2ad54e1SXu Kuohai void *target; 2149b2ad54e1SXu Kuohai 2150b2ad54e1SXu Kuohai if (!addr) { 2151b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_nop(); 2152b2ad54e1SXu Kuohai return 0; 2153b2ad54e1SXu Kuohai } 2154b2ad54e1SXu Kuohai 2155b2ad54e1SXu Kuohai if (is_long_jump(ip, addr)) 2156b2ad54e1SXu Kuohai target = plt; 2157b2ad54e1SXu Kuohai else 2158b2ad54e1SXu Kuohai target = addr; 2159b2ad54e1SXu Kuohai 2160b2ad54e1SXu Kuohai *insn = aarch64_insn_gen_branch_imm((unsigned long)ip, 2161b2ad54e1SXu Kuohai (unsigned long)target, 2162b2ad54e1SXu Kuohai type); 2163b2ad54e1SXu Kuohai 2164b2ad54e1SXu Kuohai return *insn != AARCH64_BREAK_FAULT ? 0 : -EFAULT; 2165b2ad54e1SXu Kuohai } 2166b2ad54e1SXu Kuohai 2167b2ad54e1SXu Kuohai /* Replace the branch instruction from @ip to @old_addr in a bpf prog or a bpf 2168b2ad54e1SXu Kuohai * trampoline with the branch instruction from @ip to @new_addr. If @old_addr 2169b2ad54e1SXu Kuohai * or @new_addr is NULL, the old or new instruction is NOP. 2170b2ad54e1SXu Kuohai * 2171b2ad54e1SXu Kuohai * When @ip is the bpf prog entry, a bpf trampoline is being attached or 2172b2ad54e1SXu Kuohai * detached. Since bpf trampoline and bpf prog are allocated separately with 2173b2ad54e1SXu Kuohai * vmalloc, the address distance may exceed 128MB, the maximum branch range. 2174b2ad54e1SXu Kuohai * So long jump should be handled. 2175b2ad54e1SXu Kuohai * 2176b2ad54e1SXu Kuohai * When a bpf prog is constructed, a plt pointing to empty trampoline 2177b2ad54e1SXu Kuohai * dummy_tramp is placed at the end: 2178b2ad54e1SXu Kuohai * 2179b2ad54e1SXu Kuohai * bpf_prog: 2180b2ad54e1SXu Kuohai * mov x9, lr 2181b2ad54e1SXu Kuohai * nop // patchsite 2182b2ad54e1SXu Kuohai * ... 2183b2ad54e1SXu Kuohai * ret 2184b2ad54e1SXu Kuohai * 2185b2ad54e1SXu Kuohai * plt: 2186b2ad54e1SXu Kuohai * ldr x10, target 2187b2ad54e1SXu Kuohai * br x10 2188b2ad54e1SXu Kuohai * target: 2189b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2190b2ad54e1SXu Kuohai * 2191b2ad54e1SXu Kuohai * This is also the state when no trampoline is attached. 2192b2ad54e1SXu Kuohai * 2193b2ad54e1SXu Kuohai * When a short-jump bpf trampoline is attached, the patchsite is patched 2194b2ad54e1SXu Kuohai * to a bl instruction to the trampoline directly: 2195b2ad54e1SXu Kuohai * 2196b2ad54e1SXu Kuohai * bpf_prog: 2197b2ad54e1SXu Kuohai * mov x9, lr 2198b2ad54e1SXu Kuohai * bl <short-jump bpf trampoline address> // patchsite 2199b2ad54e1SXu Kuohai * ... 2200b2ad54e1SXu Kuohai * ret 2201b2ad54e1SXu Kuohai * 2202b2ad54e1SXu Kuohai * plt: 2203b2ad54e1SXu Kuohai * ldr x10, target 2204b2ad54e1SXu Kuohai * br x10 2205b2ad54e1SXu Kuohai * target: 2206b2ad54e1SXu Kuohai * .quad dummy_tramp // plt target 2207b2ad54e1SXu Kuohai * 2208b2ad54e1SXu Kuohai * When a long-jump bpf trampoline is attached, the plt target is filled with 2209b2ad54e1SXu Kuohai * the trampoline address and the patchsite is patched to a bl instruction to 2210b2ad54e1SXu Kuohai * the plt: 2211b2ad54e1SXu Kuohai * 2212b2ad54e1SXu Kuohai * bpf_prog: 2213b2ad54e1SXu Kuohai * mov x9, lr 2214b2ad54e1SXu Kuohai * bl plt // patchsite 2215b2ad54e1SXu Kuohai * ... 2216b2ad54e1SXu Kuohai * ret 2217b2ad54e1SXu Kuohai * 2218b2ad54e1SXu Kuohai * plt: 2219b2ad54e1SXu Kuohai * ldr x10, target 2220b2ad54e1SXu Kuohai * br x10 2221b2ad54e1SXu Kuohai * target: 2222b2ad54e1SXu Kuohai * .quad <long-jump bpf trampoline address> // plt target 2223b2ad54e1SXu Kuohai * 2224b2ad54e1SXu Kuohai * The dummy_tramp is used to prevent another CPU from jumping to unknown 2225b2ad54e1SXu Kuohai * locations during the patching process, making the patching process easier. 2226b2ad54e1SXu Kuohai */ 2227b2ad54e1SXu Kuohai int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, 2228b2ad54e1SXu Kuohai void *old_addr, void *new_addr) 2229b2ad54e1SXu Kuohai { 2230b2ad54e1SXu Kuohai int ret; 2231b2ad54e1SXu Kuohai u32 old_insn; 2232b2ad54e1SXu Kuohai u32 new_insn; 2233b2ad54e1SXu Kuohai u32 replaced; 2234b2ad54e1SXu Kuohai struct bpf_plt *plt = NULL; 2235b2ad54e1SXu Kuohai unsigned long size = 0UL; 2236b2ad54e1SXu Kuohai unsigned long offset = ~0UL; 2237b2ad54e1SXu Kuohai enum aarch64_insn_branch_type branch_type; 2238b2ad54e1SXu Kuohai char namebuf[KSYM_NAME_LEN]; 2239b2ad54e1SXu Kuohai void *image = NULL; 2240b2ad54e1SXu Kuohai u64 plt_target = 0ULL; 2241b2ad54e1SXu Kuohai bool poking_bpf_entry; 2242b2ad54e1SXu Kuohai 2243b2ad54e1SXu Kuohai if (!__bpf_address_lookup((unsigned long)ip, &size, &offset, namebuf)) 2244b2ad54e1SXu Kuohai /* Only poking bpf text is supported. Since kernel function 2245b2ad54e1SXu Kuohai * entry is set up by ftrace, we reply on ftrace to poke kernel 2246b2ad54e1SXu Kuohai * functions. 2247b2ad54e1SXu Kuohai */ 2248b2ad54e1SXu Kuohai return -ENOTSUPP; 2249b2ad54e1SXu Kuohai 2250b2ad54e1SXu Kuohai image = ip - offset; 2251b2ad54e1SXu Kuohai /* zero offset means we're poking bpf prog entry */ 2252b2ad54e1SXu Kuohai poking_bpf_entry = (offset == 0UL); 2253b2ad54e1SXu Kuohai 2254b2ad54e1SXu Kuohai /* bpf prog entry, find plt and the real patchsite */ 2255b2ad54e1SXu Kuohai if (poking_bpf_entry) { 2256b2ad54e1SXu Kuohai /* plt locates at the end of bpf prog */ 2257b2ad54e1SXu Kuohai plt = image + size - PLT_TARGET_OFFSET; 2258b2ad54e1SXu Kuohai 2259b2ad54e1SXu Kuohai /* skip to the nop instruction in bpf prog entry: 2260b2ad54e1SXu Kuohai * bti c // if BTI enabled 2261b2ad54e1SXu Kuohai * mov x9, x30 2262b2ad54e1SXu Kuohai * nop 2263b2ad54e1SXu Kuohai */ 2264b2ad54e1SXu Kuohai ip = image + POKE_OFFSET * AARCH64_INSN_SIZE; 2265b2ad54e1SXu Kuohai } 2266b2ad54e1SXu Kuohai 2267b2ad54e1SXu Kuohai /* long jump is only possible at bpf prog entry */ 2268b2ad54e1SXu Kuohai if (WARN_ON((is_long_jump(ip, new_addr) || is_long_jump(ip, old_addr)) && 2269b2ad54e1SXu Kuohai !poking_bpf_entry)) 2270b2ad54e1SXu Kuohai return -EINVAL; 2271b2ad54e1SXu Kuohai 2272b2ad54e1SXu Kuohai if (poke_type == BPF_MOD_CALL) 2273b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_LINK; 2274b2ad54e1SXu Kuohai else 2275b2ad54e1SXu Kuohai branch_type = AARCH64_INSN_BRANCH_NOLINK; 2276b2ad54e1SXu Kuohai 2277b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, old_addr, plt, &old_insn) < 0) 2278b2ad54e1SXu Kuohai return -EFAULT; 2279b2ad54e1SXu Kuohai 2280b2ad54e1SXu Kuohai if (gen_branch_or_nop(branch_type, ip, new_addr, plt, &new_insn) < 0) 2281b2ad54e1SXu Kuohai return -EFAULT; 2282b2ad54e1SXu Kuohai 2283b2ad54e1SXu Kuohai if (is_long_jump(ip, new_addr)) 2284b2ad54e1SXu Kuohai plt_target = (u64)new_addr; 2285b2ad54e1SXu Kuohai else if (is_long_jump(ip, old_addr)) 2286b2ad54e1SXu Kuohai /* if the old target is a long jump and the new target is not, 2287b2ad54e1SXu Kuohai * restore the plt target to dummy_tramp, so there is always a 2288b2ad54e1SXu Kuohai * legal and harmless address stored in plt target, and we'll 2289b2ad54e1SXu Kuohai * never jump from plt to an unknown place. 2290b2ad54e1SXu Kuohai */ 2291b2ad54e1SXu Kuohai plt_target = (u64)&dummy_tramp; 2292b2ad54e1SXu Kuohai 2293b2ad54e1SXu Kuohai if (plt_target) { 2294b2ad54e1SXu Kuohai /* non-zero plt_target indicates we're patching a bpf prog, 2295b2ad54e1SXu Kuohai * which is read only. 2296b2ad54e1SXu Kuohai */ 2297b2ad54e1SXu Kuohai if (set_memory_rw(PAGE_MASK & ((uintptr_t)&plt->target), 1)) 2298b2ad54e1SXu Kuohai return -EFAULT; 2299b2ad54e1SXu Kuohai WRITE_ONCE(plt->target, plt_target); 2300b2ad54e1SXu Kuohai set_memory_ro(PAGE_MASK & ((uintptr_t)&plt->target), 1); 2301b2ad54e1SXu Kuohai /* since plt target points to either the new trampoline 2302b2ad54e1SXu Kuohai * or dummy_tramp, even if another CPU reads the old plt 2303b2ad54e1SXu Kuohai * target value before fetching the bl instruction to plt, 2304b2ad54e1SXu Kuohai * it will be brought back by dummy_tramp, so no barrier is 2305b2ad54e1SXu Kuohai * required here. 2306b2ad54e1SXu Kuohai */ 2307b2ad54e1SXu Kuohai } 2308b2ad54e1SXu Kuohai 2309b2ad54e1SXu Kuohai /* if the old target and the new target are both long jumps, no 2310b2ad54e1SXu Kuohai * patching is required 2311b2ad54e1SXu Kuohai */ 2312b2ad54e1SXu Kuohai if (old_insn == new_insn) 2313b2ad54e1SXu Kuohai return 0; 2314b2ad54e1SXu Kuohai 2315b2ad54e1SXu Kuohai mutex_lock(&text_mutex); 2316b2ad54e1SXu Kuohai if (aarch64_insn_read(ip, &replaced)) { 2317b2ad54e1SXu Kuohai ret = -EFAULT; 2318b2ad54e1SXu Kuohai goto out; 2319b2ad54e1SXu Kuohai } 2320b2ad54e1SXu Kuohai 2321b2ad54e1SXu Kuohai if (replaced != old_insn) { 2322b2ad54e1SXu Kuohai ret = -EFAULT; 2323b2ad54e1SXu Kuohai goto out; 2324b2ad54e1SXu Kuohai } 2325b2ad54e1SXu Kuohai 2326b2ad54e1SXu Kuohai /* We call aarch64_insn_patch_text_nosync() to replace instruction 2327b2ad54e1SXu Kuohai * atomically, so no other CPUs will fetch a half-new and half-old 2328b2ad54e1SXu Kuohai * instruction. But there is chance that another CPU executes the 2329b2ad54e1SXu Kuohai * old instruction after the patching operation finishes (e.g., 2330b2ad54e1SXu Kuohai * pipeline not flushed, or icache not synchronized yet). 2331b2ad54e1SXu Kuohai * 2332b2ad54e1SXu Kuohai * 1. when a new trampoline is attached, it is not a problem for 2333b2ad54e1SXu Kuohai * different CPUs to jump to different trampolines temporarily. 2334b2ad54e1SXu Kuohai * 2335b2ad54e1SXu Kuohai * 2. when an old trampoline is freed, we should wait for all other 2336b2ad54e1SXu Kuohai * CPUs to exit the trampoline and make sure the trampoline is no 2337b2ad54e1SXu Kuohai * longer reachable, since bpf_tramp_image_put() function already 2338b2ad54e1SXu Kuohai * uses percpu_ref and task-based rcu to do the sync, no need to call 2339b2ad54e1SXu Kuohai * the sync version here, see bpf_tramp_image_put() for details. 2340b2ad54e1SXu Kuohai */ 2341b2ad54e1SXu Kuohai ret = aarch64_insn_patch_text_nosync(ip, new_insn); 2342b2ad54e1SXu Kuohai out: 2343b2ad54e1SXu Kuohai mutex_unlock(&text_mutex); 2344b2ad54e1SXu Kuohai 2345b2ad54e1SXu Kuohai return ret; 2346b2ad54e1SXu Kuohai } 234718a45f12SHou Tao 234818a45f12SHou Tao bool bpf_jit_supports_ptr_xchg(void) 234918a45f12SHou Tao { 235018a45f12SHou Tao return true; 235118a45f12SHou Tao } 2352*22fc0e80SPuranjay Mohan 2353*22fc0e80SPuranjay Mohan bool bpf_jit_supports_exceptions(void) 2354*22fc0e80SPuranjay Mohan { 2355*22fc0e80SPuranjay Mohan /* We unwind through both kernel frames starting from within bpf_throw 2356*22fc0e80SPuranjay Mohan * call and BPF frames. Therefore we require FP unwinder to be enabled 2357*22fc0e80SPuranjay Mohan * to walk kernel frames and reach BPF frames in the stack trace. 2358*22fc0e80SPuranjay Mohan * ARM64 kernel is aways compiled with CONFIG_FRAME_POINTER=y 2359*22fc0e80SPuranjay Mohan */ 2360*22fc0e80SPuranjay Mohan return true; 2361*22fc0e80SPuranjay Mohan } 2362