162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Just-In-Time compiler for eBPF filters on IA32 (32bit x86) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Wang YanQing (udknight@gmail.com) 662306a36Sopenharmony_ci * The code based on code and ideas from: 762306a36Sopenharmony_ci * Eric Dumazet (eric.dumazet@gmail.com) 862306a36Sopenharmony_ci * and from: 962306a36Sopenharmony_ci * Shubham Bansal <illusionist.neo@gmail.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/filter.h> 1462306a36Sopenharmony_ci#include <linux/if_vlan.h> 1562306a36Sopenharmony_ci#include <asm/cacheflush.h> 1662306a36Sopenharmony_ci#include <asm/set_memory.h> 1762306a36Sopenharmony_ci#include <asm/nospec-branch.h> 1862306a36Sopenharmony_ci#include <asm/asm-prototypes.h> 1962306a36Sopenharmony_ci#include <linux/bpf.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * eBPF prog stack layout: 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * high 2562306a36Sopenharmony_ci * original ESP => +-----+ 2662306a36Sopenharmony_ci * | | callee saved registers 2762306a36Sopenharmony_ci * +-----+ 2862306a36Sopenharmony_ci * | ... | eBPF JIT scratch space 2962306a36Sopenharmony_ci * BPF_FP,IA32_EBP => +-----+ 3062306a36Sopenharmony_ci * | ... | eBPF prog stack 3162306a36Sopenharmony_ci * +-----+ 3262306a36Sopenharmony_ci * |RSVD | JIT scratchpad 3362306a36Sopenharmony_ci * current ESP => +-----+ 3462306a36Sopenharmony_ci * | | 3562306a36Sopenharmony_ci * | ... | Function call stack 3662306a36Sopenharmony_ci * | | 3762306a36Sopenharmony_ci * +-----+ 3862306a36Sopenharmony_ci * low 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * The callee saved registers: 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * high 4362306a36Sopenharmony_ci * original ESP => +------------------+ \ 4462306a36Sopenharmony_ci * | ebp | | 4562306a36Sopenharmony_ci * current EBP => +------------------+ } callee saved registers 4662306a36Sopenharmony_ci * | ebx,esi,edi | | 4762306a36Sopenharmony_ci * +------------------+ / 4862306a36Sopenharmony_ci * low 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci if (len == 1) 5462306a36Sopenharmony_ci *ptr = bytes; 5562306a36Sopenharmony_ci else if (len == 2) 5662306a36Sopenharmony_ci *(u16 *)ptr = bytes; 5762306a36Sopenharmony_ci else { 5862306a36Sopenharmony_ci *(u32 *)ptr = bytes; 5962306a36Sopenharmony_ci barrier(); 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci return ptr + len; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define EMIT(bytes, len) \ 6562306a36Sopenharmony_ci do { prog = emit_code(prog, bytes, len); cnt += len; } while (0) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define EMIT1(b1) EMIT(b1, 1) 6862306a36Sopenharmony_ci#define EMIT2(b1, b2) EMIT((b1) + ((b2) << 8), 2) 6962306a36Sopenharmony_ci#define EMIT3(b1, b2, b3) EMIT((b1) + ((b2) << 8) + ((b3) << 16), 3) 7062306a36Sopenharmony_ci#define EMIT4(b1, b2, b3, b4) \ 7162306a36Sopenharmony_ci EMIT((b1) + ((b2) << 8) + ((b3) << 16) + ((b4) << 24), 4) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define EMIT1_off32(b1, off) \ 7462306a36Sopenharmony_ci do { EMIT1(b1); EMIT(off, 4); } while (0) 7562306a36Sopenharmony_ci#define EMIT2_off32(b1, b2, off) \ 7662306a36Sopenharmony_ci do { EMIT2(b1, b2); EMIT(off, 4); } while (0) 7762306a36Sopenharmony_ci#define EMIT3_off32(b1, b2, b3, off) \ 7862306a36Sopenharmony_ci do { EMIT3(b1, b2, b3); EMIT(off, 4); } while (0) 7962306a36Sopenharmony_ci#define EMIT4_off32(b1, b2, b3, b4, off) \ 8062306a36Sopenharmony_ci do { EMIT4(b1, b2, b3, b4); EMIT(off, 4); } while (0) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define jmp_label(label, jmp_insn_len) (label - cnt - jmp_insn_len) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic bool is_imm8(int value) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci return value <= 127 && value >= -128; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic bool is_simm32(s64 value) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return value == (s64) (s32) value; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define STACK_OFFSET(k) (k) 9562306a36Sopenharmony_ci#define TCALL_CNT (MAX_BPF_JIT_REG + 0) /* Tail Call Count */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define IA32_EAX (0x0) 9862306a36Sopenharmony_ci#define IA32_EBX (0x3) 9962306a36Sopenharmony_ci#define IA32_ECX (0x1) 10062306a36Sopenharmony_ci#define IA32_EDX (0x2) 10162306a36Sopenharmony_ci#define IA32_ESI (0x6) 10262306a36Sopenharmony_ci#define IA32_EDI (0x7) 10362306a36Sopenharmony_ci#define IA32_EBP (0x5) 10462306a36Sopenharmony_ci#define IA32_ESP (0x4) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * List of x86 cond jumps opcodes (. + s8) 10862306a36Sopenharmony_ci * Add 0x10 (and an extra 0x0f) to generate far jumps (. + s32) 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci#define IA32_JB 0x72 11162306a36Sopenharmony_ci#define IA32_JAE 0x73 11262306a36Sopenharmony_ci#define IA32_JE 0x74 11362306a36Sopenharmony_ci#define IA32_JNE 0x75 11462306a36Sopenharmony_ci#define IA32_JBE 0x76 11562306a36Sopenharmony_ci#define IA32_JA 0x77 11662306a36Sopenharmony_ci#define IA32_JL 0x7C 11762306a36Sopenharmony_ci#define IA32_JGE 0x7D 11862306a36Sopenharmony_ci#define IA32_JLE 0x7E 11962306a36Sopenharmony_ci#define IA32_JG 0x7F 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define COND_JMP_OPCODE_INVALID (0xFF) 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * Map eBPF registers to IA32 32bit registers or stack scratch space. 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * 1. All the registers, R0-R10, are mapped to scratch space on stack. 12762306a36Sopenharmony_ci * 2. We need two 64 bit temp registers to do complex operations on eBPF 12862306a36Sopenharmony_ci * registers. 12962306a36Sopenharmony_ci * 3. For performance reason, the BPF_REG_AX for blinding constant, is 13062306a36Sopenharmony_ci * mapped to real hardware register pair, IA32_ESI and IA32_EDI. 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * As the eBPF registers are all 64 bit registers and IA32 has only 32 bit 13362306a36Sopenharmony_ci * registers, we have to map each eBPF registers with two IA32 32 bit regs 13462306a36Sopenharmony_ci * or scratch memory space and we have to build eBPF 64 bit register from those. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * We use IA32_EAX, IA32_EDX, IA32_ECX, IA32_EBX as temporary registers. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic const u8 bpf2ia32[][2] = { 13962306a36Sopenharmony_ci /* Return value from in-kernel function, and exit value from eBPF */ 14062306a36Sopenharmony_ci [BPF_REG_0] = {STACK_OFFSET(0), STACK_OFFSET(4)}, 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* The arguments from eBPF program to in-kernel function */ 14362306a36Sopenharmony_ci /* Stored on stack scratch space */ 14462306a36Sopenharmony_ci [BPF_REG_1] = {STACK_OFFSET(8), STACK_OFFSET(12)}, 14562306a36Sopenharmony_ci [BPF_REG_2] = {STACK_OFFSET(16), STACK_OFFSET(20)}, 14662306a36Sopenharmony_ci [BPF_REG_3] = {STACK_OFFSET(24), STACK_OFFSET(28)}, 14762306a36Sopenharmony_ci [BPF_REG_4] = {STACK_OFFSET(32), STACK_OFFSET(36)}, 14862306a36Sopenharmony_ci [BPF_REG_5] = {STACK_OFFSET(40), STACK_OFFSET(44)}, 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Callee saved registers that in-kernel function will preserve */ 15162306a36Sopenharmony_ci /* Stored on stack scratch space */ 15262306a36Sopenharmony_ci [BPF_REG_6] = {STACK_OFFSET(48), STACK_OFFSET(52)}, 15362306a36Sopenharmony_ci [BPF_REG_7] = {STACK_OFFSET(56), STACK_OFFSET(60)}, 15462306a36Sopenharmony_ci [BPF_REG_8] = {STACK_OFFSET(64), STACK_OFFSET(68)}, 15562306a36Sopenharmony_ci [BPF_REG_9] = {STACK_OFFSET(72), STACK_OFFSET(76)}, 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Read only Frame Pointer to access Stack */ 15862306a36Sopenharmony_ci [BPF_REG_FP] = {STACK_OFFSET(80), STACK_OFFSET(84)}, 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Temporary register for blinding constants. */ 16162306a36Sopenharmony_ci [BPF_REG_AX] = {IA32_ESI, IA32_EDI}, 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Tail call count. Stored on stack scratch space. */ 16462306a36Sopenharmony_ci [TCALL_CNT] = {STACK_OFFSET(88), STACK_OFFSET(92)}, 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define dst_lo dst[0] 16862306a36Sopenharmony_ci#define dst_hi dst[1] 16962306a36Sopenharmony_ci#define src_lo src[0] 17062306a36Sopenharmony_ci#define src_hi src[1] 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#define STACK_ALIGNMENT 8 17362306a36Sopenharmony_ci/* 17462306a36Sopenharmony_ci * Stack space for BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, 17562306a36Sopenharmony_ci * BPF_REG_5, BPF_REG_6, BPF_REG_7, BPF_REG_8, BPF_REG_9, 17662306a36Sopenharmony_ci * BPF_REG_FP, BPF_REG_AX and Tail call counts. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci#define SCRATCH_SIZE 96 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* Total stack size used in JITed code */ 18162306a36Sopenharmony_ci#define _STACK_SIZE (stack_depth + SCRATCH_SIZE) 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci#define STACK_SIZE ALIGN(_STACK_SIZE, STACK_ALIGNMENT) 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* Get the offset of eBPF REGISTERs stored on scratch space. */ 18662306a36Sopenharmony_ci#define STACK_VAR(off) (off) 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* Encode 'dst_reg' register into IA32 opcode 'byte' */ 18962306a36Sopenharmony_cistatic u8 add_1reg(u8 byte, u32 dst_reg) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci return byte + dst_reg; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* Encode 'dst_reg' and 'src_reg' registers into IA32 opcode 'byte' */ 19562306a36Sopenharmony_cistatic u8 add_2reg(u8 byte, u32 dst_reg, u32 src_reg) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci return byte + dst_reg + (src_reg << 3); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void jit_fill_hole(void *area, unsigned int size) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci /* Fill whole space with int3 instructions */ 20362306a36Sopenharmony_ci memset(area, 0xcc, size); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic inline void emit_ia32_mov_i(const u8 dst, const u32 val, bool dstk, 20762306a36Sopenharmony_ci u8 **pprog) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci u8 *prog = *pprog; 21062306a36Sopenharmony_ci int cnt = 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (dstk) { 21362306a36Sopenharmony_ci if (val == 0) { 21462306a36Sopenharmony_ci /* xor eax,eax */ 21562306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, IA32_EAX, IA32_EAX)); 21662306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 21762306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EAX), 21862306a36Sopenharmony_ci STACK_VAR(dst)); 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci EMIT3_off32(0xC7, add_1reg(0x40, IA32_EBP), 22162306a36Sopenharmony_ci STACK_VAR(dst), val); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci if (val == 0) 22562306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dst, dst)); 22662306a36Sopenharmony_ci else 22762306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, dst), 22862306a36Sopenharmony_ci val); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci *pprog = prog; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* dst = imm (4 bytes)*/ 23462306a36Sopenharmony_cistatic inline void emit_ia32_mov_r(const u8 dst, const u8 src, bool dstk, 23562306a36Sopenharmony_ci bool sstk, u8 **pprog) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci u8 *prog = *pprog; 23862306a36Sopenharmony_ci int cnt = 0; 23962306a36Sopenharmony_ci u8 sreg = sstk ? IA32_EAX : src; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (sstk) 24262306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 24362306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), STACK_VAR(src)); 24462306a36Sopenharmony_ci if (dstk) 24562306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 24662306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, sreg), STACK_VAR(dst)); 24762306a36Sopenharmony_ci else 24862306a36Sopenharmony_ci /* mov dst,sreg */ 24962306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst, sreg)); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci *pprog = prog; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* dst = src */ 25562306a36Sopenharmony_cistatic inline void emit_ia32_mov_r64(const bool is64, const u8 dst[], 25662306a36Sopenharmony_ci const u8 src[], bool dstk, 25762306a36Sopenharmony_ci bool sstk, u8 **pprog, 25862306a36Sopenharmony_ci const struct bpf_prog_aux *aux) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci emit_ia32_mov_r(dst_lo, src_lo, dstk, sstk, pprog); 26162306a36Sopenharmony_ci if (is64) 26262306a36Sopenharmony_ci /* complete 8 byte move */ 26362306a36Sopenharmony_ci emit_ia32_mov_r(dst_hi, src_hi, dstk, sstk, pprog); 26462306a36Sopenharmony_ci else if (!aux->verifier_zext) 26562306a36Sopenharmony_ci /* zero out high 4 bytes */ 26662306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, 0, dstk, pprog); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* Sign extended move */ 27062306a36Sopenharmony_cistatic inline void emit_ia32_mov_i64(const bool is64, const u8 dst[], 27162306a36Sopenharmony_ci const u32 val, bool dstk, u8 **pprog) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci u32 hi = 0; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (is64 && (val & (1<<31))) 27662306a36Sopenharmony_ci hi = (u32)~0; 27762306a36Sopenharmony_ci emit_ia32_mov_i(dst_lo, val, dstk, pprog); 27862306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, hi, dstk, pprog); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* 28262306a36Sopenharmony_ci * ALU operation (32 bit) 28362306a36Sopenharmony_ci * dst = dst * src 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistatic inline void emit_ia32_mul_r(const u8 dst, const u8 src, bool dstk, 28662306a36Sopenharmony_ci bool sstk, u8 **pprog) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci u8 *prog = *pprog; 28962306a36Sopenharmony_ci int cnt = 0; 29062306a36Sopenharmony_ci u8 sreg = sstk ? IA32_ECX : src; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (sstk) 29362306a36Sopenharmony_ci /* mov ecx,dword ptr [ebp+off] */ 29462306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), STACK_VAR(src)); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (dstk) 29762306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 29862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), STACK_VAR(dst)); 29962306a36Sopenharmony_ci else 30062306a36Sopenharmony_ci /* mov eax,dst */ 30162306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, dst, IA32_EAX)); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xE0, sreg)); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (dstk) 30762306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 30862306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EAX), 30962306a36Sopenharmony_ci STACK_VAR(dst)); 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci /* mov dst,eax */ 31262306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst, IA32_EAX)); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci *pprog = prog; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic inline void emit_ia32_to_le_r64(const u8 dst[], s32 val, 31862306a36Sopenharmony_ci bool dstk, u8 **pprog, 31962306a36Sopenharmony_ci const struct bpf_prog_aux *aux) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci u8 *prog = *pprog; 32262306a36Sopenharmony_ci int cnt = 0; 32362306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 32462306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (dstk && val != 64) { 32762306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 32862306a36Sopenharmony_ci STACK_VAR(dst_lo)); 32962306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 33062306a36Sopenharmony_ci STACK_VAR(dst_hi)); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci switch (val) { 33362306a36Sopenharmony_ci case 16: 33462306a36Sopenharmony_ci /* 33562306a36Sopenharmony_ci * Emit 'movzwl eax,ax' to zero extend 16-bit 33662306a36Sopenharmony_ci * into 64 bit 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci EMIT2(0x0F, 0xB7); 33962306a36Sopenharmony_ci EMIT1(add_2reg(0xC0, dreg_lo, dreg_lo)); 34062306a36Sopenharmony_ci if (!aux->verifier_zext) 34162306a36Sopenharmony_ci /* xor dreg_hi,dreg_hi */ 34262306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case 32: 34562306a36Sopenharmony_ci if (!aux->verifier_zext) 34662306a36Sopenharmony_ci /* xor dreg_hi,dreg_hi */ 34762306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci case 64: 35062306a36Sopenharmony_ci /* nop */ 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (dstk && val != 64) { 35562306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_lo */ 35662306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 35762306a36Sopenharmony_ci STACK_VAR(dst_lo)); 35862306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_hi */ 35962306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_hi), 36062306a36Sopenharmony_ci STACK_VAR(dst_hi)); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci *pprog = prog; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic inline void emit_ia32_to_be_r64(const u8 dst[], s32 val, 36662306a36Sopenharmony_ci bool dstk, u8 **pprog, 36762306a36Sopenharmony_ci const struct bpf_prog_aux *aux) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci u8 *prog = *pprog; 37062306a36Sopenharmony_ci int cnt = 0; 37162306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 37262306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (dstk) { 37562306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 37662306a36Sopenharmony_ci STACK_VAR(dst_lo)); 37762306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 37862306a36Sopenharmony_ci STACK_VAR(dst_hi)); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci switch (val) { 38162306a36Sopenharmony_ci case 16: 38262306a36Sopenharmony_ci /* Emit 'ror %ax, 8' to swap lower 2 bytes */ 38362306a36Sopenharmony_ci EMIT1(0x66); 38462306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xC8, dreg_lo), 8); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci EMIT2(0x0F, 0xB7); 38762306a36Sopenharmony_ci EMIT1(add_2reg(0xC0, dreg_lo, dreg_lo)); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (!aux->verifier_zext) 39062306a36Sopenharmony_ci /* xor dreg_hi,dreg_hi */ 39162306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci case 32: 39462306a36Sopenharmony_ci /* Emit 'bswap eax' to swap lower 4 bytes */ 39562306a36Sopenharmony_ci EMIT1(0x0F); 39662306a36Sopenharmony_ci EMIT1(add_1reg(0xC8, dreg_lo)); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (!aux->verifier_zext) 39962306a36Sopenharmony_ci /* xor dreg_hi,dreg_hi */ 40062306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci case 64: 40362306a36Sopenharmony_ci /* Emit 'bswap eax' to swap lower 4 bytes */ 40462306a36Sopenharmony_ci EMIT1(0x0F); 40562306a36Sopenharmony_ci EMIT1(add_1reg(0xC8, dreg_lo)); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Emit 'bswap edx' to swap lower 4 bytes */ 40862306a36Sopenharmony_ci EMIT1(0x0F); 40962306a36Sopenharmony_ci EMIT1(add_1reg(0xC8, dreg_hi)); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* mov ecx,dreg_hi */ 41262306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, IA32_ECX, dreg_hi)); 41362306a36Sopenharmony_ci /* mov dreg_hi,dreg_lo */ 41462306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_hi, dreg_lo)); 41562306a36Sopenharmony_ci /* mov dreg_lo,ecx */ 41662306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_lo, IA32_ECX)); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci if (dstk) { 42162306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_lo */ 42262306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 42362306a36Sopenharmony_ci STACK_VAR(dst_lo)); 42462306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_hi */ 42562306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_hi), 42662306a36Sopenharmony_ci STACK_VAR(dst_hi)); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci *pprog = prog; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/* 43262306a36Sopenharmony_ci * ALU operation (32 bit) 43362306a36Sopenharmony_ci * dst = dst (div|mod) src 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_cistatic inline void emit_ia32_div_mod_r(const u8 op, const u8 dst, const u8 src, 43662306a36Sopenharmony_ci bool dstk, bool sstk, u8 **pprog) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci u8 *prog = *pprog; 43962306a36Sopenharmony_ci int cnt = 0; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (sstk) 44262306a36Sopenharmony_ci /* mov ecx,dword ptr [ebp+off] */ 44362306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), 44462306a36Sopenharmony_ci STACK_VAR(src)); 44562306a36Sopenharmony_ci else if (src != IA32_ECX) 44662306a36Sopenharmony_ci /* mov ecx,src */ 44762306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, src, IA32_ECX)); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (dstk) 45062306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 45162306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 45262306a36Sopenharmony_ci STACK_VAR(dst)); 45362306a36Sopenharmony_ci else 45462306a36Sopenharmony_ci /* mov eax,dst */ 45562306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, dst, IA32_EAX)); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* xor edx,edx */ 45862306a36Sopenharmony_ci EMIT2(0x31, add_2reg(0xC0, IA32_EDX, IA32_EDX)); 45962306a36Sopenharmony_ci /* div ecx */ 46062306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xF0, IA32_ECX)); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (op == BPF_MOD) { 46362306a36Sopenharmony_ci if (dstk) 46462306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EDX), 46562306a36Sopenharmony_ci STACK_VAR(dst)); 46662306a36Sopenharmony_ci else 46762306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst, IA32_EDX)); 46862306a36Sopenharmony_ci } else { 46962306a36Sopenharmony_ci if (dstk) 47062306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EAX), 47162306a36Sopenharmony_ci STACK_VAR(dst)); 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst, IA32_EAX)); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci *pprog = prog; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* 47962306a36Sopenharmony_ci * ALU operation (32 bit) 48062306a36Sopenharmony_ci * dst = dst (shift) src 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_cistatic inline void emit_ia32_shift_r(const u8 op, const u8 dst, const u8 src, 48362306a36Sopenharmony_ci bool dstk, bool sstk, u8 **pprog) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci u8 *prog = *pprog; 48662306a36Sopenharmony_ci int cnt = 0; 48762306a36Sopenharmony_ci u8 dreg = dstk ? IA32_EAX : dst; 48862306a36Sopenharmony_ci u8 b2; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (dstk) 49162306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 49262306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), STACK_VAR(dst)); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (sstk) 49562306a36Sopenharmony_ci /* mov ecx,dword ptr [ebp+off] */ 49662306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), STACK_VAR(src)); 49762306a36Sopenharmony_ci else if (src != IA32_ECX) 49862306a36Sopenharmony_ci /* mov ecx,src */ 49962306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, src, IA32_ECX)); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci switch (op) { 50262306a36Sopenharmony_ci case BPF_LSH: 50362306a36Sopenharmony_ci b2 = 0xE0; break; 50462306a36Sopenharmony_ci case BPF_RSH: 50562306a36Sopenharmony_ci b2 = 0xE8; break; 50662306a36Sopenharmony_ci case BPF_ARSH: 50762306a36Sopenharmony_ci b2 = 0xF8; break; 50862306a36Sopenharmony_ci default: 50962306a36Sopenharmony_ci return; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci EMIT2(0xD3, add_1reg(b2, dreg)); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (dstk) 51462306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg */ 51562306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg), STACK_VAR(dst)); 51662306a36Sopenharmony_ci *pprog = prog; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/* 52062306a36Sopenharmony_ci * ALU operation (32 bit) 52162306a36Sopenharmony_ci * dst = dst (op) src 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_cistatic inline void emit_ia32_alu_r(const bool is64, const bool hi, const u8 op, 52462306a36Sopenharmony_ci const u8 dst, const u8 src, bool dstk, 52562306a36Sopenharmony_ci bool sstk, u8 **pprog) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci u8 *prog = *pprog; 52862306a36Sopenharmony_ci int cnt = 0; 52962306a36Sopenharmony_ci u8 sreg = sstk ? IA32_EAX : src; 53062306a36Sopenharmony_ci u8 dreg = dstk ? IA32_EDX : dst; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (sstk) 53362306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 53462306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), STACK_VAR(src)); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (dstk) 53762306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 53862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), STACK_VAR(dst)); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci switch (BPF_OP(op)) { 54162306a36Sopenharmony_ci /* dst = dst + src */ 54262306a36Sopenharmony_ci case BPF_ADD: 54362306a36Sopenharmony_ci if (hi && is64) 54462306a36Sopenharmony_ci EMIT2(0x11, add_2reg(0xC0, dreg, sreg)); 54562306a36Sopenharmony_ci else 54662306a36Sopenharmony_ci EMIT2(0x01, add_2reg(0xC0, dreg, sreg)); 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci /* dst = dst - src */ 54962306a36Sopenharmony_ci case BPF_SUB: 55062306a36Sopenharmony_ci if (hi && is64) 55162306a36Sopenharmony_ci EMIT2(0x19, add_2reg(0xC0, dreg, sreg)); 55262306a36Sopenharmony_ci else 55362306a36Sopenharmony_ci EMIT2(0x29, add_2reg(0xC0, dreg, sreg)); 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci /* dst = dst | src */ 55662306a36Sopenharmony_ci case BPF_OR: 55762306a36Sopenharmony_ci EMIT2(0x09, add_2reg(0xC0, dreg, sreg)); 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci /* dst = dst & src */ 56062306a36Sopenharmony_ci case BPF_AND: 56162306a36Sopenharmony_ci EMIT2(0x21, add_2reg(0xC0, dreg, sreg)); 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci /* dst = dst ^ src */ 56462306a36Sopenharmony_ci case BPF_XOR: 56562306a36Sopenharmony_ci EMIT2(0x31, add_2reg(0xC0, dreg, sreg)); 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (dstk) 57062306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg */ 57162306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg), 57262306a36Sopenharmony_ci STACK_VAR(dst)); 57362306a36Sopenharmony_ci *pprog = prog; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* ALU operation (64 bit) */ 57762306a36Sopenharmony_cistatic inline void emit_ia32_alu_r64(const bool is64, const u8 op, 57862306a36Sopenharmony_ci const u8 dst[], const u8 src[], 57962306a36Sopenharmony_ci bool dstk, bool sstk, 58062306a36Sopenharmony_ci u8 **pprog, const struct bpf_prog_aux *aux) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci u8 *prog = *pprog; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci emit_ia32_alu_r(is64, false, op, dst_lo, src_lo, dstk, sstk, &prog); 58562306a36Sopenharmony_ci if (is64) 58662306a36Sopenharmony_ci emit_ia32_alu_r(is64, true, op, dst_hi, src_hi, dstk, sstk, 58762306a36Sopenharmony_ci &prog); 58862306a36Sopenharmony_ci else if (!aux->verifier_zext) 58962306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, 0, dstk, &prog); 59062306a36Sopenharmony_ci *pprog = prog; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/* 59462306a36Sopenharmony_ci * ALU operation (32 bit) 59562306a36Sopenharmony_ci * dst = dst (op) val 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_cistatic inline void emit_ia32_alu_i(const bool is64, const bool hi, const u8 op, 59862306a36Sopenharmony_ci const u8 dst, const s32 val, bool dstk, 59962306a36Sopenharmony_ci u8 **pprog) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci u8 *prog = *pprog; 60262306a36Sopenharmony_ci int cnt = 0; 60362306a36Sopenharmony_ci u8 dreg = dstk ? IA32_EAX : dst; 60462306a36Sopenharmony_ci u8 sreg = IA32_EDX; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (dstk) 60762306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 60862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), STACK_VAR(dst)); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (!is_imm8(val)) 61162306a36Sopenharmony_ci /* mov edx,imm32*/ 61262306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_EDX), val); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci switch (op) { 61562306a36Sopenharmony_ci /* dst = dst + val */ 61662306a36Sopenharmony_ci case BPF_ADD: 61762306a36Sopenharmony_ci if (hi && is64) { 61862306a36Sopenharmony_ci if (is_imm8(val)) 61962306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xD0, dreg), val); 62062306a36Sopenharmony_ci else 62162306a36Sopenharmony_ci EMIT2(0x11, add_2reg(0xC0, dreg, sreg)); 62262306a36Sopenharmony_ci } else { 62362306a36Sopenharmony_ci if (is_imm8(val)) 62462306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xC0, dreg), val); 62562306a36Sopenharmony_ci else 62662306a36Sopenharmony_ci EMIT2(0x01, add_2reg(0xC0, dreg, sreg)); 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci /* dst = dst - val */ 63062306a36Sopenharmony_ci case BPF_SUB: 63162306a36Sopenharmony_ci if (hi && is64) { 63262306a36Sopenharmony_ci if (is_imm8(val)) 63362306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xD8, dreg), val); 63462306a36Sopenharmony_ci else 63562306a36Sopenharmony_ci EMIT2(0x19, add_2reg(0xC0, dreg, sreg)); 63662306a36Sopenharmony_ci } else { 63762306a36Sopenharmony_ci if (is_imm8(val)) 63862306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xE8, dreg), val); 63962306a36Sopenharmony_ci else 64062306a36Sopenharmony_ci EMIT2(0x29, add_2reg(0xC0, dreg, sreg)); 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci /* dst = dst | val */ 64462306a36Sopenharmony_ci case BPF_OR: 64562306a36Sopenharmony_ci if (is_imm8(val)) 64662306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xC8, dreg), val); 64762306a36Sopenharmony_ci else 64862306a36Sopenharmony_ci EMIT2(0x09, add_2reg(0xC0, dreg, sreg)); 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci /* dst = dst & val */ 65162306a36Sopenharmony_ci case BPF_AND: 65262306a36Sopenharmony_ci if (is_imm8(val)) 65362306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xE0, dreg), val); 65462306a36Sopenharmony_ci else 65562306a36Sopenharmony_ci EMIT2(0x21, add_2reg(0xC0, dreg, sreg)); 65662306a36Sopenharmony_ci break; 65762306a36Sopenharmony_ci /* dst = dst ^ val */ 65862306a36Sopenharmony_ci case BPF_XOR: 65962306a36Sopenharmony_ci if (is_imm8(val)) 66062306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xF0, dreg), val); 66162306a36Sopenharmony_ci else 66262306a36Sopenharmony_ci EMIT2(0x31, add_2reg(0xC0, dreg, sreg)); 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci case BPF_NEG: 66562306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xD8, dreg)); 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (dstk) 67062306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg */ 67162306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg), 67262306a36Sopenharmony_ci STACK_VAR(dst)); 67362306a36Sopenharmony_ci *pprog = prog; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci/* ALU operation (64 bit) */ 67762306a36Sopenharmony_cistatic inline void emit_ia32_alu_i64(const bool is64, const u8 op, 67862306a36Sopenharmony_ci const u8 dst[], const u32 val, 67962306a36Sopenharmony_ci bool dstk, u8 **pprog, 68062306a36Sopenharmony_ci const struct bpf_prog_aux *aux) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci u8 *prog = *pprog; 68362306a36Sopenharmony_ci u32 hi = 0; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (is64 && (val & (1<<31))) 68662306a36Sopenharmony_ci hi = (u32)~0; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci emit_ia32_alu_i(is64, false, op, dst_lo, val, dstk, &prog); 68962306a36Sopenharmony_ci if (is64) 69062306a36Sopenharmony_ci emit_ia32_alu_i(is64, true, op, dst_hi, hi, dstk, &prog); 69162306a36Sopenharmony_ci else if (!aux->verifier_zext) 69262306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, 0, dstk, &prog); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci *pprog = prog; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/* dst = ~dst (64 bit) */ 69862306a36Sopenharmony_cistatic inline void emit_ia32_neg64(const u8 dst[], bool dstk, u8 **pprog) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci u8 *prog = *pprog; 70162306a36Sopenharmony_ci int cnt = 0; 70262306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 70362306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (dstk) { 70662306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 70762306a36Sopenharmony_ci STACK_VAR(dst_lo)); 70862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 70962306a36Sopenharmony_ci STACK_VAR(dst_hi)); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* neg dreg_lo */ 71362306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xD8, dreg_lo)); 71462306a36Sopenharmony_ci /* adc dreg_hi,0x0 */ 71562306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xD0, dreg_hi), 0x00); 71662306a36Sopenharmony_ci /* neg dreg_hi */ 71762306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xD8, dreg_hi)); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (dstk) { 72062306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_lo */ 72162306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 72262306a36Sopenharmony_ci STACK_VAR(dst_lo)); 72362306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_hi */ 72462306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_hi), 72562306a36Sopenharmony_ci STACK_VAR(dst_hi)); 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci *pprog = prog; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci/* dst = dst << src */ 73162306a36Sopenharmony_cistatic inline void emit_ia32_lsh_r64(const u8 dst[], const u8 src[], 73262306a36Sopenharmony_ci bool dstk, bool sstk, u8 **pprog) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci u8 *prog = *pprog; 73562306a36Sopenharmony_ci int cnt = 0; 73662306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 73762306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (dstk) { 74062306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 74162306a36Sopenharmony_ci STACK_VAR(dst_lo)); 74262306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 74362306a36Sopenharmony_ci STACK_VAR(dst_hi)); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (sstk) 74762306a36Sopenharmony_ci /* mov ecx,dword ptr [ebp+off] */ 74862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), 74962306a36Sopenharmony_ci STACK_VAR(src_lo)); 75062306a36Sopenharmony_ci else 75162306a36Sopenharmony_ci /* mov ecx,src_lo */ 75262306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX)); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* shld dreg_hi,dreg_lo,cl */ 75562306a36Sopenharmony_ci EMIT3(0x0F, 0xA5, add_2reg(0xC0, dreg_hi, dreg_lo)); 75662306a36Sopenharmony_ci /* shl dreg_lo,cl */ 75762306a36Sopenharmony_ci EMIT2(0xD3, add_1reg(0xE0, dreg_lo)); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* if ecx >= 32, mov dreg_lo into dreg_hi and clear dreg_lo */ 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* cmp ecx,32 */ 76262306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); 76362306a36Sopenharmony_ci /* skip the next two instructions (4 bytes) when < 32 */ 76462306a36Sopenharmony_ci EMIT2(IA32_JB, 4); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* mov dreg_hi,dreg_lo */ 76762306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_hi, dreg_lo)); 76862306a36Sopenharmony_ci /* xor dreg_lo,dreg_lo */ 76962306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo)); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (dstk) { 77262306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_lo */ 77362306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 77462306a36Sopenharmony_ci STACK_VAR(dst_lo)); 77562306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_hi */ 77662306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_hi), 77762306a36Sopenharmony_ci STACK_VAR(dst_hi)); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci /* out: */ 78062306a36Sopenharmony_ci *pprog = prog; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci/* dst = dst >> src (signed)*/ 78462306a36Sopenharmony_cistatic inline void emit_ia32_arsh_r64(const u8 dst[], const u8 src[], 78562306a36Sopenharmony_ci bool dstk, bool sstk, u8 **pprog) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci u8 *prog = *pprog; 78862306a36Sopenharmony_ci int cnt = 0; 78962306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 79062306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (dstk) { 79362306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 79462306a36Sopenharmony_ci STACK_VAR(dst_lo)); 79562306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 79662306a36Sopenharmony_ci STACK_VAR(dst_hi)); 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (sstk) 80062306a36Sopenharmony_ci /* mov ecx,dword ptr [ebp+off] */ 80162306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), 80262306a36Sopenharmony_ci STACK_VAR(src_lo)); 80362306a36Sopenharmony_ci else 80462306a36Sopenharmony_ci /* mov ecx,src_lo */ 80562306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX)); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* shrd dreg_lo,dreg_hi,cl */ 80862306a36Sopenharmony_ci EMIT3(0x0F, 0xAD, add_2reg(0xC0, dreg_lo, dreg_hi)); 80962306a36Sopenharmony_ci /* sar dreg_hi,cl */ 81062306a36Sopenharmony_ci EMIT2(0xD3, add_1reg(0xF8, dreg_hi)); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* if ecx >= 32, mov dreg_hi to dreg_lo and set/clear dreg_hi depending on sign */ 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* cmp ecx,32 */ 81562306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); 81662306a36Sopenharmony_ci /* skip the next two instructions (5 bytes) when < 32 */ 81762306a36Sopenharmony_ci EMIT2(IA32_JB, 5); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* mov dreg_lo,dreg_hi */ 82062306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); 82162306a36Sopenharmony_ci /* sar dreg_hi,31 */ 82262306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (dstk) { 82562306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_lo */ 82662306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 82762306a36Sopenharmony_ci STACK_VAR(dst_lo)); 82862306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_hi */ 82962306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_hi), 83062306a36Sopenharmony_ci STACK_VAR(dst_hi)); 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci /* out: */ 83362306a36Sopenharmony_ci *pprog = prog; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci/* dst = dst >> src */ 83762306a36Sopenharmony_cistatic inline void emit_ia32_rsh_r64(const u8 dst[], const u8 src[], bool dstk, 83862306a36Sopenharmony_ci bool sstk, u8 **pprog) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci u8 *prog = *pprog; 84162306a36Sopenharmony_ci int cnt = 0; 84262306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 84362306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (dstk) { 84662306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 84762306a36Sopenharmony_ci STACK_VAR(dst_lo)); 84862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 84962306a36Sopenharmony_ci STACK_VAR(dst_hi)); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (sstk) 85362306a36Sopenharmony_ci /* mov ecx,dword ptr [ebp+off] */ 85462306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), 85562306a36Sopenharmony_ci STACK_VAR(src_lo)); 85662306a36Sopenharmony_ci else 85762306a36Sopenharmony_ci /* mov ecx,src_lo */ 85862306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX)); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* shrd dreg_lo,dreg_hi,cl */ 86162306a36Sopenharmony_ci EMIT3(0x0F, 0xAD, add_2reg(0xC0, dreg_lo, dreg_hi)); 86262306a36Sopenharmony_ci /* shr dreg_hi,cl */ 86362306a36Sopenharmony_ci EMIT2(0xD3, add_1reg(0xE8, dreg_hi)); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* if ecx >= 32, mov dreg_hi to dreg_lo and clear dreg_hi */ 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci /* cmp ecx,32 */ 86862306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); 86962306a36Sopenharmony_ci /* skip the next two instructions (4 bytes) when < 32 */ 87062306a36Sopenharmony_ci EMIT2(IA32_JB, 4); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* mov dreg_lo,dreg_hi */ 87362306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); 87462306a36Sopenharmony_ci /* xor dreg_hi,dreg_hi */ 87562306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (dstk) { 87862306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_lo */ 87962306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 88062306a36Sopenharmony_ci STACK_VAR(dst_lo)); 88162306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_hi */ 88262306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_hi), 88362306a36Sopenharmony_ci STACK_VAR(dst_hi)); 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci /* out: */ 88662306a36Sopenharmony_ci *pprog = prog; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci/* dst = dst << val */ 89062306a36Sopenharmony_cistatic inline void emit_ia32_lsh_i64(const u8 dst[], const u32 val, 89162306a36Sopenharmony_ci bool dstk, u8 **pprog) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci u8 *prog = *pprog; 89462306a36Sopenharmony_ci int cnt = 0; 89562306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 89662306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (dstk) { 89962306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 90062306a36Sopenharmony_ci STACK_VAR(dst_lo)); 90162306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 90262306a36Sopenharmony_ci STACK_VAR(dst_hi)); 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci /* Do LSH operation */ 90562306a36Sopenharmony_ci if (val < 32) { 90662306a36Sopenharmony_ci /* shld dreg_hi,dreg_lo,imm8 */ 90762306a36Sopenharmony_ci EMIT4(0x0F, 0xA4, add_2reg(0xC0, dreg_hi, dreg_lo), val); 90862306a36Sopenharmony_ci /* shl dreg_lo,imm8 */ 90962306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xE0, dreg_lo), val); 91062306a36Sopenharmony_ci } else if (val >= 32 && val < 64) { 91162306a36Sopenharmony_ci u32 value = val - 32; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* shl dreg_lo,imm8 */ 91462306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xE0, dreg_lo), value); 91562306a36Sopenharmony_ci /* mov dreg_hi,dreg_lo */ 91662306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_hi, dreg_lo)); 91762306a36Sopenharmony_ci /* xor dreg_lo,dreg_lo */ 91862306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo)); 91962306a36Sopenharmony_ci } else { 92062306a36Sopenharmony_ci /* xor dreg_lo,dreg_lo */ 92162306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo)); 92262306a36Sopenharmony_ci /* xor dreg_hi,dreg_hi */ 92362306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (dstk) { 92762306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_lo */ 92862306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 92962306a36Sopenharmony_ci STACK_VAR(dst_lo)); 93062306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_hi */ 93162306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_hi), 93262306a36Sopenharmony_ci STACK_VAR(dst_hi)); 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci *pprog = prog; 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci/* dst = dst >> val */ 93862306a36Sopenharmony_cistatic inline void emit_ia32_rsh_i64(const u8 dst[], const u32 val, 93962306a36Sopenharmony_ci bool dstk, u8 **pprog) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci u8 *prog = *pprog; 94262306a36Sopenharmony_ci int cnt = 0; 94362306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 94462306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (dstk) { 94762306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 94862306a36Sopenharmony_ci STACK_VAR(dst_lo)); 94962306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 95062306a36Sopenharmony_ci STACK_VAR(dst_hi)); 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* Do RSH operation */ 95462306a36Sopenharmony_ci if (val < 32) { 95562306a36Sopenharmony_ci /* shrd dreg_lo,dreg_hi,imm8 */ 95662306a36Sopenharmony_ci EMIT4(0x0F, 0xAC, add_2reg(0xC0, dreg_lo, dreg_hi), val); 95762306a36Sopenharmony_ci /* shr dreg_hi,imm8 */ 95862306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xE8, dreg_hi), val); 95962306a36Sopenharmony_ci } else if (val >= 32 && val < 64) { 96062306a36Sopenharmony_ci u32 value = val - 32; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* shr dreg_hi,imm8 */ 96362306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xE8, dreg_hi), value); 96462306a36Sopenharmony_ci /* mov dreg_lo,dreg_hi */ 96562306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); 96662306a36Sopenharmony_ci /* xor dreg_hi,dreg_hi */ 96762306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); 96862306a36Sopenharmony_ci } else { 96962306a36Sopenharmony_ci /* xor dreg_lo,dreg_lo */ 97062306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo)); 97162306a36Sopenharmony_ci /* xor dreg_hi,dreg_hi */ 97262306a36Sopenharmony_ci EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (dstk) { 97662306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_lo */ 97762306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 97862306a36Sopenharmony_ci STACK_VAR(dst_lo)); 97962306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_hi */ 98062306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_hi), 98162306a36Sopenharmony_ci STACK_VAR(dst_hi)); 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci *pprog = prog; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci/* dst = dst >> val (signed) */ 98762306a36Sopenharmony_cistatic inline void emit_ia32_arsh_i64(const u8 dst[], const u32 val, 98862306a36Sopenharmony_ci bool dstk, u8 **pprog) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci u8 *prog = *pprog; 99162306a36Sopenharmony_ci int cnt = 0; 99262306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 99362306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (dstk) { 99662306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 99762306a36Sopenharmony_ci STACK_VAR(dst_lo)); 99862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 99962306a36Sopenharmony_ci STACK_VAR(dst_hi)); 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci /* Do RSH operation */ 100262306a36Sopenharmony_ci if (val < 32) { 100362306a36Sopenharmony_ci /* shrd dreg_lo,dreg_hi,imm8 */ 100462306a36Sopenharmony_ci EMIT4(0x0F, 0xAC, add_2reg(0xC0, dreg_lo, dreg_hi), val); 100562306a36Sopenharmony_ci /* ashr dreg_hi,imm8 */ 100662306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xF8, dreg_hi), val); 100762306a36Sopenharmony_ci } else if (val >= 32 && val < 64) { 100862306a36Sopenharmony_ci u32 value = val - 32; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* ashr dreg_hi,imm8 */ 101162306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xF8, dreg_hi), value); 101262306a36Sopenharmony_ci /* mov dreg_lo,dreg_hi */ 101362306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* ashr dreg_hi,imm8 */ 101662306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31); 101762306a36Sopenharmony_ci } else { 101862306a36Sopenharmony_ci /* ashr dreg_hi,imm8 */ 101962306a36Sopenharmony_ci EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31); 102062306a36Sopenharmony_ci /* mov dreg_lo,dreg_hi */ 102162306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (dstk) { 102562306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_lo */ 102662306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 102762306a36Sopenharmony_ci STACK_VAR(dst_lo)); 102862306a36Sopenharmony_ci /* mov dword ptr [ebp+off],dreg_hi */ 102962306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_hi), 103062306a36Sopenharmony_ci STACK_VAR(dst_hi)); 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci *pprog = prog; 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic inline void emit_ia32_mul_r64(const u8 dst[], const u8 src[], bool dstk, 103662306a36Sopenharmony_ci bool sstk, u8 **pprog) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci u8 *prog = *pprog; 103962306a36Sopenharmony_ci int cnt = 0; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (dstk) 104262306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 104362306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 104462306a36Sopenharmony_ci STACK_VAR(dst_hi)); 104562306a36Sopenharmony_ci else 104662306a36Sopenharmony_ci /* mov eax,dst_hi */ 104762306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, dst_hi, IA32_EAX)); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (sstk) 105062306a36Sopenharmony_ci /* mul dword ptr [ebp+off] */ 105162306a36Sopenharmony_ci EMIT3(0xF7, add_1reg(0x60, IA32_EBP), STACK_VAR(src_lo)); 105262306a36Sopenharmony_ci else 105362306a36Sopenharmony_ci /* mul src_lo */ 105462306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xE0, src_lo)); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* mov ecx,eax */ 105762306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, IA32_ECX, IA32_EAX)); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (dstk) 106062306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 106162306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 106262306a36Sopenharmony_ci STACK_VAR(dst_lo)); 106362306a36Sopenharmony_ci else 106462306a36Sopenharmony_ci /* mov eax,dst_lo */ 106562306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, dst_lo, IA32_EAX)); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (sstk) 106862306a36Sopenharmony_ci /* mul dword ptr [ebp+off] */ 106962306a36Sopenharmony_ci EMIT3(0xF7, add_1reg(0x60, IA32_EBP), STACK_VAR(src_hi)); 107062306a36Sopenharmony_ci else 107162306a36Sopenharmony_ci /* mul src_hi */ 107262306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xE0, src_hi)); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* add eax,eax */ 107562306a36Sopenharmony_ci EMIT2(0x01, add_2reg(0xC0, IA32_ECX, IA32_EAX)); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (dstk) 107862306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 107962306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 108062306a36Sopenharmony_ci STACK_VAR(dst_lo)); 108162306a36Sopenharmony_ci else 108262306a36Sopenharmony_ci /* mov eax,dst_lo */ 108362306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, dst_lo, IA32_EAX)); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (sstk) 108662306a36Sopenharmony_ci /* mul dword ptr [ebp+off] */ 108762306a36Sopenharmony_ci EMIT3(0xF7, add_1reg(0x60, IA32_EBP), STACK_VAR(src_lo)); 108862306a36Sopenharmony_ci else 108962306a36Sopenharmony_ci /* mul src_lo */ 109062306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xE0, src_lo)); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* add ecx,edx */ 109362306a36Sopenharmony_ci EMIT2(0x01, add_2reg(0xC0, IA32_ECX, IA32_EDX)); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (dstk) { 109662306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 109762306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EAX), 109862306a36Sopenharmony_ci STACK_VAR(dst_lo)); 109962306a36Sopenharmony_ci /* mov dword ptr [ebp+off],ecx */ 110062306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_ECX), 110162306a36Sopenharmony_ci STACK_VAR(dst_hi)); 110262306a36Sopenharmony_ci } else { 110362306a36Sopenharmony_ci /* mov dst_lo,eax */ 110462306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst_lo, IA32_EAX)); 110562306a36Sopenharmony_ci /* mov dst_hi,ecx */ 110662306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst_hi, IA32_ECX)); 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci *pprog = prog; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic inline void emit_ia32_mul_i64(const u8 dst[], const u32 val, 111362306a36Sopenharmony_ci bool dstk, u8 **pprog) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci u8 *prog = *pprog; 111662306a36Sopenharmony_ci int cnt = 0; 111762306a36Sopenharmony_ci u32 hi; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci hi = val & (1<<31) ? (u32)~0 : 0; 112062306a36Sopenharmony_ci /* movl eax,imm32 */ 112162306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_EAX), val); 112262306a36Sopenharmony_ci if (dstk) 112362306a36Sopenharmony_ci /* mul dword ptr [ebp+off] */ 112462306a36Sopenharmony_ci EMIT3(0xF7, add_1reg(0x60, IA32_EBP), STACK_VAR(dst_hi)); 112562306a36Sopenharmony_ci else 112662306a36Sopenharmony_ci /* mul dst_hi */ 112762306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xE0, dst_hi)); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* mov ecx,eax */ 113062306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, IA32_ECX, IA32_EAX)); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* movl eax,imm32 */ 113362306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_EAX), hi); 113462306a36Sopenharmony_ci if (dstk) 113562306a36Sopenharmony_ci /* mul dword ptr [ebp+off] */ 113662306a36Sopenharmony_ci EMIT3(0xF7, add_1reg(0x60, IA32_EBP), STACK_VAR(dst_lo)); 113762306a36Sopenharmony_ci else 113862306a36Sopenharmony_ci /* mul dst_lo */ 113962306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xE0, dst_lo)); 114062306a36Sopenharmony_ci /* add ecx,eax */ 114162306a36Sopenharmony_ci EMIT2(0x01, add_2reg(0xC0, IA32_ECX, IA32_EAX)); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* movl eax,imm32 */ 114462306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_EAX), val); 114562306a36Sopenharmony_ci if (dstk) 114662306a36Sopenharmony_ci /* mul dword ptr [ebp+off] */ 114762306a36Sopenharmony_ci EMIT3(0xF7, add_1reg(0x60, IA32_EBP), STACK_VAR(dst_lo)); 114862306a36Sopenharmony_ci else 114962306a36Sopenharmony_ci /* mul dst_lo */ 115062306a36Sopenharmony_ci EMIT2(0xF7, add_1reg(0xE0, dst_lo)); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* add ecx,edx */ 115362306a36Sopenharmony_ci EMIT2(0x01, add_2reg(0xC0, IA32_ECX, IA32_EDX)); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (dstk) { 115662306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 115762306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EAX), 115862306a36Sopenharmony_ci STACK_VAR(dst_lo)); 115962306a36Sopenharmony_ci /* mov dword ptr [ebp+off],ecx */ 116062306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_ECX), 116162306a36Sopenharmony_ci STACK_VAR(dst_hi)); 116262306a36Sopenharmony_ci } else { 116362306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 116462306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst_lo, IA32_EAX)); 116562306a36Sopenharmony_ci /* mov dword ptr [ebp+off],ecx */ 116662306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst_hi, IA32_ECX)); 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci *pprog = prog; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic int bpf_size_to_x86_bytes(int bpf_size) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci if (bpf_size == BPF_W) 117562306a36Sopenharmony_ci return 4; 117662306a36Sopenharmony_ci else if (bpf_size == BPF_H) 117762306a36Sopenharmony_ci return 2; 117862306a36Sopenharmony_ci else if (bpf_size == BPF_B) 117962306a36Sopenharmony_ci return 1; 118062306a36Sopenharmony_ci else if (bpf_size == BPF_DW) 118162306a36Sopenharmony_ci return 4; /* imm32 */ 118262306a36Sopenharmony_ci else 118362306a36Sopenharmony_ci return 0; 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistruct jit_context { 118762306a36Sopenharmony_ci int cleanup_addr; /* Epilogue code offset */ 118862306a36Sopenharmony_ci}; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci/* Maximum number of bytes emitted while JITing one eBPF insn */ 119162306a36Sopenharmony_ci#define BPF_MAX_INSN_SIZE 128 119262306a36Sopenharmony_ci#define BPF_INSN_SAFETY 64 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci#define PROLOGUE_SIZE 35 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci/* 119762306a36Sopenharmony_ci * Emit prologue code for BPF program and check it's size. 119862306a36Sopenharmony_ci * bpf_tail_call helper will skip it while jumping into another program. 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_cistatic void emit_prologue(u8 **pprog, u32 stack_depth) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci u8 *prog = *pprog; 120362306a36Sopenharmony_ci int cnt = 0; 120462306a36Sopenharmony_ci const u8 *r1 = bpf2ia32[BPF_REG_1]; 120562306a36Sopenharmony_ci const u8 fplo = bpf2ia32[BPF_REG_FP][0]; 120662306a36Sopenharmony_ci const u8 fphi = bpf2ia32[BPF_REG_FP][1]; 120762306a36Sopenharmony_ci const u8 *tcc = bpf2ia32[TCALL_CNT]; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci /* push ebp */ 121062306a36Sopenharmony_ci EMIT1(0x55); 121162306a36Sopenharmony_ci /* mov ebp,esp */ 121262306a36Sopenharmony_ci EMIT2(0x89, 0xE5); 121362306a36Sopenharmony_ci /* push edi */ 121462306a36Sopenharmony_ci EMIT1(0x57); 121562306a36Sopenharmony_ci /* push esi */ 121662306a36Sopenharmony_ci EMIT1(0x56); 121762306a36Sopenharmony_ci /* push ebx */ 121862306a36Sopenharmony_ci EMIT1(0x53); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci /* sub esp,STACK_SIZE */ 122162306a36Sopenharmony_ci EMIT2_off32(0x81, 0xEC, STACK_SIZE); 122262306a36Sopenharmony_ci /* sub ebp,SCRATCH_SIZE+12*/ 122362306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xE8, IA32_EBP), SCRATCH_SIZE + 12); 122462306a36Sopenharmony_ci /* xor ebx,ebx */ 122562306a36Sopenharmony_ci EMIT2(0x31, add_2reg(0xC0, IA32_EBX, IA32_EBX)); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci /* Set up BPF prog stack base register */ 122862306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EBP), STACK_VAR(fplo)); 122962306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EBX), STACK_VAR(fphi)); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci /* Move BPF_CTX (EAX) to BPF_REG_R1 */ 123262306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 123362306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EAX), STACK_VAR(r1[0])); 123462306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EBX), STACK_VAR(r1[1])); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* Initialize Tail Count */ 123762306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EBX), STACK_VAR(tcc[0])); 123862306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EBX), STACK_VAR(tcc[1])); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci BUILD_BUG_ON(cnt != PROLOGUE_SIZE); 124162306a36Sopenharmony_ci *pprog = prog; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci/* Emit epilogue code for BPF program */ 124562306a36Sopenharmony_cistatic void emit_epilogue(u8 **pprog, u32 stack_depth) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci u8 *prog = *pprog; 124862306a36Sopenharmony_ci const u8 *r0 = bpf2ia32[BPF_REG_0]; 124962306a36Sopenharmony_ci int cnt = 0; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off]*/ 125262306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), STACK_VAR(r0[0])); 125362306a36Sopenharmony_ci /* mov edx,dword ptr [ebp+off]*/ 125462306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), STACK_VAR(r0[1])); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* add ebp,SCRATCH_SIZE+12*/ 125762306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xC0, IA32_EBP), SCRATCH_SIZE + 12); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* mov ebx,dword ptr [ebp-12]*/ 126062306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EBX), -12); 126162306a36Sopenharmony_ci /* mov esi,dword ptr [ebp-8]*/ 126262306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ESI), -8); 126362306a36Sopenharmony_ci /* mov edi,dword ptr [ebp-4]*/ 126462306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDI), -4); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci EMIT1(0xC9); /* leave */ 126762306a36Sopenharmony_ci EMIT1(0xC3); /* ret */ 126862306a36Sopenharmony_ci *pprog = prog; 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_cistatic int emit_jmp_edx(u8 **pprog, u8 *ip) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci u8 *prog = *pprog; 127462306a36Sopenharmony_ci int cnt = 0; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci#ifdef CONFIG_RETPOLINE 127762306a36Sopenharmony_ci EMIT1_off32(0xE9, (u8 *)__x86_indirect_thunk_edx - (ip + 5)); 127862306a36Sopenharmony_ci#else 127962306a36Sopenharmony_ci EMIT2(0xFF, 0xE2); 128062306a36Sopenharmony_ci#endif 128162306a36Sopenharmony_ci *pprog = prog; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci return cnt; 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci/* 128762306a36Sopenharmony_ci * Generate the following code: 128862306a36Sopenharmony_ci * ... bpf_tail_call(void *ctx, struct bpf_array *array, u64 index) ... 128962306a36Sopenharmony_ci * if (index >= array->map.max_entries) 129062306a36Sopenharmony_ci * goto out; 129162306a36Sopenharmony_ci * if (++tail_call_cnt > MAX_TAIL_CALL_CNT) 129262306a36Sopenharmony_ci * goto out; 129362306a36Sopenharmony_ci * prog = array->ptrs[index]; 129462306a36Sopenharmony_ci * if (prog == NULL) 129562306a36Sopenharmony_ci * goto out; 129662306a36Sopenharmony_ci * goto *(prog->bpf_func + prologue_size); 129762306a36Sopenharmony_ci * out: 129862306a36Sopenharmony_ci */ 129962306a36Sopenharmony_cistatic void emit_bpf_tail_call(u8 **pprog, u8 *ip) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci u8 *prog = *pprog; 130262306a36Sopenharmony_ci int cnt = 0; 130362306a36Sopenharmony_ci const u8 *r1 = bpf2ia32[BPF_REG_1]; 130462306a36Sopenharmony_ci const u8 *r2 = bpf2ia32[BPF_REG_2]; 130562306a36Sopenharmony_ci const u8 *r3 = bpf2ia32[BPF_REG_3]; 130662306a36Sopenharmony_ci const u8 *tcc = bpf2ia32[TCALL_CNT]; 130762306a36Sopenharmony_ci u32 lo, hi; 130862306a36Sopenharmony_ci static int jmp_label1 = -1; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* 131162306a36Sopenharmony_ci * if (index >= array->map.max_entries) 131262306a36Sopenharmony_ci * goto out; 131362306a36Sopenharmony_ci */ 131462306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 131562306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), STACK_VAR(r2[0])); 131662306a36Sopenharmony_ci /* mov edx,dword ptr [ebp+off] */ 131762306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), STACK_VAR(r3[0])); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* cmp dword ptr [eax+off],edx */ 132062306a36Sopenharmony_ci EMIT3(0x39, add_2reg(0x40, IA32_EAX, IA32_EDX), 132162306a36Sopenharmony_ci offsetof(struct bpf_array, map.max_entries)); 132262306a36Sopenharmony_ci /* jbe out */ 132362306a36Sopenharmony_ci EMIT2(IA32_JBE, jmp_label(jmp_label1, 2)); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* 132662306a36Sopenharmony_ci * if (tail_call_cnt++ >= MAX_TAIL_CALL_CNT) 132762306a36Sopenharmony_ci * goto out; 132862306a36Sopenharmony_ci */ 132962306a36Sopenharmony_ci lo = (u32)MAX_TAIL_CALL_CNT; 133062306a36Sopenharmony_ci hi = (u32)((u64)MAX_TAIL_CALL_CNT >> 32); 133162306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), STACK_VAR(tcc[0])); 133262306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EBX), STACK_VAR(tcc[1])); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* cmp edx,hi */ 133562306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xF8, IA32_EBX), hi); 133662306a36Sopenharmony_ci EMIT2(IA32_JNE, 3); 133762306a36Sopenharmony_ci /* cmp ecx,lo */ 133862306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xF8, IA32_ECX), lo); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* jae out */ 134162306a36Sopenharmony_ci EMIT2(IA32_JAE, jmp_label(jmp_label1, 2)); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci /* add eax,0x1 */ 134462306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 0x01); 134562306a36Sopenharmony_ci /* adc ebx,0x0 */ 134662306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xD0, IA32_EBX), 0x00); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 134962306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_ECX), STACK_VAR(tcc[0])); 135062306a36Sopenharmony_ci /* mov dword ptr [ebp+off],edx */ 135162306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EBX), STACK_VAR(tcc[1])); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* prog = array->ptrs[index]; */ 135462306a36Sopenharmony_ci /* mov edx, [eax + edx * 4 + offsetof(...)] */ 135562306a36Sopenharmony_ci EMIT3_off32(0x8B, 0x94, 0x90, offsetof(struct bpf_array, ptrs)); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci /* 135862306a36Sopenharmony_ci * if (prog == NULL) 135962306a36Sopenharmony_ci * goto out; 136062306a36Sopenharmony_ci */ 136162306a36Sopenharmony_ci /* test edx,edx */ 136262306a36Sopenharmony_ci EMIT2(0x85, add_2reg(0xC0, IA32_EDX, IA32_EDX)); 136362306a36Sopenharmony_ci /* je out */ 136462306a36Sopenharmony_ci EMIT2(IA32_JE, jmp_label(jmp_label1, 2)); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci /* goto *(prog->bpf_func + prologue_size); */ 136762306a36Sopenharmony_ci /* mov edx, dword ptr [edx + 32] */ 136862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EDX, IA32_EDX), 136962306a36Sopenharmony_ci offsetof(struct bpf_prog, bpf_func)); 137062306a36Sopenharmony_ci /* add edx,prologue_size */ 137162306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xC0, IA32_EDX), PROLOGUE_SIZE); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 137462306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), STACK_VAR(r1[0])); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci /* 137762306a36Sopenharmony_ci * Now we're ready to jump into next BPF program: 137862306a36Sopenharmony_ci * eax == ctx (1st arg) 137962306a36Sopenharmony_ci * edx == prog->bpf_func + prologue_size 138062306a36Sopenharmony_ci */ 138162306a36Sopenharmony_ci cnt += emit_jmp_edx(&prog, ip + cnt); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci if (jmp_label1 == -1) 138462306a36Sopenharmony_ci jmp_label1 = cnt; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci /* out: */ 138762306a36Sopenharmony_ci *pprog = prog; 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci/* Push the scratch stack register on top of the stack. */ 139162306a36Sopenharmony_cistatic inline void emit_push_r64(const u8 src[], u8 **pprog) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci u8 *prog = *pprog; 139462306a36Sopenharmony_ci int cnt = 0; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* mov ecx,dword ptr [ebp+off] */ 139762306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), STACK_VAR(src_hi)); 139862306a36Sopenharmony_ci /* push ecx */ 139962306a36Sopenharmony_ci EMIT1(0x51); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci /* mov ecx,dword ptr [ebp+off] */ 140262306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), STACK_VAR(src_lo)); 140362306a36Sopenharmony_ci /* push ecx */ 140462306a36Sopenharmony_ci EMIT1(0x51); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci *pprog = prog; 140762306a36Sopenharmony_ci} 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic void emit_push_r32(const u8 src[], u8 **pprog) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci u8 *prog = *pprog; 141262306a36Sopenharmony_ci int cnt = 0; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* mov ecx,dword ptr [ebp+off] */ 141562306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), STACK_VAR(src_lo)); 141662306a36Sopenharmony_ci /* push ecx */ 141762306a36Sopenharmony_ci EMIT1(0x51); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci *pprog = prog; 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic u8 get_cond_jmp_opcode(const u8 op, bool is_cmp_lo) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci u8 jmp_cond; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci /* Convert BPF opcode to x86 */ 142762306a36Sopenharmony_ci switch (op) { 142862306a36Sopenharmony_ci case BPF_JEQ: 142962306a36Sopenharmony_ci jmp_cond = IA32_JE; 143062306a36Sopenharmony_ci break; 143162306a36Sopenharmony_ci case BPF_JSET: 143262306a36Sopenharmony_ci case BPF_JNE: 143362306a36Sopenharmony_ci jmp_cond = IA32_JNE; 143462306a36Sopenharmony_ci break; 143562306a36Sopenharmony_ci case BPF_JGT: 143662306a36Sopenharmony_ci /* GT is unsigned '>', JA in x86 */ 143762306a36Sopenharmony_ci jmp_cond = IA32_JA; 143862306a36Sopenharmony_ci break; 143962306a36Sopenharmony_ci case BPF_JLT: 144062306a36Sopenharmony_ci /* LT is unsigned '<', JB in x86 */ 144162306a36Sopenharmony_ci jmp_cond = IA32_JB; 144262306a36Sopenharmony_ci break; 144362306a36Sopenharmony_ci case BPF_JGE: 144462306a36Sopenharmony_ci /* GE is unsigned '>=', JAE in x86 */ 144562306a36Sopenharmony_ci jmp_cond = IA32_JAE; 144662306a36Sopenharmony_ci break; 144762306a36Sopenharmony_ci case BPF_JLE: 144862306a36Sopenharmony_ci /* LE is unsigned '<=', JBE in x86 */ 144962306a36Sopenharmony_ci jmp_cond = IA32_JBE; 145062306a36Sopenharmony_ci break; 145162306a36Sopenharmony_ci case BPF_JSGT: 145262306a36Sopenharmony_ci if (!is_cmp_lo) 145362306a36Sopenharmony_ci /* Signed '>', GT in x86 */ 145462306a36Sopenharmony_ci jmp_cond = IA32_JG; 145562306a36Sopenharmony_ci else 145662306a36Sopenharmony_ci /* GT is unsigned '>', JA in x86 */ 145762306a36Sopenharmony_ci jmp_cond = IA32_JA; 145862306a36Sopenharmony_ci break; 145962306a36Sopenharmony_ci case BPF_JSLT: 146062306a36Sopenharmony_ci if (!is_cmp_lo) 146162306a36Sopenharmony_ci /* Signed '<', LT in x86 */ 146262306a36Sopenharmony_ci jmp_cond = IA32_JL; 146362306a36Sopenharmony_ci else 146462306a36Sopenharmony_ci /* LT is unsigned '<', JB in x86 */ 146562306a36Sopenharmony_ci jmp_cond = IA32_JB; 146662306a36Sopenharmony_ci break; 146762306a36Sopenharmony_ci case BPF_JSGE: 146862306a36Sopenharmony_ci if (!is_cmp_lo) 146962306a36Sopenharmony_ci /* Signed '>=', GE in x86 */ 147062306a36Sopenharmony_ci jmp_cond = IA32_JGE; 147162306a36Sopenharmony_ci else 147262306a36Sopenharmony_ci /* GE is unsigned '>=', JAE in x86 */ 147362306a36Sopenharmony_ci jmp_cond = IA32_JAE; 147462306a36Sopenharmony_ci break; 147562306a36Sopenharmony_ci case BPF_JSLE: 147662306a36Sopenharmony_ci if (!is_cmp_lo) 147762306a36Sopenharmony_ci /* Signed '<=', LE in x86 */ 147862306a36Sopenharmony_ci jmp_cond = IA32_JLE; 147962306a36Sopenharmony_ci else 148062306a36Sopenharmony_ci /* LE is unsigned '<=', JBE in x86 */ 148162306a36Sopenharmony_ci jmp_cond = IA32_JBE; 148262306a36Sopenharmony_ci break; 148362306a36Sopenharmony_ci default: /* to silence GCC warning */ 148462306a36Sopenharmony_ci jmp_cond = COND_JMP_OPCODE_INVALID; 148562306a36Sopenharmony_ci break; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci return jmp_cond; 148962306a36Sopenharmony_ci} 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci/* i386 kernel compiles with "-mregparm=3". From gcc document: 149262306a36Sopenharmony_ci * 149362306a36Sopenharmony_ci * ==== snippet ==== 149462306a36Sopenharmony_ci * regparm (number) 149562306a36Sopenharmony_ci * On x86-32 targets, the regparm attribute causes the compiler 149662306a36Sopenharmony_ci * to pass arguments number one to (number) if they are of integral 149762306a36Sopenharmony_ci * type in registers EAX, EDX, and ECX instead of on the stack. 149862306a36Sopenharmony_ci * Functions that take a variable number of arguments continue 149962306a36Sopenharmony_ci * to be passed all of their arguments on the stack. 150062306a36Sopenharmony_ci * ==== snippet ==== 150162306a36Sopenharmony_ci * 150262306a36Sopenharmony_ci * The first three args of a function will be considered for 150362306a36Sopenharmony_ci * putting into the 32bit register EAX, EDX, and ECX. 150462306a36Sopenharmony_ci * 150562306a36Sopenharmony_ci * Two 32bit registers are used to pass a 64bit arg. 150662306a36Sopenharmony_ci * 150762306a36Sopenharmony_ci * For example, 150862306a36Sopenharmony_ci * void foo(u32 a, u32 b, u32 c, u32 d): 150962306a36Sopenharmony_ci * u32 a: EAX 151062306a36Sopenharmony_ci * u32 b: EDX 151162306a36Sopenharmony_ci * u32 c: ECX 151262306a36Sopenharmony_ci * u32 d: stack 151362306a36Sopenharmony_ci * 151462306a36Sopenharmony_ci * void foo(u64 a, u32 b, u32 c): 151562306a36Sopenharmony_ci * u64 a: EAX (lo32) EDX (hi32) 151662306a36Sopenharmony_ci * u32 b: ECX 151762306a36Sopenharmony_ci * u32 c: stack 151862306a36Sopenharmony_ci * 151962306a36Sopenharmony_ci * void foo(u32 a, u64 b, u32 c): 152062306a36Sopenharmony_ci * u32 a: EAX 152162306a36Sopenharmony_ci * u64 b: EDX (lo32) ECX (hi32) 152262306a36Sopenharmony_ci * u32 c: stack 152362306a36Sopenharmony_ci * 152462306a36Sopenharmony_ci * void foo(u32 a, u32 b, u64 c): 152562306a36Sopenharmony_ci * u32 a: EAX 152662306a36Sopenharmony_ci * u32 b: EDX 152762306a36Sopenharmony_ci * u64 c: stack 152862306a36Sopenharmony_ci * 152962306a36Sopenharmony_ci * The return value will be stored in the EAX (and EDX for 64bit value). 153062306a36Sopenharmony_ci * 153162306a36Sopenharmony_ci * For example, 153262306a36Sopenharmony_ci * u32 foo(u32 a, u32 b, u32 c): 153362306a36Sopenharmony_ci * return value: EAX 153462306a36Sopenharmony_ci * 153562306a36Sopenharmony_ci * u64 foo(u32 a, u32 b, u32 c): 153662306a36Sopenharmony_ci * return value: EAX (lo32) EDX (hi32) 153762306a36Sopenharmony_ci * 153862306a36Sopenharmony_ci * Notes: 153962306a36Sopenharmony_ci * The verifier only accepts function having integer and pointers 154062306a36Sopenharmony_ci * as its args and return value, so it does not have 154162306a36Sopenharmony_ci * struct-by-value. 154262306a36Sopenharmony_ci * 154362306a36Sopenharmony_ci * emit_kfunc_call() finds out the btf_func_model by calling 154462306a36Sopenharmony_ci * bpf_jit_find_kfunc_model(). A btf_func_model 154562306a36Sopenharmony_ci * has the details about the number of args, size of each arg, 154662306a36Sopenharmony_ci * and the size of the return value. 154762306a36Sopenharmony_ci * 154862306a36Sopenharmony_ci * It first decides how many args can be passed by EAX, EDX, and ECX. 154962306a36Sopenharmony_ci * That will decide what args should be pushed to the stack: 155062306a36Sopenharmony_ci * [first_stack_regno, last_stack_regno] are the bpf regnos 155162306a36Sopenharmony_ci * that should be pushed to the stack. 155262306a36Sopenharmony_ci * 155362306a36Sopenharmony_ci * It will first push all args to the stack because the push 155462306a36Sopenharmony_ci * will need to use ECX. Then, it moves 155562306a36Sopenharmony_ci * [BPF_REG_1, first_stack_regno) to EAX, EDX, and ECX. 155662306a36Sopenharmony_ci * 155762306a36Sopenharmony_ci * When emitting a call (0xE8), it needs to figure out 155862306a36Sopenharmony_ci * the jmp_offset relative to the jit-insn address immediately 155962306a36Sopenharmony_ci * following the call (0xE8) instruction. At this point, it knows 156062306a36Sopenharmony_ci * the end of the jit-insn address after completely translated the 156162306a36Sopenharmony_ci * current (BPF_JMP | BPF_CALL) bpf-insn. It is passed as "end_addr" 156262306a36Sopenharmony_ci * to the emit_kfunc_call(). Thus, it can learn the "immediate-follow-call" 156362306a36Sopenharmony_ci * address by figuring out how many jit-insn is generated between 156462306a36Sopenharmony_ci * the call (0xE8) and the end_addr: 156562306a36Sopenharmony_ci * - 0-1 jit-insn (3 bytes each) to restore the esp pointer if there 156662306a36Sopenharmony_ci * is arg pushed to the stack. 156762306a36Sopenharmony_ci * - 0-2 jit-insns (3 bytes each) to handle the return value. 156862306a36Sopenharmony_ci */ 156962306a36Sopenharmony_cistatic int emit_kfunc_call(const struct bpf_prog *bpf_prog, u8 *end_addr, 157062306a36Sopenharmony_ci const struct bpf_insn *insn, u8 **pprog) 157162306a36Sopenharmony_ci{ 157262306a36Sopenharmony_ci const u8 arg_regs[] = { IA32_EAX, IA32_EDX, IA32_ECX }; 157362306a36Sopenharmony_ci int i, cnt = 0, first_stack_regno, last_stack_regno; 157462306a36Sopenharmony_ci int free_arg_regs = ARRAY_SIZE(arg_regs); 157562306a36Sopenharmony_ci const struct btf_func_model *fm; 157662306a36Sopenharmony_ci int bytes_in_stack = 0; 157762306a36Sopenharmony_ci const u8 *cur_arg_reg; 157862306a36Sopenharmony_ci u8 *prog = *pprog; 157962306a36Sopenharmony_ci s64 jmp_offset; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci fm = bpf_jit_find_kfunc_model(bpf_prog, insn); 158262306a36Sopenharmony_ci if (!fm) 158362306a36Sopenharmony_ci return -EINVAL; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci first_stack_regno = BPF_REG_1; 158662306a36Sopenharmony_ci for (i = 0; i < fm->nr_args; i++) { 158762306a36Sopenharmony_ci int regs_needed = fm->arg_size[i] > sizeof(u32) ? 2 : 1; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if (regs_needed > free_arg_regs) 159062306a36Sopenharmony_ci break; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci free_arg_regs -= regs_needed; 159362306a36Sopenharmony_ci first_stack_regno++; 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* Push the args to the stack */ 159762306a36Sopenharmony_ci last_stack_regno = BPF_REG_0 + fm->nr_args; 159862306a36Sopenharmony_ci for (i = last_stack_regno; i >= first_stack_regno; i--) { 159962306a36Sopenharmony_ci if (fm->arg_size[i - 1] > sizeof(u32)) { 160062306a36Sopenharmony_ci emit_push_r64(bpf2ia32[i], &prog); 160162306a36Sopenharmony_ci bytes_in_stack += 8; 160262306a36Sopenharmony_ci } else { 160362306a36Sopenharmony_ci emit_push_r32(bpf2ia32[i], &prog); 160462306a36Sopenharmony_ci bytes_in_stack += 4; 160562306a36Sopenharmony_ci } 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci cur_arg_reg = &arg_regs[0]; 160962306a36Sopenharmony_ci for (i = BPF_REG_1; i < first_stack_regno; i++) { 161062306a36Sopenharmony_ci /* mov e[adc]x,dword ptr [ebp+off] */ 161162306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, *cur_arg_reg++), 161262306a36Sopenharmony_ci STACK_VAR(bpf2ia32[i][0])); 161362306a36Sopenharmony_ci if (fm->arg_size[i - 1] > sizeof(u32)) 161462306a36Sopenharmony_ci /* mov e[adc]x,dword ptr [ebp+off] */ 161562306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, *cur_arg_reg++), 161662306a36Sopenharmony_ci STACK_VAR(bpf2ia32[i][1])); 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci if (bytes_in_stack) 162062306a36Sopenharmony_ci /* add esp,"bytes_in_stack" */ 162162306a36Sopenharmony_ci end_addr -= 3; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* mov dword ptr [ebp+off],edx */ 162462306a36Sopenharmony_ci if (fm->ret_size > sizeof(u32)) 162562306a36Sopenharmony_ci end_addr -= 3; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 162862306a36Sopenharmony_ci if (fm->ret_size) 162962306a36Sopenharmony_ci end_addr -= 3; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci jmp_offset = (u8 *)__bpf_call_base + insn->imm - end_addr; 163262306a36Sopenharmony_ci if (!is_simm32(jmp_offset)) { 163362306a36Sopenharmony_ci pr_err("unsupported BPF kernel function jmp_offset:%lld\n", 163462306a36Sopenharmony_ci jmp_offset); 163562306a36Sopenharmony_ci return -EINVAL; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci EMIT1_off32(0xE8, jmp_offset); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci if (fm->ret_size) 164162306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 164262306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EAX), 164362306a36Sopenharmony_ci STACK_VAR(bpf2ia32[BPF_REG_0][0])); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci if (fm->ret_size > sizeof(u32)) 164662306a36Sopenharmony_ci /* mov dword ptr [ebp+off],edx */ 164762306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EDX), 164862306a36Sopenharmony_ci STACK_VAR(bpf2ia32[BPF_REG_0][1])); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (bytes_in_stack) 165162306a36Sopenharmony_ci /* add esp,"bytes_in_stack" */ 165262306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xC0, IA32_ESP), bytes_in_stack); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci *pprog = prog; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci return 0; 165762306a36Sopenharmony_ci} 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_cistatic int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, 166062306a36Sopenharmony_ci int oldproglen, struct jit_context *ctx) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci struct bpf_insn *insn = bpf_prog->insnsi; 166362306a36Sopenharmony_ci int insn_cnt = bpf_prog->len; 166462306a36Sopenharmony_ci bool seen_exit = false; 166562306a36Sopenharmony_ci u8 temp[BPF_MAX_INSN_SIZE + BPF_INSN_SAFETY]; 166662306a36Sopenharmony_ci int i, cnt = 0; 166762306a36Sopenharmony_ci int proglen = 0; 166862306a36Sopenharmony_ci u8 *prog = temp; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci emit_prologue(&prog, bpf_prog->aux->stack_depth); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci for (i = 0; i < insn_cnt; i++, insn++) { 167362306a36Sopenharmony_ci const s32 imm32 = insn->imm; 167462306a36Sopenharmony_ci const bool is64 = BPF_CLASS(insn->code) == BPF_ALU64; 167562306a36Sopenharmony_ci const bool dstk = insn->dst_reg != BPF_REG_AX; 167662306a36Sopenharmony_ci const bool sstk = insn->src_reg != BPF_REG_AX; 167762306a36Sopenharmony_ci const u8 code = insn->code; 167862306a36Sopenharmony_ci const u8 *dst = bpf2ia32[insn->dst_reg]; 167962306a36Sopenharmony_ci const u8 *src = bpf2ia32[insn->src_reg]; 168062306a36Sopenharmony_ci const u8 *r0 = bpf2ia32[BPF_REG_0]; 168162306a36Sopenharmony_ci s64 jmp_offset; 168262306a36Sopenharmony_ci u8 jmp_cond; 168362306a36Sopenharmony_ci int ilen; 168462306a36Sopenharmony_ci u8 *func; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci switch (code) { 168762306a36Sopenharmony_ci /* ALU operations */ 168862306a36Sopenharmony_ci /* dst = src */ 168962306a36Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_K: 169062306a36Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_X: 169162306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_K: 169262306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_X: 169362306a36Sopenharmony_ci switch (BPF_SRC(code)) { 169462306a36Sopenharmony_ci case BPF_X: 169562306a36Sopenharmony_ci if (imm32 == 1) { 169662306a36Sopenharmony_ci /* Special mov32 for zext. */ 169762306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, 0, dstk, &prog); 169862306a36Sopenharmony_ci break; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci emit_ia32_mov_r64(is64, dst, src, dstk, sstk, 170162306a36Sopenharmony_ci &prog, bpf_prog->aux); 170262306a36Sopenharmony_ci break; 170362306a36Sopenharmony_ci case BPF_K: 170462306a36Sopenharmony_ci /* Sign-extend immediate value to dst reg */ 170562306a36Sopenharmony_ci emit_ia32_mov_i64(is64, dst, imm32, 170662306a36Sopenharmony_ci dstk, &prog); 170762306a36Sopenharmony_ci break; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci break; 171062306a36Sopenharmony_ci /* dst = dst + src/imm */ 171162306a36Sopenharmony_ci /* dst = dst - src/imm */ 171262306a36Sopenharmony_ci /* dst = dst | src/imm */ 171362306a36Sopenharmony_ci /* dst = dst & src/imm */ 171462306a36Sopenharmony_ci /* dst = dst ^ src/imm */ 171562306a36Sopenharmony_ci /* dst = dst * src/imm */ 171662306a36Sopenharmony_ci /* dst = dst << src */ 171762306a36Sopenharmony_ci /* dst = dst >> src */ 171862306a36Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_K: 171962306a36Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_X: 172062306a36Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_K: 172162306a36Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_X: 172262306a36Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_K: 172362306a36Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_X: 172462306a36Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_K: 172562306a36Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_X: 172662306a36Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_K: 172762306a36Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_X: 172862306a36Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_K: 172962306a36Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_X: 173062306a36Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_K: 173162306a36Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_X: 173262306a36Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_K: 173362306a36Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_X: 173462306a36Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_K: 173562306a36Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_X: 173662306a36Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_K: 173762306a36Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_X: 173862306a36Sopenharmony_ci switch (BPF_SRC(code)) { 173962306a36Sopenharmony_ci case BPF_X: 174062306a36Sopenharmony_ci emit_ia32_alu_r64(is64, BPF_OP(code), dst, 174162306a36Sopenharmony_ci src, dstk, sstk, &prog, 174262306a36Sopenharmony_ci bpf_prog->aux); 174362306a36Sopenharmony_ci break; 174462306a36Sopenharmony_ci case BPF_K: 174562306a36Sopenharmony_ci emit_ia32_alu_i64(is64, BPF_OP(code), dst, 174662306a36Sopenharmony_ci imm32, dstk, &prog, 174762306a36Sopenharmony_ci bpf_prog->aux); 174862306a36Sopenharmony_ci break; 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci break; 175162306a36Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_K: 175262306a36Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_X: 175362306a36Sopenharmony_ci switch (BPF_SRC(code)) { 175462306a36Sopenharmony_ci case BPF_X: 175562306a36Sopenharmony_ci emit_ia32_mul_r(dst_lo, src_lo, dstk, 175662306a36Sopenharmony_ci sstk, &prog); 175762306a36Sopenharmony_ci break; 175862306a36Sopenharmony_ci case BPF_K: 175962306a36Sopenharmony_ci /* mov ecx,imm32*/ 176062306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_ECX), 176162306a36Sopenharmony_ci imm32); 176262306a36Sopenharmony_ci emit_ia32_mul_r(dst_lo, IA32_ECX, dstk, 176362306a36Sopenharmony_ci false, &prog); 176462306a36Sopenharmony_ci break; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci if (!bpf_prog->aux->verifier_zext) 176762306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, 0, dstk, &prog); 176862306a36Sopenharmony_ci break; 176962306a36Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_X: 177062306a36Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_X: 177162306a36Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_K: 177262306a36Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_X: 177362306a36Sopenharmony_ci switch (BPF_SRC(code)) { 177462306a36Sopenharmony_ci case BPF_X: 177562306a36Sopenharmony_ci emit_ia32_shift_r(BPF_OP(code), dst_lo, src_lo, 177662306a36Sopenharmony_ci dstk, sstk, &prog); 177762306a36Sopenharmony_ci break; 177862306a36Sopenharmony_ci case BPF_K: 177962306a36Sopenharmony_ci /* mov ecx,imm32*/ 178062306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_ECX), 178162306a36Sopenharmony_ci imm32); 178262306a36Sopenharmony_ci emit_ia32_shift_r(BPF_OP(code), dst_lo, 178362306a36Sopenharmony_ci IA32_ECX, dstk, false, 178462306a36Sopenharmony_ci &prog); 178562306a36Sopenharmony_ci break; 178662306a36Sopenharmony_ci } 178762306a36Sopenharmony_ci if (!bpf_prog->aux->verifier_zext) 178862306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, 0, dstk, &prog); 178962306a36Sopenharmony_ci break; 179062306a36Sopenharmony_ci /* dst = dst / src(imm) */ 179162306a36Sopenharmony_ci /* dst = dst % src(imm) */ 179262306a36Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_K: 179362306a36Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_X: 179462306a36Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_K: 179562306a36Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_X: 179662306a36Sopenharmony_ci switch (BPF_SRC(code)) { 179762306a36Sopenharmony_ci case BPF_X: 179862306a36Sopenharmony_ci emit_ia32_div_mod_r(BPF_OP(code), dst_lo, 179962306a36Sopenharmony_ci src_lo, dstk, sstk, &prog); 180062306a36Sopenharmony_ci break; 180162306a36Sopenharmony_ci case BPF_K: 180262306a36Sopenharmony_ci /* mov ecx,imm32*/ 180362306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_ECX), 180462306a36Sopenharmony_ci imm32); 180562306a36Sopenharmony_ci emit_ia32_div_mod_r(BPF_OP(code), dst_lo, 180662306a36Sopenharmony_ci IA32_ECX, dstk, false, 180762306a36Sopenharmony_ci &prog); 180862306a36Sopenharmony_ci break; 180962306a36Sopenharmony_ci } 181062306a36Sopenharmony_ci if (!bpf_prog->aux->verifier_zext) 181162306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, 0, dstk, &prog); 181262306a36Sopenharmony_ci break; 181362306a36Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_K: 181462306a36Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_X: 181562306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_K: 181662306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_X: 181762306a36Sopenharmony_ci goto notyet; 181862306a36Sopenharmony_ci /* dst = dst >> imm */ 181962306a36Sopenharmony_ci /* dst = dst << imm */ 182062306a36Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_K: 182162306a36Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_K: 182262306a36Sopenharmony_ci if (unlikely(imm32 > 31)) 182362306a36Sopenharmony_ci return -EINVAL; 182462306a36Sopenharmony_ci /* mov ecx,imm32*/ 182562306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_ECX), imm32); 182662306a36Sopenharmony_ci emit_ia32_shift_r(BPF_OP(code), dst_lo, IA32_ECX, dstk, 182762306a36Sopenharmony_ci false, &prog); 182862306a36Sopenharmony_ci if (!bpf_prog->aux->verifier_zext) 182962306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, 0, dstk, &prog); 183062306a36Sopenharmony_ci break; 183162306a36Sopenharmony_ci /* dst = dst << imm */ 183262306a36Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_K: 183362306a36Sopenharmony_ci if (unlikely(imm32 > 63)) 183462306a36Sopenharmony_ci return -EINVAL; 183562306a36Sopenharmony_ci emit_ia32_lsh_i64(dst, imm32, dstk, &prog); 183662306a36Sopenharmony_ci break; 183762306a36Sopenharmony_ci /* dst = dst >> imm */ 183862306a36Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_K: 183962306a36Sopenharmony_ci if (unlikely(imm32 > 63)) 184062306a36Sopenharmony_ci return -EINVAL; 184162306a36Sopenharmony_ci emit_ia32_rsh_i64(dst, imm32, dstk, &prog); 184262306a36Sopenharmony_ci break; 184362306a36Sopenharmony_ci /* dst = dst << src */ 184462306a36Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_X: 184562306a36Sopenharmony_ci emit_ia32_lsh_r64(dst, src, dstk, sstk, &prog); 184662306a36Sopenharmony_ci break; 184762306a36Sopenharmony_ci /* dst = dst >> src */ 184862306a36Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_X: 184962306a36Sopenharmony_ci emit_ia32_rsh_r64(dst, src, dstk, sstk, &prog); 185062306a36Sopenharmony_ci break; 185162306a36Sopenharmony_ci /* dst = dst >> src (signed) */ 185262306a36Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_X: 185362306a36Sopenharmony_ci emit_ia32_arsh_r64(dst, src, dstk, sstk, &prog); 185462306a36Sopenharmony_ci break; 185562306a36Sopenharmony_ci /* dst = dst >> imm (signed) */ 185662306a36Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_K: 185762306a36Sopenharmony_ci if (unlikely(imm32 > 63)) 185862306a36Sopenharmony_ci return -EINVAL; 185962306a36Sopenharmony_ci emit_ia32_arsh_i64(dst, imm32, dstk, &prog); 186062306a36Sopenharmony_ci break; 186162306a36Sopenharmony_ci /* dst = ~dst */ 186262306a36Sopenharmony_ci case BPF_ALU | BPF_NEG: 186362306a36Sopenharmony_ci emit_ia32_alu_i(is64, false, BPF_OP(code), 186462306a36Sopenharmony_ci dst_lo, 0, dstk, &prog); 186562306a36Sopenharmony_ci if (!bpf_prog->aux->verifier_zext) 186662306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, 0, dstk, &prog); 186762306a36Sopenharmony_ci break; 186862306a36Sopenharmony_ci /* dst = ~dst (64 bit) */ 186962306a36Sopenharmony_ci case BPF_ALU64 | BPF_NEG: 187062306a36Sopenharmony_ci emit_ia32_neg64(dst, dstk, &prog); 187162306a36Sopenharmony_ci break; 187262306a36Sopenharmony_ci /* dst = dst * src/imm */ 187362306a36Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_X: 187462306a36Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_K: 187562306a36Sopenharmony_ci switch (BPF_SRC(code)) { 187662306a36Sopenharmony_ci case BPF_X: 187762306a36Sopenharmony_ci emit_ia32_mul_r64(dst, src, dstk, sstk, &prog); 187862306a36Sopenharmony_ci break; 187962306a36Sopenharmony_ci case BPF_K: 188062306a36Sopenharmony_ci emit_ia32_mul_i64(dst, imm32, dstk, &prog); 188162306a36Sopenharmony_ci break; 188262306a36Sopenharmony_ci } 188362306a36Sopenharmony_ci break; 188462306a36Sopenharmony_ci /* dst = htole(dst) */ 188562306a36Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_LE: 188662306a36Sopenharmony_ci emit_ia32_to_le_r64(dst, imm32, dstk, &prog, 188762306a36Sopenharmony_ci bpf_prog->aux); 188862306a36Sopenharmony_ci break; 188962306a36Sopenharmony_ci /* dst = htobe(dst) */ 189062306a36Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_BE: 189162306a36Sopenharmony_ci emit_ia32_to_be_r64(dst, imm32, dstk, &prog, 189262306a36Sopenharmony_ci bpf_prog->aux); 189362306a36Sopenharmony_ci break; 189462306a36Sopenharmony_ci /* dst = imm64 */ 189562306a36Sopenharmony_ci case BPF_LD | BPF_IMM | BPF_DW: { 189662306a36Sopenharmony_ci s32 hi, lo = imm32; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci hi = insn[1].imm; 189962306a36Sopenharmony_ci emit_ia32_mov_i(dst_lo, lo, dstk, &prog); 190062306a36Sopenharmony_ci emit_ia32_mov_i(dst_hi, hi, dstk, &prog); 190162306a36Sopenharmony_ci insn++; 190262306a36Sopenharmony_ci i++; 190362306a36Sopenharmony_ci break; 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci /* speculation barrier */ 190662306a36Sopenharmony_ci case BPF_ST | BPF_NOSPEC: 190762306a36Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_XMM2)) 190862306a36Sopenharmony_ci /* Emit 'lfence' */ 190962306a36Sopenharmony_ci EMIT3(0x0F, 0xAE, 0xE8); 191062306a36Sopenharmony_ci break; 191162306a36Sopenharmony_ci /* ST: *(u8*)(dst_reg + off) = imm */ 191262306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_H: 191362306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_B: 191462306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_W: 191562306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_DW: 191662306a36Sopenharmony_ci if (dstk) 191762306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 191862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 191962306a36Sopenharmony_ci STACK_VAR(dst_lo)); 192062306a36Sopenharmony_ci else 192162306a36Sopenharmony_ci /* mov eax,dst_lo */ 192262306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, dst_lo, IA32_EAX)); 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci switch (BPF_SIZE(code)) { 192562306a36Sopenharmony_ci case BPF_B: 192662306a36Sopenharmony_ci EMIT(0xC6, 1); break; 192762306a36Sopenharmony_ci case BPF_H: 192862306a36Sopenharmony_ci EMIT2(0x66, 0xC7); break; 192962306a36Sopenharmony_ci case BPF_W: 193062306a36Sopenharmony_ci case BPF_DW: 193162306a36Sopenharmony_ci EMIT(0xC7, 1); break; 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci if (is_imm8(insn->off)) 193562306a36Sopenharmony_ci EMIT2(add_1reg(0x40, IA32_EAX), insn->off); 193662306a36Sopenharmony_ci else 193762306a36Sopenharmony_ci EMIT1_off32(add_1reg(0x80, IA32_EAX), 193862306a36Sopenharmony_ci insn->off); 193962306a36Sopenharmony_ci EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(code))); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci if (BPF_SIZE(code) == BPF_DW) { 194262306a36Sopenharmony_ci u32 hi; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci hi = imm32 & (1<<31) ? (u32)~0 : 0; 194562306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0x80, IA32_EAX), 194662306a36Sopenharmony_ci insn->off + 4); 194762306a36Sopenharmony_ci EMIT(hi, 4); 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci break; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci /* STX: *(u8*)(dst_reg + off) = src_reg */ 195262306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_B: 195362306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_H: 195462306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_W: 195562306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_DW: 195662306a36Sopenharmony_ci if (dstk) 195762306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 195862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 195962306a36Sopenharmony_ci STACK_VAR(dst_lo)); 196062306a36Sopenharmony_ci else 196162306a36Sopenharmony_ci /* mov eax,dst_lo */ 196262306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, dst_lo, IA32_EAX)); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci if (sstk) 196562306a36Sopenharmony_ci /* mov edx,dword ptr [ebp+off] */ 196662306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 196762306a36Sopenharmony_ci STACK_VAR(src_lo)); 196862306a36Sopenharmony_ci else 196962306a36Sopenharmony_ci /* mov edx,src_lo */ 197062306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_EDX)); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci switch (BPF_SIZE(code)) { 197362306a36Sopenharmony_ci case BPF_B: 197462306a36Sopenharmony_ci EMIT(0x88, 1); break; 197562306a36Sopenharmony_ci case BPF_H: 197662306a36Sopenharmony_ci EMIT2(0x66, 0x89); break; 197762306a36Sopenharmony_ci case BPF_W: 197862306a36Sopenharmony_ci case BPF_DW: 197962306a36Sopenharmony_ci EMIT(0x89, 1); break; 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci if (is_imm8(insn->off)) 198362306a36Sopenharmony_ci EMIT2(add_2reg(0x40, IA32_EAX, IA32_EDX), 198462306a36Sopenharmony_ci insn->off); 198562306a36Sopenharmony_ci else 198662306a36Sopenharmony_ci EMIT1_off32(add_2reg(0x80, IA32_EAX, IA32_EDX), 198762306a36Sopenharmony_ci insn->off); 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci if (BPF_SIZE(code) == BPF_DW) { 199062306a36Sopenharmony_ci if (sstk) 199162306a36Sopenharmony_ci /* mov edi,dword ptr [ebp+off] */ 199262306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, 199362306a36Sopenharmony_ci IA32_EDX), 199462306a36Sopenharmony_ci STACK_VAR(src_hi)); 199562306a36Sopenharmony_ci else 199662306a36Sopenharmony_ci /* mov edi,src_hi */ 199762306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, src_hi, 199862306a36Sopenharmony_ci IA32_EDX)); 199962306a36Sopenharmony_ci EMIT1(0x89); 200062306a36Sopenharmony_ci if (is_imm8(insn->off + 4)) { 200162306a36Sopenharmony_ci EMIT2(add_2reg(0x40, IA32_EAX, 200262306a36Sopenharmony_ci IA32_EDX), 200362306a36Sopenharmony_ci insn->off + 4); 200462306a36Sopenharmony_ci } else { 200562306a36Sopenharmony_ci EMIT1(add_2reg(0x80, IA32_EAX, 200662306a36Sopenharmony_ci IA32_EDX)); 200762306a36Sopenharmony_ci EMIT(insn->off + 4, 4); 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci } 201062306a36Sopenharmony_ci break; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* LDX: dst_reg = *(u8*)(src_reg + off) */ 201362306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_B: 201462306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_H: 201562306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_W: 201662306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_DW: 201762306a36Sopenharmony_ci if (sstk) 201862306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 201962306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 202062306a36Sopenharmony_ci STACK_VAR(src_lo)); 202162306a36Sopenharmony_ci else 202262306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 202362306a36Sopenharmony_ci EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_EAX)); 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci switch (BPF_SIZE(code)) { 202662306a36Sopenharmony_ci case BPF_B: 202762306a36Sopenharmony_ci EMIT2(0x0F, 0xB6); break; 202862306a36Sopenharmony_ci case BPF_H: 202962306a36Sopenharmony_ci EMIT2(0x0F, 0xB7); break; 203062306a36Sopenharmony_ci case BPF_W: 203162306a36Sopenharmony_ci case BPF_DW: 203262306a36Sopenharmony_ci EMIT(0x8B, 1); break; 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci if (is_imm8(insn->off)) 203662306a36Sopenharmony_ci EMIT2(add_2reg(0x40, IA32_EAX, IA32_EDX), 203762306a36Sopenharmony_ci insn->off); 203862306a36Sopenharmony_ci else 203962306a36Sopenharmony_ci EMIT1_off32(add_2reg(0x80, IA32_EAX, IA32_EDX), 204062306a36Sopenharmony_ci insn->off); 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci if (dstk) 204362306a36Sopenharmony_ci /* mov dword ptr [ebp+off],edx */ 204462306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EDX), 204562306a36Sopenharmony_ci STACK_VAR(dst_lo)); 204662306a36Sopenharmony_ci else 204762306a36Sopenharmony_ci /* mov dst_lo,edx */ 204862306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dst_lo, IA32_EDX)); 204962306a36Sopenharmony_ci switch (BPF_SIZE(code)) { 205062306a36Sopenharmony_ci case BPF_B: 205162306a36Sopenharmony_ci case BPF_H: 205262306a36Sopenharmony_ci case BPF_W: 205362306a36Sopenharmony_ci if (bpf_prog->aux->verifier_zext) 205462306a36Sopenharmony_ci break; 205562306a36Sopenharmony_ci if (dstk) { 205662306a36Sopenharmony_ci EMIT3(0xC7, add_1reg(0x40, IA32_EBP), 205762306a36Sopenharmony_ci STACK_VAR(dst_hi)); 205862306a36Sopenharmony_ci EMIT(0x0, 4); 205962306a36Sopenharmony_ci } else { 206062306a36Sopenharmony_ci /* xor dst_hi,dst_hi */ 206162306a36Sopenharmony_ci EMIT2(0x33, 206262306a36Sopenharmony_ci add_2reg(0xC0, dst_hi, dst_hi)); 206362306a36Sopenharmony_ci } 206462306a36Sopenharmony_ci break; 206562306a36Sopenharmony_ci case BPF_DW: 206662306a36Sopenharmony_ci EMIT2_off32(0x8B, 206762306a36Sopenharmony_ci add_2reg(0x80, IA32_EAX, IA32_EDX), 206862306a36Sopenharmony_ci insn->off + 4); 206962306a36Sopenharmony_ci if (dstk) 207062306a36Sopenharmony_ci EMIT3(0x89, 207162306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 207262306a36Sopenharmony_ci IA32_EDX), 207362306a36Sopenharmony_ci STACK_VAR(dst_hi)); 207462306a36Sopenharmony_ci else 207562306a36Sopenharmony_ci EMIT2(0x89, 207662306a36Sopenharmony_ci add_2reg(0xC0, dst_hi, IA32_EDX)); 207762306a36Sopenharmony_ci break; 207862306a36Sopenharmony_ci default: 207962306a36Sopenharmony_ci break; 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci break; 208262306a36Sopenharmony_ci /* call */ 208362306a36Sopenharmony_ci case BPF_JMP | BPF_CALL: 208462306a36Sopenharmony_ci { 208562306a36Sopenharmony_ci const u8 *r1 = bpf2ia32[BPF_REG_1]; 208662306a36Sopenharmony_ci const u8 *r2 = bpf2ia32[BPF_REG_2]; 208762306a36Sopenharmony_ci const u8 *r3 = bpf2ia32[BPF_REG_3]; 208862306a36Sopenharmony_ci const u8 *r4 = bpf2ia32[BPF_REG_4]; 208962306a36Sopenharmony_ci const u8 *r5 = bpf2ia32[BPF_REG_5]; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci if (insn->src_reg == BPF_PSEUDO_CALL) 209262306a36Sopenharmony_ci goto notyet; 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { 209562306a36Sopenharmony_ci int err; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci err = emit_kfunc_call(bpf_prog, 209862306a36Sopenharmony_ci image + addrs[i], 209962306a36Sopenharmony_ci insn, &prog); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci if (err) 210262306a36Sopenharmony_ci return err; 210362306a36Sopenharmony_ci break; 210462306a36Sopenharmony_ci } 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci func = (u8 *) __bpf_call_base + imm32; 210762306a36Sopenharmony_ci jmp_offset = func - (image + addrs[i]); 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci if (!imm32 || !is_simm32(jmp_offset)) { 211062306a36Sopenharmony_ci pr_err("unsupported BPF func %d addr %p image %p\n", 211162306a36Sopenharmony_ci imm32, func, image); 211262306a36Sopenharmony_ci return -EINVAL; 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci /* mov eax,dword ptr [ebp+off] */ 211662306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 211762306a36Sopenharmony_ci STACK_VAR(r1[0])); 211862306a36Sopenharmony_ci /* mov edx,dword ptr [ebp+off] */ 211962306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX), 212062306a36Sopenharmony_ci STACK_VAR(r1[1])); 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci emit_push_r64(r5, &prog); 212362306a36Sopenharmony_ci emit_push_r64(r4, &prog); 212462306a36Sopenharmony_ci emit_push_r64(r3, &prog); 212562306a36Sopenharmony_ci emit_push_r64(r2, &prog); 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci EMIT1_off32(0xE8, jmp_offset + 9); 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci /* mov dword ptr [ebp+off],eax */ 213062306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EAX), 213162306a36Sopenharmony_ci STACK_VAR(r0[0])); 213262306a36Sopenharmony_ci /* mov dword ptr [ebp+off],edx */ 213362306a36Sopenharmony_ci EMIT3(0x89, add_2reg(0x40, IA32_EBP, IA32_EDX), 213462306a36Sopenharmony_ci STACK_VAR(r0[1])); 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci /* add esp,32 */ 213762306a36Sopenharmony_ci EMIT3(0x83, add_1reg(0xC0, IA32_ESP), 32); 213862306a36Sopenharmony_ci break; 213962306a36Sopenharmony_ci } 214062306a36Sopenharmony_ci case BPF_JMP | BPF_TAIL_CALL: 214162306a36Sopenharmony_ci emit_bpf_tail_call(&prog, image + addrs[i - 1]); 214262306a36Sopenharmony_ci break; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci /* cond jump */ 214562306a36Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_X: 214662306a36Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_X: 214762306a36Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_X: 214862306a36Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_X: 214962306a36Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_X: 215062306a36Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_X: 215162306a36Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_X: 215262306a36Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_X: 215362306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_X: 215462306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_X: 215562306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_X: 215662306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_X: 215762306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_X: 215862306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_X: 215962306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_X: 216062306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_X: { 216162306a36Sopenharmony_ci bool is_jmp64 = BPF_CLASS(insn->code) == BPF_JMP; 216262306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 216362306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 216462306a36Sopenharmony_ci u8 sreg_lo = sstk ? IA32_ECX : src_lo; 216562306a36Sopenharmony_ci u8 sreg_hi = sstk ? IA32_EBX : src_hi; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci if (dstk) { 216862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 216962306a36Sopenharmony_ci STACK_VAR(dst_lo)); 217062306a36Sopenharmony_ci if (is_jmp64) 217162306a36Sopenharmony_ci EMIT3(0x8B, 217262306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 217362306a36Sopenharmony_ci IA32_EDX), 217462306a36Sopenharmony_ci STACK_VAR(dst_hi)); 217562306a36Sopenharmony_ci } 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci if (sstk) { 217862306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), 217962306a36Sopenharmony_ci STACK_VAR(src_lo)); 218062306a36Sopenharmony_ci if (is_jmp64) 218162306a36Sopenharmony_ci EMIT3(0x8B, 218262306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 218362306a36Sopenharmony_ci IA32_EBX), 218462306a36Sopenharmony_ci STACK_VAR(src_hi)); 218562306a36Sopenharmony_ci } 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci if (is_jmp64) { 218862306a36Sopenharmony_ci /* cmp dreg_hi,sreg_hi */ 218962306a36Sopenharmony_ci EMIT2(0x39, add_2reg(0xC0, dreg_hi, sreg_hi)); 219062306a36Sopenharmony_ci EMIT2(IA32_JNE, 2); 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci /* cmp dreg_lo,sreg_lo */ 219362306a36Sopenharmony_ci EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo)); 219462306a36Sopenharmony_ci goto emit_cond_jmp; 219562306a36Sopenharmony_ci } 219662306a36Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_X: 219762306a36Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_X: 219862306a36Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_X: 219962306a36Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_X: { 220062306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 220162306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 220262306a36Sopenharmony_ci u8 sreg_lo = sstk ? IA32_ECX : src_lo; 220362306a36Sopenharmony_ci u8 sreg_hi = sstk ? IA32_EBX : src_hi; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci if (dstk) { 220662306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 220762306a36Sopenharmony_ci STACK_VAR(dst_lo)); 220862306a36Sopenharmony_ci EMIT3(0x8B, 220962306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 221062306a36Sopenharmony_ci IA32_EDX), 221162306a36Sopenharmony_ci STACK_VAR(dst_hi)); 221262306a36Sopenharmony_ci } 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci if (sstk) { 221562306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), 221662306a36Sopenharmony_ci STACK_VAR(src_lo)); 221762306a36Sopenharmony_ci EMIT3(0x8B, 221862306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 221962306a36Sopenharmony_ci IA32_EBX), 222062306a36Sopenharmony_ci STACK_VAR(src_hi)); 222162306a36Sopenharmony_ci } 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci /* cmp dreg_hi,sreg_hi */ 222462306a36Sopenharmony_ci EMIT2(0x39, add_2reg(0xC0, dreg_hi, sreg_hi)); 222562306a36Sopenharmony_ci EMIT2(IA32_JNE, 10); 222662306a36Sopenharmony_ci /* cmp dreg_lo,sreg_lo */ 222762306a36Sopenharmony_ci EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo)); 222862306a36Sopenharmony_ci goto emit_cond_jmp_signed; 222962306a36Sopenharmony_ci } 223062306a36Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_X: 223162306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_X: { 223262306a36Sopenharmony_ci bool is_jmp64 = BPF_CLASS(insn->code) == BPF_JMP; 223362306a36Sopenharmony_ci u8 dreg_lo = IA32_EAX; 223462306a36Sopenharmony_ci u8 dreg_hi = IA32_EDX; 223562306a36Sopenharmony_ci u8 sreg_lo = sstk ? IA32_ECX : src_lo; 223662306a36Sopenharmony_ci u8 sreg_hi = sstk ? IA32_EBX : src_hi; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci if (dstk) { 223962306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 224062306a36Sopenharmony_ci STACK_VAR(dst_lo)); 224162306a36Sopenharmony_ci if (is_jmp64) 224262306a36Sopenharmony_ci EMIT3(0x8B, 224362306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 224462306a36Sopenharmony_ci IA32_EDX), 224562306a36Sopenharmony_ci STACK_VAR(dst_hi)); 224662306a36Sopenharmony_ci } else { 224762306a36Sopenharmony_ci /* mov dreg_lo,dst_lo */ 224862306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_lo, dst_lo)); 224962306a36Sopenharmony_ci if (is_jmp64) 225062306a36Sopenharmony_ci /* mov dreg_hi,dst_hi */ 225162306a36Sopenharmony_ci EMIT2(0x89, 225262306a36Sopenharmony_ci add_2reg(0xC0, dreg_hi, dst_hi)); 225362306a36Sopenharmony_ci } 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci if (sstk) { 225662306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), 225762306a36Sopenharmony_ci STACK_VAR(src_lo)); 225862306a36Sopenharmony_ci if (is_jmp64) 225962306a36Sopenharmony_ci EMIT3(0x8B, 226062306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 226162306a36Sopenharmony_ci IA32_EBX), 226262306a36Sopenharmony_ci STACK_VAR(src_hi)); 226362306a36Sopenharmony_ci } 226462306a36Sopenharmony_ci /* and dreg_lo,sreg_lo */ 226562306a36Sopenharmony_ci EMIT2(0x23, add_2reg(0xC0, sreg_lo, dreg_lo)); 226662306a36Sopenharmony_ci if (is_jmp64) { 226762306a36Sopenharmony_ci /* and dreg_hi,sreg_hi */ 226862306a36Sopenharmony_ci EMIT2(0x23, add_2reg(0xC0, sreg_hi, dreg_hi)); 226962306a36Sopenharmony_ci /* or dreg_lo,dreg_hi */ 227062306a36Sopenharmony_ci EMIT2(0x09, add_2reg(0xC0, dreg_lo, dreg_hi)); 227162306a36Sopenharmony_ci } 227262306a36Sopenharmony_ci goto emit_cond_jmp; 227362306a36Sopenharmony_ci } 227462306a36Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_K: 227562306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_K: { 227662306a36Sopenharmony_ci bool is_jmp64 = BPF_CLASS(insn->code) == BPF_JMP; 227762306a36Sopenharmony_ci u8 dreg_lo = IA32_EAX; 227862306a36Sopenharmony_ci u8 dreg_hi = IA32_EDX; 227962306a36Sopenharmony_ci u8 sreg_lo = IA32_ECX; 228062306a36Sopenharmony_ci u8 sreg_hi = IA32_EBX; 228162306a36Sopenharmony_ci u32 hi; 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci if (dstk) { 228462306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 228562306a36Sopenharmony_ci STACK_VAR(dst_lo)); 228662306a36Sopenharmony_ci if (is_jmp64) 228762306a36Sopenharmony_ci EMIT3(0x8B, 228862306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 228962306a36Sopenharmony_ci IA32_EDX), 229062306a36Sopenharmony_ci STACK_VAR(dst_hi)); 229162306a36Sopenharmony_ci } else { 229262306a36Sopenharmony_ci /* mov dreg_lo,dst_lo */ 229362306a36Sopenharmony_ci EMIT2(0x89, add_2reg(0xC0, dreg_lo, dst_lo)); 229462306a36Sopenharmony_ci if (is_jmp64) 229562306a36Sopenharmony_ci /* mov dreg_hi,dst_hi */ 229662306a36Sopenharmony_ci EMIT2(0x89, 229762306a36Sopenharmony_ci add_2reg(0xC0, dreg_hi, dst_hi)); 229862306a36Sopenharmony_ci } 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci /* mov ecx,imm32 */ 230162306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, sreg_lo), imm32); 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci /* and dreg_lo,sreg_lo */ 230462306a36Sopenharmony_ci EMIT2(0x23, add_2reg(0xC0, sreg_lo, dreg_lo)); 230562306a36Sopenharmony_ci if (is_jmp64) { 230662306a36Sopenharmony_ci hi = imm32 & (1 << 31) ? (u32)~0 : 0; 230762306a36Sopenharmony_ci /* mov ebx,imm32 */ 230862306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, sreg_hi), hi); 230962306a36Sopenharmony_ci /* and dreg_hi,sreg_hi */ 231062306a36Sopenharmony_ci EMIT2(0x23, add_2reg(0xC0, sreg_hi, dreg_hi)); 231162306a36Sopenharmony_ci /* or dreg_lo,dreg_hi */ 231262306a36Sopenharmony_ci EMIT2(0x09, add_2reg(0xC0, dreg_lo, dreg_hi)); 231362306a36Sopenharmony_ci } 231462306a36Sopenharmony_ci goto emit_cond_jmp; 231562306a36Sopenharmony_ci } 231662306a36Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_K: 231762306a36Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_K: 231862306a36Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_K: 231962306a36Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_K: 232062306a36Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_K: 232162306a36Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_K: 232262306a36Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_K: 232362306a36Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_K: 232462306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_K: 232562306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_K: 232662306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_K: 232762306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_K: 232862306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_K: 232962306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_K: 233062306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_K: 233162306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_K: { 233262306a36Sopenharmony_ci bool is_jmp64 = BPF_CLASS(insn->code) == BPF_JMP; 233362306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 233462306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 233562306a36Sopenharmony_ci u8 sreg_lo = IA32_ECX; 233662306a36Sopenharmony_ci u8 sreg_hi = IA32_EBX; 233762306a36Sopenharmony_ci u32 hi; 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci if (dstk) { 234062306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 234162306a36Sopenharmony_ci STACK_VAR(dst_lo)); 234262306a36Sopenharmony_ci if (is_jmp64) 234362306a36Sopenharmony_ci EMIT3(0x8B, 234462306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 234562306a36Sopenharmony_ci IA32_EDX), 234662306a36Sopenharmony_ci STACK_VAR(dst_hi)); 234762306a36Sopenharmony_ci } 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci /* mov ecx,imm32 */ 235062306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_ECX), imm32); 235162306a36Sopenharmony_ci if (is_jmp64) { 235262306a36Sopenharmony_ci hi = imm32 & (1 << 31) ? (u32)~0 : 0; 235362306a36Sopenharmony_ci /* mov ebx,imm32 */ 235462306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_EBX), hi); 235562306a36Sopenharmony_ci /* cmp dreg_hi,sreg_hi */ 235662306a36Sopenharmony_ci EMIT2(0x39, add_2reg(0xC0, dreg_hi, sreg_hi)); 235762306a36Sopenharmony_ci EMIT2(IA32_JNE, 2); 235862306a36Sopenharmony_ci } 235962306a36Sopenharmony_ci /* cmp dreg_lo,sreg_lo */ 236062306a36Sopenharmony_ci EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo)); 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ciemit_cond_jmp: jmp_cond = get_cond_jmp_opcode(BPF_OP(code), false); 236362306a36Sopenharmony_ci if (jmp_cond == COND_JMP_OPCODE_INVALID) 236462306a36Sopenharmony_ci return -EFAULT; 236562306a36Sopenharmony_ci jmp_offset = addrs[i + insn->off] - addrs[i]; 236662306a36Sopenharmony_ci if (is_imm8(jmp_offset)) { 236762306a36Sopenharmony_ci EMIT2(jmp_cond, jmp_offset); 236862306a36Sopenharmony_ci } else if (is_simm32(jmp_offset)) { 236962306a36Sopenharmony_ci EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset); 237062306a36Sopenharmony_ci } else { 237162306a36Sopenharmony_ci pr_err("cond_jmp gen bug %llx\n", jmp_offset); 237262306a36Sopenharmony_ci return -EFAULT; 237362306a36Sopenharmony_ci } 237462306a36Sopenharmony_ci break; 237562306a36Sopenharmony_ci } 237662306a36Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_K: 237762306a36Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_K: 237862306a36Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_K: 237962306a36Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_K: { 238062306a36Sopenharmony_ci u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 238162306a36Sopenharmony_ci u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 238262306a36Sopenharmony_ci u8 sreg_lo = IA32_ECX; 238362306a36Sopenharmony_ci u8 sreg_hi = IA32_EBX; 238462306a36Sopenharmony_ci u32 hi; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci if (dstk) { 238762306a36Sopenharmony_ci EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), 238862306a36Sopenharmony_ci STACK_VAR(dst_lo)); 238962306a36Sopenharmony_ci EMIT3(0x8B, 239062306a36Sopenharmony_ci add_2reg(0x40, IA32_EBP, 239162306a36Sopenharmony_ci IA32_EDX), 239262306a36Sopenharmony_ci STACK_VAR(dst_hi)); 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci /* mov ecx,imm32 */ 239662306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_ECX), imm32); 239762306a36Sopenharmony_ci hi = imm32 & (1 << 31) ? (u32)~0 : 0; 239862306a36Sopenharmony_ci /* mov ebx,imm32 */ 239962306a36Sopenharmony_ci EMIT2_off32(0xC7, add_1reg(0xC0, IA32_EBX), hi); 240062306a36Sopenharmony_ci /* cmp dreg_hi,sreg_hi */ 240162306a36Sopenharmony_ci EMIT2(0x39, add_2reg(0xC0, dreg_hi, sreg_hi)); 240262306a36Sopenharmony_ci EMIT2(IA32_JNE, 10); 240362306a36Sopenharmony_ci /* cmp dreg_lo,sreg_lo */ 240462306a36Sopenharmony_ci EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo)); 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci /* 240762306a36Sopenharmony_ci * For simplicity of branch offset computation, 240862306a36Sopenharmony_ci * let's use fixed jump coding here. 240962306a36Sopenharmony_ci */ 241062306a36Sopenharmony_ciemit_cond_jmp_signed: /* Check the condition for low 32-bit comparison */ 241162306a36Sopenharmony_ci jmp_cond = get_cond_jmp_opcode(BPF_OP(code), true); 241262306a36Sopenharmony_ci if (jmp_cond == COND_JMP_OPCODE_INVALID) 241362306a36Sopenharmony_ci return -EFAULT; 241462306a36Sopenharmony_ci jmp_offset = addrs[i + insn->off] - addrs[i] + 8; 241562306a36Sopenharmony_ci if (is_simm32(jmp_offset)) { 241662306a36Sopenharmony_ci EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset); 241762306a36Sopenharmony_ci } else { 241862306a36Sopenharmony_ci pr_err("cond_jmp gen bug %llx\n", jmp_offset); 241962306a36Sopenharmony_ci return -EFAULT; 242062306a36Sopenharmony_ci } 242162306a36Sopenharmony_ci EMIT2(0xEB, 6); 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci /* Check the condition for high 32-bit comparison */ 242462306a36Sopenharmony_ci jmp_cond = get_cond_jmp_opcode(BPF_OP(code), false); 242562306a36Sopenharmony_ci if (jmp_cond == COND_JMP_OPCODE_INVALID) 242662306a36Sopenharmony_ci return -EFAULT; 242762306a36Sopenharmony_ci jmp_offset = addrs[i + insn->off] - addrs[i]; 242862306a36Sopenharmony_ci if (is_simm32(jmp_offset)) { 242962306a36Sopenharmony_ci EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset); 243062306a36Sopenharmony_ci } else { 243162306a36Sopenharmony_ci pr_err("cond_jmp gen bug %llx\n", jmp_offset); 243262306a36Sopenharmony_ci return -EFAULT; 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci break; 243562306a36Sopenharmony_ci } 243662306a36Sopenharmony_ci case BPF_JMP | BPF_JA: 243762306a36Sopenharmony_ci if (insn->off == -1) 243862306a36Sopenharmony_ci /* -1 jmp instructions will always jump 243962306a36Sopenharmony_ci * backwards two bytes. Explicitly handling 244062306a36Sopenharmony_ci * this case avoids wasting too many passes 244162306a36Sopenharmony_ci * when there are long sequences of replaced 244262306a36Sopenharmony_ci * dead code. 244362306a36Sopenharmony_ci */ 244462306a36Sopenharmony_ci jmp_offset = -2; 244562306a36Sopenharmony_ci else 244662306a36Sopenharmony_ci jmp_offset = addrs[i + insn->off] - addrs[i]; 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci if (!jmp_offset) 244962306a36Sopenharmony_ci /* Optimize out nop jumps */ 245062306a36Sopenharmony_ci break; 245162306a36Sopenharmony_ciemit_jmp: 245262306a36Sopenharmony_ci if (is_imm8(jmp_offset)) { 245362306a36Sopenharmony_ci EMIT2(0xEB, jmp_offset); 245462306a36Sopenharmony_ci } else if (is_simm32(jmp_offset)) { 245562306a36Sopenharmony_ci EMIT1_off32(0xE9, jmp_offset); 245662306a36Sopenharmony_ci } else { 245762306a36Sopenharmony_ci pr_err("jmp gen bug %llx\n", jmp_offset); 245862306a36Sopenharmony_ci return -EFAULT; 245962306a36Sopenharmony_ci } 246062306a36Sopenharmony_ci break; 246162306a36Sopenharmony_ci case BPF_STX | BPF_ATOMIC | BPF_W: 246262306a36Sopenharmony_ci case BPF_STX | BPF_ATOMIC | BPF_DW: 246362306a36Sopenharmony_ci goto notyet; 246462306a36Sopenharmony_ci case BPF_JMP | BPF_EXIT: 246562306a36Sopenharmony_ci if (seen_exit) { 246662306a36Sopenharmony_ci jmp_offset = ctx->cleanup_addr - addrs[i]; 246762306a36Sopenharmony_ci goto emit_jmp; 246862306a36Sopenharmony_ci } 246962306a36Sopenharmony_ci seen_exit = true; 247062306a36Sopenharmony_ci /* Update cleanup_addr */ 247162306a36Sopenharmony_ci ctx->cleanup_addr = proglen; 247262306a36Sopenharmony_ci emit_epilogue(&prog, bpf_prog->aux->stack_depth); 247362306a36Sopenharmony_ci break; 247462306a36Sopenharmony_cinotyet: 247562306a36Sopenharmony_ci pr_info_once("*** NOT YET: opcode %02x ***\n", code); 247662306a36Sopenharmony_ci return -EFAULT; 247762306a36Sopenharmony_ci default: 247862306a36Sopenharmony_ci /* 247962306a36Sopenharmony_ci * This error will be seen if new instruction was added 248062306a36Sopenharmony_ci * to interpreter, but not to JIT or if there is junk in 248162306a36Sopenharmony_ci * bpf_prog 248262306a36Sopenharmony_ci */ 248362306a36Sopenharmony_ci pr_err("bpf_jit: unknown opcode %02x\n", code); 248462306a36Sopenharmony_ci return -EINVAL; 248562306a36Sopenharmony_ci } 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci ilen = prog - temp; 248862306a36Sopenharmony_ci if (ilen > BPF_MAX_INSN_SIZE) { 248962306a36Sopenharmony_ci pr_err("bpf_jit: fatal insn size error\n"); 249062306a36Sopenharmony_ci return -EFAULT; 249162306a36Sopenharmony_ci } 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci if (image) { 249462306a36Sopenharmony_ci /* 249562306a36Sopenharmony_ci * When populating the image, assert that: 249662306a36Sopenharmony_ci * 249762306a36Sopenharmony_ci * i) We do not write beyond the allocated space, and 249862306a36Sopenharmony_ci * ii) addrs[i] did not change from the prior run, in order 249962306a36Sopenharmony_ci * to validate assumptions made for computing branch 250062306a36Sopenharmony_ci * displacements. 250162306a36Sopenharmony_ci */ 250262306a36Sopenharmony_ci if (unlikely(proglen + ilen > oldproglen || 250362306a36Sopenharmony_ci proglen + ilen != addrs[i])) { 250462306a36Sopenharmony_ci pr_err("bpf_jit: fatal error\n"); 250562306a36Sopenharmony_ci return -EFAULT; 250662306a36Sopenharmony_ci } 250762306a36Sopenharmony_ci memcpy(image + proglen, temp, ilen); 250862306a36Sopenharmony_ci } 250962306a36Sopenharmony_ci proglen += ilen; 251062306a36Sopenharmony_ci addrs[i] = proglen; 251162306a36Sopenharmony_ci prog = temp; 251262306a36Sopenharmony_ci } 251362306a36Sopenharmony_ci return proglen; 251462306a36Sopenharmony_ci} 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_cibool bpf_jit_needs_zext(void) 251762306a36Sopenharmony_ci{ 251862306a36Sopenharmony_ci return true; 251962306a36Sopenharmony_ci} 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_cistruct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 252262306a36Sopenharmony_ci{ 252362306a36Sopenharmony_ci struct bpf_binary_header *header = NULL; 252462306a36Sopenharmony_ci struct bpf_prog *tmp, *orig_prog = prog; 252562306a36Sopenharmony_ci int proglen, oldproglen = 0; 252662306a36Sopenharmony_ci struct jit_context ctx = {}; 252762306a36Sopenharmony_ci bool tmp_blinded = false; 252862306a36Sopenharmony_ci u8 *image = NULL; 252962306a36Sopenharmony_ci int *addrs; 253062306a36Sopenharmony_ci int pass; 253162306a36Sopenharmony_ci int i; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci if (!prog->jit_requested) 253462306a36Sopenharmony_ci return orig_prog; 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci tmp = bpf_jit_blind_constants(prog); 253762306a36Sopenharmony_ci /* 253862306a36Sopenharmony_ci * If blinding was requested and we failed during blinding, 253962306a36Sopenharmony_ci * we must fall back to the interpreter. 254062306a36Sopenharmony_ci */ 254162306a36Sopenharmony_ci if (IS_ERR(tmp)) 254262306a36Sopenharmony_ci return orig_prog; 254362306a36Sopenharmony_ci if (tmp != prog) { 254462306a36Sopenharmony_ci tmp_blinded = true; 254562306a36Sopenharmony_ci prog = tmp; 254662306a36Sopenharmony_ci } 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci addrs = kmalloc_array(prog->len, sizeof(*addrs), GFP_KERNEL); 254962306a36Sopenharmony_ci if (!addrs) { 255062306a36Sopenharmony_ci prog = orig_prog; 255162306a36Sopenharmony_ci goto out; 255262306a36Sopenharmony_ci } 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci /* 255562306a36Sopenharmony_ci * Before first pass, make a rough estimation of addrs[] 255662306a36Sopenharmony_ci * each BPF instruction is translated to less than 64 bytes 255762306a36Sopenharmony_ci */ 255862306a36Sopenharmony_ci for (proglen = 0, i = 0; i < prog->len; i++) { 255962306a36Sopenharmony_ci proglen += 64; 256062306a36Sopenharmony_ci addrs[i] = proglen; 256162306a36Sopenharmony_ci } 256262306a36Sopenharmony_ci ctx.cleanup_addr = proglen; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci /* 256562306a36Sopenharmony_ci * JITed image shrinks with every pass and the loop iterates 256662306a36Sopenharmony_ci * until the image stops shrinking. Very large BPF programs 256762306a36Sopenharmony_ci * may converge on the last pass. In such case do one more 256862306a36Sopenharmony_ci * pass to emit the final image. 256962306a36Sopenharmony_ci */ 257062306a36Sopenharmony_ci for (pass = 0; pass < 20 || image; pass++) { 257162306a36Sopenharmony_ci proglen = do_jit(prog, addrs, image, oldproglen, &ctx); 257262306a36Sopenharmony_ci if (proglen <= 0) { 257362306a36Sopenharmony_ciout_image: 257462306a36Sopenharmony_ci image = NULL; 257562306a36Sopenharmony_ci if (header) 257662306a36Sopenharmony_ci bpf_jit_binary_free(header); 257762306a36Sopenharmony_ci prog = orig_prog; 257862306a36Sopenharmony_ci goto out_addrs; 257962306a36Sopenharmony_ci } 258062306a36Sopenharmony_ci if (image) { 258162306a36Sopenharmony_ci if (proglen != oldproglen) { 258262306a36Sopenharmony_ci pr_err("bpf_jit: proglen=%d != oldproglen=%d\n", 258362306a36Sopenharmony_ci proglen, oldproglen); 258462306a36Sopenharmony_ci goto out_image; 258562306a36Sopenharmony_ci } 258662306a36Sopenharmony_ci break; 258762306a36Sopenharmony_ci } 258862306a36Sopenharmony_ci if (proglen == oldproglen) { 258962306a36Sopenharmony_ci header = bpf_jit_binary_alloc(proglen, &image, 259062306a36Sopenharmony_ci 1, jit_fill_hole); 259162306a36Sopenharmony_ci if (!header) { 259262306a36Sopenharmony_ci prog = orig_prog; 259362306a36Sopenharmony_ci goto out_addrs; 259462306a36Sopenharmony_ci } 259562306a36Sopenharmony_ci } 259662306a36Sopenharmony_ci oldproglen = proglen; 259762306a36Sopenharmony_ci cond_resched(); 259862306a36Sopenharmony_ci } 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci if (bpf_jit_enable > 1) 260162306a36Sopenharmony_ci bpf_jit_dump(prog->len, proglen, pass + 1, image); 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_ci if (image) { 260462306a36Sopenharmony_ci bpf_jit_binary_lock_ro(header); 260562306a36Sopenharmony_ci prog->bpf_func = (void *)image; 260662306a36Sopenharmony_ci prog->jited = 1; 260762306a36Sopenharmony_ci prog->jited_len = proglen; 260862306a36Sopenharmony_ci } else { 260962306a36Sopenharmony_ci prog = orig_prog; 261062306a36Sopenharmony_ci } 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ciout_addrs: 261362306a36Sopenharmony_ci kfree(addrs); 261462306a36Sopenharmony_ciout: 261562306a36Sopenharmony_ci if (tmp_blinded) 261662306a36Sopenharmony_ci bpf_jit_prog_release_other(prog, prog == orig_prog ? 261762306a36Sopenharmony_ci tmp : orig_prog); 261862306a36Sopenharmony_ci return prog; 261962306a36Sopenharmony_ci} 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_cibool bpf_jit_supports_kfunc_call(void) 262262306a36Sopenharmony_ci{ 262362306a36Sopenharmony_ci return true; 262462306a36Sopenharmony_ci} 2625