18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * BPF JIT compiler for ARM64 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "bpf_jit: " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 118c2ecf20Sopenharmony_ci#include <linux/bpf.h> 128c2ecf20Sopenharmony_ci#include <linux/filter.h> 138c2ecf20Sopenharmony_ci#include <linux/printk.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 178c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 188c2ecf20Sopenharmony_ci#include <asm/debug-monitors.h> 198c2ecf20Sopenharmony_ci#include <asm/set_memory.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "bpf_jit.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define TMP_REG_1 (MAX_BPF_JIT_REG + 0) 248c2ecf20Sopenharmony_ci#define TMP_REG_2 (MAX_BPF_JIT_REG + 1) 258c2ecf20Sopenharmony_ci#define TCALL_CNT (MAX_BPF_JIT_REG + 2) 268c2ecf20Sopenharmony_ci#define TMP_REG_3 (MAX_BPF_JIT_REG + 3) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Map BPF registers to A64 registers */ 298c2ecf20Sopenharmony_cistatic const int bpf2a64[] = { 308c2ecf20Sopenharmony_ci /* return value from in-kernel function, and exit value from eBPF */ 318c2ecf20Sopenharmony_ci [BPF_REG_0] = A64_R(7), 328c2ecf20Sopenharmony_ci /* arguments from eBPF program to in-kernel function */ 338c2ecf20Sopenharmony_ci [BPF_REG_1] = A64_R(0), 348c2ecf20Sopenharmony_ci [BPF_REG_2] = A64_R(1), 358c2ecf20Sopenharmony_ci [BPF_REG_3] = A64_R(2), 368c2ecf20Sopenharmony_ci [BPF_REG_4] = A64_R(3), 378c2ecf20Sopenharmony_ci [BPF_REG_5] = A64_R(4), 388c2ecf20Sopenharmony_ci /* callee saved registers that in-kernel function will preserve */ 398c2ecf20Sopenharmony_ci [BPF_REG_6] = A64_R(19), 408c2ecf20Sopenharmony_ci [BPF_REG_7] = A64_R(20), 418c2ecf20Sopenharmony_ci [BPF_REG_8] = A64_R(21), 428c2ecf20Sopenharmony_ci [BPF_REG_9] = A64_R(22), 438c2ecf20Sopenharmony_ci /* read-only frame pointer to access stack */ 448c2ecf20Sopenharmony_ci [BPF_REG_FP] = A64_R(25), 458c2ecf20Sopenharmony_ci /* temporary registers for internal BPF JIT */ 468c2ecf20Sopenharmony_ci [TMP_REG_1] = A64_R(10), 478c2ecf20Sopenharmony_ci [TMP_REG_2] = A64_R(11), 488c2ecf20Sopenharmony_ci [TMP_REG_3] = A64_R(12), 498c2ecf20Sopenharmony_ci /* tail_call_cnt */ 508c2ecf20Sopenharmony_ci [TCALL_CNT] = A64_R(26), 518c2ecf20Sopenharmony_ci /* temporary register for blinding constants */ 528c2ecf20Sopenharmony_ci [BPF_REG_AX] = A64_R(9), 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct jit_ctx { 568c2ecf20Sopenharmony_ci const struct bpf_prog *prog; 578c2ecf20Sopenharmony_ci int idx; 588c2ecf20Sopenharmony_ci int epilogue_offset; 598c2ecf20Sopenharmony_ci int *offset; 608c2ecf20Sopenharmony_ci int exentry_idx; 618c2ecf20Sopenharmony_ci __le32 *image; 628c2ecf20Sopenharmony_ci u32 stack_size; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline void emit(const u32 insn, struct jit_ctx *ctx) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci if (ctx->image != NULL) 688c2ecf20Sopenharmony_ci ctx->image[ctx->idx] = cpu_to_le32(insn); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ctx->idx++; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic inline void emit_a64_mov_i(const int is64, const int reg, 748c2ecf20Sopenharmony_ci const s32 val, struct jit_ctx *ctx) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u16 hi = val >> 16; 778c2ecf20Sopenharmony_ci u16 lo = val & 0xffff; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (hi & 0x8000) { 808c2ecf20Sopenharmony_ci if (hi == 0xffff) { 818c2ecf20Sopenharmony_ci emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx); 828c2ecf20Sopenharmony_ci } else { 838c2ecf20Sopenharmony_ci emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx); 848c2ecf20Sopenharmony_ci if (lo != 0xffff) 858c2ecf20Sopenharmony_ci emit(A64_MOVK(is64, reg, lo, 0), ctx); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci } else { 888c2ecf20Sopenharmony_ci emit(A64_MOVZ(is64, reg, lo, 0), ctx); 898c2ecf20Sopenharmony_ci if (hi) 908c2ecf20Sopenharmony_ci emit(A64_MOVK(is64, reg, hi, 16), ctx); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int i64_i16_blocks(const u64 val, bool inverse) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci return (((val >> 0) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 978c2ecf20Sopenharmony_ci (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 988c2ecf20Sopenharmony_ci (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) + 998c2ecf20Sopenharmony_ci (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000)); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic inline void emit_a64_mov_i64(const int reg, const u64 val, 1038c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci u64 nrm_tmp = val, rev_tmp = ~val; 1068c2ecf20Sopenharmony_ci bool inverse; 1078c2ecf20Sopenharmony_ci int shift; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (!(nrm_tmp >> 32)) 1108c2ecf20Sopenharmony_ci return emit_a64_mov_i(0, reg, (u32)val, ctx); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false); 1138c2ecf20Sopenharmony_ci shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) : 1148c2ecf20Sopenharmony_ci (fls64(nrm_tmp) - 1)), 16), 0); 1158c2ecf20Sopenharmony_ci if (inverse) 1168c2ecf20Sopenharmony_ci emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx); 1178c2ecf20Sopenharmony_ci else 1188c2ecf20Sopenharmony_ci emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1198c2ecf20Sopenharmony_ci shift -= 16; 1208c2ecf20Sopenharmony_ci while (shift >= 0) { 1218c2ecf20Sopenharmony_ci if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000)) 1228c2ecf20Sopenharmony_ci emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx); 1238c2ecf20Sopenharmony_ci shift -= 16; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* 1288c2ecf20Sopenharmony_ci * Kernel addresses in the vmalloc space use at most 48 bits, and the 1298c2ecf20Sopenharmony_ci * remaining bits are guaranteed to be 0x1. So we can compose the address 1308c2ecf20Sopenharmony_ci * with a fixed length movn/movk/movk sequence. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic inline void emit_addr_mov_i64(const int reg, const u64 val, 1338c2ecf20Sopenharmony_ci struct jit_ctx *ctx) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci u64 tmp = val; 1368c2ecf20Sopenharmony_ci int shift = 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci emit(A64_MOVN(1, reg, ~tmp & 0xffff, shift), ctx); 1398c2ecf20Sopenharmony_ci while (shift < 32) { 1408c2ecf20Sopenharmony_ci tmp >>= 16; 1418c2ecf20Sopenharmony_ci shift += 16; 1428c2ecf20Sopenharmony_ci emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic inline int bpf2a64_offset(int bpf_insn, int off, 1478c2ecf20Sopenharmony_ci const struct jit_ctx *ctx) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci /* BPF JMP offset is relative to the next instruction */ 1508c2ecf20Sopenharmony_ci bpf_insn++; 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * Whereas arm64 branch instructions encode the offset 1538c2ecf20Sopenharmony_ci * from the branch itself, so we must subtract 1 from the 1548c2ecf20Sopenharmony_ci * instruction offset. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci return ctx->offset[bpf_insn + off] - (ctx->offset[bpf_insn] - 1); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void jit_fill_hole(void *area, unsigned int size) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci __le32 *ptr; 1628c2ecf20Sopenharmony_ci /* We are guaranteed to have aligned memory. */ 1638c2ecf20Sopenharmony_ci for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 1648c2ecf20Sopenharmony_ci *ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic inline int epilogue_offset(const struct jit_ctx *ctx) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int to = ctx->epilogue_offset; 1708c2ecf20Sopenharmony_ci int from = ctx->idx; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return to - from; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic bool is_addsub_imm(u32 imm) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci /* Either imm12 or shifted imm12. */ 1788c2ecf20Sopenharmony_ci return !(imm & ~0xfff) || !(imm & ~0xfff000); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* Stack must be multiples of 16B */ 1828c2ecf20Sopenharmony_ci#define STACK_ALIGN(sz) (((sz) + 15) & ~15) 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* Tail call offset to jump into */ 1858c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) 1868c2ecf20Sopenharmony_ci#define PROLOGUE_OFFSET 8 1878c2ecf20Sopenharmony_ci#else 1888c2ecf20Sopenharmony_ci#define PROLOGUE_OFFSET 7 1898c2ecf20Sopenharmony_ci#endif 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci const struct bpf_prog *prog = ctx->prog; 1948c2ecf20Sopenharmony_ci const u8 r6 = bpf2a64[BPF_REG_6]; 1958c2ecf20Sopenharmony_ci const u8 r7 = bpf2a64[BPF_REG_7]; 1968c2ecf20Sopenharmony_ci const u8 r8 = bpf2a64[BPF_REG_8]; 1978c2ecf20Sopenharmony_ci const u8 r9 = bpf2a64[BPF_REG_9]; 1988c2ecf20Sopenharmony_ci const u8 fp = bpf2a64[BPF_REG_FP]; 1998c2ecf20Sopenharmony_ci const u8 tcc = bpf2a64[TCALL_CNT]; 2008c2ecf20Sopenharmony_ci const int idx0 = ctx->idx; 2018c2ecf20Sopenharmony_ci int cur_offset; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * BPF prog stack layout 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * high 2078c2ecf20Sopenharmony_ci * original A64_SP => 0:+-----+ BPF prologue 2088c2ecf20Sopenharmony_ci * |FP/LR| 2098c2ecf20Sopenharmony_ci * current A64_FP => -16:+-----+ 2108c2ecf20Sopenharmony_ci * | ... | callee saved registers 2118c2ecf20Sopenharmony_ci * BPF fp register => -64:+-----+ <= (BPF_FP) 2128c2ecf20Sopenharmony_ci * | | 2138c2ecf20Sopenharmony_ci * | ... | BPF prog stack 2148c2ecf20Sopenharmony_ci * | | 2158c2ecf20Sopenharmony_ci * +-----+ <= (BPF_FP - prog->aux->stack_depth) 2168c2ecf20Sopenharmony_ci * |RSVD | padding 2178c2ecf20Sopenharmony_ci * current A64_SP => +-----+ <= (BPF_FP - ctx->stack_size) 2188c2ecf20Sopenharmony_ci * | | 2198c2ecf20Sopenharmony_ci * | ... | Function call stack 2208c2ecf20Sopenharmony_ci * | | 2218c2ecf20Sopenharmony_ci * +-----+ 2228c2ecf20Sopenharmony_ci * low 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* BTI landing pad */ 2278c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) 2288c2ecf20Sopenharmony_ci emit(A64_BTI_C, ctx); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Save FP and LR registers to stay align with ARM64 AAPCS */ 2318c2ecf20Sopenharmony_ci emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); 2328c2ecf20Sopenharmony_ci emit(A64_MOV(1, A64_FP, A64_SP), ctx); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Save callee-saved registers */ 2358c2ecf20Sopenharmony_ci emit(A64_PUSH(r6, r7, A64_SP), ctx); 2368c2ecf20Sopenharmony_ci emit(A64_PUSH(r8, r9, A64_SP), ctx); 2378c2ecf20Sopenharmony_ci emit(A64_PUSH(fp, tcc, A64_SP), ctx); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Set up BPF prog stack base register */ 2408c2ecf20Sopenharmony_ci emit(A64_MOV(1, fp, A64_SP), ctx); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (!ebpf_from_cbpf) { 2438c2ecf20Sopenharmony_ci /* Initialize tail_call_cnt */ 2448c2ecf20Sopenharmony_ci emit(A64_MOVZ(1, tcc, 0, 0), ctx); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci cur_offset = ctx->idx - idx0; 2478c2ecf20Sopenharmony_ci if (cur_offset != PROLOGUE_OFFSET) { 2488c2ecf20Sopenharmony_ci pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n", 2498c2ecf20Sopenharmony_ci cur_offset, PROLOGUE_OFFSET); 2508c2ecf20Sopenharmony_ci return -1; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* BTI landing pad for the tail call, done with a BR */ 2548c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) 2558c2ecf20Sopenharmony_ci emit(A64_BTI_J, ctx); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci ctx->stack_size = STACK_ALIGN(prog->aux->stack_depth); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Set up function call stack */ 2618c2ecf20Sopenharmony_ci emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int out_offset = -1; /* initialized on the first pass of build_body() */ 2668c2ecf20Sopenharmony_cistatic int emit_bpf_tail_call(struct jit_ctx *ctx) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci /* bpf_tail_call(void *prog_ctx, struct bpf_array *array, u64 index) */ 2698c2ecf20Sopenharmony_ci const u8 r2 = bpf2a64[BPF_REG_2]; 2708c2ecf20Sopenharmony_ci const u8 r3 = bpf2a64[BPF_REG_3]; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci const u8 tmp = bpf2a64[TMP_REG_1]; 2738c2ecf20Sopenharmony_ci const u8 prg = bpf2a64[TMP_REG_2]; 2748c2ecf20Sopenharmony_ci const u8 tcc = bpf2a64[TCALL_CNT]; 2758c2ecf20Sopenharmony_ci const int idx0 = ctx->idx; 2768c2ecf20Sopenharmony_ci#define cur_offset (ctx->idx - idx0) 2778c2ecf20Sopenharmony_ci#define jmp_offset (out_offset - (cur_offset)) 2788c2ecf20Sopenharmony_ci size_t off; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* if (index >= array->map.max_entries) 2818c2ecf20Sopenharmony_ci * goto out; 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci off = offsetof(struct bpf_array, map.max_entries); 2848c2ecf20Sopenharmony_ci emit_a64_mov_i64(tmp, off, ctx); 2858c2ecf20Sopenharmony_ci emit(A64_LDR32(tmp, r2, tmp), ctx); 2868c2ecf20Sopenharmony_ci emit(A64_MOV(0, r3, r3), ctx); 2878c2ecf20Sopenharmony_ci emit(A64_CMP(0, r3, tmp), ctx); 2888c2ecf20Sopenharmony_ci emit(A64_B_(A64_COND_CS, jmp_offset), ctx); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* if (tail_call_cnt > MAX_TAIL_CALL_CNT) 2918c2ecf20Sopenharmony_ci * goto out; 2928c2ecf20Sopenharmony_ci * tail_call_cnt++; 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx); 2958c2ecf20Sopenharmony_ci emit(A64_CMP(1, tcc, tmp), ctx); 2968c2ecf20Sopenharmony_ci emit(A64_B_(A64_COND_HI, jmp_offset), ctx); 2978c2ecf20Sopenharmony_ci emit(A64_ADD_I(1, tcc, tcc, 1), ctx); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* prog = array->ptrs[index]; 3008c2ecf20Sopenharmony_ci * if (prog == NULL) 3018c2ecf20Sopenharmony_ci * goto out; 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci off = offsetof(struct bpf_array, ptrs); 3048c2ecf20Sopenharmony_ci emit_a64_mov_i64(tmp, off, ctx); 3058c2ecf20Sopenharmony_ci emit(A64_ADD(1, tmp, r2, tmp), ctx); 3068c2ecf20Sopenharmony_ci emit(A64_LSL(1, prg, r3, 3), ctx); 3078c2ecf20Sopenharmony_ci emit(A64_LDR64(prg, tmp, prg), ctx); 3088c2ecf20Sopenharmony_ci emit(A64_CBZ(1, prg, jmp_offset), ctx); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* goto *(prog->bpf_func + prologue_offset); */ 3118c2ecf20Sopenharmony_ci off = offsetof(struct bpf_prog, bpf_func); 3128c2ecf20Sopenharmony_ci emit_a64_mov_i64(tmp, off, ctx); 3138c2ecf20Sopenharmony_ci emit(A64_LDR64(tmp, prg, tmp), ctx); 3148c2ecf20Sopenharmony_ci emit(A64_ADD_I(1, tmp, tmp, sizeof(u32) * PROLOGUE_OFFSET), ctx); 3158c2ecf20Sopenharmony_ci emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 3168c2ecf20Sopenharmony_ci emit(A64_BR(tmp), ctx); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* out: */ 3198c2ecf20Sopenharmony_ci if (out_offset == -1) 3208c2ecf20Sopenharmony_ci out_offset = cur_offset; 3218c2ecf20Sopenharmony_ci if (cur_offset != out_offset) { 3228c2ecf20Sopenharmony_ci pr_err_once("tail_call out_offset = %d, expected %d!\n", 3238c2ecf20Sopenharmony_ci cur_offset, out_offset); 3248c2ecf20Sopenharmony_ci return -1; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci#undef cur_offset 3288c2ecf20Sopenharmony_ci#undef jmp_offset 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void build_epilogue(struct jit_ctx *ctx) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci const u8 r0 = bpf2a64[BPF_REG_0]; 3348c2ecf20Sopenharmony_ci const u8 r6 = bpf2a64[BPF_REG_6]; 3358c2ecf20Sopenharmony_ci const u8 r7 = bpf2a64[BPF_REG_7]; 3368c2ecf20Sopenharmony_ci const u8 r8 = bpf2a64[BPF_REG_8]; 3378c2ecf20Sopenharmony_ci const u8 r9 = bpf2a64[BPF_REG_9]; 3388c2ecf20Sopenharmony_ci const u8 fp = bpf2a64[BPF_REG_FP]; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* We're done with BPF stack */ 3418c2ecf20Sopenharmony_ci emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Restore fs (x25) and x26 */ 3448c2ecf20Sopenharmony_ci emit(A64_POP(fp, A64_R(26), A64_SP), ctx); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Restore callee-saved register */ 3478c2ecf20Sopenharmony_ci emit(A64_POP(r8, r9, A64_SP), ctx); 3488c2ecf20Sopenharmony_ci emit(A64_POP(r6, r7, A64_SP), ctx); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Restore FP/LR registers */ 3518c2ecf20Sopenharmony_ci emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Set return value */ 3548c2ecf20Sopenharmony_ci emit(A64_MOV(1, A64_R(0), r0), ctx); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci emit(A64_RET(A64_LR), ctx); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci#define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) 3608c2ecf20Sopenharmony_ci#define BPF_FIXUP_REG_MASK GENMASK(31, 27) 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ciint arm64_bpf_fixup_exception(const struct exception_table_entry *ex, 3638c2ecf20Sopenharmony_ci struct pt_regs *regs) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup); 3668c2ecf20Sopenharmony_ci int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci regs->regs[dst_reg] = 0; 3698c2ecf20Sopenharmony_ci regs->pc = (unsigned long)&ex->fixup - offset; 3708c2ecf20Sopenharmony_ci return 1; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/* For accesses to BTF pointers, add an entry to the exception table */ 3748c2ecf20Sopenharmony_cistatic int add_exception_handler(const struct bpf_insn *insn, 3758c2ecf20Sopenharmony_ci struct jit_ctx *ctx, 3768c2ecf20Sopenharmony_ci int dst_reg) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci off_t offset; 3798c2ecf20Sopenharmony_ci unsigned long pc; 3808c2ecf20Sopenharmony_ci struct exception_table_entry *ex; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (!ctx->image) 3838c2ecf20Sopenharmony_ci /* First pass */ 3848c2ecf20Sopenharmony_ci return 0; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (BPF_MODE(insn->code) != BPF_PROBE_MEM) 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (!ctx->prog->aux->extable || 3908c2ecf20Sopenharmony_ci WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries)) 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci ex = &ctx->prog->aux->extable[ctx->exentry_idx]; 3948c2ecf20Sopenharmony_ci pc = (unsigned long)&ctx->image[ctx->idx - 1]; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci offset = pc - (long)&ex->insn; 3978c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN)) 3988c2ecf20Sopenharmony_ci return -ERANGE; 3998c2ecf20Sopenharmony_ci ex->insn = offset; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * Since the extable follows the program, the fixup offset is always 4038c2ecf20Sopenharmony_ci * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value 4048c2ecf20Sopenharmony_ci * to keep things simple, and put the destination register in the upper 4058c2ecf20Sopenharmony_ci * bits. We don't need to worry about buildtime or runtime sort 4068c2ecf20Sopenharmony_ci * modifying the upper bits because the table is already sorted, and 4078c2ecf20Sopenharmony_ci * isn't part of the main exception table. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE); 4108c2ecf20Sopenharmony_ci if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset)) 4118c2ecf20Sopenharmony_ci return -ERANGE; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) | 4148c2ecf20Sopenharmony_ci FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ctx->exentry_idx++; 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/* JITs an eBPF instruction. 4218c2ecf20Sopenharmony_ci * Returns: 4228c2ecf20Sopenharmony_ci * 0 - successfully JITed an 8-byte eBPF instruction. 4238c2ecf20Sopenharmony_ci * >0 - successfully JITed a 16-byte eBPF instruction. 4248c2ecf20Sopenharmony_ci * <0 - failed to JIT. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_cistatic int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, 4278c2ecf20Sopenharmony_ci bool extra_pass) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci const u8 code = insn->code; 4308c2ecf20Sopenharmony_ci const u8 dst = bpf2a64[insn->dst_reg]; 4318c2ecf20Sopenharmony_ci const u8 src = bpf2a64[insn->src_reg]; 4328c2ecf20Sopenharmony_ci const u8 tmp = bpf2a64[TMP_REG_1]; 4338c2ecf20Sopenharmony_ci const u8 tmp2 = bpf2a64[TMP_REG_2]; 4348c2ecf20Sopenharmony_ci const u8 tmp3 = bpf2a64[TMP_REG_3]; 4358c2ecf20Sopenharmony_ci const s16 off = insn->off; 4368c2ecf20Sopenharmony_ci const s32 imm = insn->imm; 4378c2ecf20Sopenharmony_ci const int i = insn - ctx->prog->insnsi; 4388c2ecf20Sopenharmony_ci const bool is64 = BPF_CLASS(code) == BPF_ALU64 || 4398c2ecf20Sopenharmony_ci BPF_CLASS(code) == BPF_JMP; 4408c2ecf20Sopenharmony_ci const bool isdw = BPF_SIZE(code) == BPF_DW; 4418c2ecf20Sopenharmony_ci u8 jmp_cond, reg; 4428c2ecf20Sopenharmony_ci s32 jmp_offset; 4438c2ecf20Sopenharmony_ci u32 a64_insn; 4448c2ecf20Sopenharmony_ci int ret; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci#define check_imm(bits, imm) do { \ 4478c2ecf20Sopenharmony_ci if ((((imm) > 0) && ((imm) >> (bits))) || \ 4488c2ecf20Sopenharmony_ci (((imm) < 0) && (~(imm) >> (bits)))) { \ 4498c2ecf20Sopenharmony_ci pr_info("[%2d] imm=%d(0x%x) out of range\n", \ 4508c2ecf20Sopenharmony_ci i, imm, imm); \ 4518c2ecf20Sopenharmony_ci return -EINVAL; \ 4528c2ecf20Sopenharmony_ci } \ 4538c2ecf20Sopenharmony_ci} while (0) 4548c2ecf20Sopenharmony_ci#define check_imm19(imm) check_imm(19, imm) 4558c2ecf20Sopenharmony_ci#define check_imm26(imm) check_imm(26, imm) 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci switch (code) { 4588c2ecf20Sopenharmony_ci /* dst = src */ 4598c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_X: 4608c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_X: 4618c2ecf20Sopenharmony_ci emit(A64_MOV(is64, dst, src), ctx); 4628c2ecf20Sopenharmony_ci break; 4638c2ecf20Sopenharmony_ci /* dst = dst OP src */ 4648c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_X: 4658c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_X: 4668c2ecf20Sopenharmony_ci emit(A64_ADD(is64, dst, dst, src), ctx); 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_X: 4698c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_X: 4708c2ecf20Sopenharmony_ci emit(A64_SUB(is64, dst, dst, src), ctx); 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_X: 4738c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_X: 4748c2ecf20Sopenharmony_ci emit(A64_AND(is64, dst, dst, src), ctx); 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_X: 4778c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_X: 4788c2ecf20Sopenharmony_ci emit(A64_ORR(is64, dst, dst, src), ctx); 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_X: 4818c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_X: 4828c2ecf20Sopenharmony_ci emit(A64_EOR(is64, dst, dst, src), ctx); 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_X: 4858c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_X: 4868c2ecf20Sopenharmony_ci emit(A64_MUL(is64, dst, dst, src), ctx); 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_X: 4898c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_X: 4908c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_X: 4918c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_X: 4928c2ecf20Sopenharmony_ci switch (BPF_OP(code)) { 4938c2ecf20Sopenharmony_ci case BPF_DIV: 4948c2ecf20Sopenharmony_ci emit(A64_UDIV(is64, dst, dst, src), ctx); 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci case BPF_MOD: 4978c2ecf20Sopenharmony_ci emit(A64_UDIV(is64, tmp, dst, src), ctx); 4988c2ecf20Sopenharmony_ci emit(A64_MSUB(is64, dst, dst, tmp, src), ctx); 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_X: 5038c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_X: 5048c2ecf20Sopenharmony_ci emit(A64_LSLV(is64, dst, dst, src), ctx); 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_X: 5078c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_X: 5088c2ecf20Sopenharmony_ci emit(A64_LSRV(is64, dst, dst, src), ctx); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_X: 5118c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_X: 5128c2ecf20Sopenharmony_ci emit(A64_ASRV(is64, dst, dst, src), ctx); 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci /* dst = -dst */ 5158c2ecf20Sopenharmony_ci case BPF_ALU | BPF_NEG: 5168c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_NEG: 5178c2ecf20Sopenharmony_ci emit(A64_NEG(is64, dst, dst), ctx); 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci /* dst = BSWAP##imm(dst) */ 5208c2ecf20Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_LE: 5218c2ecf20Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_BE: 5228c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN 5238c2ecf20Sopenharmony_ci if (BPF_SRC(code) == BPF_FROM_BE) 5248c2ecf20Sopenharmony_ci goto emit_bswap_uxt; 5258c2ecf20Sopenharmony_ci#else /* !CONFIG_CPU_BIG_ENDIAN */ 5268c2ecf20Sopenharmony_ci if (BPF_SRC(code) == BPF_FROM_LE) 5278c2ecf20Sopenharmony_ci goto emit_bswap_uxt; 5288c2ecf20Sopenharmony_ci#endif 5298c2ecf20Sopenharmony_ci switch (imm) { 5308c2ecf20Sopenharmony_ci case 16: 5318c2ecf20Sopenharmony_ci emit(A64_REV16(is64, dst, dst), ctx); 5328c2ecf20Sopenharmony_ci /* zero-extend 16 bits into 64 bits */ 5338c2ecf20Sopenharmony_ci emit(A64_UXTH(is64, dst, dst), ctx); 5348c2ecf20Sopenharmony_ci break; 5358c2ecf20Sopenharmony_ci case 32: 5368c2ecf20Sopenharmony_ci emit(A64_REV32(is64, dst, dst), ctx); 5378c2ecf20Sopenharmony_ci /* upper 32 bits already cleared */ 5388c2ecf20Sopenharmony_ci break; 5398c2ecf20Sopenharmony_ci case 64: 5408c2ecf20Sopenharmony_ci emit(A64_REV64(dst, dst), ctx); 5418c2ecf20Sopenharmony_ci break; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ciemit_bswap_uxt: 5458c2ecf20Sopenharmony_ci switch (imm) { 5468c2ecf20Sopenharmony_ci case 16: 5478c2ecf20Sopenharmony_ci /* zero-extend 16 bits into 64 bits */ 5488c2ecf20Sopenharmony_ci emit(A64_UXTH(is64, dst, dst), ctx); 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci case 32: 5518c2ecf20Sopenharmony_ci /* zero-extend 32 bits into 64 bits */ 5528c2ecf20Sopenharmony_ci emit(A64_UXTW(is64, dst, dst), ctx); 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci case 64: 5558c2ecf20Sopenharmony_ci /* nop */ 5568c2ecf20Sopenharmony_ci break; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci /* dst = imm */ 5608c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_K: 5618c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_K: 5628c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, dst, imm, ctx); 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci /* dst = dst OP imm */ 5658c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_K: 5668c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_K: 5678c2ecf20Sopenharmony_ci if (is_addsub_imm(imm)) { 5688c2ecf20Sopenharmony_ci emit(A64_ADD_I(is64, dst, dst, imm), ctx); 5698c2ecf20Sopenharmony_ci } else if (is_addsub_imm(-imm)) { 5708c2ecf20Sopenharmony_ci emit(A64_SUB_I(is64, dst, dst, -imm), ctx); 5718c2ecf20Sopenharmony_ci } else { 5728c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp, imm, ctx); 5738c2ecf20Sopenharmony_ci emit(A64_ADD(is64, dst, dst, tmp), ctx); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_K: 5778c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_K: 5788c2ecf20Sopenharmony_ci if (is_addsub_imm(imm)) { 5798c2ecf20Sopenharmony_ci emit(A64_SUB_I(is64, dst, dst, imm), ctx); 5808c2ecf20Sopenharmony_ci } else if (is_addsub_imm(-imm)) { 5818c2ecf20Sopenharmony_ci emit(A64_ADD_I(is64, dst, dst, -imm), ctx); 5828c2ecf20Sopenharmony_ci } else { 5838c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp, imm, ctx); 5848c2ecf20Sopenharmony_ci emit(A64_SUB(is64, dst, dst, tmp), ctx); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_K: 5888c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_K: 5898c2ecf20Sopenharmony_ci a64_insn = A64_AND_I(is64, dst, dst, imm); 5908c2ecf20Sopenharmony_ci if (a64_insn != AARCH64_BREAK_FAULT) { 5918c2ecf20Sopenharmony_ci emit(a64_insn, ctx); 5928c2ecf20Sopenharmony_ci } else { 5938c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp, imm, ctx); 5948c2ecf20Sopenharmony_ci emit(A64_AND(is64, dst, dst, tmp), ctx); 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci break; 5978c2ecf20Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_K: 5988c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_K: 5998c2ecf20Sopenharmony_ci a64_insn = A64_ORR_I(is64, dst, dst, imm); 6008c2ecf20Sopenharmony_ci if (a64_insn != AARCH64_BREAK_FAULT) { 6018c2ecf20Sopenharmony_ci emit(a64_insn, ctx); 6028c2ecf20Sopenharmony_ci } else { 6038c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp, imm, ctx); 6048c2ecf20Sopenharmony_ci emit(A64_ORR(is64, dst, dst, tmp), ctx); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_K: 6088c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_K: 6098c2ecf20Sopenharmony_ci a64_insn = A64_EOR_I(is64, dst, dst, imm); 6108c2ecf20Sopenharmony_ci if (a64_insn != AARCH64_BREAK_FAULT) { 6118c2ecf20Sopenharmony_ci emit(a64_insn, ctx); 6128c2ecf20Sopenharmony_ci } else { 6138c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp, imm, ctx); 6148c2ecf20Sopenharmony_ci emit(A64_EOR(is64, dst, dst, tmp), ctx); 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_K: 6188c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_K: 6198c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp, imm, ctx); 6208c2ecf20Sopenharmony_ci emit(A64_MUL(is64, dst, dst, tmp), ctx); 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_K: 6238c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_K: 6248c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp, imm, ctx); 6258c2ecf20Sopenharmony_ci emit(A64_UDIV(is64, dst, dst, tmp), ctx); 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_K: 6288c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_K: 6298c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp2, imm, ctx); 6308c2ecf20Sopenharmony_ci emit(A64_UDIV(is64, tmp, dst, tmp2), ctx); 6318c2ecf20Sopenharmony_ci emit(A64_MSUB(is64, dst, dst, tmp, tmp2), ctx); 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_K: 6348c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_K: 6358c2ecf20Sopenharmony_ci emit(A64_LSL(is64, dst, dst, imm), ctx); 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_K: 6388c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_K: 6398c2ecf20Sopenharmony_ci emit(A64_LSR(is64, dst, dst, imm), ctx); 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_K: 6428c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_K: 6438c2ecf20Sopenharmony_ci emit(A64_ASR(is64, dst, dst, imm), ctx); 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* JUMP off */ 6478c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JA: 6488c2ecf20Sopenharmony_ci jmp_offset = bpf2a64_offset(i, off, ctx); 6498c2ecf20Sopenharmony_ci check_imm26(jmp_offset); 6508c2ecf20Sopenharmony_ci emit(A64_B(jmp_offset), ctx); 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci /* IF (dst COND src) JUMP off */ 6538c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_X: 6548c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_X: 6558c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_X: 6568c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_X: 6578c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_X: 6588c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_X: 6598c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_X: 6608c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_X: 6618c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_X: 6628c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_X: 6638c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_X: 6648c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_X: 6658c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_X: 6668c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_X: 6678c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_X: 6688c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_X: 6698c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_X: 6708c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_X: 6718c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_X: 6728c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_X: 6738c2ecf20Sopenharmony_ci emit(A64_CMP(is64, dst, src), ctx); 6748c2ecf20Sopenharmony_ciemit_cond_jmp: 6758c2ecf20Sopenharmony_ci jmp_offset = bpf2a64_offset(i, off, ctx); 6768c2ecf20Sopenharmony_ci check_imm19(jmp_offset); 6778c2ecf20Sopenharmony_ci switch (BPF_OP(code)) { 6788c2ecf20Sopenharmony_ci case BPF_JEQ: 6798c2ecf20Sopenharmony_ci jmp_cond = A64_COND_EQ; 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci case BPF_JGT: 6828c2ecf20Sopenharmony_ci jmp_cond = A64_COND_HI; 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci case BPF_JLT: 6858c2ecf20Sopenharmony_ci jmp_cond = A64_COND_CC; 6868c2ecf20Sopenharmony_ci break; 6878c2ecf20Sopenharmony_ci case BPF_JGE: 6888c2ecf20Sopenharmony_ci jmp_cond = A64_COND_CS; 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci case BPF_JLE: 6918c2ecf20Sopenharmony_ci jmp_cond = A64_COND_LS; 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci case BPF_JSET: 6948c2ecf20Sopenharmony_ci case BPF_JNE: 6958c2ecf20Sopenharmony_ci jmp_cond = A64_COND_NE; 6968c2ecf20Sopenharmony_ci break; 6978c2ecf20Sopenharmony_ci case BPF_JSGT: 6988c2ecf20Sopenharmony_ci jmp_cond = A64_COND_GT; 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci case BPF_JSLT: 7018c2ecf20Sopenharmony_ci jmp_cond = A64_COND_LT; 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci case BPF_JSGE: 7048c2ecf20Sopenharmony_ci jmp_cond = A64_COND_GE; 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci case BPF_JSLE: 7078c2ecf20Sopenharmony_ci jmp_cond = A64_COND_LE; 7088c2ecf20Sopenharmony_ci break; 7098c2ecf20Sopenharmony_ci default: 7108c2ecf20Sopenharmony_ci return -EFAULT; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci emit(A64_B_(jmp_cond, jmp_offset), ctx); 7138c2ecf20Sopenharmony_ci break; 7148c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_X: 7158c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_X: 7168c2ecf20Sopenharmony_ci emit(A64_TST(is64, dst, src), ctx); 7178c2ecf20Sopenharmony_ci goto emit_cond_jmp; 7188c2ecf20Sopenharmony_ci /* IF (dst COND imm) JUMP off */ 7198c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_K: 7208c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_K: 7218c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_K: 7228c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_K: 7238c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_K: 7248c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_K: 7258c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_K: 7268c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_K: 7278c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_K: 7288c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_K: 7298c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_K: 7308c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_K: 7318c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_K: 7328c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_K: 7338c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_K: 7348c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_K: 7358c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_K: 7368c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_K: 7378c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_K: 7388c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_K: 7398c2ecf20Sopenharmony_ci if (is_addsub_imm(imm)) { 7408c2ecf20Sopenharmony_ci emit(A64_CMP_I(is64, dst, imm), ctx); 7418c2ecf20Sopenharmony_ci } else if (is_addsub_imm(-imm)) { 7428c2ecf20Sopenharmony_ci emit(A64_CMN_I(is64, dst, -imm), ctx); 7438c2ecf20Sopenharmony_ci } else { 7448c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp, imm, ctx); 7458c2ecf20Sopenharmony_ci emit(A64_CMP(is64, dst, tmp), ctx); 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci goto emit_cond_jmp; 7488c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_K: 7498c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_K: 7508c2ecf20Sopenharmony_ci a64_insn = A64_TST_I(is64, dst, imm); 7518c2ecf20Sopenharmony_ci if (a64_insn != AARCH64_BREAK_FAULT) { 7528c2ecf20Sopenharmony_ci emit(a64_insn, ctx); 7538c2ecf20Sopenharmony_ci } else { 7548c2ecf20Sopenharmony_ci emit_a64_mov_i(is64, tmp, imm, ctx); 7558c2ecf20Sopenharmony_ci emit(A64_TST(is64, dst, tmp), ctx); 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci goto emit_cond_jmp; 7588c2ecf20Sopenharmony_ci /* function call */ 7598c2ecf20Sopenharmony_ci case BPF_JMP | BPF_CALL: 7608c2ecf20Sopenharmony_ci { 7618c2ecf20Sopenharmony_ci const u8 r0 = bpf2a64[BPF_REG_0]; 7628c2ecf20Sopenharmony_ci bool func_addr_fixed; 7638c2ecf20Sopenharmony_ci u64 func_addr; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 7668c2ecf20Sopenharmony_ci &func_addr, &func_addr_fixed); 7678c2ecf20Sopenharmony_ci if (ret < 0) 7688c2ecf20Sopenharmony_ci return ret; 7698c2ecf20Sopenharmony_ci emit_addr_mov_i64(tmp, func_addr, ctx); 7708c2ecf20Sopenharmony_ci emit(A64_BLR(tmp), ctx); 7718c2ecf20Sopenharmony_ci emit(A64_MOV(1, r0, A64_R(0)), ctx); 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci /* tail call */ 7758c2ecf20Sopenharmony_ci case BPF_JMP | BPF_TAIL_CALL: 7768c2ecf20Sopenharmony_ci if (emit_bpf_tail_call(ctx)) 7778c2ecf20Sopenharmony_ci return -EFAULT; 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci /* function return */ 7808c2ecf20Sopenharmony_ci case BPF_JMP | BPF_EXIT: 7818c2ecf20Sopenharmony_ci /* Optimization: when last instruction is EXIT, 7828c2ecf20Sopenharmony_ci simply fallthrough to epilogue. */ 7838c2ecf20Sopenharmony_ci if (i == ctx->prog->len - 1) 7848c2ecf20Sopenharmony_ci break; 7858c2ecf20Sopenharmony_ci jmp_offset = epilogue_offset(ctx); 7868c2ecf20Sopenharmony_ci check_imm26(jmp_offset); 7878c2ecf20Sopenharmony_ci emit(A64_B(jmp_offset), ctx); 7888c2ecf20Sopenharmony_ci break; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* dst = imm64 */ 7918c2ecf20Sopenharmony_ci case BPF_LD | BPF_IMM | BPF_DW: 7928c2ecf20Sopenharmony_ci { 7938c2ecf20Sopenharmony_ci const struct bpf_insn insn1 = insn[1]; 7948c2ecf20Sopenharmony_ci u64 imm64; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci imm64 = (u64)insn1.imm << 32 | (u32)imm; 7978c2ecf20Sopenharmony_ci emit_a64_mov_i64(dst, imm64, ctx); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci return 1; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* LDX: dst = *(size *)(src + off) */ 8038c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_W: 8048c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_H: 8058c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_B: 8068c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_DW: 8078c2ecf20Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 8088c2ecf20Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_W: 8098c2ecf20Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_H: 8108c2ecf20Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_B: 8118c2ecf20Sopenharmony_ci emit_a64_mov_i(1, tmp, off, ctx); 8128c2ecf20Sopenharmony_ci switch (BPF_SIZE(code)) { 8138c2ecf20Sopenharmony_ci case BPF_W: 8148c2ecf20Sopenharmony_ci emit(A64_LDR32(dst, src, tmp), ctx); 8158c2ecf20Sopenharmony_ci break; 8168c2ecf20Sopenharmony_ci case BPF_H: 8178c2ecf20Sopenharmony_ci emit(A64_LDRH(dst, src, tmp), ctx); 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci case BPF_B: 8208c2ecf20Sopenharmony_ci emit(A64_LDRB(dst, src, tmp), ctx); 8218c2ecf20Sopenharmony_ci break; 8228c2ecf20Sopenharmony_ci case BPF_DW: 8238c2ecf20Sopenharmony_ci emit(A64_LDR64(dst, src, tmp), ctx); 8248c2ecf20Sopenharmony_ci break; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci ret = add_exception_handler(insn, ctx, dst); 8288c2ecf20Sopenharmony_ci if (ret) 8298c2ecf20Sopenharmony_ci return ret; 8308c2ecf20Sopenharmony_ci break; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* speculation barrier */ 8338c2ecf20Sopenharmony_ci case BPF_ST | BPF_NOSPEC: 8348c2ecf20Sopenharmony_ci /* 8358c2ecf20Sopenharmony_ci * Nothing required here. 8368c2ecf20Sopenharmony_ci * 8378c2ecf20Sopenharmony_ci * In case of arm64, we rely on the firmware mitigation of 8388c2ecf20Sopenharmony_ci * Speculative Store Bypass as controlled via the ssbd kernel 8398c2ecf20Sopenharmony_ci * parameter. Whenever the mitigation is enabled, it works 8408c2ecf20Sopenharmony_ci * for all of the kernel code with no need to provide any 8418c2ecf20Sopenharmony_ci * additional instructions. 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* ST: *(size *)(dst + off) = imm */ 8468c2ecf20Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_W: 8478c2ecf20Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_H: 8488c2ecf20Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_B: 8498c2ecf20Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_DW: 8508c2ecf20Sopenharmony_ci /* Load imm to a register then store it */ 8518c2ecf20Sopenharmony_ci emit_a64_mov_i(1, tmp2, off, ctx); 8528c2ecf20Sopenharmony_ci emit_a64_mov_i(1, tmp, imm, ctx); 8538c2ecf20Sopenharmony_ci switch (BPF_SIZE(code)) { 8548c2ecf20Sopenharmony_ci case BPF_W: 8558c2ecf20Sopenharmony_ci emit(A64_STR32(tmp, dst, tmp2), ctx); 8568c2ecf20Sopenharmony_ci break; 8578c2ecf20Sopenharmony_ci case BPF_H: 8588c2ecf20Sopenharmony_ci emit(A64_STRH(tmp, dst, tmp2), ctx); 8598c2ecf20Sopenharmony_ci break; 8608c2ecf20Sopenharmony_ci case BPF_B: 8618c2ecf20Sopenharmony_ci emit(A64_STRB(tmp, dst, tmp2), ctx); 8628c2ecf20Sopenharmony_ci break; 8638c2ecf20Sopenharmony_ci case BPF_DW: 8648c2ecf20Sopenharmony_ci emit(A64_STR64(tmp, dst, tmp2), ctx); 8658c2ecf20Sopenharmony_ci break; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci break; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* STX: *(size *)(dst + off) = src */ 8708c2ecf20Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_W: 8718c2ecf20Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_H: 8728c2ecf20Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_B: 8738c2ecf20Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_DW: 8748c2ecf20Sopenharmony_ci emit_a64_mov_i(1, tmp, off, ctx); 8758c2ecf20Sopenharmony_ci switch (BPF_SIZE(code)) { 8768c2ecf20Sopenharmony_ci case BPF_W: 8778c2ecf20Sopenharmony_ci emit(A64_STR32(src, dst, tmp), ctx); 8788c2ecf20Sopenharmony_ci break; 8798c2ecf20Sopenharmony_ci case BPF_H: 8808c2ecf20Sopenharmony_ci emit(A64_STRH(src, dst, tmp), ctx); 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci case BPF_B: 8838c2ecf20Sopenharmony_ci emit(A64_STRB(src, dst, tmp), ctx); 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci case BPF_DW: 8868c2ecf20Sopenharmony_ci emit(A64_STR64(src, dst, tmp), ctx); 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* STX XADD: lock *(u32 *)(dst + off) += src */ 8928c2ecf20Sopenharmony_ci case BPF_STX | BPF_XADD | BPF_W: 8938c2ecf20Sopenharmony_ci /* STX XADD: lock *(u64 *)(dst + off) += src */ 8948c2ecf20Sopenharmony_ci case BPF_STX | BPF_XADD | BPF_DW: 8958c2ecf20Sopenharmony_ci if (!off) { 8968c2ecf20Sopenharmony_ci reg = dst; 8978c2ecf20Sopenharmony_ci } else { 8988c2ecf20Sopenharmony_ci emit_a64_mov_i(1, tmp, off, ctx); 8998c2ecf20Sopenharmony_ci emit(A64_ADD(1, tmp, tmp, dst), ctx); 9008c2ecf20Sopenharmony_ci reg = tmp; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) { 9038c2ecf20Sopenharmony_ci emit(A64_STADD(isdw, reg, src), ctx); 9048c2ecf20Sopenharmony_ci } else { 9058c2ecf20Sopenharmony_ci emit(A64_LDXR(isdw, tmp2, reg), ctx); 9068c2ecf20Sopenharmony_ci emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); 9078c2ecf20Sopenharmony_ci emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); 9088c2ecf20Sopenharmony_ci jmp_offset = -3; 9098c2ecf20Sopenharmony_ci check_imm19(jmp_offset); 9108c2ecf20Sopenharmony_ci emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci break; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci default: 9158c2ecf20Sopenharmony_ci pr_err_once("unknown opcode %02x\n", code); 9168c2ecf20Sopenharmony_ci return -EINVAL; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci return 0; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic int build_body(struct jit_ctx *ctx, bool extra_pass) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci const struct bpf_prog *prog = ctx->prog; 9258c2ecf20Sopenharmony_ci int i; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * - offset[0] offset of the end of prologue, 9298c2ecf20Sopenharmony_ci * start of the 1st instruction. 9308c2ecf20Sopenharmony_ci * - offset[1] - offset of the end of 1st instruction, 9318c2ecf20Sopenharmony_ci * start of the 2nd instruction 9328c2ecf20Sopenharmony_ci * [....] 9338c2ecf20Sopenharmony_ci * - offset[3] - offset of the end of 3rd instruction, 9348c2ecf20Sopenharmony_ci * start of 4th instruction 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ci for (i = 0; i < prog->len; i++) { 9378c2ecf20Sopenharmony_ci const struct bpf_insn *insn = &prog->insnsi[i]; 9388c2ecf20Sopenharmony_ci int ret; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (ctx->image == NULL) 9418c2ecf20Sopenharmony_ci ctx->offset[i] = ctx->idx; 9428c2ecf20Sopenharmony_ci ret = build_insn(insn, ctx, extra_pass); 9438c2ecf20Sopenharmony_ci if (ret > 0) { 9448c2ecf20Sopenharmony_ci i++; 9458c2ecf20Sopenharmony_ci if (ctx->image == NULL) 9468c2ecf20Sopenharmony_ci ctx->offset[i] = ctx->idx; 9478c2ecf20Sopenharmony_ci continue; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci if (ret) 9508c2ecf20Sopenharmony_ci return ret; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci /* 9538c2ecf20Sopenharmony_ci * offset is allocated with prog->len + 1 so fill in 9548c2ecf20Sopenharmony_ci * the last element with the offset after the last 9558c2ecf20Sopenharmony_ci * instruction (end of program) 9568c2ecf20Sopenharmony_ci */ 9578c2ecf20Sopenharmony_ci if (ctx->image == NULL) 9588c2ecf20Sopenharmony_ci ctx->offset[i] = ctx->idx; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return 0; 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic int validate_code(struct jit_ctx *ctx) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci int i; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci for (i = 0; i < ctx->idx; i++) { 9688c2ecf20Sopenharmony_ci u32 a64_insn = le32_to_cpu(ctx->image[i]); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (a64_insn == AARCH64_BREAK_FAULT) 9718c2ecf20Sopenharmony_ci return -1; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries)) 9758c2ecf20Sopenharmony_ci return -1; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci return 0; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic inline void bpf_flush_icache(void *start, void *end) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci flush_icache_range((unsigned long)start, (unsigned long)end); 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistruct arm64_jit_data { 9868c2ecf20Sopenharmony_ci struct bpf_binary_header *header; 9878c2ecf20Sopenharmony_ci u8 *image; 9888c2ecf20Sopenharmony_ci struct jit_ctx ctx; 9898c2ecf20Sopenharmony_ci}; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistruct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci int image_size, prog_size, extable_size; 9948c2ecf20Sopenharmony_ci struct bpf_prog *tmp, *orig_prog = prog; 9958c2ecf20Sopenharmony_ci struct bpf_binary_header *header; 9968c2ecf20Sopenharmony_ci struct arm64_jit_data *jit_data; 9978c2ecf20Sopenharmony_ci bool was_classic = bpf_prog_was_classic(prog); 9988c2ecf20Sopenharmony_ci bool tmp_blinded = false; 9998c2ecf20Sopenharmony_ci bool extra_pass = false; 10008c2ecf20Sopenharmony_ci struct jit_ctx ctx; 10018c2ecf20Sopenharmony_ci u8 *image_ptr; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (!prog->jit_requested) 10048c2ecf20Sopenharmony_ci return orig_prog; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci tmp = bpf_jit_blind_constants(prog); 10078c2ecf20Sopenharmony_ci /* If blinding was requested and we failed during blinding, 10088c2ecf20Sopenharmony_ci * we must fall back to the interpreter. 10098c2ecf20Sopenharmony_ci */ 10108c2ecf20Sopenharmony_ci if (IS_ERR(tmp)) 10118c2ecf20Sopenharmony_ci return orig_prog; 10128c2ecf20Sopenharmony_ci if (tmp != prog) { 10138c2ecf20Sopenharmony_ci tmp_blinded = true; 10148c2ecf20Sopenharmony_ci prog = tmp; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci jit_data = prog->aux->jit_data; 10188c2ecf20Sopenharmony_ci if (!jit_data) { 10198c2ecf20Sopenharmony_ci jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 10208c2ecf20Sopenharmony_ci if (!jit_data) { 10218c2ecf20Sopenharmony_ci prog = orig_prog; 10228c2ecf20Sopenharmony_ci goto out; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci prog->aux->jit_data = jit_data; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci if (jit_data->ctx.offset) { 10278c2ecf20Sopenharmony_ci ctx = jit_data->ctx; 10288c2ecf20Sopenharmony_ci image_ptr = jit_data->image; 10298c2ecf20Sopenharmony_ci header = jit_data->header; 10308c2ecf20Sopenharmony_ci extra_pass = true; 10318c2ecf20Sopenharmony_ci prog_size = sizeof(u32) * ctx.idx; 10328c2ecf20Sopenharmony_ci goto skip_init_ctx; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci memset(&ctx, 0, sizeof(ctx)); 10358c2ecf20Sopenharmony_ci ctx.prog = prog; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci ctx.offset = kcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); 10388c2ecf20Sopenharmony_ci if (ctx.offset == NULL) { 10398c2ecf20Sopenharmony_ci prog = orig_prog; 10408c2ecf20Sopenharmony_ci goto out_off; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* 10448c2ecf20Sopenharmony_ci * 1. Initial fake pass to compute ctx->idx and ctx->offset. 10458c2ecf20Sopenharmony_ci * 10468c2ecf20Sopenharmony_ci * BPF line info needs ctx->offset[i] to be the offset of 10478c2ecf20Sopenharmony_ci * instruction[i] in jited image, so build prologue first. 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci if (build_prologue(&ctx, was_classic)) { 10508c2ecf20Sopenharmony_ci prog = orig_prog; 10518c2ecf20Sopenharmony_ci goto out_off; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (build_body(&ctx, extra_pass)) { 10558c2ecf20Sopenharmony_ci prog = orig_prog; 10568c2ecf20Sopenharmony_ci goto out_off; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci ctx.epilogue_offset = ctx.idx; 10608c2ecf20Sopenharmony_ci build_epilogue(&ctx); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci extable_size = prog->aux->num_exentries * 10638c2ecf20Sopenharmony_ci sizeof(struct exception_table_entry); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci /* Now we know the actual image size. */ 10668c2ecf20Sopenharmony_ci prog_size = sizeof(u32) * ctx.idx; 10678c2ecf20Sopenharmony_ci image_size = prog_size + extable_size; 10688c2ecf20Sopenharmony_ci header = bpf_jit_binary_alloc(image_size, &image_ptr, 10698c2ecf20Sopenharmony_ci sizeof(u32), jit_fill_hole); 10708c2ecf20Sopenharmony_ci if (header == NULL) { 10718c2ecf20Sopenharmony_ci prog = orig_prog; 10728c2ecf20Sopenharmony_ci goto out_off; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* 2. Now, the actual pass. */ 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci ctx.image = (__le32 *)image_ptr; 10788c2ecf20Sopenharmony_ci if (extable_size) 10798c2ecf20Sopenharmony_ci prog->aux->extable = (void *)image_ptr + prog_size; 10808c2ecf20Sopenharmony_ciskip_init_ctx: 10818c2ecf20Sopenharmony_ci ctx.idx = 0; 10828c2ecf20Sopenharmony_ci ctx.exentry_idx = 0; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci build_prologue(&ctx, was_classic); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (build_body(&ctx, extra_pass)) { 10878c2ecf20Sopenharmony_ci bpf_jit_binary_free(header); 10888c2ecf20Sopenharmony_ci prog = orig_prog; 10898c2ecf20Sopenharmony_ci goto out_off; 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci build_epilogue(&ctx); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci /* 3. Extra pass to validate JITed code. */ 10958c2ecf20Sopenharmony_ci if (validate_code(&ctx)) { 10968c2ecf20Sopenharmony_ci bpf_jit_binary_free(header); 10978c2ecf20Sopenharmony_ci prog = orig_prog; 10988c2ecf20Sopenharmony_ci goto out_off; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* And we're done. */ 11028c2ecf20Sopenharmony_ci if (bpf_jit_enable > 1) 11038c2ecf20Sopenharmony_ci bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci bpf_flush_icache(header, ctx.image + ctx.idx); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (!prog->is_func || extra_pass) { 11088c2ecf20Sopenharmony_ci if (extra_pass && ctx.idx != jit_data->ctx.idx) { 11098c2ecf20Sopenharmony_ci pr_err_once("multi-func JIT bug %d != %d\n", 11108c2ecf20Sopenharmony_ci ctx.idx, jit_data->ctx.idx); 11118c2ecf20Sopenharmony_ci bpf_jit_binary_free(header); 11128c2ecf20Sopenharmony_ci prog->bpf_func = NULL; 11138c2ecf20Sopenharmony_ci prog->jited = 0; 11148c2ecf20Sopenharmony_ci prog->jited_len = 0; 11158c2ecf20Sopenharmony_ci goto out_off; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci bpf_jit_binary_lock_ro(header); 11188c2ecf20Sopenharmony_ci } else { 11198c2ecf20Sopenharmony_ci jit_data->ctx = ctx; 11208c2ecf20Sopenharmony_ci jit_data->image = image_ptr; 11218c2ecf20Sopenharmony_ci jit_data->header = header; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci prog->bpf_func = (void *)ctx.image; 11248c2ecf20Sopenharmony_ci prog->jited = 1; 11258c2ecf20Sopenharmony_ci prog->jited_len = prog_size; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (!prog->is_func || extra_pass) { 11288c2ecf20Sopenharmony_ci int i; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* offset[prog->len] is the size of program */ 11318c2ecf20Sopenharmony_ci for (i = 0; i <= prog->len; i++) 11328c2ecf20Sopenharmony_ci ctx.offset[i] *= AARCH64_INSN_SIZE; 11338c2ecf20Sopenharmony_ci bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 11348c2ecf20Sopenharmony_ciout_off: 11358c2ecf20Sopenharmony_ci kfree(ctx.offset); 11368c2ecf20Sopenharmony_ci kfree(jit_data); 11378c2ecf20Sopenharmony_ci prog->aux->jit_data = NULL; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ciout: 11408c2ecf20Sopenharmony_ci if (tmp_blinded) 11418c2ecf20Sopenharmony_ci bpf_jit_prog_release_other(prog, prog == orig_prog ? 11428c2ecf20Sopenharmony_ci tmp : orig_prog); 11438c2ecf20Sopenharmony_ci return prog; 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ciu64 bpf_jit_alloc_exec_limit(void) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci return BPF_JIT_REGION_SIZE; 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_civoid *bpf_jit_alloc_exec(unsigned long size) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci return __vmalloc_node_range(size, PAGE_SIZE, BPF_JIT_REGION_START, 11548c2ecf20Sopenharmony_ci BPF_JIT_REGION_END, GFP_KERNEL, 11558c2ecf20Sopenharmony_ci PAGE_KERNEL, 0, NUMA_NO_NODE, 11568c2ecf20Sopenharmony_ci __builtin_return_address(0)); 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_civoid bpf_jit_free_exec(void *addr) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci return vfree(addr); 11628c2ecf20Sopenharmony_ci} 1163