162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * BPF JIT compiler for PA-RISC (64-bit) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright(c) 2023 Helge Deller <deller@gmx.de> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * The code is based on the BPF JIT compiler for RV64 by Björn Töpel. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * TODO: 1062306a36Sopenharmony_ci * - check if bpf_jit_needs_zext() is needed (currently enabled) 1162306a36Sopenharmony_ci * - implement arch_prepare_bpf_trampoline(), poke(), ... 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/bitfield.h> 1562306a36Sopenharmony_ci#include <linux/bpf.h> 1662306a36Sopenharmony_ci#include <linux/filter.h> 1762306a36Sopenharmony_ci#include <linux/libgcc.h> 1862306a36Sopenharmony_ci#include "bpf_jit.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic const int regmap[] = { 2162306a36Sopenharmony_ci [BPF_REG_0] = HPPA_REG_RET0, 2262306a36Sopenharmony_ci [BPF_REG_1] = HPPA_R(5), 2362306a36Sopenharmony_ci [BPF_REG_2] = HPPA_R(6), 2462306a36Sopenharmony_ci [BPF_REG_3] = HPPA_R(7), 2562306a36Sopenharmony_ci [BPF_REG_4] = HPPA_R(8), 2662306a36Sopenharmony_ci [BPF_REG_5] = HPPA_R(9), 2762306a36Sopenharmony_ci [BPF_REG_6] = HPPA_R(10), 2862306a36Sopenharmony_ci [BPF_REG_7] = HPPA_R(11), 2962306a36Sopenharmony_ci [BPF_REG_8] = HPPA_R(12), 3062306a36Sopenharmony_ci [BPF_REG_9] = HPPA_R(13), 3162306a36Sopenharmony_ci [BPF_REG_FP] = HPPA_R(14), 3262306a36Sopenharmony_ci [BPF_REG_AX] = HPPA_R(15), 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * Stack layout during BPF program execution (note: stack grows up): 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * high 3962306a36Sopenharmony_ci * HPPA64 sp => +----------+ <= HPPA64 fp 4062306a36Sopenharmony_ci * | saved sp | 4162306a36Sopenharmony_ci * | saved rp | 4262306a36Sopenharmony_ci * | ... | HPPA64 callee-saved registers 4362306a36Sopenharmony_ci * | curr args| 4462306a36Sopenharmony_ci * | local var| 4562306a36Sopenharmony_ci * +----------+ <= (BPF FP) 4662306a36Sopenharmony_ci * | | 4762306a36Sopenharmony_ci * | ... | BPF program stack 4862306a36Sopenharmony_ci * | | 4962306a36Sopenharmony_ci * | ... | Function call stack 5062306a36Sopenharmony_ci * | | 5162306a36Sopenharmony_ci * +----------+ 5262306a36Sopenharmony_ci * low 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Offset from fp for BPF registers stored on stack. */ 5662306a36Sopenharmony_ci#define STACK_ALIGN FRAME_SIZE 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define EXIT_PTR_LOAD(reg) hppa64_ldd_im16(-FRAME_SIZE, HPPA_REG_SP, reg) 5962306a36Sopenharmony_ci#define EXIT_PTR_STORE(reg) hppa64_std_im16(reg, -FRAME_SIZE, HPPA_REG_SP) 6062306a36Sopenharmony_ci#define EXIT_PTR_JUMP(reg, nop) hppa_bv(HPPA_REG_ZERO, reg, nop) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic u8 bpf_to_hppa_reg(int bpf_reg, struct hppa_jit_context *ctx) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci u8 reg = regmap[bpf_reg]; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci REG_SET_SEEN(ctx, reg); 6762306a36Sopenharmony_ci return reg; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void emit_hppa_copy(const s8 rs, const s8 rd, struct hppa_jit_context *ctx) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci REG_SET_SEEN(ctx, rd); 7362306a36Sopenharmony_ci if (OPTIMIZE_HPPA && (rs == rd)) 7462306a36Sopenharmony_ci return; 7562306a36Sopenharmony_ci REG_SET_SEEN(ctx, rs); 7662306a36Sopenharmony_ci emit(hppa_copy(rs, rd), ctx); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void emit_hppa64_depd(u8 src, u8 pos, u8 len, u8 target, bool no_zero, struct hppa_jit_context *ctx) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int c; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci pos &= (BITS_PER_LONG - 1); 8462306a36Sopenharmony_ci pos = 63 - pos; 8562306a36Sopenharmony_ci len = 64 - len; 8662306a36Sopenharmony_ci c = (len < 32) ? 0x4 : 0; 8762306a36Sopenharmony_ci c |= (pos >= 32) ? 0x2 : 0; 8862306a36Sopenharmony_ci c |= (no_zero) ? 0x1 : 0; 8962306a36Sopenharmony_ci emit(hppa_t10_insn(0x3c, target, src, 0, c, pos & 0x1f, len & 0x1f), ctx); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void emit_hppa64_shld(u8 src, int num, u8 target, struct hppa_jit_context *ctx) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci emit_hppa64_depd(src, 63-num, 64-num, target, 0, ctx); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void emit_hppa64_extrd(u8 src, u8 pos, u8 len, u8 target, bool signed_op, struct hppa_jit_context *ctx) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci int c; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci pos &= (BITS_PER_LONG - 1); 10262306a36Sopenharmony_ci len = 64 - len; 10362306a36Sopenharmony_ci c = (len < 32) ? 0x4 : 0; 10462306a36Sopenharmony_ci c |= (pos >= 32) ? 0x2 : 0; 10562306a36Sopenharmony_ci c |= signed_op ? 0x1 : 0; 10662306a36Sopenharmony_ci emit(hppa_t10_insn(0x36, src, target, 0, c, pos & 0x1f, len & 0x1f), ctx); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void emit_hppa64_extrw(u8 src, u8 pos, u8 len, u8 target, bool signed_op, struct hppa_jit_context *ctx) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int c; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci pos &= (32 - 1); 11462306a36Sopenharmony_ci len = 32 - len; 11562306a36Sopenharmony_ci c = 0x06 | (signed_op ? 1 : 0); 11662306a36Sopenharmony_ci emit(hppa_t10_insn(0x34, src, target, 0, c, pos, len), ctx); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define emit_hppa64_zext32(r, target, ctx) \ 12062306a36Sopenharmony_ci emit_hppa64_extrd(r, 63, 32, target, false, ctx) 12162306a36Sopenharmony_ci#define emit_hppa64_sext32(r, target, ctx) \ 12262306a36Sopenharmony_ci emit_hppa64_extrd(r, 63, 32, target, true, ctx) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void emit_hppa64_shrd(u8 src, int num, u8 target, bool signed_op, struct hppa_jit_context *ctx) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci emit_hppa64_extrd(src, 63-num, 64-num, target, signed_op, ctx); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void emit_hppa64_shrw(u8 src, int num, u8 target, bool signed_op, struct hppa_jit_context *ctx) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci emit_hppa64_extrw(src, 31-num, 32-num, target, signed_op, ctx); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* Emit variable-length instructions for 32-bit imm */ 13562306a36Sopenharmony_cistatic void emit_imm32(u8 rd, s32 imm, struct hppa_jit_context *ctx) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci u32 lower = im11(imm); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci REG_SET_SEEN(ctx, rd); 14062306a36Sopenharmony_ci if (OPTIMIZE_HPPA && relative_bits_ok(imm, 14)) { 14162306a36Sopenharmony_ci emit(hppa_ldi(imm, rd), ctx); 14262306a36Sopenharmony_ci return; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci if (OPTIMIZE_HPPA && lower == imm) { 14562306a36Sopenharmony_ci emit(hppa_ldo(lower, HPPA_REG_ZERO, rd), ctx); 14662306a36Sopenharmony_ci return; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci emit(hppa_ldil(imm, rd), ctx); 14962306a36Sopenharmony_ci if (OPTIMIZE_HPPA && (lower == 0)) 15062306a36Sopenharmony_ci return; 15162306a36Sopenharmony_ci emit(hppa_ldo(lower, rd, rd), ctx); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic bool is_32b_int(s64 val) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return val == (s32) val; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Emit variable-length instructions for 64-bit imm */ 16062306a36Sopenharmony_cistatic void emit_imm(u8 rd, s64 imm, u8 tmpreg, struct hppa_jit_context *ctx) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci u32 upper32; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* get lower 32-bits into rd, sign extended */ 16562306a36Sopenharmony_ci emit_imm32(rd, imm, ctx); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* do we have upper 32-bits too ? */ 16862306a36Sopenharmony_ci if (OPTIMIZE_HPPA && is_32b_int(imm)) 16962306a36Sopenharmony_ci return; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* load upper 32-bits into lower tmpreg and deposit into rd */ 17262306a36Sopenharmony_ci upper32 = imm >> 32; 17362306a36Sopenharmony_ci if (upper32 || !OPTIMIZE_HPPA) { 17462306a36Sopenharmony_ci emit_imm32(tmpreg, upper32, ctx); 17562306a36Sopenharmony_ci emit_hppa64_depd(tmpreg, 31, 32, rd, 1, ctx); 17662306a36Sopenharmony_ci } else 17762306a36Sopenharmony_ci emit_hppa64_depd(HPPA_REG_ZERO, 31, 32, rd, 1, ctx); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int emit_jump(signed long paoff, bool force_far, 18262306a36Sopenharmony_ci struct hppa_jit_context *ctx) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci unsigned long pc, addr; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* Note: Use 2 instructions for jumps if force_far is set. */ 18762306a36Sopenharmony_ci if (relative_bits_ok(paoff - HPPA_BRANCH_DISPLACEMENT, 22)) { 18862306a36Sopenharmony_ci /* use BL,long branch followed by nop() */ 18962306a36Sopenharmony_ci emit(hppa64_bl_long(paoff - HPPA_BRANCH_DISPLACEMENT), ctx); 19062306a36Sopenharmony_ci if (force_far) 19162306a36Sopenharmony_ci emit(hppa_nop(), ctx); 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci pc = (uintptr_t) &ctx->insns[ctx->ninsns]; 19662306a36Sopenharmony_ci addr = pc + (paoff * HPPA_INSN_SIZE); 19762306a36Sopenharmony_ci /* even the 64-bit kernel runs in memory below 4GB */ 19862306a36Sopenharmony_ci if (WARN_ON_ONCE(addr >> 32)) 19962306a36Sopenharmony_ci return -E2BIG; 20062306a36Sopenharmony_ci emit(hppa_ldil(addr, HPPA_REG_R31), ctx); 20162306a36Sopenharmony_ci emit(hppa_be_l(im11(addr) >> 2, HPPA_REG_R31, NOP_NEXT_INSTR), ctx); 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void __build_epilogue(bool is_tail_call, struct hppa_jit_context *ctx) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (is_tail_call) { 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * goto *(t0 + 4); 21262306a36Sopenharmony_ci * Skips first instruction of prologue which initializes tail 21362306a36Sopenharmony_ci * call counter. Assumes t0 contains address of target program, 21462306a36Sopenharmony_ci * see emit_bpf_tail_call. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci emit(hppa_ldo(1 * HPPA_INSN_SIZE, HPPA_REG_T0, HPPA_REG_T0), ctx); 21762306a36Sopenharmony_ci emit(hppa_bv(HPPA_REG_ZERO, HPPA_REG_T0, EXEC_NEXT_INSTR), ctx); 21862306a36Sopenharmony_ci /* in delay slot: */ 21962306a36Sopenharmony_ci emit(hppa_copy(HPPA_REG_TCC, HPPA_REG_TCC_IN_INIT), ctx); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* load epilogue function pointer and jump to it. */ 22562306a36Sopenharmony_ci /* exit point is either at next instruction, or the outest TCC exit function */ 22662306a36Sopenharmony_ci emit(EXIT_PTR_LOAD(HPPA_REG_RP), ctx); 22762306a36Sopenharmony_ci emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* NOTE: we are 64-bit and big-endian, so return lower sign-extended 32-bit value */ 23062306a36Sopenharmony_ci emit_hppa64_sext32(regmap[BPF_REG_0], HPPA_REG_RET0, ctx); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* Restore callee-saved registers. */ 23362306a36Sopenharmony_ci for (i = 3; i <= 15; i++) { 23462306a36Sopenharmony_ci if (OPTIMIZE_HPPA && !REG_WAS_SEEN(ctx, HPPA_R(i))) 23562306a36Sopenharmony_ci continue; 23662306a36Sopenharmony_ci emit(hppa64_ldd_im16(-REG_SIZE * i, HPPA_REG_SP, HPPA_R(i)), ctx); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* load original return pointer (stored by outest TCC function) */ 24062306a36Sopenharmony_ci emit(hppa64_ldd_im16(-2*REG_SIZE, HPPA_REG_SP, HPPA_REG_RP), ctx); 24162306a36Sopenharmony_ci emit(hppa_bv(HPPA_REG_ZERO, HPPA_REG_RP, EXEC_NEXT_INSTR), ctx); 24262306a36Sopenharmony_ci /* in delay slot: */ 24362306a36Sopenharmony_ci emit(hppa64_ldd_im5(-REG_SIZE, HPPA_REG_SP, HPPA_REG_SP), ctx); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci emit(hppa_nop(), ctx); // XXX WARUM einer zu wenig ?? 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int emit_branch(u8 op, u8 rd, u8 rs, signed long paoff, 24962306a36Sopenharmony_ci struct hppa_jit_context *ctx) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int e, s; 25262306a36Sopenharmony_ci bool far = false; 25362306a36Sopenharmony_ci int off; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (op == BPF_JSET) { 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * BPF_JSET is a special case: it has no inverse so translate 25862306a36Sopenharmony_ci * to and() function and compare against zero 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci emit(hppa_and(rd, rs, HPPA_REG_T0), ctx); 26162306a36Sopenharmony_ci paoff -= 1; /* reduce offset due to hppa_and() above */ 26262306a36Sopenharmony_ci rd = HPPA_REG_T0; 26362306a36Sopenharmony_ci rs = HPPA_REG_ZERO; 26462306a36Sopenharmony_ci op = BPF_JNE; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* set start after BPF_JSET */ 26862306a36Sopenharmony_ci s = ctx->ninsns; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (!relative_branch_ok(paoff - HPPA_BRANCH_DISPLACEMENT + 1, 12)) { 27162306a36Sopenharmony_ci op = invert_bpf_cond(op); 27262306a36Sopenharmony_ci far = true; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* 27662306a36Sopenharmony_ci * For a far branch, the condition is negated and we jump over the 27762306a36Sopenharmony_ci * branch itself, and the two instructions from emit_jump. 27862306a36Sopenharmony_ci * For a near branch, just use paoff. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci off = far ? (2 - HPPA_BRANCH_DISPLACEMENT) : paoff - HPPA_BRANCH_DISPLACEMENT; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci switch (op) { 28362306a36Sopenharmony_ci /* IF (dst COND src) JUMP off */ 28462306a36Sopenharmony_ci case BPF_JEQ: 28562306a36Sopenharmony_ci emit(hppa_beq(rd, rs, off), ctx); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci case BPF_JGT: 28862306a36Sopenharmony_ci emit(hppa_bgtu(rd, rs, off), ctx); 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case BPF_JLT: 29162306a36Sopenharmony_ci emit(hppa_bltu(rd, rs, off), ctx); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case BPF_JGE: 29462306a36Sopenharmony_ci emit(hppa_bgeu(rd, rs, off), ctx); 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci case BPF_JLE: 29762306a36Sopenharmony_ci emit(hppa_bleu(rd, rs, off), ctx); 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci case BPF_JNE: 30062306a36Sopenharmony_ci emit(hppa_bne(rd, rs, off), ctx); 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case BPF_JSGT: 30362306a36Sopenharmony_ci emit(hppa_bgt(rd, rs, off), ctx); 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci case BPF_JSLT: 30662306a36Sopenharmony_ci emit(hppa_blt(rd, rs, off), ctx); 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci case BPF_JSGE: 30962306a36Sopenharmony_ci emit(hppa_bge(rd, rs, off), ctx); 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci case BPF_JSLE: 31262306a36Sopenharmony_ci emit(hppa_ble(rd, rs, off), ctx); 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci default: 31562306a36Sopenharmony_ci WARN_ON(1); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (far) { 31962306a36Sopenharmony_ci int ret; 32062306a36Sopenharmony_ci e = ctx->ninsns; 32162306a36Sopenharmony_ci /* Adjust for extra insns. */ 32262306a36Sopenharmony_ci paoff -= (e - s); 32362306a36Sopenharmony_ci ret = emit_jump(paoff, true, ctx); 32462306a36Sopenharmony_ci if (ret) 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci } else { 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * always allocate 2 nops instead of the far branch to 32962306a36Sopenharmony_ci * reduce translation loops 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci emit(hppa_nop(), ctx); 33262306a36Sopenharmony_ci emit(hppa_nop(), ctx); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void emit_zext_32(u8 reg, struct hppa_jit_context *ctx) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci emit_hppa64_zext32(reg, reg, ctx); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic void emit_bpf_tail_call(int insn, struct hppa_jit_context *ctx) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * R1 -> &ctx 34662306a36Sopenharmony_ci * R2 -> &array 34762306a36Sopenharmony_ci * R3 -> index 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci int off; 35062306a36Sopenharmony_ci const s8 arr_reg = regmap[BPF_REG_2]; 35162306a36Sopenharmony_ci const s8 idx_reg = regmap[BPF_REG_3]; 35262306a36Sopenharmony_ci struct bpf_array bpfa; 35362306a36Sopenharmony_ci struct bpf_prog bpfp; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* if there is any tail call, we need to save & restore all registers */ 35662306a36Sopenharmony_ci REG_SET_SEEN_ALL(ctx); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* get address of TCC main exit function for error case into rp */ 35962306a36Sopenharmony_ci emit(EXIT_PTR_LOAD(HPPA_REG_RP), ctx); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* max_entries = array->map.max_entries; */ 36262306a36Sopenharmony_ci off = offsetof(struct bpf_array, map.max_entries); 36362306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(bpfa.map.max_entries) != 4); 36462306a36Sopenharmony_ci emit(hppa_ldw(off, arr_reg, HPPA_REG_T1), ctx); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* 36762306a36Sopenharmony_ci * if (index >= max_entries) 36862306a36Sopenharmony_ci * goto out; 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ci emit(hppa_bltu(idx_reg, HPPA_REG_T1, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); 37162306a36Sopenharmony_ci emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* 37462306a36Sopenharmony_ci * if (--tcc < 0) 37562306a36Sopenharmony_ci * goto out; 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci REG_FORCE_SEEN(ctx, HPPA_REG_TCC); 37862306a36Sopenharmony_ci emit(hppa_ldo(-1, HPPA_REG_TCC, HPPA_REG_TCC), ctx); 37962306a36Sopenharmony_ci emit(hppa_bge(HPPA_REG_TCC, HPPA_REG_ZERO, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); 38062306a36Sopenharmony_ci emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* 38362306a36Sopenharmony_ci * prog = array->ptrs[index]; 38462306a36Sopenharmony_ci * if (!prog) 38562306a36Sopenharmony_ci * goto out; 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(bpfa.ptrs[0]) != 8); 38862306a36Sopenharmony_ci emit(hppa64_shladd(idx_reg, 3, arr_reg, HPPA_REG_T0), ctx); 38962306a36Sopenharmony_ci off = offsetof(struct bpf_array, ptrs); 39062306a36Sopenharmony_ci BUILD_BUG_ON(off < 16); 39162306a36Sopenharmony_ci emit(hppa64_ldd_im16(off, HPPA_REG_T0, HPPA_REG_T0), ctx); 39262306a36Sopenharmony_ci emit(hppa_bne(HPPA_REG_T0, HPPA_REG_ZERO, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); 39362306a36Sopenharmony_ci emit(EXIT_PTR_JUMP(HPPA_REG_RP, NOP_NEXT_INSTR), ctx); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* 39662306a36Sopenharmony_ci * tcc = temp_tcc; 39762306a36Sopenharmony_ci * goto *(prog->bpf_func + 4); 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci off = offsetof(struct bpf_prog, bpf_func); 40062306a36Sopenharmony_ci BUILD_BUG_ON(off < 16); 40162306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(bpfp.bpf_func) != 8); 40262306a36Sopenharmony_ci emit(hppa64_ldd_im16(off, HPPA_REG_T0, HPPA_REG_T0), ctx); 40362306a36Sopenharmony_ci /* Epilogue jumps to *(t0 + 4). */ 40462306a36Sopenharmony_ci __build_epilogue(true, ctx); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic void init_regs(u8 *rd, u8 *rs, const struct bpf_insn *insn, 40862306a36Sopenharmony_ci struct hppa_jit_context *ctx) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci u8 code = insn->code; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci switch (code) { 41362306a36Sopenharmony_ci case BPF_JMP | BPF_JA: 41462306a36Sopenharmony_ci case BPF_JMP | BPF_CALL: 41562306a36Sopenharmony_ci case BPF_JMP | BPF_EXIT: 41662306a36Sopenharmony_ci case BPF_JMP | BPF_TAIL_CALL: 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci default: 41962306a36Sopenharmony_ci *rd = bpf_to_hppa_reg(insn->dst_reg, ctx); 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (code & (BPF_ALU | BPF_X) || code & (BPF_ALU64 | BPF_X) || 42362306a36Sopenharmony_ci code & (BPF_JMP | BPF_X) || code & (BPF_JMP32 | BPF_X) || 42462306a36Sopenharmony_ci code & BPF_LDX || code & BPF_STX) 42562306a36Sopenharmony_ci *rs = bpf_to_hppa_reg(insn->src_reg, ctx); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void emit_zext_32_rd_rs(u8 *rd, u8 *rs, struct hppa_jit_context *ctx) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci emit_hppa64_zext32(*rd, HPPA_REG_T2, ctx); 43162306a36Sopenharmony_ci *rd = HPPA_REG_T2; 43262306a36Sopenharmony_ci emit_hppa64_zext32(*rs, HPPA_REG_T1, ctx); 43362306a36Sopenharmony_ci *rs = HPPA_REG_T1; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void emit_sext_32_rd_rs(u8 *rd, u8 *rs, struct hppa_jit_context *ctx) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci emit_hppa64_sext32(*rd, HPPA_REG_T2, ctx); 43962306a36Sopenharmony_ci *rd = HPPA_REG_T2; 44062306a36Sopenharmony_ci emit_hppa64_sext32(*rs, HPPA_REG_T1, ctx); 44162306a36Sopenharmony_ci *rs = HPPA_REG_T1; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void emit_zext_32_rd_t1(u8 *rd, struct hppa_jit_context *ctx) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci emit_hppa64_zext32(*rd, HPPA_REG_T2, ctx); 44762306a36Sopenharmony_ci *rd = HPPA_REG_T2; 44862306a36Sopenharmony_ci emit_zext_32(HPPA_REG_T1, ctx); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void emit_sext_32_rd(u8 *rd, struct hppa_jit_context *ctx) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci emit_hppa64_sext32(*rd, HPPA_REG_T2, ctx); 45462306a36Sopenharmony_ci *rd = HPPA_REG_T2; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic bool is_signed_bpf_cond(u8 cond) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci return cond == BPF_JSGT || cond == BPF_JSLT || 46062306a36Sopenharmony_ci cond == BPF_JSGE || cond == BPF_JSLE; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic void emit_call(u64 addr, bool fixed, struct hppa_jit_context *ctx) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci const int offset_sp = 2*FRAME_SIZE; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci emit(hppa_ldo(offset_sp, HPPA_REG_SP, HPPA_REG_SP), ctx); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci emit_hppa_copy(regmap[BPF_REG_1], HPPA_REG_ARG0, ctx); 47062306a36Sopenharmony_ci emit_hppa_copy(regmap[BPF_REG_2], HPPA_REG_ARG1, ctx); 47162306a36Sopenharmony_ci emit_hppa_copy(regmap[BPF_REG_3], HPPA_REG_ARG2, ctx); 47262306a36Sopenharmony_ci emit_hppa_copy(regmap[BPF_REG_4], HPPA_REG_ARG3, ctx); 47362306a36Sopenharmony_ci emit_hppa_copy(regmap[BPF_REG_5], HPPA_REG_ARG4, ctx); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* Backup TCC. */ 47662306a36Sopenharmony_ci REG_FORCE_SEEN(ctx, HPPA_REG_TCC_SAVED); 47762306a36Sopenharmony_ci if (REG_WAS_SEEN(ctx, HPPA_REG_TCC)) 47862306a36Sopenharmony_ci emit(hppa_copy(HPPA_REG_TCC, HPPA_REG_TCC_SAVED), ctx); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * Use ldil() to load absolute address. Don't use emit_imm as the 48262306a36Sopenharmony_ci * number of emitted instructions should not depend on the value of 48362306a36Sopenharmony_ci * addr. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci WARN_ON(addr >> 32); 48662306a36Sopenharmony_ci /* load function address and gp from Elf64_Fdesc descriptor */ 48762306a36Sopenharmony_ci emit(hppa_ldil(addr, HPPA_REG_R31), ctx); 48862306a36Sopenharmony_ci emit(hppa_ldo(im11(addr), HPPA_REG_R31, HPPA_REG_R31), ctx); 48962306a36Sopenharmony_ci emit(hppa64_ldd_im16(offsetof(struct elf64_fdesc, addr), 49062306a36Sopenharmony_ci HPPA_REG_R31, HPPA_REG_RP), ctx); 49162306a36Sopenharmony_ci emit(hppa64_bve_l_rp(HPPA_REG_RP), ctx); 49262306a36Sopenharmony_ci emit(hppa64_ldd_im16(offsetof(struct elf64_fdesc, gp), 49362306a36Sopenharmony_ci HPPA_REG_R31, HPPA_REG_GP), ctx); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* Restore TCC. */ 49662306a36Sopenharmony_ci if (REG_WAS_SEEN(ctx, HPPA_REG_TCC)) 49762306a36Sopenharmony_ci emit(hppa_copy(HPPA_REG_TCC_SAVED, HPPA_REG_TCC), ctx); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci emit(hppa_ldo(-offset_sp, HPPA_REG_SP, HPPA_REG_SP), ctx); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Set return value. */ 50262306a36Sopenharmony_ci emit_hppa_copy(HPPA_REG_RET0, regmap[BPF_REG_0], ctx); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic void emit_call_libgcc_ll(void *func, const s8 arg0, 50662306a36Sopenharmony_ci const s8 arg1, u8 opcode, struct hppa_jit_context *ctx) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci u64 func_addr; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (BPF_CLASS(opcode) == BPF_ALU) { 51162306a36Sopenharmony_ci emit_hppa64_zext32(arg0, HPPA_REG_ARG0, ctx); 51262306a36Sopenharmony_ci emit_hppa64_zext32(arg1, HPPA_REG_ARG1, ctx); 51362306a36Sopenharmony_ci } else { 51462306a36Sopenharmony_ci emit_hppa_copy(arg0, HPPA_REG_ARG0, ctx); 51562306a36Sopenharmony_ci emit_hppa_copy(arg1, HPPA_REG_ARG1, ctx); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* libcgcc overwrites HPPA_REG_RET0, so keep copy in HPPA_REG_TCC_SAVED */ 51962306a36Sopenharmony_ci if (arg0 != HPPA_REG_RET0) { 52062306a36Sopenharmony_ci REG_SET_SEEN(ctx, HPPA_REG_TCC_SAVED); 52162306a36Sopenharmony_ci emit(hppa_copy(HPPA_REG_RET0, HPPA_REG_TCC_SAVED), ctx); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* set up stack */ 52562306a36Sopenharmony_ci emit(hppa_ldo(FRAME_SIZE, HPPA_REG_SP, HPPA_REG_SP), ctx); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci func_addr = (uintptr_t) func; 52862306a36Sopenharmony_ci /* load function func_address and gp from Elf64_Fdesc descriptor */ 52962306a36Sopenharmony_ci emit_imm(HPPA_REG_R31, func_addr, arg0, ctx); 53062306a36Sopenharmony_ci emit(hppa64_ldd_im16(offsetof(struct elf64_fdesc, addr), 53162306a36Sopenharmony_ci HPPA_REG_R31, HPPA_REG_RP), ctx); 53262306a36Sopenharmony_ci /* skip the following bve_l instruction if divisor is 0. */ 53362306a36Sopenharmony_ci if (BPF_OP(opcode) == BPF_DIV || BPF_OP(opcode) == BPF_MOD) { 53462306a36Sopenharmony_ci if (BPF_OP(opcode) == BPF_DIV) 53562306a36Sopenharmony_ci emit_hppa_copy(HPPA_REG_ZERO, HPPA_REG_RET0, ctx); 53662306a36Sopenharmony_ci else { 53762306a36Sopenharmony_ci emit_hppa_copy(HPPA_REG_ARG0, HPPA_REG_RET0, ctx); 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci emit(hppa_beq(HPPA_REG_ARG1, HPPA_REG_ZERO, 2 - HPPA_BRANCH_DISPLACEMENT), ctx); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci emit(hppa64_bve_l_rp(HPPA_REG_RP), ctx); 54262306a36Sopenharmony_ci emit(hppa64_ldd_im16(offsetof(struct elf64_fdesc, gp), 54362306a36Sopenharmony_ci HPPA_REG_R31, HPPA_REG_GP), ctx); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci emit(hppa_ldo(-FRAME_SIZE, HPPA_REG_SP, HPPA_REG_SP), ctx); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci emit_hppa_copy(HPPA_REG_RET0, arg0, ctx); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* restore HPPA_REG_RET0 */ 55062306a36Sopenharmony_ci if (arg0 != HPPA_REG_RET0) 55162306a36Sopenharmony_ci emit(hppa_copy(HPPA_REG_TCC_SAVED, HPPA_REG_RET0), ctx); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic void emit_store(const s8 rd, const s8 rs, s16 off, 55562306a36Sopenharmony_ci struct hppa_jit_context *ctx, const u8 size, 55662306a36Sopenharmony_ci const u8 mode) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci s8 dstreg; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* need to calculate address since offset does not fit in 14 bits? */ 56162306a36Sopenharmony_ci if (relative_bits_ok(off, 14)) 56262306a36Sopenharmony_ci dstreg = rd; 56362306a36Sopenharmony_ci else { 56462306a36Sopenharmony_ci /* need to use R1 here, since addil puts result into R1 */ 56562306a36Sopenharmony_ci dstreg = HPPA_REG_R1; 56662306a36Sopenharmony_ci emit(hppa_addil(off, rd), ctx); 56762306a36Sopenharmony_ci off = im11(off); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci switch (size) { 57162306a36Sopenharmony_ci case BPF_B: 57262306a36Sopenharmony_ci emit(hppa_stb(rs, off, dstreg), ctx); 57362306a36Sopenharmony_ci break; 57462306a36Sopenharmony_ci case BPF_H: 57562306a36Sopenharmony_ci emit(hppa_sth(rs, off, dstreg), ctx); 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci case BPF_W: 57862306a36Sopenharmony_ci emit(hppa_stw(rs, off, dstreg), ctx); 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci case BPF_DW: 58162306a36Sopenharmony_ci if (off & 7) { 58262306a36Sopenharmony_ci emit(hppa_ldo(off, dstreg, HPPA_REG_R1), ctx); 58362306a36Sopenharmony_ci emit(hppa64_std_im5(rs, 0, HPPA_REG_R1), ctx); 58462306a36Sopenharmony_ci } else if (off >= -16 && off <= 15) 58562306a36Sopenharmony_ci emit(hppa64_std_im5(rs, off, dstreg), ctx); 58662306a36Sopenharmony_ci else 58762306a36Sopenharmony_ci emit(hppa64_std_im16(rs, off, dstreg), ctx); 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ciint bpf_jit_emit_insn(const struct bpf_insn *insn, struct hppa_jit_context *ctx, 59362306a36Sopenharmony_ci bool extra_pass) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci bool is64 = BPF_CLASS(insn->code) == BPF_ALU64 || 59662306a36Sopenharmony_ci BPF_CLASS(insn->code) == BPF_JMP; 59762306a36Sopenharmony_ci int s, e, ret, i = insn - ctx->prog->insnsi; 59862306a36Sopenharmony_ci s64 paoff; 59962306a36Sopenharmony_ci struct bpf_prog_aux *aux = ctx->prog->aux; 60062306a36Sopenharmony_ci u8 rd = -1, rs = -1, code = insn->code; 60162306a36Sopenharmony_ci s16 off = insn->off; 60262306a36Sopenharmony_ci s32 imm = insn->imm; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci init_regs(&rd, &rs, insn, ctx); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci switch (code) { 60762306a36Sopenharmony_ci /* dst = src */ 60862306a36Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_X: 60962306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_X: 61062306a36Sopenharmony_ci if (imm == 1) { 61162306a36Sopenharmony_ci /* Special mov32 for zext */ 61262306a36Sopenharmony_ci emit_zext_32(rd, ctx); 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 61662306a36Sopenharmony_ci emit_hppa64_zext32(rs, rd, ctx); 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci emit_hppa_copy(rs, rd, ctx); 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* dst = dst OP src */ 62262306a36Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_X: 62362306a36Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_X: 62462306a36Sopenharmony_ci emit(hppa_add(rd, rs, rd), ctx); 62562306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 62662306a36Sopenharmony_ci emit_zext_32(rd, ctx); 62762306a36Sopenharmony_ci break; 62862306a36Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_X: 62962306a36Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_X: 63062306a36Sopenharmony_ci emit(hppa_sub(rd, rs, rd), ctx); 63162306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 63262306a36Sopenharmony_ci emit_zext_32(rd, ctx); 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_X: 63562306a36Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_X: 63662306a36Sopenharmony_ci emit(hppa_and(rd, rs, rd), ctx); 63762306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 63862306a36Sopenharmony_ci emit_zext_32(rd, ctx); 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_X: 64162306a36Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_X: 64262306a36Sopenharmony_ci emit(hppa_or(rd, rs, rd), ctx); 64362306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 64462306a36Sopenharmony_ci emit_zext_32(rd, ctx); 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_X: 64762306a36Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_X: 64862306a36Sopenharmony_ci emit(hppa_xor(rd, rs, rd), ctx); 64962306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext && rs != rd) 65062306a36Sopenharmony_ci emit_zext_32(rd, ctx); 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_K: 65362306a36Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_K: 65462306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, is64 ? (s64)(s32)imm : (u32)imm, HPPA_REG_T2, ctx); 65562306a36Sopenharmony_ci rs = HPPA_REG_T1; 65662306a36Sopenharmony_ci fallthrough; 65762306a36Sopenharmony_ci case BPF_ALU | BPF_MUL | BPF_X: 65862306a36Sopenharmony_ci case BPF_ALU64 | BPF_MUL | BPF_X: 65962306a36Sopenharmony_ci emit_call_libgcc_ll(__muldi3, rd, rs, code, ctx); 66062306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 66162306a36Sopenharmony_ci emit_zext_32(rd, ctx); 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_K: 66462306a36Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_K: 66562306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, is64 ? (s64)(s32)imm : (u32)imm, HPPA_REG_T2, ctx); 66662306a36Sopenharmony_ci rs = HPPA_REG_T1; 66762306a36Sopenharmony_ci fallthrough; 66862306a36Sopenharmony_ci case BPF_ALU | BPF_DIV | BPF_X: 66962306a36Sopenharmony_ci case BPF_ALU64 | BPF_DIV | BPF_X: 67062306a36Sopenharmony_ci emit_call_libgcc_ll(&hppa_div64, rd, rs, code, ctx); 67162306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 67262306a36Sopenharmony_ci emit_zext_32(rd, ctx); 67362306a36Sopenharmony_ci break; 67462306a36Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_K: 67562306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_K: 67662306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, is64 ? (s64)(s32)imm : (u32)imm, HPPA_REG_T2, ctx); 67762306a36Sopenharmony_ci rs = HPPA_REG_T1; 67862306a36Sopenharmony_ci fallthrough; 67962306a36Sopenharmony_ci case BPF_ALU | BPF_MOD | BPF_X: 68062306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOD | BPF_X: 68162306a36Sopenharmony_ci emit_call_libgcc_ll(&hppa_div64_rem, rd, rs, code, ctx); 68262306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 68362306a36Sopenharmony_ci emit_zext_32(rd, ctx); 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_X: 68762306a36Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_X: 68862306a36Sopenharmony_ci emit_hppa64_sext32(rs, HPPA_REG_T0, ctx); 68962306a36Sopenharmony_ci emit(hppa64_mtsarcm(HPPA_REG_T0), ctx); 69062306a36Sopenharmony_ci if (is64) 69162306a36Sopenharmony_ci emit(hppa64_depdz_sar(rd, rd), ctx); 69262306a36Sopenharmony_ci else 69362306a36Sopenharmony_ci emit(hppa_depwz_sar(rd, rd), ctx); 69462306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 69562306a36Sopenharmony_ci emit_zext_32(rd, ctx); 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_X: 69862306a36Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_X: 69962306a36Sopenharmony_ci emit(hppa_mtsar(rs), ctx); 70062306a36Sopenharmony_ci if (is64) 70162306a36Sopenharmony_ci emit(hppa64_shrpd_sar(rd, rd), ctx); 70262306a36Sopenharmony_ci else 70362306a36Sopenharmony_ci emit(hppa_shrpw_sar(rd, rd), ctx); 70462306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 70562306a36Sopenharmony_ci emit_zext_32(rd, ctx); 70662306a36Sopenharmony_ci break; 70762306a36Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_X: 70862306a36Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_X: 70962306a36Sopenharmony_ci emit_hppa64_sext32(rs, HPPA_REG_T0, ctx); 71062306a36Sopenharmony_ci emit(hppa64_mtsarcm(HPPA_REG_T0), ctx); 71162306a36Sopenharmony_ci if (is64) 71262306a36Sopenharmony_ci emit(hppa_extrd_sar(rd, rd, 1), ctx); 71362306a36Sopenharmony_ci else 71462306a36Sopenharmony_ci emit(hppa_extrws_sar(rd, rd), ctx); 71562306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 71662306a36Sopenharmony_ci emit_zext_32(rd, ctx); 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* dst = -dst */ 72062306a36Sopenharmony_ci case BPF_ALU | BPF_NEG: 72162306a36Sopenharmony_ci case BPF_ALU64 | BPF_NEG: 72262306a36Sopenharmony_ci emit(hppa_sub(HPPA_REG_ZERO, rd, rd), ctx); 72362306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 72462306a36Sopenharmony_ci emit_zext_32(rd, ctx); 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* dst = BSWAP##imm(dst) */ 72862306a36Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_BE: 72962306a36Sopenharmony_ci switch (imm) { 73062306a36Sopenharmony_ci case 16: 73162306a36Sopenharmony_ci /* zero-extend 16 bits into 64 bits */ 73262306a36Sopenharmony_ci emit_hppa64_depd(HPPA_REG_ZERO, 63-16, 64-16, rd, 1, ctx); 73362306a36Sopenharmony_ci break; 73462306a36Sopenharmony_ci case 32: 73562306a36Sopenharmony_ci if (!aux->verifier_zext) 73662306a36Sopenharmony_ci emit_zext_32(rd, ctx); 73762306a36Sopenharmony_ci break; 73862306a36Sopenharmony_ci case 64: 73962306a36Sopenharmony_ci /* Do nothing */ 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci break; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci case BPF_ALU | BPF_END | BPF_FROM_LE: 74562306a36Sopenharmony_ci switch (imm) { 74662306a36Sopenharmony_ci case 16: 74762306a36Sopenharmony_ci emit(hppa_extru(rd, 31 - 8, 8, HPPA_REG_T1), ctx); 74862306a36Sopenharmony_ci emit(hppa_depwz(rd, 23, 8, HPPA_REG_T1), ctx); 74962306a36Sopenharmony_ci emit(hppa_extru(HPPA_REG_T1, 31, 16, rd), ctx); 75062306a36Sopenharmony_ci emit_hppa64_extrd(HPPA_REG_T1, 63, 16, rd, 0, ctx); 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci case 32: 75362306a36Sopenharmony_ci emit(hppa_shrpw(rd, rd, 16, HPPA_REG_T1), ctx); 75462306a36Sopenharmony_ci emit_hppa64_depd(HPPA_REG_T1, 63-16, 8, HPPA_REG_T1, 1, ctx); 75562306a36Sopenharmony_ci emit(hppa_shrpw(rd, HPPA_REG_T1, 8, HPPA_REG_T1), ctx); 75662306a36Sopenharmony_ci emit_hppa64_extrd(HPPA_REG_T1, 63, 32, rd, 0, ctx); 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci case 64: 75962306a36Sopenharmony_ci emit(hppa64_permh_3210(rd, HPPA_REG_T1), ctx); 76062306a36Sopenharmony_ci emit(hppa64_hshl(HPPA_REG_T1, 8, HPPA_REG_T2), ctx); 76162306a36Sopenharmony_ci emit(hppa64_hshr_u(HPPA_REG_T1, 8, HPPA_REG_T1), ctx); 76262306a36Sopenharmony_ci emit(hppa_or(HPPA_REG_T2, HPPA_REG_T1, rd), ctx); 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci default: 76562306a36Sopenharmony_ci pr_err("bpf-jit: BPF_END imm %d invalid\n", imm); 76662306a36Sopenharmony_ci return -1; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci break; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* dst = imm */ 77162306a36Sopenharmony_ci case BPF_ALU | BPF_MOV | BPF_K: 77262306a36Sopenharmony_ci case BPF_ALU64 | BPF_MOV | BPF_K: 77362306a36Sopenharmony_ci emit_imm(rd, imm, HPPA_REG_T2, ctx); 77462306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 77562306a36Sopenharmony_ci emit_zext_32(rd, ctx); 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* dst = dst OP imm */ 77962306a36Sopenharmony_ci case BPF_ALU | BPF_ADD | BPF_K: 78062306a36Sopenharmony_ci case BPF_ALU64 | BPF_ADD | BPF_K: 78162306a36Sopenharmony_ci if (relative_bits_ok(imm, 14)) { 78262306a36Sopenharmony_ci emit(hppa_ldo(imm, rd, rd), ctx); 78362306a36Sopenharmony_ci } else { 78462306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); 78562306a36Sopenharmony_ci emit(hppa_add(rd, HPPA_REG_T1, rd), ctx); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 78862306a36Sopenharmony_ci emit_zext_32(rd, ctx); 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci case BPF_ALU | BPF_SUB | BPF_K: 79162306a36Sopenharmony_ci case BPF_ALU64 | BPF_SUB | BPF_K: 79262306a36Sopenharmony_ci if (relative_bits_ok(-imm, 14)) { 79362306a36Sopenharmony_ci emit(hppa_ldo(-imm, rd, rd), ctx); 79462306a36Sopenharmony_ci } else { 79562306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); 79662306a36Sopenharmony_ci emit(hppa_sub(rd, HPPA_REG_T1, rd), ctx); 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 79962306a36Sopenharmony_ci emit_zext_32(rd, ctx); 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci case BPF_ALU | BPF_AND | BPF_K: 80262306a36Sopenharmony_ci case BPF_ALU64 | BPF_AND | BPF_K: 80362306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); 80462306a36Sopenharmony_ci emit(hppa_and(rd, HPPA_REG_T1, rd), ctx); 80562306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 80662306a36Sopenharmony_ci emit_zext_32(rd, ctx); 80762306a36Sopenharmony_ci break; 80862306a36Sopenharmony_ci case BPF_ALU | BPF_OR | BPF_K: 80962306a36Sopenharmony_ci case BPF_ALU64 | BPF_OR | BPF_K: 81062306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); 81162306a36Sopenharmony_ci emit(hppa_or(rd, HPPA_REG_T1, rd), ctx); 81262306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 81362306a36Sopenharmony_ci emit_zext_32(rd, ctx); 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci case BPF_ALU | BPF_XOR | BPF_K: 81662306a36Sopenharmony_ci case BPF_ALU64 | BPF_XOR | BPF_K: 81762306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); 81862306a36Sopenharmony_ci emit(hppa_xor(rd, HPPA_REG_T1, rd), ctx); 81962306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 82062306a36Sopenharmony_ci emit_zext_32(rd, ctx); 82162306a36Sopenharmony_ci break; 82262306a36Sopenharmony_ci case BPF_ALU | BPF_LSH | BPF_K: 82362306a36Sopenharmony_ci case BPF_ALU64 | BPF_LSH | BPF_K: 82462306a36Sopenharmony_ci if (imm != 0) { 82562306a36Sopenharmony_ci emit_hppa64_shld(rd, imm, rd, ctx); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 82962306a36Sopenharmony_ci emit_zext_32(rd, ctx); 83062306a36Sopenharmony_ci break; 83162306a36Sopenharmony_ci case BPF_ALU | BPF_RSH | BPF_K: 83262306a36Sopenharmony_ci case BPF_ALU64 | BPF_RSH | BPF_K: 83362306a36Sopenharmony_ci if (imm != 0) { 83462306a36Sopenharmony_ci if (is64) 83562306a36Sopenharmony_ci emit_hppa64_shrd(rd, imm, rd, false, ctx); 83662306a36Sopenharmony_ci else 83762306a36Sopenharmony_ci emit_hppa64_shrw(rd, imm, rd, false, ctx); 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 84162306a36Sopenharmony_ci emit_zext_32(rd, ctx); 84262306a36Sopenharmony_ci break; 84362306a36Sopenharmony_ci case BPF_ALU | BPF_ARSH | BPF_K: 84462306a36Sopenharmony_ci case BPF_ALU64 | BPF_ARSH | BPF_K: 84562306a36Sopenharmony_ci if (imm != 0) { 84662306a36Sopenharmony_ci if (is64) 84762306a36Sopenharmony_ci emit_hppa64_shrd(rd, imm, rd, true, ctx); 84862306a36Sopenharmony_ci else 84962306a36Sopenharmony_ci emit_hppa64_shrw(rd, imm, rd, true, ctx); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (!is64 && !aux->verifier_zext) 85362306a36Sopenharmony_ci emit_zext_32(rd, ctx); 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* JUMP off */ 85762306a36Sopenharmony_ci case BPF_JMP | BPF_JA: 85862306a36Sopenharmony_ci paoff = hppa_offset(i, off, ctx); 85962306a36Sopenharmony_ci ret = emit_jump(paoff, false, ctx); 86062306a36Sopenharmony_ci if (ret) 86162306a36Sopenharmony_ci return ret; 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* IF (dst COND src) JUMP off */ 86562306a36Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_X: 86662306a36Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_X: 86762306a36Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_X: 86862306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_X: 86962306a36Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_X: 87062306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_X: 87162306a36Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_X: 87262306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_X: 87362306a36Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_X: 87462306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_X: 87562306a36Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_X: 87662306a36Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_X: 87762306a36Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_X: 87862306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_X: 87962306a36Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_X: 88062306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_X: 88162306a36Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_X: 88262306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_X: 88362306a36Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_X: 88462306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_X: 88562306a36Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_X: 88662306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_X: 88762306a36Sopenharmony_ci paoff = hppa_offset(i, off, ctx); 88862306a36Sopenharmony_ci if (!is64) { 88962306a36Sopenharmony_ci s = ctx->ninsns; 89062306a36Sopenharmony_ci if (is_signed_bpf_cond(BPF_OP(code))) 89162306a36Sopenharmony_ci emit_sext_32_rd_rs(&rd, &rs, ctx); 89262306a36Sopenharmony_ci else 89362306a36Sopenharmony_ci emit_zext_32_rd_rs(&rd, &rs, ctx); 89462306a36Sopenharmony_ci e = ctx->ninsns; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* Adjust for extra insns */ 89762306a36Sopenharmony_ci paoff -= (e - s); 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci if (BPF_OP(code) == BPF_JSET) { 90062306a36Sopenharmony_ci /* Adjust for and */ 90162306a36Sopenharmony_ci paoff -= 1; 90262306a36Sopenharmony_ci emit(hppa_and(rs, rd, HPPA_REG_T1), ctx); 90362306a36Sopenharmony_ci emit_branch(BPF_JNE, HPPA_REG_T1, HPPA_REG_ZERO, paoff, 90462306a36Sopenharmony_ci ctx); 90562306a36Sopenharmony_ci } else { 90662306a36Sopenharmony_ci emit_branch(BPF_OP(code), rd, rs, paoff, ctx); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* IF (dst COND imm) JUMP off */ 91162306a36Sopenharmony_ci case BPF_JMP | BPF_JEQ | BPF_K: 91262306a36Sopenharmony_ci case BPF_JMP32 | BPF_JEQ | BPF_K: 91362306a36Sopenharmony_ci case BPF_JMP | BPF_JGT | BPF_K: 91462306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGT | BPF_K: 91562306a36Sopenharmony_ci case BPF_JMP | BPF_JLT | BPF_K: 91662306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLT | BPF_K: 91762306a36Sopenharmony_ci case BPF_JMP | BPF_JGE | BPF_K: 91862306a36Sopenharmony_ci case BPF_JMP32 | BPF_JGE | BPF_K: 91962306a36Sopenharmony_ci case BPF_JMP | BPF_JLE | BPF_K: 92062306a36Sopenharmony_ci case BPF_JMP32 | BPF_JLE | BPF_K: 92162306a36Sopenharmony_ci case BPF_JMP | BPF_JNE | BPF_K: 92262306a36Sopenharmony_ci case BPF_JMP32 | BPF_JNE | BPF_K: 92362306a36Sopenharmony_ci case BPF_JMP | BPF_JSGT | BPF_K: 92462306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGT | BPF_K: 92562306a36Sopenharmony_ci case BPF_JMP | BPF_JSLT | BPF_K: 92662306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLT | BPF_K: 92762306a36Sopenharmony_ci case BPF_JMP | BPF_JSGE | BPF_K: 92862306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSGE | BPF_K: 92962306a36Sopenharmony_ci case BPF_JMP | BPF_JSLE | BPF_K: 93062306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSLE | BPF_K: 93162306a36Sopenharmony_ci paoff = hppa_offset(i, off, ctx); 93262306a36Sopenharmony_ci s = ctx->ninsns; 93362306a36Sopenharmony_ci if (imm) { 93462306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); 93562306a36Sopenharmony_ci rs = HPPA_REG_T1; 93662306a36Sopenharmony_ci } else { 93762306a36Sopenharmony_ci rs = HPPA_REG_ZERO; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci if (!is64) { 94062306a36Sopenharmony_ci if (is_signed_bpf_cond(BPF_OP(code))) 94162306a36Sopenharmony_ci emit_sext_32_rd(&rd, ctx); 94262306a36Sopenharmony_ci else 94362306a36Sopenharmony_ci emit_zext_32_rd_t1(&rd, ctx); 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci e = ctx->ninsns; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* Adjust for extra insns */ 94862306a36Sopenharmony_ci paoff -= (e - s); 94962306a36Sopenharmony_ci emit_branch(BPF_OP(code), rd, rs, paoff, ctx); 95062306a36Sopenharmony_ci break; 95162306a36Sopenharmony_ci case BPF_JMP | BPF_JSET | BPF_K: 95262306a36Sopenharmony_ci case BPF_JMP32 | BPF_JSET | BPF_K: 95362306a36Sopenharmony_ci paoff = hppa_offset(i, off, ctx); 95462306a36Sopenharmony_ci s = ctx->ninsns; 95562306a36Sopenharmony_ci emit_imm(HPPA_REG_T1, imm, HPPA_REG_T2, ctx); 95662306a36Sopenharmony_ci emit(hppa_and(HPPA_REG_T1, rd, HPPA_REG_T1), ctx); 95762306a36Sopenharmony_ci /* For jset32, we should clear the upper 32 bits of t1, but 95862306a36Sopenharmony_ci * sign-extension is sufficient here and saves one instruction, 95962306a36Sopenharmony_ci * as t1 is used only in comparison against zero. 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci if (!is64 && imm < 0) 96262306a36Sopenharmony_ci emit_hppa64_sext32(HPPA_REG_T1, HPPA_REG_T1, ctx); 96362306a36Sopenharmony_ci e = ctx->ninsns; 96462306a36Sopenharmony_ci paoff -= (e - s); 96562306a36Sopenharmony_ci emit_branch(BPF_JNE, HPPA_REG_T1, HPPA_REG_ZERO, paoff, ctx); 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci /* function call */ 96862306a36Sopenharmony_ci case BPF_JMP | BPF_CALL: 96962306a36Sopenharmony_ci { 97062306a36Sopenharmony_ci bool fixed_addr; 97162306a36Sopenharmony_ci u64 addr; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 97462306a36Sopenharmony_ci &addr, &fixed_addr); 97562306a36Sopenharmony_ci if (ret < 0) 97662306a36Sopenharmony_ci return ret; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci REG_SET_SEEN_ALL(ctx); 97962306a36Sopenharmony_ci emit_call(addr, fixed_addr, ctx); 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci /* tail call */ 98362306a36Sopenharmony_ci case BPF_JMP | BPF_TAIL_CALL: 98462306a36Sopenharmony_ci emit_bpf_tail_call(i, ctx); 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* function return */ 98862306a36Sopenharmony_ci case BPF_JMP | BPF_EXIT: 98962306a36Sopenharmony_ci if (i == ctx->prog->len - 1) 99062306a36Sopenharmony_ci break; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci paoff = epilogue_offset(ctx); 99362306a36Sopenharmony_ci ret = emit_jump(paoff, false, ctx); 99462306a36Sopenharmony_ci if (ret) 99562306a36Sopenharmony_ci return ret; 99662306a36Sopenharmony_ci break; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* dst = imm64 */ 99962306a36Sopenharmony_ci case BPF_LD | BPF_IMM | BPF_DW: 100062306a36Sopenharmony_ci { 100162306a36Sopenharmony_ci struct bpf_insn insn1 = insn[1]; 100262306a36Sopenharmony_ci u64 imm64 = (u64)insn1.imm << 32 | (u32)imm; 100362306a36Sopenharmony_ci if (bpf_pseudo_func(insn)) 100462306a36Sopenharmony_ci imm64 = (uintptr_t)dereference_function_descriptor((void*)imm64); 100562306a36Sopenharmony_ci emit_imm(rd, imm64, HPPA_REG_T2, ctx); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci return 1; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* LDX: dst = *(size *)(src + off) */ 101162306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_B: 101262306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_H: 101362306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_W: 101462306a36Sopenharmony_ci case BPF_LDX | BPF_MEM | BPF_DW: 101562306a36Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_B: 101662306a36Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_H: 101762306a36Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_W: 101862306a36Sopenharmony_ci case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 101962306a36Sopenharmony_ci { 102062306a36Sopenharmony_ci u8 srcreg; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci /* need to calculate address since offset does not fit in 14 bits? */ 102362306a36Sopenharmony_ci if (relative_bits_ok(off, 14)) 102462306a36Sopenharmony_ci srcreg = rs; 102562306a36Sopenharmony_ci else { 102662306a36Sopenharmony_ci /* need to use R1 here, since addil puts result into R1 */ 102762306a36Sopenharmony_ci srcreg = HPPA_REG_R1; 102862306a36Sopenharmony_ci BUG_ON(rs == HPPA_REG_R1); 102962306a36Sopenharmony_ci BUG_ON(rd == HPPA_REG_R1); 103062306a36Sopenharmony_ci emit(hppa_addil(off, rs), ctx); 103162306a36Sopenharmony_ci off = im11(off); 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci switch (BPF_SIZE(code)) { 103562306a36Sopenharmony_ci case BPF_B: 103662306a36Sopenharmony_ci emit(hppa_ldb(off, srcreg, rd), ctx); 103762306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 103862306a36Sopenharmony_ci return 1; 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci case BPF_H: 104162306a36Sopenharmony_ci emit(hppa_ldh(off, srcreg, rd), ctx); 104262306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 104362306a36Sopenharmony_ci return 1; 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci case BPF_W: 104662306a36Sopenharmony_ci emit(hppa_ldw(off, srcreg, rd), ctx); 104762306a36Sopenharmony_ci if (insn_is_zext(&insn[1])) 104862306a36Sopenharmony_ci return 1; 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci case BPF_DW: 105162306a36Sopenharmony_ci if (off & 7) { 105262306a36Sopenharmony_ci emit(hppa_ldo(off, srcreg, HPPA_REG_R1), ctx); 105362306a36Sopenharmony_ci emit(hppa64_ldd_reg(HPPA_REG_ZERO, HPPA_REG_R1, rd), ctx); 105462306a36Sopenharmony_ci } else if (off >= -16 && off <= 15) 105562306a36Sopenharmony_ci emit(hppa64_ldd_im5(off, srcreg, rd), ctx); 105662306a36Sopenharmony_ci else 105762306a36Sopenharmony_ci emit(hppa64_ldd_im16(off, srcreg, rd), ctx); 105862306a36Sopenharmony_ci break; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci /* speculation barrier */ 106362306a36Sopenharmony_ci case BPF_ST | BPF_NOSPEC: 106462306a36Sopenharmony_ci break; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* ST: *(size *)(dst + off) = imm */ 106762306a36Sopenharmony_ci /* STX: *(size *)(dst + off) = src */ 106862306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_B: 106962306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_H: 107062306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_W: 107162306a36Sopenharmony_ci case BPF_ST | BPF_MEM | BPF_DW: 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_B: 107462306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_H: 107562306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_W: 107662306a36Sopenharmony_ci case BPF_STX | BPF_MEM | BPF_DW: 107762306a36Sopenharmony_ci if (BPF_CLASS(code) == BPF_ST) { 107862306a36Sopenharmony_ci emit_imm(HPPA_REG_T2, imm, HPPA_REG_T1, ctx); 107962306a36Sopenharmony_ci rs = HPPA_REG_T2; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci emit_store(rd, rs, off, ctx, BPF_SIZE(code), BPF_MODE(code)); 108362306a36Sopenharmony_ci break; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci case BPF_STX | BPF_ATOMIC | BPF_W: 108662306a36Sopenharmony_ci case BPF_STX | BPF_ATOMIC | BPF_DW: 108762306a36Sopenharmony_ci pr_info_once( 108862306a36Sopenharmony_ci "bpf-jit: not supported: atomic operation %02x ***\n", 108962306a36Sopenharmony_ci insn->imm); 109062306a36Sopenharmony_ci return -EFAULT; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci default: 109362306a36Sopenharmony_ci pr_err("bpf-jit: unknown opcode %02x\n", code); 109462306a36Sopenharmony_ci return -EINVAL; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci return 0; 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_civoid bpf_jit_build_prologue(struct hppa_jit_context *ctx) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci int bpf_stack_adjust, stack_adjust, i; 110362306a36Sopenharmony_ci unsigned long addr; 110462306a36Sopenharmony_ci s8 reg; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* 110762306a36Sopenharmony_ci * stack on hppa grows up, so if tail calls are used we need to 110862306a36Sopenharmony_ci * allocate the maximum stack size 110962306a36Sopenharmony_ci */ 111062306a36Sopenharmony_ci if (REG_ALL_SEEN(ctx)) 111162306a36Sopenharmony_ci bpf_stack_adjust = MAX_BPF_STACK; 111262306a36Sopenharmony_ci else 111362306a36Sopenharmony_ci bpf_stack_adjust = ctx->prog->aux->stack_depth; 111462306a36Sopenharmony_ci bpf_stack_adjust = round_up(bpf_stack_adjust, STACK_ALIGN); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci stack_adjust = FRAME_SIZE + bpf_stack_adjust; 111762306a36Sopenharmony_ci stack_adjust = round_up(stack_adjust, STACK_ALIGN); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci /* 112062306a36Sopenharmony_ci * NOTE: We construct an Elf64_Fdesc descriptor here. 112162306a36Sopenharmony_ci * The first 4 words initialize the TCC and compares them. 112262306a36Sopenharmony_ci * Then follows the virtual address of the eBPF function, 112362306a36Sopenharmony_ci * and the gp for this function. 112462306a36Sopenharmony_ci * 112562306a36Sopenharmony_ci * The first instruction sets the tail-call-counter (TCC) register. 112662306a36Sopenharmony_ci * This instruction is skipped by tail calls. 112762306a36Sopenharmony_ci * Use a temporary register instead of a caller-saved register initially. 112862306a36Sopenharmony_ci */ 112962306a36Sopenharmony_ci REG_FORCE_SEEN(ctx, HPPA_REG_TCC_IN_INIT); 113062306a36Sopenharmony_ci emit(hppa_ldi(MAX_TAIL_CALL_CNT, HPPA_REG_TCC_IN_INIT), ctx); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* 113362306a36Sopenharmony_ci * Skip all initializations when called as BPF TAIL call. 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci emit(hppa_ldi(MAX_TAIL_CALL_CNT, HPPA_REG_R1), ctx); 113662306a36Sopenharmony_ci emit(hppa_beq(HPPA_REG_TCC_IN_INIT, HPPA_REG_R1, 6 - HPPA_BRANCH_DISPLACEMENT), ctx); 113762306a36Sopenharmony_ci emit(hppa64_bl_long(ctx->prologue_len - 3 - HPPA_BRANCH_DISPLACEMENT), ctx); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* store entry address of this eBPF function */ 114062306a36Sopenharmony_ci addr = (uintptr_t) &ctx->insns[0]; 114162306a36Sopenharmony_ci emit(addr >> 32, ctx); 114262306a36Sopenharmony_ci emit(addr & 0xffffffff, ctx); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci /* store gp of this eBPF function */ 114562306a36Sopenharmony_ci asm("copy %%r27,%0" : "=r" (addr) ); 114662306a36Sopenharmony_ci emit(addr >> 32, ctx); 114762306a36Sopenharmony_ci emit(addr & 0xffffffff, ctx); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* Set up hppa stack frame. */ 115062306a36Sopenharmony_ci emit_hppa_copy(HPPA_REG_SP, HPPA_REG_R1, ctx); 115162306a36Sopenharmony_ci emit(hppa_ldo(stack_adjust, HPPA_REG_SP, HPPA_REG_SP), ctx); 115262306a36Sopenharmony_ci emit(hppa64_std_im5 (HPPA_REG_R1, -REG_SIZE, HPPA_REG_SP), ctx); 115362306a36Sopenharmony_ci emit(hppa64_std_im16(HPPA_REG_RP, -2*REG_SIZE, HPPA_REG_SP), ctx); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci /* Save callee-save registers. */ 115662306a36Sopenharmony_ci for (i = 3; i <= 15; i++) { 115762306a36Sopenharmony_ci if (OPTIMIZE_HPPA && !REG_WAS_SEEN(ctx, HPPA_R(i))) 115862306a36Sopenharmony_ci continue; 115962306a36Sopenharmony_ci emit(hppa64_std_im16(HPPA_R(i), -REG_SIZE * i, HPPA_REG_SP), ctx); 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci /* load function parameters; load all if we use tail functions */ 116362306a36Sopenharmony_ci #define LOAD_PARAM(arg, dst) \ 116462306a36Sopenharmony_ci if (REG_WAS_SEEN(ctx, regmap[dst]) || \ 116562306a36Sopenharmony_ci REG_WAS_SEEN(ctx, HPPA_REG_TCC)) \ 116662306a36Sopenharmony_ci emit_hppa_copy(arg, regmap[dst], ctx) 116762306a36Sopenharmony_ci LOAD_PARAM(HPPA_REG_ARG0, BPF_REG_1); 116862306a36Sopenharmony_ci LOAD_PARAM(HPPA_REG_ARG1, BPF_REG_2); 116962306a36Sopenharmony_ci LOAD_PARAM(HPPA_REG_ARG2, BPF_REG_3); 117062306a36Sopenharmony_ci LOAD_PARAM(HPPA_REG_ARG3, BPF_REG_4); 117162306a36Sopenharmony_ci LOAD_PARAM(HPPA_REG_ARG4, BPF_REG_5); 117262306a36Sopenharmony_ci #undef LOAD_PARAM 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci REG_FORCE_SEEN(ctx, HPPA_REG_T0); 117562306a36Sopenharmony_ci REG_FORCE_SEEN(ctx, HPPA_REG_T1); 117662306a36Sopenharmony_ci REG_FORCE_SEEN(ctx, HPPA_REG_T2); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* 117962306a36Sopenharmony_ci * Now really set the tail call counter (TCC) register. 118062306a36Sopenharmony_ci */ 118162306a36Sopenharmony_ci if (REG_WAS_SEEN(ctx, HPPA_REG_TCC)) 118262306a36Sopenharmony_ci emit(hppa_ldi(MAX_TAIL_CALL_CNT, HPPA_REG_TCC), ctx); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci /* 118562306a36Sopenharmony_ci * Save epilogue function pointer for outer TCC call chain. 118662306a36Sopenharmony_ci * The main TCC call stores the final RP on stack. 118762306a36Sopenharmony_ci */ 118862306a36Sopenharmony_ci addr = (uintptr_t) &ctx->insns[ctx->epilogue_offset]; 118962306a36Sopenharmony_ci /* skip first two instructions which jump to exit */ 119062306a36Sopenharmony_ci addr += 2 * HPPA_INSN_SIZE; 119162306a36Sopenharmony_ci emit_imm(HPPA_REG_T2, addr, HPPA_REG_T1, ctx); 119262306a36Sopenharmony_ci emit(EXIT_PTR_STORE(HPPA_REG_T2), ctx); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* Set up BPF frame pointer. */ 119562306a36Sopenharmony_ci reg = regmap[BPF_REG_FP]; /* -> HPPA_REG_FP */ 119662306a36Sopenharmony_ci if (REG_WAS_SEEN(ctx, reg)) { 119762306a36Sopenharmony_ci emit(hppa_ldo(-FRAME_SIZE, HPPA_REG_SP, reg), ctx); 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_civoid bpf_jit_build_epilogue(struct hppa_jit_context *ctx) 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci __build_epilogue(false, ctx); 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cibool bpf_jit_supports_kfunc_call(void) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci return true; 120962306a36Sopenharmony_ci} 1210