18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * bpf_jit_comp.c: BPF JIT compiler 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011-2013 Eric Dumazet (eric.dumazet@gmail.com) 68c2ecf20Sopenharmony_ci * Internal BPF Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 98c2ecf20Sopenharmony_ci#include <linux/filter.h> 108c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 118c2ecf20Sopenharmony_ci#include <linux/bpf.h> 128c2ecf20Sopenharmony_ci#include <linux/memory.h> 138c2ecf20Sopenharmony_ci#include <linux/sort.h> 148c2ecf20Sopenharmony_ci#include <asm/extable.h> 158c2ecf20Sopenharmony_ci#include <asm/set_memory.h> 168c2ecf20Sopenharmony_ci#include <asm/nospec-branch.h> 178c2ecf20Sopenharmony_ci#include <asm/text-patching.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci if (len == 1) 228c2ecf20Sopenharmony_ci *ptr = bytes; 238c2ecf20Sopenharmony_ci else if (len == 2) 248c2ecf20Sopenharmony_ci *(u16 *)ptr = bytes; 258c2ecf20Sopenharmony_ci else { 268c2ecf20Sopenharmony_ci *(u32 *)ptr = bytes; 278c2ecf20Sopenharmony_ci barrier(); 288c2ecf20Sopenharmony_ci } 298c2ecf20Sopenharmony_ci return ptr + len; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define EMIT(bytes, len) \ 338c2ecf20Sopenharmony_ci do { prog = emit_code(prog, bytes, len); cnt += len; } while (0) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define EMIT1(b1) EMIT(b1, 1) 368c2ecf20Sopenharmony_ci#define EMIT2(b1, b2) EMIT((b1) + ((b2) << 8), 2) 378c2ecf20Sopenharmony_ci#define EMIT3(b1, b2, b3) EMIT((b1) + ((b2) << 8) + ((b3) << 16), 3) 388c2ecf20Sopenharmony_ci#define EMIT4(b1, b2, b3, b4) EMIT((b1) + ((b2) << 8) + ((b3) << 16) + ((b4) << 24), 4) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define EMIT1_off32(b1, off) \ 418c2ecf20Sopenharmony_ci do { EMIT1(b1); EMIT(off, 4); } while (0) 428c2ecf20Sopenharmony_ci#define EMIT2_off32(b1, b2, off) \ 438c2ecf20Sopenharmony_ci do { EMIT2(b1, b2); EMIT(off, 4); } while (0) 448c2ecf20Sopenharmony_ci#define EMIT3_off32(b1, b2, b3, off) \ 458c2ecf20Sopenharmony_ci do { EMIT3(b1, b2, b3); EMIT(off, 4); } while (0) 468c2ecf20Sopenharmony_ci#define EMIT4_off32(b1, b2, b3, b4, off) \ 478c2ecf20Sopenharmony_ci do { EMIT4(b1, b2, b3, b4); EMIT(off, 4); } while (0) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic bool is_imm8(int value) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci return value <= 127 && value >= -128; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic bool is_simm32(s64 value) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return value == (s64)(s32)value; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic bool is_uimm32(u64 value) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return value == (u64)(u32)value; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* mov dst, src */ 658c2ecf20Sopenharmony_ci#define EMIT_mov(DST, SRC) \ 668c2ecf20Sopenharmony_ci do { \ 678c2ecf20Sopenharmony_ci if (DST != SRC) \ 688c2ecf20Sopenharmony_ci EMIT3(add_2mod(0x48, DST, SRC), 0x89, add_2reg(0xC0, DST, SRC)); \ 698c2ecf20Sopenharmony_ci } while (0) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int bpf_size_to_x86_bytes(int bpf_size) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci if (bpf_size == BPF_W) 748c2ecf20Sopenharmony_ci return 4; 758c2ecf20Sopenharmony_ci else if (bpf_size == BPF_H) 768c2ecf20Sopenharmony_ci return 2; 778c2ecf20Sopenharmony_ci else if (bpf_size == BPF_B) 788c2ecf20Sopenharmony_ci return 1; 798c2ecf20Sopenharmony_ci else if (bpf_size == BPF_DW) 808c2ecf20Sopenharmony_ci return 4; /* imm32 */ 818c2ecf20Sopenharmony_ci else 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * List of x86 cond jumps opcodes (. + s8) 878c2ecf20Sopenharmony_ci * Add 0x10 (and an extra 0x0f) to generate far jumps (. + s32) 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci#define X86_JB 0x72 908c2ecf20Sopenharmony_ci#define X86_JAE 0x73 918c2ecf20Sopenharmony_ci#define X86_JE 0x74 928c2ecf20Sopenharmony_ci#define X86_JNE 0x75 938c2ecf20Sopenharmony_ci#define X86_JBE 0x76 948c2ecf20Sopenharmony_ci#define X86_JA 0x77 958c2ecf20Sopenharmony_ci#define X86_JL 0x7C 968c2ecf20Sopenharmony_ci#define X86_JGE 0x7D 978c2ecf20Sopenharmony_ci#define X86_JLE 0x7E 988c2ecf20Sopenharmony_ci#define X86_JG 0x7F 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Pick a register outside of BPF range for JIT internal work */ 1018c2ecf20Sopenharmony_ci#define AUX_REG (MAX_BPF_JIT_REG + 1) 1028c2ecf20Sopenharmony_ci#define X86_REG_R9 (MAX_BPF_JIT_REG + 2) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * The following table maps BPF registers to x86-64 registers. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * x86-64 register R12 is unused, since if used as base address 1088c2ecf20Sopenharmony_ci * register in load/store instructions, it always needs an 1098c2ecf20Sopenharmony_ci * extra byte of encoding and is callee saved. 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * x86-64 register R9 is not used by BPF programs, but can be used by BPF 1128c2ecf20Sopenharmony_ci * trampoline. x86-64 register R10 is used for blinding (if enabled). 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic const int reg2hex[] = { 1158c2ecf20Sopenharmony_ci [BPF_REG_0] = 0, /* RAX */ 1168c2ecf20Sopenharmony_ci [BPF_REG_1] = 7, /* RDI */ 1178c2ecf20Sopenharmony_ci [BPF_REG_2] = 6, /* RSI */ 1188c2ecf20Sopenharmony_ci [BPF_REG_3] = 2, /* RDX */ 1198c2ecf20Sopenharmony_ci [BPF_REG_4] = 1, /* RCX */ 1208c2ecf20Sopenharmony_ci [BPF_REG_5] = 0, /* R8 */ 1218c2ecf20Sopenharmony_ci [BPF_REG_6] = 3, /* RBX callee saved */ 1228c2ecf20Sopenharmony_ci [BPF_REG_7] = 5, /* R13 callee saved */ 1238c2ecf20Sopenharmony_ci [BPF_REG_8] = 6, /* R14 callee saved */ 1248c2ecf20Sopenharmony_ci [BPF_REG_9] = 7, /* R15 callee saved */ 1258c2ecf20Sopenharmony_ci [BPF_REG_FP] = 5, /* RBP readonly */ 1268c2ecf20Sopenharmony_ci [BPF_REG_AX] = 2, /* R10 temp register */ 1278c2ecf20Sopenharmony_ci [AUX_REG] = 3, /* R11 temp register */ 1288c2ecf20Sopenharmony_ci [X86_REG_R9] = 1, /* R9 register, 6th function argument */ 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic const int reg2pt_regs[] = { 1328c2ecf20Sopenharmony_ci [BPF_REG_0] = offsetof(struct pt_regs, ax), 1338c2ecf20Sopenharmony_ci [BPF_REG_1] = offsetof(struct pt_regs, di), 1348c2ecf20Sopenharmony_ci [BPF_REG_2] = offsetof(struct pt_regs, si), 1358c2ecf20Sopenharmony_ci [BPF_REG_3] = offsetof(struct pt_regs, dx), 1368c2ecf20Sopenharmony_ci [BPF_REG_4] = offsetof(struct pt_regs, cx), 1378c2ecf20Sopenharmony_ci [BPF_REG_5] = offsetof(struct pt_regs, r8), 1388c2ecf20Sopenharmony_ci [BPF_REG_6] = offsetof(struct pt_regs, bx), 1398c2ecf20Sopenharmony_ci [BPF_REG_7] = offsetof(struct pt_regs, r13), 1408c2ecf20Sopenharmony_ci [BPF_REG_8] = offsetof(struct pt_regs, r14), 1418c2ecf20Sopenharmony_ci [BPF_REG_9] = offsetof(struct pt_regs, r15), 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * is_ereg() == true if BPF register 'reg' maps to x86-64 r8..r15 1468c2ecf20Sopenharmony_ci * which need extra byte of encoding. 1478c2ecf20Sopenharmony_ci * rax,rcx,...,rbp have simpler encoding 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cistatic bool is_ereg(u32 reg) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci return (1 << reg) & (BIT(BPF_REG_5) | 1528c2ecf20Sopenharmony_ci BIT(AUX_REG) | 1538c2ecf20Sopenharmony_ci BIT(BPF_REG_7) | 1548c2ecf20Sopenharmony_ci BIT(BPF_REG_8) | 1558c2ecf20Sopenharmony_ci BIT(BPF_REG_9) | 1568c2ecf20Sopenharmony_ci BIT(X86_REG_R9) | 1578c2ecf20Sopenharmony_ci BIT(BPF_REG_AX)); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * is_ereg_8l() == true if BPF register 'reg' is mapped to access x86-64 1628c2ecf20Sopenharmony_ci * lower 8-bit registers dil,sil,bpl,spl,r8b..r15b, which need extra byte 1638c2ecf20Sopenharmony_ci * of encoding. al,cl,dl,bl have simpler encoding. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_cistatic bool is_ereg_8l(u32 reg) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci return is_ereg(reg) || 1688c2ecf20Sopenharmony_ci (1 << reg) & (BIT(BPF_REG_1) | 1698c2ecf20Sopenharmony_ci BIT(BPF_REG_2) | 1708c2ecf20Sopenharmony_ci BIT(BPF_REG_FP)); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic bool is_axreg(u32 reg) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci return reg == BPF_REG_0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* Add modifiers if 'reg' maps to x86-64 registers R8..R15 */ 1798c2ecf20Sopenharmony_cistatic u8 add_1mod(u8 byte, u32 reg) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci if (is_ereg(reg)) 1828c2ecf20Sopenharmony_ci byte |= 1; 1838c2ecf20Sopenharmony_ci return byte; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic u8 add_2mod(u8 byte, u32 r1, u32 r2) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci if (is_ereg(r1)) 1898c2ecf20Sopenharmony_ci byte |= 1; 1908c2ecf20Sopenharmony_ci if (is_ereg(r2)) 1918c2ecf20Sopenharmony_ci byte |= 4; 1928c2ecf20Sopenharmony_ci return byte; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* Encode 'dst_reg' register into x86-64 opcode 'byte' */ 1968c2ecf20Sopenharmony_cistatic u8 add_1reg(u8 byte, u32 dst_reg) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci return byte + reg2hex[dst_reg]; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* Encode 'dst_reg' and 'src_reg' registers into x86-64 opcode 'byte' */ 2028c2ecf20Sopenharmony_cistatic u8 add_2reg(u8 byte, u32 dst_reg, u32 src_reg) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return byte + reg2hex[dst_reg] + (reg2hex[src_reg] << 3); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void jit_fill_hole(void *area, unsigned int size) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci /* Fill whole space with INT3 instructions */ 2108c2ecf20Sopenharmony_ci memset(area, 0xcc, size); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistruct jit_context { 2148c2ecf20Sopenharmony_ci int cleanup_addr; /* Epilogue code offset */ 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * Program specific offsets of labels in the code; these rely on the 2188c2ecf20Sopenharmony_ci * JIT doing at least 2 passes, recording the position on the first 2198c2ecf20Sopenharmony_ci * pass, only to generate the correct offset on the second pass. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci int tail_call_direct_label; 2228c2ecf20Sopenharmony_ci int tail_call_indirect_label; 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* Maximum number of bytes emitted while JITing one eBPF insn */ 2268c2ecf20Sopenharmony_ci#define BPF_MAX_INSN_SIZE 128 2278c2ecf20Sopenharmony_ci#define BPF_INSN_SAFETY 64 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* Number of bytes emit_patch() needs to generate instructions */ 2308c2ecf20Sopenharmony_ci#define X86_PATCH_SIZE 5 2318c2ecf20Sopenharmony_ci/* Number of bytes that will be skipped on tailcall */ 2328c2ecf20Sopenharmony_ci#define X86_TAIL_CALL_OFFSET 11 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void push_callee_regs(u8 **pprog, bool *callee_regs_used) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci u8 *prog = *pprog; 2378c2ecf20Sopenharmony_ci int cnt = 0; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (callee_regs_used[0]) 2408c2ecf20Sopenharmony_ci EMIT1(0x53); /* push rbx */ 2418c2ecf20Sopenharmony_ci if (callee_regs_used[1]) 2428c2ecf20Sopenharmony_ci EMIT2(0x41, 0x55); /* push r13 */ 2438c2ecf20Sopenharmony_ci if (callee_regs_used[2]) 2448c2ecf20Sopenharmony_ci EMIT2(0x41, 0x56); /* push r14 */ 2458c2ecf20Sopenharmony_ci if (callee_regs_used[3]) 2468c2ecf20Sopenharmony_ci EMIT2(0x41, 0x57); /* push r15 */ 2478c2ecf20Sopenharmony_ci *pprog = prog; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void pop_callee_regs(u8 **pprog, bool *callee_regs_used) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci u8 *prog = *pprog; 2538c2ecf20Sopenharmony_ci int cnt = 0; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (callee_regs_used[3]) 2568c2ecf20Sopenharmony_ci EMIT2(0x41, 0x5F); /* pop r15 */ 2578c2ecf20Sopenharmony_ci if (callee_regs_used[2]) 2588c2ecf20Sopenharmony_ci EMIT2(0x41, 0x5E); /* pop r14 */ 2598c2ecf20Sopenharmony_ci if (callee_regs_used[1]) 2608c2ecf20Sopenharmony_ci EMIT2(0x41, 0x5D); /* pop r13 */ 2618c2ecf20Sopenharmony_ci if (callee_regs_used[0]) 2628c2ecf20Sopenharmony_ci EMIT1(0x5B); /* pop rbx */ 2638c2ecf20Sopenharmony_ci *pprog = prog; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * Emit x86-64 prologue code for BPF program. 2688c2ecf20Sopenharmony_ci * bpf_tail_call helper will skip the first X86_TAIL_CALL_OFFSET bytes 2698c2ecf20Sopenharmony_ci * while jumping to another program 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_cistatic void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf, 2728c2ecf20Sopenharmony_ci bool tail_call_reachable, bool is_subprog) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci u8 *prog = *pprog; 2758c2ecf20Sopenharmony_ci int cnt = X86_PATCH_SIZE; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* BPF trampoline can be made to work without these nops, 2788c2ecf20Sopenharmony_ci * but let's waste 5 bytes for now and optimize later 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci memcpy(prog, ideal_nops[NOP_ATOMIC5], cnt); 2818c2ecf20Sopenharmony_ci prog += cnt; 2828c2ecf20Sopenharmony_ci if (!ebpf_from_cbpf) { 2838c2ecf20Sopenharmony_ci if (tail_call_reachable && !is_subprog) 2848c2ecf20Sopenharmony_ci EMIT2(0x31, 0xC0); /* xor eax, eax */ 2858c2ecf20Sopenharmony_ci else 2868c2ecf20Sopenharmony_ci EMIT2(0x66, 0x90); /* nop2 */ 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci EMIT1(0x55); /* push rbp */ 2898c2ecf20Sopenharmony_ci EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */ 2908c2ecf20Sopenharmony_ci /* sub rsp, rounded_stack_depth */ 2918c2ecf20Sopenharmony_ci if (stack_depth) 2928c2ecf20Sopenharmony_ci EMIT3_off32(0x48, 0x81, 0xEC, round_up(stack_depth, 8)); 2938c2ecf20Sopenharmony_ci if (tail_call_reachable) 2948c2ecf20Sopenharmony_ci EMIT1(0x50); /* push rax */ 2958c2ecf20Sopenharmony_ci *pprog = prog; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int emit_patch(u8 **pprog, void *func, void *ip, u8 opcode) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci u8 *prog = *pprog; 3018c2ecf20Sopenharmony_ci int cnt = 0; 3028c2ecf20Sopenharmony_ci s64 offset; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci offset = func - (ip + X86_PATCH_SIZE); 3058c2ecf20Sopenharmony_ci if (!is_simm32(offset)) { 3068c2ecf20Sopenharmony_ci pr_err("Target call %p is out of range\n", func); 3078c2ecf20Sopenharmony_ci return -ERANGE; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci EMIT1_off32(opcode, offset); 3108c2ecf20Sopenharmony_ci *pprog = prog; 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int emit_call(u8 **pprog, void *func, void *ip) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci return emit_patch(pprog, func, ip, 0xE8); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int emit_jump(u8 **pprog, void *func, void *ip) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci return emit_patch(pprog, func, ip, 0xE9); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t, 3258c2ecf20Sopenharmony_ci void *old_addr, void *new_addr, 3268c2ecf20Sopenharmony_ci const bool text_live) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci const u8 *nop_insn = ideal_nops[NOP_ATOMIC5]; 3298c2ecf20Sopenharmony_ci u8 old_insn[X86_PATCH_SIZE]; 3308c2ecf20Sopenharmony_ci u8 new_insn[X86_PATCH_SIZE]; 3318c2ecf20Sopenharmony_ci u8 *prog; 3328c2ecf20Sopenharmony_ci int ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci memcpy(old_insn, nop_insn, X86_PATCH_SIZE); 3358c2ecf20Sopenharmony_ci if (old_addr) { 3368c2ecf20Sopenharmony_ci prog = old_insn; 3378c2ecf20Sopenharmony_ci ret = t == BPF_MOD_CALL ? 3388c2ecf20Sopenharmony_ci emit_call(&prog, old_addr, ip) : 3398c2ecf20Sopenharmony_ci emit_jump(&prog, old_addr, ip); 3408c2ecf20Sopenharmony_ci if (ret) 3418c2ecf20Sopenharmony_ci return ret; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci memcpy(new_insn, nop_insn, X86_PATCH_SIZE); 3458c2ecf20Sopenharmony_ci if (new_addr) { 3468c2ecf20Sopenharmony_ci prog = new_insn; 3478c2ecf20Sopenharmony_ci ret = t == BPF_MOD_CALL ? 3488c2ecf20Sopenharmony_ci emit_call(&prog, new_addr, ip) : 3498c2ecf20Sopenharmony_ci emit_jump(&prog, new_addr, ip); 3508c2ecf20Sopenharmony_ci if (ret) 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci ret = -EBUSY; 3558c2ecf20Sopenharmony_ci mutex_lock(&text_mutex); 3568c2ecf20Sopenharmony_ci if (memcmp(ip, old_insn, X86_PATCH_SIZE)) 3578c2ecf20Sopenharmony_ci goto out; 3588c2ecf20Sopenharmony_ci ret = 1; 3598c2ecf20Sopenharmony_ci if (memcmp(ip, new_insn, X86_PATCH_SIZE)) { 3608c2ecf20Sopenharmony_ci if (text_live) 3618c2ecf20Sopenharmony_ci text_poke_bp(ip, new_insn, X86_PATCH_SIZE, NULL); 3628c2ecf20Sopenharmony_ci else 3638c2ecf20Sopenharmony_ci memcpy(ip, new_insn, X86_PATCH_SIZE); 3648c2ecf20Sopenharmony_ci ret = 0; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ciout: 3678c2ecf20Sopenharmony_ci mutex_unlock(&text_mutex); 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ciint bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t, 3728c2ecf20Sopenharmony_ci void *old_addr, void *new_addr) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci if (!is_kernel_text((long)ip) && 3758c2ecf20Sopenharmony_ci !is_bpf_text_address((long)ip)) 3768c2ecf20Sopenharmony_ci /* BPF poking in modules is not supported */ 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return __bpf_arch_text_poke(ip, t, old_addr, new_addr, true); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci#define EMIT_LFENCE() EMIT3(0x0F, 0xAE, 0xE8) 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void emit_indirect_jump(u8 **pprog, int reg, u8 *ip) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci u8 *prog = *pprog; 3878c2ecf20Sopenharmony_ci int cnt = 0; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci#ifdef CONFIG_RETPOLINE 3908c2ecf20Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) { 3918c2ecf20Sopenharmony_ci EMIT_LFENCE(); 3928c2ecf20Sopenharmony_ci EMIT2(0xFF, 0xE0 + reg); 3938c2ecf20Sopenharmony_ci } else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) { 3948c2ecf20Sopenharmony_ci emit_jump(&prog, &__x86_indirect_thunk_array[reg], ip); 3958c2ecf20Sopenharmony_ci } else 3968c2ecf20Sopenharmony_ci#endif 3978c2ecf20Sopenharmony_ci EMIT2(0xFF, 0xE0 + reg); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci *pprog = prog; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic void emit_return(u8 **pprog, u8 *ip) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci u8 *prog = *pprog; 4058c2ecf20Sopenharmony_ci int cnt = 0; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) { 4088c2ecf20Sopenharmony_ci emit_jump(&prog, &__x86_return_thunk, ip); 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci EMIT1(0xC3); /* ret */ 4118c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SLS)) 4128c2ecf20Sopenharmony_ci EMIT1(0xCC); /* int3 */ 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci *pprog = prog; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/* 4198c2ecf20Sopenharmony_ci * Generate the following code: 4208c2ecf20Sopenharmony_ci * 4218c2ecf20Sopenharmony_ci * ... bpf_tail_call(void *ctx, struct bpf_array *array, u64 index) ... 4228c2ecf20Sopenharmony_ci * if (index >= array->map.max_entries) 4238c2ecf20Sopenharmony_ci * goto out; 4248c2ecf20Sopenharmony_ci * if (++tail_call_cnt > MAX_TAIL_CALL_CNT) 4258c2ecf20Sopenharmony_ci * goto out; 4268c2ecf20Sopenharmony_ci * prog = array->ptrs[index]; 4278c2ecf20Sopenharmony_ci * if (prog == NULL) 4288c2ecf20Sopenharmony_ci * goto out; 4298c2ecf20Sopenharmony_ci * goto *(prog->bpf_func + prologue_size); 4308c2ecf20Sopenharmony_ci * out: 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_cistatic void emit_bpf_tail_call_indirect(u8 **pprog, bool *callee_regs_used, 4338c2ecf20Sopenharmony_ci u32 stack_depth, u8 *ip, 4348c2ecf20Sopenharmony_ci struct jit_context *ctx) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci int tcc_off = -4 - round_up(stack_depth, 8); 4378c2ecf20Sopenharmony_ci u8 *prog = *pprog, *start = *pprog; 4388c2ecf20Sopenharmony_ci int cnt = 0, offset; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* 4418c2ecf20Sopenharmony_ci * rdi - pointer to ctx 4428c2ecf20Sopenharmony_ci * rsi - pointer to bpf_array 4438c2ecf20Sopenharmony_ci * rdx - index in bpf_array 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* 4478c2ecf20Sopenharmony_ci * if (index >= array->map.max_entries) 4488c2ecf20Sopenharmony_ci * goto out; 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci EMIT2(0x89, 0xD2); /* mov edx, edx */ 4518c2ecf20Sopenharmony_ci EMIT3(0x39, 0x56, /* cmp dword ptr [rsi + 16], edx */ 4528c2ecf20Sopenharmony_ci offsetof(struct bpf_array, map.max_entries)); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci offset = ctx->tail_call_indirect_label - (prog + 2 - start); 4558c2ecf20Sopenharmony_ci EMIT2(X86_JBE, offset); /* jbe out */ 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * if (tail_call_cnt > MAX_TAIL_CALL_CNT) 4598c2ecf20Sopenharmony_ci * goto out; 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci EMIT2_off32(0x8B, 0x85, tcc_off); /* mov eax, dword ptr [rbp - tcc_off] */ 4628c2ecf20Sopenharmony_ci EMIT3(0x83, 0xF8, MAX_TAIL_CALL_CNT); /* cmp eax, MAX_TAIL_CALL_CNT */ 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci offset = ctx->tail_call_indirect_label - (prog + 2 - start); 4658c2ecf20Sopenharmony_ci EMIT2(X86_JA, offset); /* ja out */ 4668c2ecf20Sopenharmony_ci EMIT3(0x83, 0xC0, 0x01); /* add eax, 1 */ 4678c2ecf20Sopenharmony_ci EMIT2_off32(0x89, 0x85, tcc_off); /* mov dword ptr [rbp - tcc_off], eax */ 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* prog = array->ptrs[index]; */ 4708c2ecf20Sopenharmony_ci EMIT4_off32(0x48, 0x8B, 0x8C, 0xD6, /* mov rcx, [rsi + rdx * 8 + offsetof(...)] */ 4718c2ecf20Sopenharmony_ci offsetof(struct bpf_array, ptrs)); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* 4748c2ecf20Sopenharmony_ci * if (prog == NULL) 4758c2ecf20Sopenharmony_ci * goto out; 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci EMIT3(0x48, 0x85, 0xC9); /* test rcx,rcx */ 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci offset = ctx->tail_call_indirect_label - (prog + 2 - start); 4808c2ecf20Sopenharmony_ci EMIT2(X86_JE, offset); /* je out */ 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci pop_callee_regs(&prog, callee_regs_used); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci EMIT1(0x58); /* pop rax */ 4858c2ecf20Sopenharmony_ci if (stack_depth) 4868c2ecf20Sopenharmony_ci EMIT3_off32(0x48, 0x81, 0xC4, /* add rsp, sd */ 4878c2ecf20Sopenharmony_ci round_up(stack_depth, 8)); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* goto *(prog->bpf_func + X86_TAIL_CALL_OFFSET); */ 4908c2ecf20Sopenharmony_ci EMIT4(0x48, 0x8B, 0x49, /* mov rcx, qword ptr [rcx + 32] */ 4918c2ecf20Sopenharmony_ci offsetof(struct bpf_prog, bpf_func)); 4928c2ecf20Sopenharmony_ci EMIT4(0x48, 0x83, 0xC1, /* add rcx, X86_TAIL_CALL_OFFSET */ 4938c2ecf20Sopenharmony_ci X86_TAIL_CALL_OFFSET); 4948c2ecf20Sopenharmony_ci /* 4958c2ecf20Sopenharmony_ci * Now we're ready to jump into next BPF program 4968c2ecf20Sopenharmony_ci * rdi == ctx (1st arg) 4978c2ecf20Sopenharmony_ci * rcx == prog->bpf_func + X86_TAIL_CALL_OFFSET 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_ci emit_indirect_jump(&prog, 1 /* rcx */, ip + (prog - start)); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* out: */ 5028c2ecf20Sopenharmony_ci ctx->tail_call_indirect_label = prog - start; 5038c2ecf20Sopenharmony_ci *pprog = prog; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic void emit_bpf_tail_call_direct(struct bpf_jit_poke_descriptor *poke, 5078c2ecf20Sopenharmony_ci u8 **pprog, u8 *ip, 5088c2ecf20Sopenharmony_ci bool *callee_regs_used, u32 stack_depth, 5098c2ecf20Sopenharmony_ci struct jit_context *ctx) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci int tcc_off = -4 - round_up(stack_depth, 8); 5128c2ecf20Sopenharmony_ci u8 *prog = *pprog, *start = *pprog; 5138c2ecf20Sopenharmony_ci int cnt = 0, offset; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* 5168c2ecf20Sopenharmony_ci * if (tail_call_cnt > MAX_TAIL_CALL_CNT) 5178c2ecf20Sopenharmony_ci * goto out; 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci EMIT2_off32(0x8B, 0x85, tcc_off); /* mov eax, dword ptr [rbp - tcc_off] */ 5208c2ecf20Sopenharmony_ci EMIT3(0x83, 0xF8, MAX_TAIL_CALL_CNT); /* cmp eax, MAX_TAIL_CALL_CNT */ 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci offset = ctx->tail_call_direct_label - (prog + 2 - start); 5238c2ecf20Sopenharmony_ci EMIT2(X86_JA, offset); /* ja out */ 5248c2ecf20Sopenharmony_ci EMIT3(0x83, 0xC0, 0x01); /* add eax, 1 */ 5258c2ecf20Sopenharmony_ci EMIT2_off32(0x89, 0x85, tcc_off); /* mov dword ptr [rbp - tcc_off], eax */ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci poke->tailcall_bypass = ip + (prog - start); 5288c2ecf20Sopenharmony_ci poke->adj_off = X86_TAIL_CALL_OFFSET; 5298c2ecf20Sopenharmony_ci poke->tailcall_target = ip + ctx->tail_call_direct_label - X86_PATCH_SIZE; 5308c2ecf20Sopenharmony_ci poke->bypass_addr = (u8 *)poke->tailcall_target + X86_PATCH_SIZE; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci emit_jump(&prog, (u8 *)poke->tailcall_target + X86_PATCH_SIZE, 5338c2ecf20Sopenharmony_ci poke->tailcall_bypass); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci pop_callee_regs(&prog, callee_regs_used); 5368c2ecf20Sopenharmony_ci EMIT1(0x58); /* pop rax */ 5378c2ecf20Sopenharmony_ci if (stack_depth) 5388c2ecf20Sopenharmony_ci EMIT3_off32(0x48, 0x81, 0xC4, round_up(stack_depth, 8)); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci memcpy(prog, ideal_nops[NOP_ATOMIC5], X86_PATCH_SIZE); 5418c2ecf20Sopenharmony_ci prog += X86_PATCH_SIZE; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* out: */ 5448c2ecf20Sopenharmony_ci ctx->tail_call_direct_label = prog - start; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci *pprog = prog; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic void bpf_tail_call_direct_fixup(struct bpf_prog *prog) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct bpf_jit_poke_descriptor *poke; 5528c2ecf20Sopenharmony_ci struct bpf_array *array; 5538c2ecf20Sopenharmony_ci struct bpf_prog *target; 5548c2ecf20Sopenharmony_ci int i, ret; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci for (i = 0; i < prog->aux->size_poke_tab; i++) { 5578c2ecf20Sopenharmony_ci poke = &prog->aux->poke_tab[i]; 5588c2ecf20Sopenharmony_ci if (poke->aux && poke->aux != prog->aux) 5598c2ecf20Sopenharmony_ci continue; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci WARN_ON_ONCE(READ_ONCE(poke->tailcall_target_stable)); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (poke->reason != BPF_POKE_REASON_TAIL_CALL) 5648c2ecf20Sopenharmony_ci continue; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci array = container_of(poke->tail_call.map, struct bpf_array, map); 5678c2ecf20Sopenharmony_ci mutex_lock(&array->aux->poke_mutex); 5688c2ecf20Sopenharmony_ci target = array->ptrs[poke->tail_call.key]; 5698c2ecf20Sopenharmony_ci if (target) { 5708c2ecf20Sopenharmony_ci /* Plain memcpy is used when image is not live yet 5718c2ecf20Sopenharmony_ci * and still not locked as read-only. Once poke 5728c2ecf20Sopenharmony_ci * location is active (poke->tailcall_target_stable), 5738c2ecf20Sopenharmony_ci * any parallel bpf_arch_text_poke() might occur 5748c2ecf20Sopenharmony_ci * still on the read-write image until we finally 5758c2ecf20Sopenharmony_ci * locked it as read-only. Both modifications on 5768c2ecf20Sopenharmony_ci * the given image are under text_mutex to avoid 5778c2ecf20Sopenharmony_ci * interference. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci ret = __bpf_arch_text_poke(poke->tailcall_target, 5808c2ecf20Sopenharmony_ci BPF_MOD_JUMP, NULL, 5818c2ecf20Sopenharmony_ci (u8 *)target->bpf_func + 5828c2ecf20Sopenharmony_ci poke->adj_off, false); 5838c2ecf20Sopenharmony_ci BUG_ON(ret < 0); 5848c2ecf20Sopenharmony_ci ret = __bpf_arch_text_poke(poke->tailcall_bypass, 5858c2ecf20Sopenharmony_ci BPF_MOD_JUMP, 5868c2ecf20Sopenharmony_ci (u8 *)poke->tailcall_target + 5878c2ecf20Sopenharmony_ci X86_PATCH_SIZE, NULL, false); 5888c2ecf20Sopenharmony_ci BUG_ON(ret < 0); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci WRITE_ONCE(poke->tailcall_target_stable, true); 5918c2ecf20Sopenharmony_ci mutex_unlock(&array->aux->poke_mutex); 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic void emit_mov_imm32(u8 **pprog, bool sign_propagate, 5968c2ecf20Sopenharmony_ci u32 dst_reg, const u32 imm32) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci u8 *prog = *pprog; 5998c2ecf20Sopenharmony_ci u8 b1, b2, b3; 6008c2ecf20Sopenharmony_ci int cnt = 0; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* 6038c2ecf20Sopenharmony_ci * Optimization: if imm32 is positive, use 'mov %eax, imm32' 6048c2ecf20Sopenharmony_ci * (which zero-extends imm32) to save 2 bytes. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_ci if (sign_propagate && (s32)imm32 < 0) { 6078c2ecf20Sopenharmony_ci /* 'mov %rax, imm32' sign extends imm32 */ 6088c2ecf20Sopenharmony_ci b1 = add_1mod(0x48, dst_reg); 6098c2ecf20Sopenharmony_ci b2 = 0xC7; 6108c2ecf20Sopenharmony_ci b3 = 0xC0; 6118c2ecf20Sopenharmony_ci EMIT3_off32(b1, b2, add_1reg(b3, dst_reg), imm32); 6128c2ecf20Sopenharmony_ci goto done; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* 6168c2ecf20Sopenharmony_ci * Optimization: if imm32 is zero, use 'xor %eax, %eax' 6178c2ecf20Sopenharmony_ci * to save 3 bytes. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci if (imm32 == 0) { 6208c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 6218c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x40, dst_reg, dst_reg)); 6228c2ecf20Sopenharmony_ci b2 = 0x31; /* xor */ 6238c2ecf20Sopenharmony_ci b3 = 0xC0; 6248c2ecf20Sopenharmony_ci EMIT2(b2, add_2reg(b3, dst_reg, dst_reg)); 6258c2ecf20Sopenharmony_ci goto done; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* mov %eax, imm32 */ 6298c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 6308c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x40, dst_reg)); 6318c2ecf20Sopenharmony_ci EMIT1_off32(add_1reg(0xB8, dst_reg), imm32); 6328c2ecf20Sopenharmony_cidone: 6338c2ecf20Sopenharmony_ci *pprog = prog; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic void emit_mov_imm64(u8 **pprog, u32 dst_reg, 6378c2ecf20Sopenharmony_ci const u32 imm32_hi, const u32 imm32_lo) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci u8 *prog = *pprog; 6408c2ecf20Sopenharmony_ci int cnt = 0; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (is_uimm32(((u64)imm32_hi << 32) | (u32)imm32_lo)) { 6438c2ecf20Sopenharmony_ci /* 6448c2ecf20Sopenharmony_ci * For emitting plain u32, where sign bit must not be 6458c2ecf20Sopenharmony_ci * propagated LLVM tends to load imm64 over mov32 6468c2ecf20Sopenharmony_ci * directly, so save couple of bytes by just doing 6478c2ecf20Sopenharmony_ci * 'mov %eax, imm32' instead. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ci emit_mov_imm32(&prog, false, dst_reg, imm32_lo); 6508c2ecf20Sopenharmony_ci } else { 6518c2ecf20Sopenharmony_ci /* movabsq %rax, imm64 */ 6528c2ecf20Sopenharmony_ci EMIT2(add_1mod(0x48, dst_reg), add_1reg(0xB8, dst_reg)); 6538c2ecf20Sopenharmony_ci EMIT(imm32_lo, 4); 6548c2ecf20Sopenharmony_ci EMIT(imm32_hi, 4); 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci *pprog = prog; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic void emit_mov_reg(u8 **pprog, bool is64, u32 dst_reg, u32 src_reg) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci u8 *prog = *pprog; 6638c2ecf20Sopenharmony_ci int cnt = 0; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (is64) { 6668c2ecf20Sopenharmony_ci /* mov dst, src */ 6678c2ecf20Sopenharmony_ci EMIT_mov(dst_reg, src_reg); 6688c2ecf20Sopenharmony_ci } else { 6698c2ecf20Sopenharmony_ci /* mov32 dst, src */ 6708c2ecf20Sopenharmony_ci if (is_ereg(dst_reg) || is_ereg(src_reg)) 6718c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x40, dst_reg, src_reg)); 6728c2ecf20Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst_reg, src_reg)); 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci *pprog = prog; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/* LDX: dst_reg = *(u8*)(src_reg + off) */ 6798c2ecf20Sopenharmony_cistatic void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci u8 *prog = *pprog; 6828c2ecf20Sopenharmony_ci int cnt = 0; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci switch (size) { 6858c2ecf20Sopenharmony_ci case BPF_B: 6868c2ecf20Sopenharmony_ci /* Emit 'movzx rax, byte ptr [rax + off]' */ 6878c2ecf20Sopenharmony_ci EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB6); 6888c2ecf20Sopenharmony_ci break; 6898c2ecf20Sopenharmony_ci case BPF_H: 6908c2ecf20Sopenharmony_ci /* Emit 'movzx rax, word ptr [rax + off]' */ 6918c2ecf20Sopenharmony_ci EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB7); 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci case BPF_W: 6948c2ecf20Sopenharmony_ci /* Emit 'mov eax, dword ptr [rax+0x14]' */ 6958c2ecf20Sopenharmony_ci if (is_ereg(dst_reg) || is_ereg(src_reg)) 6968c2ecf20Sopenharmony_ci EMIT2(add_2mod(0x40, src_reg, dst_reg), 0x8B); 6978c2ecf20Sopenharmony_ci else 6988c2ecf20Sopenharmony_ci EMIT1(0x8B); 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci case BPF_DW: 7018c2ecf20Sopenharmony_ci /* Emit 'mov rax, qword ptr [rax+0x14]' */ 7028c2ecf20Sopenharmony_ci EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x8B); 7038c2ecf20Sopenharmony_ci break; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci /* 7068c2ecf20Sopenharmony_ci * If insn->off == 0 we can save one extra byte, but 7078c2ecf20Sopenharmony_ci * special case of x86 R13 which always needs an offset 7088c2ecf20Sopenharmony_ci * is not worth the hassle 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ci if (is_imm8(off)) 7118c2ecf20Sopenharmony_ci EMIT2(add_2reg(0x40, src_reg, dst_reg), off); 7128c2ecf20Sopenharmony_ci else 7138c2ecf20Sopenharmony_ci EMIT1_off32(add_2reg(0x80, src_reg, dst_reg), off); 7148c2ecf20Sopenharmony_ci *pprog = prog; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/* STX: *(u8*)(dst_reg + off) = src_reg */ 7188c2ecf20Sopenharmony_cistatic void emit_stx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci u8 *prog = *pprog; 7218c2ecf20Sopenharmony_ci int cnt = 0; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci switch (size) { 7248c2ecf20Sopenharmony_ci case BPF_B: 7258c2ecf20Sopenharmony_ci /* Emit 'mov byte ptr [rax + off], al' */ 7268c2ecf20Sopenharmony_ci if (is_ereg(dst_reg) || is_ereg_8l(src_reg)) 7278c2ecf20Sopenharmony_ci /* Add extra byte for eregs or SIL,DIL,BPL in src_reg */ 7288c2ecf20Sopenharmony_ci EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x88); 7298c2ecf20Sopenharmony_ci else 7308c2ecf20Sopenharmony_ci EMIT1(0x88); 7318c2ecf20Sopenharmony_ci break; 7328c2ecf20Sopenharmony_ci case BPF_H: 7338c2ecf20Sopenharmony_ci if (is_ereg(dst_reg) || is_ereg(src_reg)) 7348c2ecf20Sopenharmony_ci EMIT3(0x66, add_2mod(0x40, dst_reg, src_reg), 0x89); 7358c2ecf20Sopenharmony_ci else 7368c2ecf20Sopenharmony_ci EMIT2(0x66, 0x89); 7378c2ecf20Sopenharmony_ci break; 7388c2ecf20Sopenharmony_ci case BPF_W: 7398c2ecf20Sopenharmony_ci if (is_ereg(dst_reg) || is_ereg(src_reg)) 7408c2ecf20Sopenharmony_ci EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x89); 7418c2ecf20Sopenharmony_ci else 7428c2ecf20Sopenharmony_ci EMIT1(0x89); 7438c2ecf20Sopenharmony_ci break; 7448c2ecf20Sopenharmony_ci case BPF_DW: 7458c2ecf20Sopenharmony_ci EMIT2(add_2mod(0x48, dst_reg, src_reg), 0x89); 7468c2ecf20Sopenharmony_ci break; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci if (is_imm8(off)) 7498c2ecf20Sopenharmony_ci EMIT2(add_2reg(0x40, dst_reg, src_reg), off); 7508c2ecf20Sopenharmony_ci else 7518c2ecf20Sopenharmony_ci EMIT1_off32(add_2reg(0x80, dst_reg, src_reg), off); 7528c2ecf20Sopenharmony_ci *pprog = prog; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic bool ex_handler_bpf(const struct exception_table_entry *x, 7568c2ecf20Sopenharmony_ci struct pt_regs *regs, int trapnr, 7578c2ecf20Sopenharmony_ci unsigned long error_code, unsigned long fault_addr) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci u32 reg = x->fixup >> 8; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* jump over faulting load and clear dest register */ 7628c2ecf20Sopenharmony_ci *(unsigned long *)((void *)regs + reg) = 0; 7638c2ecf20Sopenharmony_ci regs->ip += x->fixup & 0xff; 7648c2ecf20Sopenharmony_ci return true; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic void detect_reg_usage(struct bpf_insn *insn, int insn_cnt, 7688c2ecf20Sopenharmony_ci bool *regs_used, bool *tail_call_seen) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci int i; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci for (i = 1; i <= insn_cnt; i++, insn++) { 7738c2ecf20Sopenharmony_ci if (insn->code == (BPF_JMP | BPF_TAIL_CALL)) 7748c2ecf20Sopenharmony_ci *tail_call_seen = true; 7758c2ecf20Sopenharmony_ci if (insn->dst_reg == BPF_REG_6 || insn->src_reg == BPF_REG_6) 7768c2ecf20Sopenharmony_ci regs_used[0] = true; 7778c2ecf20Sopenharmony_ci if (insn->dst_reg == BPF_REG_7 || insn->src_reg == BPF_REG_7) 7788c2ecf20Sopenharmony_ci regs_used[1] = true; 7798c2ecf20Sopenharmony_ci if (insn->dst_reg == BPF_REG_8 || insn->src_reg == BPF_REG_8) 7808c2ecf20Sopenharmony_ci regs_used[2] = true; 7818c2ecf20Sopenharmony_ci if (insn->dst_reg == BPF_REG_9 || insn->src_reg == BPF_REG_9) 7828c2ecf20Sopenharmony_ci regs_used[3] = true; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, 7878c2ecf20Sopenharmony_ci int oldproglen, struct jit_context *ctx) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci bool tail_call_reachable = bpf_prog->aux->tail_call_reachable; 7908c2ecf20Sopenharmony_ci struct bpf_insn *insn = bpf_prog->insnsi; 7918c2ecf20Sopenharmony_ci bool callee_regs_used[4] = {}; 7928c2ecf20Sopenharmony_ci int insn_cnt = bpf_prog->len; 7938c2ecf20Sopenharmony_ci bool tail_call_seen = false; 7948c2ecf20Sopenharmony_ci bool seen_exit = false; 7958c2ecf20Sopenharmony_ci u8 temp[BPF_MAX_INSN_SIZE + BPF_INSN_SAFETY]; 7968c2ecf20Sopenharmony_ci int i, cnt = 0, excnt = 0; 7978c2ecf20Sopenharmony_ci int proglen = 0; 7988c2ecf20Sopenharmony_ci u8 *prog = temp; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci detect_reg_usage(insn, insn_cnt, callee_regs_used, 8018c2ecf20Sopenharmony_ci &tail_call_seen); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci /* tail call's presence in current prog implies it is reachable */ 8048c2ecf20Sopenharmony_ci tail_call_reachable |= tail_call_seen; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci emit_prologue(&prog, bpf_prog->aux->stack_depth, 8078c2ecf20Sopenharmony_ci bpf_prog_was_classic(bpf_prog), tail_call_reachable, 8088c2ecf20Sopenharmony_ci bpf_prog->aux->func_idx != 0); 8098c2ecf20Sopenharmony_ci push_callee_regs(&prog, callee_regs_used); 8108c2ecf20Sopenharmony_ci addrs[0] = prog - temp; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci for (i = 1; i <= insn_cnt; i++, insn++) { 8138c2ecf20Sopenharmony_ci const s32 imm32 = insn->imm; 8148c2ecf20Sopenharmony_ci u32 dst_reg = insn->dst_reg; 8158c2ecf20Sopenharmony_ci u32 src_reg = insn->src_reg; 8168c2ecf20Sopenharmony_ci u8 b2 = 0, b3 = 0; 8178c2ecf20Sopenharmony_ci s64 jmp_offset; 8188c2ecf20Sopenharmony_ci u8 jmp_cond; 8198c2ecf20Sopenharmony_ci int ilen; 8208c2ecf20Sopenharmony_ci u8 *func; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci switch (insn->code) { 8238c2ecf20Sopenharmony_ci /* ALU */ 8248c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_X: 8258c2ecf20Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_X: 8268c2ecf20Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_X: 8278c2ecf20Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_X: 8288c2ecf20Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_X: 8298c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_X: 8308c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_X: 8318c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_X: 8328c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_X: 8338c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_X: 8348c2ecf20Sopenharmony_ci switch (BPF_OP(insn->code)) { 8358c2ecf20Sopenharmony_ci case BPF_ADD: b2 = 0x01; break; 8368c2ecf20Sopenharmony_ci case BPF_SUB: b2 = 0x29; break; 8378c2ecf20Sopenharmony_ci case BPF_AND: b2 = 0x21; break; 8388c2ecf20Sopenharmony_ci case BPF_OR: b2 = 0x09; break; 8398c2ecf20Sopenharmony_ci case BPF_XOR: b2 = 0x31; break; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_ALU64) 8428c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x48, dst_reg, src_reg)); 8438c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg) || is_ereg(src_reg)) 8448c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x40, dst_reg, src_reg)); 8458c2ecf20Sopenharmony_ci EMIT2(b2, add_2reg(0xC0, dst_reg, src_reg)); 8468c2ecf20Sopenharmony_ci break; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_X: 8498c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_X: 8508c2ecf20Sopenharmony_ci emit_mov_reg(&prog, 8518c2ecf20Sopenharmony_ci BPF_CLASS(insn->code) == BPF_ALU64, 8528c2ecf20Sopenharmony_ci dst_reg, src_reg); 8538c2ecf20Sopenharmony_ci break; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* neg dst */ 8568c2ecf20Sopenharmony_ci case BPF_ALU | BPF_NEG: 8578c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_NEG: 8588c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_ALU64) 8598c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x48, dst_reg)); 8608c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg)) 8618c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x40, dst_reg)); 8628c2ecf20Sopenharmony_ci EMIT2(0xF7, add_1reg(0xD8, dst_reg)); 8638c2ecf20Sopenharmony_ci break; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_K: 8668c2ecf20Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_K: 8678c2ecf20Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_K: 8688c2ecf20Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_K: 8698c2ecf20Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_K: 8708c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_K: 8718c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_K: 8728c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_K: 8738c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_K: 8748c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_K: 8758c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_ALU64) 8768c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x48, dst_reg)); 8778c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg)) 8788c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x40, dst_reg)); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* 8818c2ecf20Sopenharmony_ci * b3 holds 'normal' opcode, b2 short form only valid 8828c2ecf20Sopenharmony_ci * in case dst is eax/rax. 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci switch (BPF_OP(insn->code)) { 8858c2ecf20Sopenharmony_ci case BPF_ADD: 8868c2ecf20Sopenharmony_ci b3 = 0xC0; 8878c2ecf20Sopenharmony_ci b2 = 0x05; 8888c2ecf20Sopenharmony_ci break; 8898c2ecf20Sopenharmony_ci case BPF_SUB: 8908c2ecf20Sopenharmony_ci b3 = 0xE8; 8918c2ecf20Sopenharmony_ci b2 = 0x2D; 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci case BPF_AND: 8948c2ecf20Sopenharmony_ci b3 = 0xE0; 8958c2ecf20Sopenharmony_ci b2 = 0x25; 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci case BPF_OR: 8988c2ecf20Sopenharmony_ci b3 = 0xC8; 8998c2ecf20Sopenharmony_ci b2 = 0x0D; 9008c2ecf20Sopenharmony_ci break; 9018c2ecf20Sopenharmony_ci case BPF_XOR: 9028c2ecf20Sopenharmony_ci b3 = 0xF0; 9038c2ecf20Sopenharmony_ci b2 = 0x35; 9048c2ecf20Sopenharmony_ci break; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (is_imm8(imm32)) 9088c2ecf20Sopenharmony_ci EMIT3(0x83, add_1reg(b3, dst_reg), imm32); 9098c2ecf20Sopenharmony_ci else if (is_axreg(dst_reg)) 9108c2ecf20Sopenharmony_ci EMIT1_off32(b2, imm32); 9118c2ecf20Sopenharmony_ci else 9128c2ecf20Sopenharmony_ci EMIT2_off32(0x81, add_1reg(b3, dst_reg), imm32); 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_K: 9168c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_K: 9178c2ecf20Sopenharmony_ci emit_mov_imm32(&prog, BPF_CLASS(insn->code) == BPF_ALU64, 9188c2ecf20Sopenharmony_ci dst_reg, imm32); 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci case BPF_LD | BPF_IMM | BPF_DW: 9228c2ecf20Sopenharmony_ci emit_mov_imm64(&prog, dst_reg, insn[1].imm, insn[0].imm); 9238c2ecf20Sopenharmony_ci insn++; 9248c2ecf20Sopenharmony_ci i++; 9258c2ecf20Sopenharmony_ci break; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* dst %= src, dst /= src, dst %= imm32, dst /= imm32 */ 9288c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_X: 9298c2ecf20Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_X: 9308c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_K: 9318c2ecf20Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_K: 9328c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_X: 9338c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_X: 9348c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_K: 9358c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_K: 9368c2ecf20Sopenharmony_ci EMIT1(0x50); /* push rax */ 9378c2ecf20Sopenharmony_ci EMIT1(0x52); /* push rdx */ 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (BPF_SRC(insn->code) == BPF_X) 9408c2ecf20Sopenharmony_ci /* mov r11, src_reg */ 9418c2ecf20Sopenharmony_ci EMIT_mov(AUX_REG, src_reg); 9428c2ecf20Sopenharmony_ci else 9438c2ecf20Sopenharmony_ci /* mov r11, imm32 */ 9448c2ecf20Sopenharmony_ci EMIT3_off32(0x49, 0xC7, 0xC3, imm32); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* mov rax, dst_reg */ 9478c2ecf20Sopenharmony_ci EMIT_mov(BPF_REG_0, dst_reg); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* 9508c2ecf20Sopenharmony_ci * xor edx, edx 9518c2ecf20Sopenharmony_ci * equivalent to 'xor rdx, rdx', but one byte less 9528c2ecf20Sopenharmony_ci */ 9538c2ecf20Sopenharmony_ci EMIT2(0x31, 0xd2); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_ALU64) 9568c2ecf20Sopenharmony_ci /* div r11 */ 9578c2ecf20Sopenharmony_ci EMIT3(0x49, 0xF7, 0xF3); 9588c2ecf20Sopenharmony_ci else 9598c2ecf20Sopenharmony_ci /* div r11d */ 9608c2ecf20Sopenharmony_ci EMIT3(0x41, 0xF7, 0xF3); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (BPF_OP(insn->code) == BPF_MOD) 9638c2ecf20Sopenharmony_ci /* mov r11, rdx */ 9648c2ecf20Sopenharmony_ci EMIT3(0x49, 0x89, 0xD3); 9658c2ecf20Sopenharmony_ci else 9668c2ecf20Sopenharmony_ci /* mov r11, rax */ 9678c2ecf20Sopenharmony_ci EMIT3(0x49, 0x89, 0xC3); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci EMIT1(0x5A); /* pop rdx */ 9708c2ecf20Sopenharmony_ci EMIT1(0x58); /* pop rax */ 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* mov dst_reg, r11 */ 9738c2ecf20Sopenharmony_ci EMIT_mov(dst_reg, AUX_REG); 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_K: 9778c2ecf20Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_X: 9788c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_K: 9798c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_X: 9808c2ecf20Sopenharmony_ci { 9818c2ecf20Sopenharmony_ci bool is64 = BPF_CLASS(insn->code) == BPF_ALU64; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (dst_reg != BPF_REG_0) 9848c2ecf20Sopenharmony_ci EMIT1(0x50); /* push rax */ 9858c2ecf20Sopenharmony_ci if (dst_reg != BPF_REG_3) 9868c2ecf20Sopenharmony_ci EMIT1(0x52); /* push rdx */ 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* mov r11, dst_reg */ 9898c2ecf20Sopenharmony_ci EMIT_mov(AUX_REG, dst_reg); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci if (BPF_SRC(insn->code) == BPF_X) 9928c2ecf20Sopenharmony_ci emit_mov_reg(&prog, is64, BPF_REG_0, src_reg); 9938c2ecf20Sopenharmony_ci else 9948c2ecf20Sopenharmony_ci emit_mov_imm32(&prog, is64, BPF_REG_0, imm32); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci if (is64) 9978c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x48, AUX_REG)); 9988c2ecf20Sopenharmony_ci else if (is_ereg(AUX_REG)) 9998c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x40, AUX_REG)); 10008c2ecf20Sopenharmony_ci /* mul(q) r11 */ 10018c2ecf20Sopenharmony_ci EMIT2(0xF7, add_1reg(0xE0, AUX_REG)); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (dst_reg != BPF_REG_3) 10048c2ecf20Sopenharmony_ci EMIT1(0x5A); /* pop rdx */ 10058c2ecf20Sopenharmony_ci if (dst_reg != BPF_REG_0) { 10068c2ecf20Sopenharmony_ci /* mov dst_reg, rax */ 10078c2ecf20Sopenharmony_ci EMIT_mov(dst_reg, BPF_REG_0); 10088c2ecf20Sopenharmony_ci EMIT1(0x58); /* pop rax */ 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci break; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci /* Shifts */ 10138c2ecf20Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_K: 10148c2ecf20Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_K: 10158c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_K: 10168c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_K: 10178c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_K: 10188c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_K: 10198c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_ALU64) 10208c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x48, dst_reg)); 10218c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg)) 10228c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x40, dst_reg)); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci switch (BPF_OP(insn->code)) { 10258c2ecf20Sopenharmony_ci case BPF_LSH: b3 = 0xE0; break; 10268c2ecf20Sopenharmony_ci case BPF_RSH: b3 = 0xE8; break; 10278c2ecf20Sopenharmony_ci case BPF_ARSH: b3 = 0xF8; break; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (imm32 == 1) 10318c2ecf20Sopenharmony_ci EMIT2(0xD1, add_1reg(b3, dst_reg)); 10328c2ecf20Sopenharmony_ci else 10338c2ecf20Sopenharmony_ci EMIT3(0xC1, add_1reg(b3, dst_reg), imm32); 10348c2ecf20Sopenharmony_ci break; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_X: 10378c2ecf20Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_X: 10388c2ecf20Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_X: 10398c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_X: 10408c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_X: 10418c2ecf20Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_X: 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* Check for bad case when dst_reg == rcx */ 10448c2ecf20Sopenharmony_ci if (dst_reg == BPF_REG_4) { 10458c2ecf20Sopenharmony_ci /* mov r11, dst_reg */ 10468c2ecf20Sopenharmony_ci EMIT_mov(AUX_REG, dst_reg); 10478c2ecf20Sopenharmony_ci dst_reg = AUX_REG; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (src_reg != BPF_REG_4) { /* common case */ 10518c2ecf20Sopenharmony_ci EMIT1(0x51); /* push rcx */ 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* mov rcx, src_reg */ 10548c2ecf20Sopenharmony_ci EMIT_mov(BPF_REG_4, src_reg); 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* shl %rax, %cl | shr %rax, %cl | sar %rax, %cl */ 10588c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_ALU64) 10598c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x48, dst_reg)); 10608c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg)) 10618c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x40, dst_reg)); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci switch (BPF_OP(insn->code)) { 10648c2ecf20Sopenharmony_ci case BPF_LSH: b3 = 0xE0; break; 10658c2ecf20Sopenharmony_ci case BPF_RSH: b3 = 0xE8; break; 10668c2ecf20Sopenharmony_ci case BPF_ARSH: b3 = 0xF8; break; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci EMIT2(0xD3, add_1reg(b3, dst_reg)); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci if (src_reg != BPF_REG_4) 10718c2ecf20Sopenharmony_ci EMIT1(0x59); /* pop rcx */ 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (insn->dst_reg == BPF_REG_4) 10748c2ecf20Sopenharmony_ci /* mov dst_reg, r11 */ 10758c2ecf20Sopenharmony_ci EMIT_mov(insn->dst_reg, AUX_REG); 10768c2ecf20Sopenharmony_ci break; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_BE: 10798c2ecf20Sopenharmony_ci switch (imm32) { 10808c2ecf20Sopenharmony_ci case 16: 10818c2ecf20Sopenharmony_ci /* Emit 'ror %ax, 8' to swap lower 2 bytes */ 10828c2ecf20Sopenharmony_ci EMIT1(0x66); 10838c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 10848c2ecf20Sopenharmony_ci EMIT1(0x41); 10858c2ecf20Sopenharmony_ci EMIT3(0xC1, add_1reg(0xC8, dst_reg), 8); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci /* Emit 'movzwl eax, ax' */ 10888c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 10898c2ecf20Sopenharmony_ci EMIT3(0x45, 0x0F, 0xB7); 10908c2ecf20Sopenharmony_ci else 10918c2ecf20Sopenharmony_ci EMIT2(0x0F, 0xB7); 10928c2ecf20Sopenharmony_ci EMIT1(add_2reg(0xC0, dst_reg, dst_reg)); 10938c2ecf20Sopenharmony_ci break; 10948c2ecf20Sopenharmony_ci case 32: 10958c2ecf20Sopenharmony_ci /* Emit 'bswap eax' to swap lower 4 bytes */ 10968c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 10978c2ecf20Sopenharmony_ci EMIT2(0x41, 0x0F); 10988c2ecf20Sopenharmony_ci else 10998c2ecf20Sopenharmony_ci EMIT1(0x0F); 11008c2ecf20Sopenharmony_ci EMIT1(add_1reg(0xC8, dst_reg)); 11018c2ecf20Sopenharmony_ci break; 11028c2ecf20Sopenharmony_ci case 64: 11038c2ecf20Sopenharmony_ci /* Emit 'bswap rax' to swap 8 bytes */ 11048c2ecf20Sopenharmony_ci EMIT3(add_1mod(0x48, dst_reg), 0x0F, 11058c2ecf20Sopenharmony_ci add_1reg(0xC8, dst_reg)); 11068c2ecf20Sopenharmony_ci break; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci break; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_LE: 11118c2ecf20Sopenharmony_ci switch (imm32) { 11128c2ecf20Sopenharmony_ci case 16: 11138c2ecf20Sopenharmony_ci /* 11148c2ecf20Sopenharmony_ci * Emit 'movzwl eax, ax' to zero extend 16-bit 11158c2ecf20Sopenharmony_ci * into 64 bit 11168c2ecf20Sopenharmony_ci */ 11178c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 11188c2ecf20Sopenharmony_ci EMIT3(0x45, 0x0F, 0xB7); 11198c2ecf20Sopenharmony_ci else 11208c2ecf20Sopenharmony_ci EMIT2(0x0F, 0xB7); 11218c2ecf20Sopenharmony_ci EMIT1(add_2reg(0xC0, dst_reg, dst_reg)); 11228c2ecf20Sopenharmony_ci break; 11238c2ecf20Sopenharmony_ci case 32: 11248c2ecf20Sopenharmony_ci /* Emit 'mov eax, eax' to clear upper 32-bits */ 11258c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 11268c2ecf20Sopenharmony_ci EMIT1(0x45); 11278c2ecf20Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst_reg, dst_reg)); 11288c2ecf20Sopenharmony_ci break; 11298c2ecf20Sopenharmony_ci case 64: 11308c2ecf20Sopenharmony_ci /* nop */ 11318c2ecf20Sopenharmony_ci break; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci break; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci /* speculation barrier */ 11368c2ecf20Sopenharmony_ci case BPF_ST | BPF_NOSPEC: 11378c2ecf20Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_XMM2)) 11388c2ecf20Sopenharmony_ci EMIT_LFENCE(); 11398c2ecf20Sopenharmony_ci break; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* ST: *(u8*)(dst_reg + off) = imm */ 11428c2ecf20Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_B: 11438c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 11448c2ecf20Sopenharmony_ci EMIT2(0x41, 0xC6); 11458c2ecf20Sopenharmony_ci else 11468c2ecf20Sopenharmony_ci EMIT1(0xC6); 11478c2ecf20Sopenharmony_ci goto st; 11488c2ecf20Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_H: 11498c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 11508c2ecf20Sopenharmony_ci EMIT3(0x66, 0x41, 0xC7); 11518c2ecf20Sopenharmony_ci else 11528c2ecf20Sopenharmony_ci EMIT2(0x66, 0xC7); 11538c2ecf20Sopenharmony_ci goto st; 11548c2ecf20Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_W: 11558c2ecf20Sopenharmony_ci if (is_ereg(dst_reg)) 11568c2ecf20Sopenharmony_ci EMIT2(0x41, 0xC7); 11578c2ecf20Sopenharmony_ci else 11588c2ecf20Sopenharmony_ci EMIT1(0xC7); 11598c2ecf20Sopenharmony_ci goto st; 11608c2ecf20Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_DW: 11618c2ecf20Sopenharmony_ci EMIT2(add_1mod(0x48, dst_reg), 0xC7); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cist: if (is_imm8(insn->off)) 11648c2ecf20Sopenharmony_ci EMIT2(add_1reg(0x40, dst_reg), insn->off); 11658c2ecf20Sopenharmony_ci else 11668c2ecf20Sopenharmony_ci EMIT1_off32(add_1reg(0x80, dst_reg), insn->off); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn->code))); 11698c2ecf20Sopenharmony_ci break; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci /* STX: *(u8*)(dst_reg + off) = src_reg */ 11728c2ecf20Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_B: 11738c2ecf20Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_H: 11748c2ecf20Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_W: 11758c2ecf20Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_DW: 11768c2ecf20Sopenharmony_ci emit_stx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off); 11778c2ecf20Sopenharmony_ci break; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci /* LDX: dst_reg = *(u8*)(src_reg + off) */ 11808c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_B: 11818c2ecf20Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_B: 11828c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_H: 11838c2ecf20Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_H: 11848c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_W: 11858c2ecf20Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_W: 11868c2ecf20Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_DW: 11878c2ecf20Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 11888c2ecf20Sopenharmony_ci emit_ldx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off); 11898c2ecf20Sopenharmony_ci if (BPF_MODE(insn->code) == BPF_PROBE_MEM) { 11908c2ecf20Sopenharmony_ci struct exception_table_entry *ex; 11918c2ecf20Sopenharmony_ci u8 *_insn = image + proglen; 11928c2ecf20Sopenharmony_ci s64 delta; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (!bpf_prog->aux->extable) 11958c2ecf20Sopenharmony_ci break; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (excnt >= bpf_prog->aux->num_exentries) { 11988c2ecf20Sopenharmony_ci pr_err("ex gen bug\n"); 11998c2ecf20Sopenharmony_ci return -EFAULT; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci ex = &bpf_prog->aux->extable[excnt++]; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci delta = _insn - (u8 *)&ex->insn; 12048c2ecf20Sopenharmony_ci if (!is_simm32(delta)) { 12058c2ecf20Sopenharmony_ci pr_err("extable->insn doesn't fit into 32-bit\n"); 12068c2ecf20Sopenharmony_ci return -EFAULT; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci ex->insn = delta; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci delta = (u8 *)ex_handler_bpf - (u8 *)&ex->handler; 12118c2ecf20Sopenharmony_ci if (!is_simm32(delta)) { 12128c2ecf20Sopenharmony_ci pr_err("extable->handler doesn't fit into 32-bit\n"); 12138c2ecf20Sopenharmony_ci return -EFAULT; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci ex->handler = delta; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (dst_reg > BPF_REG_9) { 12188c2ecf20Sopenharmony_ci pr_err("verifier error\n"); 12198c2ecf20Sopenharmony_ci return -EFAULT; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci /* 12228c2ecf20Sopenharmony_ci * Compute size of x86 insn and its target dest x86 register. 12238c2ecf20Sopenharmony_ci * ex_handler_bpf() will use lower 8 bits to adjust 12248c2ecf20Sopenharmony_ci * pt_regs->ip to jump over this x86 instruction 12258c2ecf20Sopenharmony_ci * and upper bits to figure out which pt_regs to zero out. 12268c2ecf20Sopenharmony_ci * End result: x86 insn "mov rbx, qword ptr [rax+0x14]" 12278c2ecf20Sopenharmony_ci * of 4 bytes will be ignored and rbx will be zero inited. 12288c2ecf20Sopenharmony_ci */ 12298c2ecf20Sopenharmony_ci ex->fixup = (prog - temp) | (reg2pt_regs[dst_reg] << 8); 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci break; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci /* STX XADD: lock *(u32*)(dst_reg + off) += src_reg */ 12348c2ecf20Sopenharmony_ci case BPF_STX | BPF_XADD | BPF_W: 12358c2ecf20Sopenharmony_ci /* Emit 'lock add dword ptr [rax + off], eax' */ 12368c2ecf20Sopenharmony_ci if (is_ereg(dst_reg) || is_ereg(src_reg)) 12378c2ecf20Sopenharmony_ci EMIT3(0xF0, add_2mod(0x40, dst_reg, src_reg), 0x01); 12388c2ecf20Sopenharmony_ci else 12398c2ecf20Sopenharmony_ci EMIT2(0xF0, 0x01); 12408c2ecf20Sopenharmony_ci goto xadd; 12418c2ecf20Sopenharmony_ci case BPF_STX | BPF_XADD | BPF_DW: 12428c2ecf20Sopenharmony_ci EMIT3(0xF0, add_2mod(0x48, dst_reg, src_reg), 0x01); 12438c2ecf20Sopenharmony_cixadd: if (is_imm8(insn->off)) 12448c2ecf20Sopenharmony_ci EMIT2(add_2reg(0x40, dst_reg, src_reg), insn->off); 12458c2ecf20Sopenharmony_ci else 12468c2ecf20Sopenharmony_ci EMIT1_off32(add_2reg(0x80, dst_reg, src_reg), 12478c2ecf20Sopenharmony_ci insn->off); 12488c2ecf20Sopenharmony_ci break; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* call */ 12518c2ecf20Sopenharmony_ci case BPF_JMP | BPF_CALL: 12528c2ecf20Sopenharmony_ci func = (u8 *) __bpf_call_base + imm32; 12538c2ecf20Sopenharmony_ci if (tail_call_reachable) { 12548c2ecf20Sopenharmony_ci /* mov rax, qword ptr [rbp - rounded_stack_depth - 8] */ 12558c2ecf20Sopenharmony_ci EMIT3_off32(0x48, 0x8B, 0x85, 12568c2ecf20Sopenharmony_ci -round_up(bpf_prog->aux->stack_depth, 8) - 8); 12578c2ecf20Sopenharmony_ci if (!imm32 || emit_call(&prog, func, image + addrs[i - 1] + 7)) 12588c2ecf20Sopenharmony_ci return -EINVAL; 12598c2ecf20Sopenharmony_ci } else { 12608c2ecf20Sopenharmony_ci if (!imm32 || emit_call(&prog, func, image + addrs[i - 1])) 12618c2ecf20Sopenharmony_ci return -EINVAL; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci break; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci case BPF_JMP | BPF_TAIL_CALL: 12668c2ecf20Sopenharmony_ci if (imm32) 12678c2ecf20Sopenharmony_ci emit_bpf_tail_call_direct(&bpf_prog->aux->poke_tab[imm32 - 1], 12688c2ecf20Sopenharmony_ci &prog, image + addrs[i - 1], 12698c2ecf20Sopenharmony_ci callee_regs_used, 12708c2ecf20Sopenharmony_ci bpf_prog->aux->stack_depth, 12718c2ecf20Sopenharmony_ci ctx); 12728c2ecf20Sopenharmony_ci else 12738c2ecf20Sopenharmony_ci emit_bpf_tail_call_indirect(&prog, 12748c2ecf20Sopenharmony_ci callee_regs_used, 12758c2ecf20Sopenharmony_ci bpf_prog->aux->stack_depth, 12768c2ecf20Sopenharmony_ci image + addrs[i - 1], 12778c2ecf20Sopenharmony_ci ctx); 12788c2ecf20Sopenharmony_ci break; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci /* cond jump */ 12818c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_X: 12828c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_X: 12838c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_X: 12848c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_X: 12858c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_X: 12868c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_X: 12878c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_X: 12888c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_X: 12898c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_X: 12908c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_X: 12918c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_X: 12928c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_X: 12938c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_X: 12948c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_X: 12958c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_X: 12968c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_X: 12978c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_X: 12988c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_X: 12998c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_X: 13008c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_X: 13018c2ecf20Sopenharmony_ci /* cmp dst_reg, src_reg */ 13028c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_JMP) 13038c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x48, dst_reg, src_reg)); 13048c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg) || is_ereg(src_reg)) 13058c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x40, dst_reg, src_reg)); 13068c2ecf20Sopenharmony_ci EMIT2(0x39, add_2reg(0xC0, dst_reg, src_reg)); 13078c2ecf20Sopenharmony_ci goto emit_cond_jmp; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_X: 13108c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_X: 13118c2ecf20Sopenharmony_ci /* test dst_reg, src_reg */ 13128c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_JMP) 13138c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x48, dst_reg, src_reg)); 13148c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg) || is_ereg(src_reg)) 13158c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x40, dst_reg, src_reg)); 13168c2ecf20Sopenharmony_ci EMIT2(0x85, add_2reg(0xC0, dst_reg, src_reg)); 13178c2ecf20Sopenharmony_ci goto emit_cond_jmp; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_K: 13208c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_K: 13218c2ecf20Sopenharmony_ci /* test dst_reg, imm32 */ 13228c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_JMP) 13238c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x48, dst_reg)); 13248c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg)) 13258c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x40, dst_reg)); 13268c2ecf20Sopenharmony_ci EMIT2_off32(0xF7, add_1reg(0xC0, dst_reg), imm32); 13278c2ecf20Sopenharmony_ci goto emit_cond_jmp; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_K: 13308c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_K: 13318c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_K: 13328c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_K: 13338c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_K: 13348c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_K: 13358c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_K: 13368c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_K: 13378c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_K: 13388c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_K: 13398c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_K: 13408c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_K: 13418c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_K: 13428c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_K: 13438c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_K: 13448c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_K: 13458c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_K: 13468c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_K: 13478c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_K: 13488c2ecf20Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_K: 13498c2ecf20Sopenharmony_ci /* test dst_reg, dst_reg to save one extra byte */ 13508c2ecf20Sopenharmony_ci if (imm32 == 0) { 13518c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_JMP) 13528c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x48, dst_reg, dst_reg)); 13538c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg)) 13548c2ecf20Sopenharmony_ci EMIT1(add_2mod(0x40, dst_reg, dst_reg)); 13558c2ecf20Sopenharmony_ci EMIT2(0x85, add_2reg(0xC0, dst_reg, dst_reg)); 13568c2ecf20Sopenharmony_ci goto emit_cond_jmp; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci /* cmp dst_reg, imm8/32 */ 13608c2ecf20Sopenharmony_ci if (BPF_CLASS(insn->code) == BPF_JMP) 13618c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x48, dst_reg)); 13628c2ecf20Sopenharmony_ci else if (is_ereg(dst_reg)) 13638c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x40, dst_reg)); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci if (is_imm8(imm32)) 13668c2ecf20Sopenharmony_ci EMIT3(0x83, add_1reg(0xF8, dst_reg), imm32); 13678c2ecf20Sopenharmony_ci else 13688c2ecf20Sopenharmony_ci EMIT2_off32(0x81, add_1reg(0xF8, dst_reg), imm32); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ciemit_cond_jmp: /* Convert BPF opcode to x86 */ 13718c2ecf20Sopenharmony_ci switch (BPF_OP(insn->code)) { 13728c2ecf20Sopenharmony_ci case BPF_JEQ: 13738c2ecf20Sopenharmony_ci jmp_cond = X86_JE; 13748c2ecf20Sopenharmony_ci break; 13758c2ecf20Sopenharmony_ci case BPF_JSET: 13768c2ecf20Sopenharmony_ci case BPF_JNE: 13778c2ecf20Sopenharmony_ci jmp_cond = X86_JNE; 13788c2ecf20Sopenharmony_ci break; 13798c2ecf20Sopenharmony_ci case BPF_JGT: 13808c2ecf20Sopenharmony_ci /* GT is unsigned '>', JA in x86 */ 13818c2ecf20Sopenharmony_ci jmp_cond = X86_JA; 13828c2ecf20Sopenharmony_ci break; 13838c2ecf20Sopenharmony_ci case BPF_JLT: 13848c2ecf20Sopenharmony_ci /* LT is unsigned '<', JB in x86 */ 13858c2ecf20Sopenharmony_ci jmp_cond = X86_JB; 13868c2ecf20Sopenharmony_ci break; 13878c2ecf20Sopenharmony_ci case BPF_JGE: 13888c2ecf20Sopenharmony_ci /* GE is unsigned '>=', JAE in x86 */ 13898c2ecf20Sopenharmony_ci jmp_cond = X86_JAE; 13908c2ecf20Sopenharmony_ci break; 13918c2ecf20Sopenharmony_ci case BPF_JLE: 13928c2ecf20Sopenharmony_ci /* LE is unsigned '<=', JBE in x86 */ 13938c2ecf20Sopenharmony_ci jmp_cond = X86_JBE; 13948c2ecf20Sopenharmony_ci break; 13958c2ecf20Sopenharmony_ci case BPF_JSGT: 13968c2ecf20Sopenharmony_ci /* Signed '>', GT in x86 */ 13978c2ecf20Sopenharmony_ci jmp_cond = X86_JG; 13988c2ecf20Sopenharmony_ci break; 13998c2ecf20Sopenharmony_ci case BPF_JSLT: 14008c2ecf20Sopenharmony_ci /* Signed '<', LT in x86 */ 14018c2ecf20Sopenharmony_ci jmp_cond = X86_JL; 14028c2ecf20Sopenharmony_ci break; 14038c2ecf20Sopenharmony_ci case BPF_JSGE: 14048c2ecf20Sopenharmony_ci /* Signed '>=', GE in x86 */ 14058c2ecf20Sopenharmony_ci jmp_cond = X86_JGE; 14068c2ecf20Sopenharmony_ci break; 14078c2ecf20Sopenharmony_ci case BPF_JSLE: 14088c2ecf20Sopenharmony_ci /* Signed '<=', LE in x86 */ 14098c2ecf20Sopenharmony_ci jmp_cond = X86_JLE; 14108c2ecf20Sopenharmony_ci break; 14118c2ecf20Sopenharmony_ci default: /* to silence GCC warning */ 14128c2ecf20Sopenharmony_ci return -EFAULT; 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci jmp_offset = addrs[i + insn->off] - addrs[i]; 14158c2ecf20Sopenharmony_ci if (is_imm8(jmp_offset)) { 14168c2ecf20Sopenharmony_ci EMIT2(jmp_cond, jmp_offset); 14178c2ecf20Sopenharmony_ci } else if (is_simm32(jmp_offset)) { 14188c2ecf20Sopenharmony_ci EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset); 14198c2ecf20Sopenharmony_ci } else { 14208c2ecf20Sopenharmony_ci pr_err("cond_jmp gen bug %llx\n", jmp_offset); 14218c2ecf20Sopenharmony_ci return -EFAULT; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci break; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci case BPF_JMP | BPF_JA: 14278c2ecf20Sopenharmony_ci if (insn->off == -1) 14288c2ecf20Sopenharmony_ci /* -1 jmp instructions will always jump 14298c2ecf20Sopenharmony_ci * backwards two bytes. Explicitly handling 14308c2ecf20Sopenharmony_ci * this case avoids wasting too many passes 14318c2ecf20Sopenharmony_ci * when there are long sequences of replaced 14328c2ecf20Sopenharmony_ci * dead code. 14338c2ecf20Sopenharmony_ci */ 14348c2ecf20Sopenharmony_ci jmp_offset = -2; 14358c2ecf20Sopenharmony_ci else 14368c2ecf20Sopenharmony_ci jmp_offset = addrs[i + insn->off] - addrs[i]; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci if (!jmp_offset) 14398c2ecf20Sopenharmony_ci /* Optimize out nop jumps */ 14408c2ecf20Sopenharmony_ci break; 14418c2ecf20Sopenharmony_ciemit_jmp: 14428c2ecf20Sopenharmony_ci if (is_imm8(jmp_offset)) { 14438c2ecf20Sopenharmony_ci EMIT2(0xEB, jmp_offset); 14448c2ecf20Sopenharmony_ci } else if (is_simm32(jmp_offset)) { 14458c2ecf20Sopenharmony_ci EMIT1_off32(0xE9, jmp_offset); 14468c2ecf20Sopenharmony_ci } else { 14478c2ecf20Sopenharmony_ci pr_err("jmp gen bug %llx\n", jmp_offset); 14488c2ecf20Sopenharmony_ci return -EFAULT; 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci break; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci case BPF_JMP | BPF_EXIT: 14538c2ecf20Sopenharmony_ci if (seen_exit) { 14548c2ecf20Sopenharmony_ci jmp_offset = ctx->cleanup_addr - addrs[i]; 14558c2ecf20Sopenharmony_ci goto emit_jmp; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci seen_exit = true; 14588c2ecf20Sopenharmony_ci /* Update cleanup_addr */ 14598c2ecf20Sopenharmony_ci ctx->cleanup_addr = proglen; 14608c2ecf20Sopenharmony_ci pop_callee_regs(&prog, callee_regs_used); 14618c2ecf20Sopenharmony_ci EMIT1(0xC9); /* leave */ 14628c2ecf20Sopenharmony_ci emit_return(&prog, image + addrs[i - 1] + (prog - temp)); 14638c2ecf20Sopenharmony_ci break; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci default: 14668c2ecf20Sopenharmony_ci /* 14678c2ecf20Sopenharmony_ci * By design x86-64 JIT should support all BPF instructions. 14688c2ecf20Sopenharmony_ci * This error will be seen if new instruction was added 14698c2ecf20Sopenharmony_ci * to the interpreter, but not to the JIT, or if there is 14708c2ecf20Sopenharmony_ci * junk in bpf_prog. 14718c2ecf20Sopenharmony_ci */ 14728c2ecf20Sopenharmony_ci pr_err("bpf_jit: unknown opcode %02x\n", insn->code); 14738c2ecf20Sopenharmony_ci return -EINVAL; 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci ilen = prog - temp; 14778c2ecf20Sopenharmony_ci if (ilen > BPF_MAX_INSN_SIZE) { 14788c2ecf20Sopenharmony_ci pr_err("bpf_jit: fatal insn size error\n"); 14798c2ecf20Sopenharmony_ci return -EFAULT; 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci if (image) { 14838c2ecf20Sopenharmony_ci /* 14848c2ecf20Sopenharmony_ci * When populating the image, assert that: 14858c2ecf20Sopenharmony_ci * 14868c2ecf20Sopenharmony_ci * i) We do not write beyond the allocated space, and 14878c2ecf20Sopenharmony_ci * ii) addrs[i] did not change from the prior run, in order 14888c2ecf20Sopenharmony_ci * to validate assumptions made for computing branch 14898c2ecf20Sopenharmony_ci * displacements. 14908c2ecf20Sopenharmony_ci */ 14918c2ecf20Sopenharmony_ci if (unlikely(proglen + ilen > oldproglen || 14928c2ecf20Sopenharmony_ci proglen + ilen != addrs[i])) { 14938c2ecf20Sopenharmony_ci pr_err("bpf_jit: fatal error\n"); 14948c2ecf20Sopenharmony_ci return -EFAULT; 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci memcpy(image + proglen, temp, ilen); 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci proglen += ilen; 14998c2ecf20Sopenharmony_ci addrs[i] = proglen; 15008c2ecf20Sopenharmony_ci prog = temp; 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci if (image && excnt != bpf_prog->aux->num_exentries) { 15048c2ecf20Sopenharmony_ci pr_err("extable is not populated\n"); 15058c2ecf20Sopenharmony_ci return -EFAULT; 15068c2ecf20Sopenharmony_ci } 15078c2ecf20Sopenharmony_ci return proglen; 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_cistatic void save_regs(const struct btf_func_model *m, u8 **prog, int nr_args, 15118c2ecf20Sopenharmony_ci int stack_size) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci int i; 15148c2ecf20Sopenharmony_ci /* Store function arguments to stack. 15158c2ecf20Sopenharmony_ci * For a function that accepts two pointers the sequence will be: 15168c2ecf20Sopenharmony_ci * mov QWORD PTR [rbp-0x10],rdi 15178c2ecf20Sopenharmony_ci * mov QWORD PTR [rbp-0x8],rsi 15188c2ecf20Sopenharmony_ci */ 15198c2ecf20Sopenharmony_ci for (i = 0; i < min(nr_args, 6); i++) 15208c2ecf20Sopenharmony_ci emit_stx(prog, bytes_to_bpf_size(m->arg_size[i]), 15218c2ecf20Sopenharmony_ci BPF_REG_FP, 15228c2ecf20Sopenharmony_ci i == 5 ? X86_REG_R9 : BPF_REG_1 + i, 15238c2ecf20Sopenharmony_ci -(stack_size - i * 8)); 15248c2ecf20Sopenharmony_ci} 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_cistatic void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_args, 15278c2ecf20Sopenharmony_ci int stack_size) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci int i; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci /* Restore function arguments from stack. 15328c2ecf20Sopenharmony_ci * For a function that accepts two pointers the sequence will be: 15338c2ecf20Sopenharmony_ci * EMIT4(0x48, 0x8B, 0x7D, 0xF0); mov rdi,QWORD PTR [rbp-0x10] 15348c2ecf20Sopenharmony_ci * EMIT4(0x48, 0x8B, 0x75, 0xF8); mov rsi,QWORD PTR [rbp-0x8] 15358c2ecf20Sopenharmony_ci */ 15368c2ecf20Sopenharmony_ci for (i = 0; i < min(nr_args, 6); i++) 15378c2ecf20Sopenharmony_ci emit_ldx(prog, bytes_to_bpf_size(m->arg_size[i]), 15388c2ecf20Sopenharmony_ci i == 5 ? X86_REG_R9 : BPF_REG_1 + i, 15398c2ecf20Sopenharmony_ci BPF_REG_FP, 15408c2ecf20Sopenharmony_ci -(stack_size - i * 8)); 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, 15448c2ecf20Sopenharmony_ci struct bpf_prog *p, int stack_size, bool save_ret) 15458c2ecf20Sopenharmony_ci{ 15468c2ecf20Sopenharmony_ci u8 *prog = *pprog; 15478c2ecf20Sopenharmony_ci int cnt = 0; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci if (p->aux->sleepable) { 15508c2ecf20Sopenharmony_ci if (emit_call(&prog, __bpf_prog_enter_sleepable, prog)) 15518c2ecf20Sopenharmony_ci return -EINVAL; 15528c2ecf20Sopenharmony_ci } else { 15538c2ecf20Sopenharmony_ci if (emit_call(&prog, __bpf_prog_enter, prog)) 15548c2ecf20Sopenharmony_ci return -EINVAL; 15558c2ecf20Sopenharmony_ci /* remember prog start time returned by __bpf_prog_enter */ 15568c2ecf20Sopenharmony_ci emit_mov_reg(&prog, true, BPF_REG_6, BPF_REG_0); 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci /* arg1: lea rdi, [rbp - stack_size] */ 15608c2ecf20Sopenharmony_ci EMIT4(0x48, 0x8D, 0x7D, -stack_size); 15618c2ecf20Sopenharmony_ci /* arg2: progs[i]->insnsi for interpreter */ 15628c2ecf20Sopenharmony_ci if (!p->jited) 15638c2ecf20Sopenharmony_ci emit_mov_imm64(&prog, BPF_REG_2, 15648c2ecf20Sopenharmony_ci (long) p->insnsi >> 32, 15658c2ecf20Sopenharmony_ci (u32) (long) p->insnsi); 15668c2ecf20Sopenharmony_ci /* call JITed bpf program or interpreter */ 15678c2ecf20Sopenharmony_ci if (emit_call(&prog, p->bpf_func, prog)) 15688c2ecf20Sopenharmony_ci return -EINVAL; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci /* 15718c2ecf20Sopenharmony_ci * BPF_TRAMP_MODIFY_RETURN trampolines can modify the return 15728c2ecf20Sopenharmony_ci * of the previous call which is then passed on the stack to 15738c2ecf20Sopenharmony_ci * the next BPF program. 15748c2ecf20Sopenharmony_ci * 15758c2ecf20Sopenharmony_ci * BPF_TRAMP_FENTRY trampoline may need to return the return 15768c2ecf20Sopenharmony_ci * value of BPF_PROG_TYPE_STRUCT_OPS prog. 15778c2ecf20Sopenharmony_ci */ 15788c2ecf20Sopenharmony_ci if (save_ret) 15798c2ecf20Sopenharmony_ci emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (p->aux->sleepable) { 15828c2ecf20Sopenharmony_ci if (emit_call(&prog, __bpf_prog_exit_sleepable, prog)) 15838c2ecf20Sopenharmony_ci return -EINVAL; 15848c2ecf20Sopenharmony_ci } else { 15858c2ecf20Sopenharmony_ci /* arg1: mov rdi, progs[i] */ 15868c2ecf20Sopenharmony_ci emit_mov_imm64(&prog, BPF_REG_1, (long) p >> 32, 15878c2ecf20Sopenharmony_ci (u32) (long) p); 15888c2ecf20Sopenharmony_ci /* arg2: mov rsi, rbx <- start time in nsec */ 15898c2ecf20Sopenharmony_ci emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6); 15908c2ecf20Sopenharmony_ci if (emit_call(&prog, __bpf_prog_exit, prog)) 15918c2ecf20Sopenharmony_ci return -EINVAL; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci *pprog = prog; 15958c2ecf20Sopenharmony_ci return 0; 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_cistatic void emit_nops(u8 **pprog, unsigned int len) 15998c2ecf20Sopenharmony_ci{ 16008c2ecf20Sopenharmony_ci unsigned int i, noplen; 16018c2ecf20Sopenharmony_ci u8 *prog = *pprog; 16028c2ecf20Sopenharmony_ci int cnt = 0; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci while (len > 0) { 16058c2ecf20Sopenharmony_ci noplen = len; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci if (noplen > ASM_NOP_MAX) 16088c2ecf20Sopenharmony_ci noplen = ASM_NOP_MAX; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci for (i = 0; i < noplen; i++) 16118c2ecf20Sopenharmony_ci EMIT1(ideal_nops[noplen][i]); 16128c2ecf20Sopenharmony_ci len -= noplen; 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci *pprog = prog; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_cistatic void emit_align(u8 **pprog, u32 align) 16198c2ecf20Sopenharmony_ci{ 16208c2ecf20Sopenharmony_ci u8 *target, *prog = *pprog; 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci target = PTR_ALIGN(prog, align); 16238c2ecf20Sopenharmony_ci if (target != prog) 16248c2ecf20Sopenharmony_ci emit_nops(&prog, target - prog); 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci *pprog = prog; 16278c2ecf20Sopenharmony_ci} 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_cistatic int emit_cond_near_jump(u8 **pprog, void *func, void *ip, u8 jmp_cond) 16308c2ecf20Sopenharmony_ci{ 16318c2ecf20Sopenharmony_ci u8 *prog = *pprog; 16328c2ecf20Sopenharmony_ci int cnt = 0; 16338c2ecf20Sopenharmony_ci s64 offset; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci offset = func - (ip + 2 + 4); 16368c2ecf20Sopenharmony_ci if (!is_simm32(offset)) { 16378c2ecf20Sopenharmony_ci pr_err("Target %p is out of range\n", func); 16388c2ecf20Sopenharmony_ci return -EINVAL; 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci EMIT2_off32(0x0F, jmp_cond + 0x10, offset); 16418c2ecf20Sopenharmony_ci *pprog = prog; 16428c2ecf20Sopenharmony_ci return 0; 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_cistatic int invoke_bpf(const struct btf_func_model *m, u8 **pprog, 16468c2ecf20Sopenharmony_ci struct bpf_tramp_progs *tp, int stack_size, 16478c2ecf20Sopenharmony_ci bool save_ret) 16488c2ecf20Sopenharmony_ci{ 16498c2ecf20Sopenharmony_ci int i; 16508c2ecf20Sopenharmony_ci u8 *prog = *pprog; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci for (i = 0; i < tp->nr_progs; i++) { 16538c2ecf20Sopenharmony_ci if (invoke_bpf_prog(m, &prog, tp->progs[i], stack_size, 16548c2ecf20Sopenharmony_ci save_ret)) 16558c2ecf20Sopenharmony_ci return -EINVAL; 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci *pprog = prog; 16588c2ecf20Sopenharmony_ci return 0; 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic int invoke_bpf_mod_ret(const struct btf_func_model *m, u8 **pprog, 16628c2ecf20Sopenharmony_ci struct bpf_tramp_progs *tp, int stack_size, 16638c2ecf20Sopenharmony_ci u8 **branches) 16648c2ecf20Sopenharmony_ci{ 16658c2ecf20Sopenharmony_ci u8 *prog = *pprog; 16668c2ecf20Sopenharmony_ci int i, cnt = 0; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci /* The first fmod_ret program will receive a garbage return value. 16698c2ecf20Sopenharmony_ci * Set this to 0 to avoid confusing the program. 16708c2ecf20Sopenharmony_ci */ 16718c2ecf20Sopenharmony_ci emit_mov_imm32(&prog, false, BPF_REG_0, 0); 16728c2ecf20Sopenharmony_ci emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8); 16738c2ecf20Sopenharmony_ci for (i = 0; i < tp->nr_progs; i++) { 16748c2ecf20Sopenharmony_ci if (invoke_bpf_prog(m, &prog, tp->progs[i], stack_size, true)) 16758c2ecf20Sopenharmony_ci return -EINVAL; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci /* mod_ret prog stored return value into [rbp - 8]. Emit: 16788c2ecf20Sopenharmony_ci * if (*(u64 *)(rbp - 8) != 0) 16798c2ecf20Sopenharmony_ci * goto do_fexit; 16808c2ecf20Sopenharmony_ci */ 16818c2ecf20Sopenharmony_ci /* cmp QWORD PTR [rbp - 0x8], 0x0 */ 16828c2ecf20Sopenharmony_ci EMIT4(0x48, 0x83, 0x7d, 0xf8); EMIT1(0x00); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci /* Save the location of the branch and Generate 6 nops 16858c2ecf20Sopenharmony_ci * (4 bytes for an offset and 2 bytes for the jump) These nops 16868c2ecf20Sopenharmony_ci * are replaced with a conditional jump once do_fexit (i.e. the 16878c2ecf20Sopenharmony_ci * start of the fexit invocation) is finalized. 16888c2ecf20Sopenharmony_ci */ 16898c2ecf20Sopenharmony_ci branches[i] = prog; 16908c2ecf20Sopenharmony_ci emit_nops(&prog, 4 + 2); 16918c2ecf20Sopenharmony_ci } 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci *pprog = prog; 16948c2ecf20Sopenharmony_ci return 0; 16958c2ecf20Sopenharmony_ci} 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_cistatic bool is_valid_bpf_tramp_flags(unsigned int flags) 16988c2ecf20Sopenharmony_ci{ 16998c2ecf20Sopenharmony_ci if ((flags & BPF_TRAMP_F_RESTORE_REGS) && 17008c2ecf20Sopenharmony_ci (flags & BPF_TRAMP_F_SKIP_FRAME)) 17018c2ecf20Sopenharmony_ci return false; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci /* 17048c2ecf20Sopenharmony_ci * BPF_TRAMP_F_RET_FENTRY_RET is only used by bpf_struct_ops, 17058c2ecf20Sopenharmony_ci * and it must be used alone. 17068c2ecf20Sopenharmony_ci */ 17078c2ecf20Sopenharmony_ci if ((flags & BPF_TRAMP_F_RET_FENTRY_RET) && 17088c2ecf20Sopenharmony_ci (flags & ~BPF_TRAMP_F_RET_FENTRY_RET)) 17098c2ecf20Sopenharmony_ci return false; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci return true; 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci/* Example: 17158c2ecf20Sopenharmony_ci * __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev); 17168c2ecf20Sopenharmony_ci * its 'struct btf_func_model' will be nr_args=2 17178c2ecf20Sopenharmony_ci * The assembly code when eth_type_trans is executing after trampoline: 17188c2ecf20Sopenharmony_ci * 17198c2ecf20Sopenharmony_ci * push rbp 17208c2ecf20Sopenharmony_ci * mov rbp, rsp 17218c2ecf20Sopenharmony_ci * sub rsp, 16 // space for skb and dev 17228c2ecf20Sopenharmony_ci * push rbx // temp regs to pass start time 17238c2ecf20Sopenharmony_ci * mov qword ptr [rbp - 16], rdi // save skb pointer to stack 17248c2ecf20Sopenharmony_ci * mov qword ptr [rbp - 8], rsi // save dev pointer to stack 17258c2ecf20Sopenharmony_ci * call __bpf_prog_enter // rcu_read_lock and preempt_disable 17268c2ecf20Sopenharmony_ci * mov rbx, rax // remember start time in bpf stats are enabled 17278c2ecf20Sopenharmony_ci * lea rdi, [rbp - 16] // R1==ctx of bpf prog 17288c2ecf20Sopenharmony_ci * call addr_of_jited_FENTRY_prog 17298c2ecf20Sopenharmony_ci * movabsq rdi, 64bit_addr_of_struct_bpf_prog // unused if bpf stats are off 17308c2ecf20Sopenharmony_ci * mov rsi, rbx // prog start time 17318c2ecf20Sopenharmony_ci * call __bpf_prog_exit // rcu_read_unlock, preempt_enable and stats math 17328c2ecf20Sopenharmony_ci * mov rdi, qword ptr [rbp - 16] // restore skb pointer from stack 17338c2ecf20Sopenharmony_ci * mov rsi, qword ptr [rbp - 8] // restore dev pointer from stack 17348c2ecf20Sopenharmony_ci * pop rbx 17358c2ecf20Sopenharmony_ci * leave 17368c2ecf20Sopenharmony_ci * ret 17378c2ecf20Sopenharmony_ci * 17388c2ecf20Sopenharmony_ci * eth_type_trans has 5 byte nop at the beginning. These 5 bytes will be 17398c2ecf20Sopenharmony_ci * replaced with 'call generated_bpf_trampoline'. When it returns 17408c2ecf20Sopenharmony_ci * eth_type_trans will continue executing with original skb and dev pointers. 17418c2ecf20Sopenharmony_ci * 17428c2ecf20Sopenharmony_ci * The assembly code when eth_type_trans is called from trampoline: 17438c2ecf20Sopenharmony_ci * 17448c2ecf20Sopenharmony_ci * push rbp 17458c2ecf20Sopenharmony_ci * mov rbp, rsp 17468c2ecf20Sopenharmony_ci * sub rsp, 24 // space for skb, dev, return value 17478c2ecf20Sopenharmony_ci * push rbx // temp regs to pass start time 17488c2ecf20Sopenharmony_ci * mov qword ptr [rbp - 24], rdi // save skb pointer to stack 17498c2ecf20Sopenharmony_ci * mov qword ptr [rbp - 16], rsi // save dev pointer to stack 17508c2ecf20Sopenharmony_ci * call __bpf_prog_enter // rcu_read_lock and preempt_disable 17518c2ecf20Sopenharmony_ci * mov rbx, rax // remember start time if bpf stats are enabled 17528c2ecf20Sopenharmony_ci * lea rdi, [rbp - 24] // R1==ctx of bpf prog 17538c2ecf20Sopenharmony_ci * call addr_of_jited_FENTRY_prog // bpf prog can access skb and dev 17548c2ecf20Sopenharmony_ci * movabsq rdi, 64bit_addr_of_struct_bpf_prog // unused if bpf stats are off 17558c2ecf20Sopenharmony_ci * mov rsi, rbx // prog start time 17568c2ecf20Sopenharmony_ci * call __bpf_prog_exit // rcu_read_unlock, preempt_enable and stats math 17578c2ecf20Sopenharmony_ci * mov rdi, qword ptr [rbp - 24] // restore skb pointer from stack 17588c2ecf20Sopenharmony_ci * mov rsi, qword ptr [rbp - 16] // restore dev pointer from stack 17598c2ecf20Sopenharmony_ci * call eth_type_trans+5 // execute body of eth_type_trans 17608c2ecf20Sopenharmony_ci * mov qword ptr [rbp - 8], rax // save return value 17618c2ecf20Sopenharmony_ci * call __bpf_prog_enter // rcu_read_lock and preempt_disable 17628c2ecf20Sopenharmony_ci * mov rbx, rax // remember start time in bpf stats are enabled 17638c2ecf20Sopenharmony_ci * lea rdi, [rbp - 24] // R1==ctx of bpf prog 17648c2ecf20Sopenharmony_ci * call addr_of_jited_FEXIT_prog // bpf prog can access skb, dev, return value 17658c2ecf20Sopenharmony_ci * movabsq rdi, 64bit_addr_of_struct_bpf_prog // unused if bpf stats are off 17668c2ecf20Sopenharmony_ci * mov rsi, rbx // prog start time 17678c2ecf20Sopenharmony_ci * call __bpf_prog_exit // rcu_read_unlock, preempt_enable and stats math 17688c2ecf20Sopenharmony_ci * mov rax, qword ptr [rbp - 8] // restore eth_type_trans's return value 17698c2ecf20Sopenharmony_ci * pop rbx 17708c2ecf20Sopenharmony_ci * leave 17718c2ecf20Sopenharmony_ci * add rsp, 8 // skip eth_type_trans's frame 17728c2ecf20Sopenharmony_ci * ret // return to its caller 17738c2ecf20Sopenharmony_ci */ 17748c2ecf20Sopenharmony_ciint arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *image_end, 17758c2ecf20Sopenharmony_ci const struct btf_func_model *m, u32 flags, 17768c2ecf20Sopenharmony_ci struct bpf_tramp_progs *tprogs, 17778c2ecf20Sopenharmony_ci void *orig_call) 17788c2ecf20Sopenharmony_ci{ 17798c2ecf20Sopenharmony_ci int ret, i, cnt = 0, nr_args = m->nr_args; 17808c2ecf20Sopenharmony_ci int stack_size = nr_args * 8; 17818c2ecf20Sopenharmony_ci struct bpf_tramp_progs *fentry = &tprogs[BPF_TRAMP_FENTRY]; 17828c2ecf20Sopenharmony_ci struct bpf_tramp_progs *fexit = &tprogs[BPF_TRAMP_FEXIT]; 17838c2ecf20Sopenharmony_ci struct bpf_tramp_progs *fmod_ret = &tprogs[BPF_TRAMP_MODIFY_RETURN]; 17848c2ecf20Sopenharmony_ci u8 **branches = NULL; 17858c2ecf20Sopenharmony_ci u8 *prog; 17868c2ecf20Sopenharmony_ci bool save_ret; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci /* x86-64 supports up to 6 arguments. 7+ can be added in the future */ 17898c2ecf20Sopenharmony_ci if (nr_args > 6) 17908c2ecf20Sopenharmony_ci return -ENOTSUPP; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci if (!is_valid_bpf_tramp_flags(flags)) 17938c2ecf20Sopenharmony_ci return -EINVAL; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci /* room for return value of orig_call or fentry prog */ 17968c2ecf20Sopenharmony_ci save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET); 17978c2ecf20Sopenharmony_ci if (save_ret) 17988c2ecf20Sopenharmony_ci stack_size += 8; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci if (flags & BPF_TRAMP_F_SKIP_FRAME) 18018c2ecf20Sopenharmony_ci /* skip patched call instruction and point orig_call to actual 18028c2ecf20Sopenharmony_ci * body of the kernel function. 18038c2ecf20Sopenharmony_ci */ 18048c2ecf20Sopenharmony_ci orig_call += X86_PATCH_SIZE; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci prog = image; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci EMIT1(0x55); /* push rbp */ 18098c2ecf20Sopenharmony_ci EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */ 18108c2ecf20Sopenharmony_ci EMIT4(0x48, 0x83, 0xEC, stack_size); /* sub rsp, stack_size */ 18118c2ecf20Sopenharmony_ci EMIT1(0x53); /* push rbx */ 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci save_regs(m, &prog, nr_args, stack_size); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (flags & BPF_TRAMP_F_CALL_ORIG) { 18168c2ecf20Sopenharmony_ci /* arg1: mov rdi, im */ 18178c2ecf20Sopenharmony_ci emit_mov_imm64(&prog, BPF_REG_1, (long) im >> 32, (u32) (long) im); 18188c2ecf20Sopenharmony_ci if (emit_call(&prog, __bpf_tramp_enter, prog)) { 18198c2ecf20Sopenharmony_ci ret = -EINVAL; 18208c2ecf20Sopenharmony_ci goto cleanup; 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci if (fentry->nr_progs) 18258c2ecf20Sopenharmony_ci if (invoke_bpf(m, &prog, fentry, stack_size, 18268c2ecf20Sopenharmony_ci flags & BPF_TRAMP_F_RET_FENTRY_RET)) 18278c2ecf20Sopenharmony_ci return -EINVAL; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci if (fmod_ret->nr_progs) { 18308c2ecf20Sopenharmony_ci branches = kcalloc(fmod_ret->nr_progs, sizeof(u8 *), 18318c2ecf20Sopenharmony_ci GFP_KERNEL); 18328c2ecf20Sopenharmony_ci if (!branches) 18338c2ecf20Sopenharmony_ci return -ENOMEM; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci if (invoke_bpf_mod_ret(m, &prog, fmod_ret, stack_size, 18368c2ecf20Sopenharmony_ci branches)) { 18378c2ecf20Sopenharmony_ci ret = -EINVAL; 18388c2ecf20Sopenharmony_ci goto cleanup; 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (flags & BPF_TRAMP_F_CALL_ORIG) { 18438c2ecf20Sopenharmony_ci restore_regs(m, &prog, nr_args, stack_size); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci /* call original function */ 18468c2ecf20Sopenharmony_ci if (emit_call(&prog, orig_call, prog)) { 18478c2ecf20Sopenharmony_ci ret = -EINVAL; 18488c2ecf20Sopenharmony_ci goto cleanup; 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci /* remember return value in a stack for bpf prog to access */ 18518c2ecf20Sopenharmony_ci emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8); 18528c2ecf20Sopenharmony_ci im->ip_after_call = prog; 18538c2ecf20Sopenharmony_ci memcpy(prog, ideal_nops[NOP_ATOMIC5], X86_PATCH_SIZE); 18548c2ecf20Sopenharmony_ci prog += X86_PATCH_SIZE; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci if (fmod_ret->nr_progs) { 18588c2ecf20Sopenharmony_ci /* From Intel 64 and IA-32 Architectures Optimization 18598c2ecf20Sopenharmony_ci * Reference Manual, 3.4.1.4 Code Alignment, Assembly/Compiler 18608c2ecf20Sopenharmony_ci * Coding Rule 11: All branch targets should be 16-byte 18618c2ecf20Sopenharmony_ci * aligned. 18628c2ecf20Sopenharmony_ci */ 18638c2ecf20Sopenharmony_ci emit_align(&prog, 16); 18648c2ecf20Sopenharmony_ci /* Update the branches saved in invoke_bpf_mod_ret with the 18658c2ecf20Sopenharmony_ci * aligned address of do_fexit. 18668c2ecf20Sopenharmony_ci */ 18678c2ecf20Sopenharmony_ci for (i = 0; i < fmod_ret->nr_progs; i++) 18688c2ecf20Sopenharmony_ci emit_cond_near_jump(&branches[i], prog, branches[i], 18698c2ecf20Sopenharmony_ci X86_JNE); 18708c2ecf20Sopenharmony_ci } 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci if (fexit->nr_progs) 18738c2ecf20Sopenharmony_ci if (invoke_bpf(m, &prog, fexit, stack_size, false)) { 18748c2ecf20Sopenharmony_ci ret = -EINVAL; 18758c2ecf20Sopenharmony_ci goto cleanup; 18768c2ecf20Sopenharmony_ci } 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci if (flags & BPF_TRAMP_F_RESTORE_REGS) 18798c2ecf20Sopenharmony_ci restore_regs(m, &prog, nr_args, stack_size); 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci /* This needs to be done regardless. If there were fmod_ret programs, 18828c2ecf20Sopenharmony_ci * the return value is only updated on the stack and still needs to be 18838c2ecf20Sopenharmony_ci * restored to R0. 18848c2ecf20Sopenharmony_ci */ 18858c2ecf20Sopenharmony_ci if (flags & BPF_TRAMP_F_CALL_ORIG) { 18868c2ecf20Sopenharmony_ci im->ip_epilogue = prog; 18878c2ecf20Sopenharmony_ci /* arg1: mov rdi, im */ 18888c2ecf20Sopenharmony_ci emit_mov_imm64(&prog, BPF_REG_1, (long) im >> 32, (u32) (long) im); 18898c2ecf20Sopenharmony_ci if (emit_call(&prog, __bpf_tramp_exit, prog)) { 18908c2ecf20Sopenharmony_ci ret = -EINVAL; 18918c2ecf20Sopenharmony_ci goto cleanup; 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci } 18948c2ecf20Sopenharmony_ci /* restore return value of orig_call or fentry prog back into RAX */ 18958c2ecf20Sopenharmony_ci if (save_ret) 18968c2ecf20Sopenharmony_ci emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci EMIT1(0x5B); /* pop rbx */ 18998c2ecf20Sopenharmony_ci EMIT1(0xC9); /* leave */ 19008c2ecf20Sopenharmony_ci if (flags & BPF_TRAMP_F_SKIP_FRAME) 19018c2ecf20Sopenharmony_ci /* skip our return address and return to parent */ 19028c2ecf20Sopenharmony_ci EMIT4(0x48, 0x83, 0xC4, 8); /* add rsp, 8 */ 19038c2ecf20Sopenharmony_ci emit_return(&prog, prog); 19048c2ecf20Sopenharmony_ci /* Make sure the trampoline generation logic doesn't overflow */ 19058c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(prog > (u8 *)image_end - BPF_INSN_SAFETY)) { 19068c2ecf20Sopenharmony_ci ret = -EFAULT; 19078c2ecf20Sopenharmony_ci goto cleanup; 19088c2ecf20Sopenharmony_ci } 19098c2ecf20Sopenharmony_ci ret = prog - (u8 *)image; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_cicleanup: 19128c2ecf20Sopenharmony_ci kfree(branches); 19138c2ecf20Sopenharmony_ci return ret; 19148c2ecf20Sopenharmony_ci} 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_cistatic int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs) 19178c2ecf20Sopenharmony_ci{ 19188c2ecf20Sopenharmony_ci u8 *jg_reloc, *prog = *pprog; 19198c2ecf20Sopenharmony_ci int pivot, err, jg_bytes = 1, cnt = 0; 19208c2ecf20Sopenharmony_ci s64 jg_offset; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci if (a == b) { 19238c2ecf20Sopenharmony_ci /* Leaf node of recursion, i.e. not a range of indices 19248c2ecf20Sopenharmony_ci * anymore. 19258c2ecf20Sopenharmony_ci */ 19268c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x48, BPF_REG_3)); /* cmp rdx,func */ 19278c2ecf20Sopenharmony_ci if (!is_simm32(progs[a])) 19288c2ecf20Sopenharmony_ci return -1; 19298c2ecf20Sopenharmony_ci EMIT2_off32(0x81, add_1reg(0xF8, BPF_REG_3), 19308c2ecf20Sopenharmony_ci progs[a]); 19318c2ecf20Sopenharmony_ci err = emit_cond_near_jump(&prog, /* je func */ 19328c2ecf20Sopenharmony_ci (void *)progs[a], prog, 19338c2ecf20Sopenharmony_ci X86_JE); 19348c2ecf20Sopenharmony_ci if (err) 19358c2ecf20Sopenharmony_ci return err; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci emit_indirect_jump(&prog, 2 /* rdx */, prog); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci *pprog = prog; 19408c2ecf20Sopenharmony_ci return 0; 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci /* Not a leaf node, so we pivot, and recursively descend into 19448c2ecf20Sopenharmony_ci * the lower and upper ranges. 19458c2ecf20Sopenharmony_ci */ 19468c2ecf20Sopenharmony_ci pivot = (b - a) / 2; 19478c2ecf20Sopenharmony_ci EMIT1(add_1mod(0x48, BPF_REG_3)); /* cmp rdx,func */ 19488c2ecf20Sopenharmony_ci if (!is_simm32(progs[a + pivot])) 19498c2ecf20Sopenharmony_ci return -1; 19508c2ecf20Sopenharmony_ci EMIT2_off32(0x81, add_1reg(0xF8, BPF_REG_3), progs[a + pivot]); 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci if (pivot > 2) { /* jg upper_part */ 19538c2ecf20Sopenharmony_ci /* Require near jump. */ 19548c2ecf20Sopenharmony_ci jg_bytes = 4; 19558c2ecf20Sopenharmony_ci EMIT2_off32(0x0F, X86_JG + 0x10, 0); 19568c2ecf20Sopenharmony_ci } else { 19578c2ecf20Sopenharmony_ci EMIT2(X86_JG, 0); 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci jg_reloc = prog; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci err = emit_bpf_dispatcher(&prog, a, a + pivot, /* emit lower_part */ 19628c2ecf20Sopenharmony_ci progs); 19638c2ecf20Sopenharmony_ci if (err) 19648c2ecf20Sopenharmony_ci return err; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci /* From Intel 64 and IA-32 Architectures Optimization 19678c2ecf20Sopenharmony_ci * Reference Manual, 3.4.1.4 Code Alignment, Assembly/Compiler 19688c2ecf20Sopenharmony_ci * Coding Rule 11: All branch targets should be 16-byte 19698c2ecf20Sopenharmony_ci * aligned. 19708c2ecf20Sopenharmony_ci */ 19718c2ecf20Sopenharmony_ci emit_align(&prog, 16); 19728c2ecf20Sopenharmony_ci jg_offset = prog - jg_reloc; 19738c2ecf20Sopenharmony_ci emit_code(jg_reloc - jg_bytes, jg_offset, jg_bytes); 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci err = emit_bpf_dispatcher(&prog, a + pivot + 1, /* emit upper_part */ 19768c2ecf20Sopenharmony_ci b, progs); 19778c2ecf20Sopenharmony_ci if (err) 19788c2ecf20Sopenharmony_ci return err; 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci *pprog = prog; 19818c2ecf20Sopenharmony_ci return 0; 19828c2ecf20Sopenharmony_ci} 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_cistatic int cmp_ips(const void *a, const void *b) 19858c2ecf20Sopenharmony_ci{ 19868c2ecf20Sopenharmony_ci const s64 *ipa = a; 19878c2ecf20Sopenharmony_ci const s64 *ipb = b; 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci if (*ipa > *ipb) 19908c2ecf20Sopenharmony_ci return 1; 19918c2ecf20Sopenharmony_ci if (*ipa < *ipb) 19928c2ecf20Sopenharmony_ci return -1; 19938c2ecf20Sopenharmony_ci return 0; 19948c2ecf20Sopenharmony_ci} 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ciint arch_prepare_bpf_dispatcher(void *image, s64 *funcs, int num_funcs) 19978c2ecf20Sopenharmony_ci{ 19988c2ecf20Sopenharmony_ci u8 *prog = image; 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci sort(funcs, num_funcs, sizeof(funcs[0]), cmp_ips, NULL); 20018c2ecf20Sopenharmony_ci return emit_bpf_dispatcher(&prog, 0, num_funcs - 1, funcs); 20028c2ecf20Sopenharmony_ci} 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_cistruct x64_jit_data { 20058c2ecf20Sopenharmony_ci struct bpf_binary_header *header; 20068c2ecf20Sopenharmony_ci int *addrs; 20078c2ecf20Sopenharmony_ci u8 *image; 20088c2ecf20Sopenharmony_ci int proglen; 20098c2ecf20Sopenharmony_ci struct jit_context ctx; 20108c2ecf20Sopenharmony_ci}; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_cistruct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 20138c2ecf20Sopenharmony_ci{ 20148c2ecf20Sopenharmony_ci struct bpf_binary_header *header = NULL; 20158c2ecf20Sopenharmony_ci struct bpf_prog *tmp, *orig_prog = prog; 20168c2ecf20Sopenharmony_ci struct x64_jit_data *jit_data; 20178c2ecf20Sopenharmony_ci int proglen, oldproglen = 0; 20188c2ecf20Sopenharmony_ci struct jit_context ctx = {}; 20198c2ecf20Sopenharmony_ci bool tmp_blinded = false; 20208c2ecf20Sopenharmony_ci bool extra_pass = false; 20218c2ecf20Sopenharmony_ci u8 *image = NULL; 20228c2ecf20Sopenharmony_ci int *addrs; 20238c2ecf20Sopenharmony_ci int pass; 20248c2ecf20Sopenharmony_ci int i; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci if (!prog->jit_requested) 20278c2ecf20Sopenharmony_ci return orig_prog; 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci tmp = bpf_jit_blind_constants(prog); 20308c2ecf20Sopenharmony_ci /* 20318c2ecf20Sopenharmony_ci * If blinding was requested and we failed during blinding, 20328c2ecf20Sopenharmony_ci * we must fall back to the interpreter. 20338c2ecf20Sopenharmony_ci */ 20348c2ecf20Sopenharmony_ci if (IS_ERR(tmp)) 20358c2ecf20Sopenharmony_ci return orig_prog; 20368c2ecf20Sopenharmony_ci if (tmp != prog) { 20378c2ecf20Sopenharmony_ci tmp_blinded = true; 20388c2ecf20Sopenharmony_ci prog = tmp; 20398c2ecf20Sopenharmony_ci } 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci jit_data = prog->aux->jit_data; 20428c2ecf20Sopenharmony_ci if (!jit_data) { 20438c2ecf20Sopenharmony_ci jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 20448c2ecf20Sopenharmony_ci if (!jit_data) { 20458c2ecf20Sopenharmony_ci prog = orig_prog; 20468c2ecf20Sopenharmony_ci goto out; 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci prog->aux->jit_data = jit_data; 20498c2ecf20Sopenharmony_ci } 20508c2ecf20Sopenharmony_ci addrs = jit_data->addrs; 20518c2ecf20Sopenharmony_ci if (addrs) { 20528c2ecf20Sopenharmony_ci ctx = jit_data->ctx; 20538c2ecf20Sopenharmony_ci oldproglen = jit_data->proglen; 20548c2ecf20Sopenharmony_ci image = jit_data->image; 20558c2ecf20Sopenharmony_ci header = jit_data->header; 20568c2ecf20Sopenharmony_ci extra_pass = true; 20578c2ecf20Sopenharmony_ci goto skip_init_addrs; 20588c2ecf20Sopenharmony_ci } 20598c2ecf20Sopenharmony_ci addrs = kvmalloc_array(prog->len + 1, sizeof(*addrs), GFP_KERNEL); 20608c2ecf20Sopenharmony_ci if (!addrs) { 20618c2ecf20Sopenharmony_ci prog = orig_prog; 20628c2ecf20Sopenharmony_ci goto out_addrs; 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci /* 20668c2ecf20Sopenharmony_ci * Before first pass, make a rough estimation of addrs[] 20678c2ecf20Sopenharmony_ci * each BPF instruction is translated to less than 64 bytes 20688c2ecf20Sopenharmony_ci */ 20698c2ecf20Sopenharmony_ci for (proglen = 0, i = 0; i <= prog->len; i++) { 20708c2ecf20Sopenharmony_ci proglen += 64; 20718c2ecf20Sopenharmony_ci addrs[i] = proglen; 20728c2ecf20Sopenharmony_ci } 20738c2ecf20Sopenharmony_ci ctx.cleanup_addr = proglen; 20748c2ecf20Sopenharmony_ciskip_init_addrs: 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci /* 20778c2ecf20Sopenharmony_ci * JITed image shrinks with every pass and the loop iterates 20788c2ecf20Sopenharmony_ci * until the image stops shrinking. Very large BPF programs 20798c2ecf20Sopenharmony_ci * may converge on the last pass. In such case do one more 20808c2ecf20Sopenharmony_ci * pass to emit the final image. 20818c2ecf20Sopenharmony_ci */ 20828c2ecf20Sopenharmony_ci for (pass = 0; pass < 20 || image; pass++) { 20838c2ecf20Sopenharmony_ci proglen = do_jit(prog, addrs, image, oldproglen, &ctx); 20848c2ecf20Sopenharmony_ci if (proglen <= 0) { 20858c2ecf20Sopenharmony_ciout_image: 20868c2ecf20Sopenharmony_ci image = NULL; 20878c2ecf20Sopenharmony_ci if (header) 20888c2ecf20Sopenharmony_ci bpf_jit_binary_free(header); 20898c2ecf20Sopenharmony_ci prog = orig_prog; 20908c2ecf20Sopenharmony_ci goto out_addrs; 20918c2ecf20Sopenharmony_ci } 20928c2ecf20Sopenharmony_ci if (image) { 20938c2ecf20Sopenharmony_ci if (proglen != oldproglen) { 20948c2ecf20Sopenharmony_ci pr_err("bpf_jit: proglen=%d != oldproglen=%d\n", 20958c2ecf20Sopenharmony_ci proglen, oldproglen); 20968c2ecf20Sopenharmony_ci goto out_image; 20978c2ecf20Sopenharmony_ci } 20988c2ecf20Sopenharmony_ci break; 20998c2ecf20Sopenharmony_ci } 21008c2ecf20Sopenharmony_ci if (proglen == oldproglen) { 21018c2ecf20Sopenharmony_ci /* 21028c2ecf20Sopenharmony_ci * The number of entries in extable is the number of BPF_LDX 21038c2ecf20Sopenharmony_ci * insns that access kernel memory via "pointer to BTF type". 21048c2ecf20Sopenharmony_ci * The verifier changed their opcode from LDX|MEM|size 21058c2ecf20Sopenharmony_ci * to LDX|PROBE_MEM|size to make JITing easier. 21068c2ecf20Sopenharmony_ci */ 21078c2ecf20Sopenharmony_ci u32 align = __alignof__(struct exception_table_entry); 21088c2ecf20Sopenharmony_ci u32 extable_size = prog->aux->num_exentries * 21098c2ecf20Sopenharmony_ci sizeof(struct exception_table_entry); 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci /* allocate module memory for x86 insns and extable */ 21128c2ecf20Sopenharmony_ci header = bpf_jit_binary_alloc(roundup(proglen, align) + extable_size, 21138c2ecf20Sopenharmony_ci &image, align, jit_fill_hole); 21148c2ecf20Sopenharmony_ci if (!header) { 21158c2ecf20Sopenharmony_ci prog = orig_prog; 21168c2ecf20Sopenharmony_ci goto out_addrs; 21178c2ecf20Sopenharmony_ci } 21188c2ecf20Sopenharmony_ci prog->aux->extable = (void *) image + roundup(proglen, align); 21198c2ecf20Sopenharmony_ci } 21208c2ecf20Sopenharmony_ci oldproglen = proglen; 21218c2ecf20Sopenharmony_ci cond_resched(); 21228c2ecf20Sopenharmony_ci } 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci if (bpf_jit_enable > 1) 21258c2ecf20Sopenharmony_ci bpf_jit_dump(prog->len, proglen, pass + 1, image); 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci if (image) { 21288c2ecf20Sopenharmony_ci if (!prog->is_func || extra_pass) { 21298c2ecf20Sopenharmony_ci bpf_tail_call_direct_fixup(prog); 21308c2ecf20Sopenharmony_ci bpf_jit_binary_lock_ro(header); 21318c2ecf20Sopenharmony_ci } else { 21328c2ecf20Sopenharmony_ci jit_data->addrs = addrs; 21338c2ecf20Sopenharmony_ci jit_data->ctx = ctx; 21348c2ecf20Sopenharmony_ci jit_data->proglen = proglen; 21358c2ecf20Sopenharmony_ci jit_data->image = image; 21368c2ecf20Sopenharmony_ci jit_data->header = header; 21378c2ecf20Sopenharmony_ci } 21388c2ecf20Sopenharmony_ci prog->bpf_func = (void *)image; 21398c2ecf20Sopenharmony_ci prog->jited = 1; 21408c2ecf20Sopenharmony_ci prog->jited_len = proglen; 21418c2ecf20Sopenharmony_ci } else { 21428c2ecf20Sopenharmony_ci prog = orig_prog; 21438c2ecf20Sopenharmony_ci } 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci if (!image || !prog->is_func || extra_pass) { 21468c2ecf20Sopenharmony_ci if (image) 21478c2ecf20Sopenharmony_ci bpf_prog_fill_jited_linfo(prog, addrs + 1); 21488c2ecf20Sopenharmony_ciout_addrs: 21498c2ecf20Sopenharmony_ci kvfree(addrs); 21508c2ecf20Sopenharmony_ci kfree(jit_data); 21518c2ecf20Sopenharmony_ci prog->aux->jit_data = NULL; 21528c2ecf20Sopenharmony_ci } 21538c2ecf20Sopenharmony_ciout: 21548c2ecf20Sopenharmony_ci if (tmp_blinded) 21558c2ecf20Sopenharmony_ci bpf_jit_prog_release_other(prog, prog == orig_prog ? 21568c2ecf20Sopenharmony_ci tmp : orig_prog); 21578c2ecf20Sopenharmony_ci return prog; 21588c2ecf20Sopenharmony_ci} 2159