15dc61552STiezhu Yang // SPDX-License-Identifier: GPL-2.0-only 25dc61552STiezhu Yang /* 35dc61552STiezhu Yang * BPF JIT compiler for LoongArch 45dc61552STiezhu Yang * 55dc61552STiezhu Yang * Copyright (C) 2022 Loongson Technology Corporation Limited 65dc61552STiezhu Yang */ 75dc61552STiezhu Yang #include "bpf_jit.h" 85dc61552STiezhu Yang 95dc61552STiezhu Yang #define REG_TCC LOONGARCH_GPR_A6 105dc61552STiezhu Yang #define TCC_SAVED LOONGARCH_GPR_S5 115dc61552STiezhu Yang 125dc61552STiezhu Yang #define SAVE_RA BIT(0) 135dc61552STiezhu Yang #define SAVE_TCC BIT(1) 145dc61552STiezhu Yang 155dc61552STiezhu Yang static const int regmap[] = { 165dc61552STiezhu Yang /* return value from in-kernel function, and exit value for eBPF program */ 175dc61552STiezhu Yang [BPF_REG_0] = LOONGARCH_GPR_A5, 185dc61552STiezhu Yang /* arguments from eBPF program to in-kernel function */ 195dc61552STiezhu Yang [BPF_REG_1] = LOONGARCH_GPR_A0, 205dc61552STiezhu Yang [BPF_REG_2] = LOONGARCH_GPR_A1, 215dc61552STiezhu Yang [BPF_REG_3] = LOONGARCH_GPR_A2, 225dc61552STiezhu Yang [BPF_REG_4] = LOONGARCH_GPR_A3, 235dc61552STiezhu Yang [BPF_REG_5] = LOONGARCH_GPR_A4, 245dc61552STiezhu Yang /* callee saved registers that in-kernel function will preserve */ 255dc61552STiezhu Yang [BPF_REG_6] = LOONGARCH_GPR_S0, 265dc61552STiezhu Yang [BPF_REG_7] = LOONGARCH_GPR_S1, 275dc61552STiezhu Yang [BPF_REG_8] = LOONGARCH_GPR_S2, 285dc61552STiezhu Yang [BPF_REG_9] = LOONGARCH_GPR_S3, 295dc61552STiezhu Yang /* read-only frame pointer to access stack */ 305dc61552STiezhu Yang [BPF_REG_FP] = LOONGARCH_GPR_S4, 315dc61552STiezhu Yang /* temporary register for blinding constants */ 325dc61552STiezhu Yang [BPF_REG_AX] = LOONGARCH_GPR_T0, 335dc61552STiezhu Yang }; 345dc61552STiezhu Yang 355dc61552STiezhu Yang static void mark_call(struct jit_ctx *ctx) 365dc61552STiezhu Yang { 375dc61552STiezhu Yang ctx->flags |= SAVE_RA; 385dc61552STiezhu Yang } 395dc61552STiezhu Yang 405dc61552STiezhu Yang static void mark_tail_call(struct jit_ctx *ctx) 415dc61552STiezhu Yang { 425dc61552STiezhu Yang ctx->flags |= SAVE_TCC; 435dc61552STiezhu Yang } 445dc61552STiezhu Yang 455dc61552STiezhu Yang static bool seen_call(struct jit_ctx *ctx) 465dc61552STiezhu Yang { 475dc61552STiezhu Yang return (ctx->flags & SAVE_RA); 485dc61552STiezhu Yang } 495dc61552STiezhu Yang 505dc61552STiezhu Yang static bool seen_tail_call(struct jit_ctx *ctx) 515dc61552STiezhu Yang { 525dc61552STiezhu Yang return (ctx->flags & SAVE_TCC); 535dc61552STiezhu Yang } 545dc61552STiezhu Yang 555dc61552STiezhu Yang static u8 tail_call_reg(struct jit_ctx *ctx) 565dc61552STiezhu Yang { 575dc61552STiezhu Yang if (seen_call(ctx)) 585dc61552STiezhu Yang return TCC_SAVED; 595dc61552STiezhu Yang 605dc61552STiezhu Yang return REG_TCC; 615dc61552STiezhu Yang } 625dc61552STiezhu Yang 635dc61552STiezhu Yang /* 645dc61552STiezhu Yang * eBPF prog stack layout: 655dc61552STiezhu Yang * 665dc61552STiezhu Yang * high 675dc61552STiezhu Yang * original $sp ------------> +-------------------------+ <--LOONGARCH_GPR_FP 685dc61552STiezhu Yang * | $ra | 695dc61552STiezhu Yang * +-------------------------+ 705dc61552STiezhu Yang * | $fp | 715dc61552STiezhu Yang * +-------------------------+ 725dc61552STiezhu Yang * | $s0 | 735dc61552STiezhu Yang * +-------------------------+ 745dc61552STiezhu Yang * | $s1 | 755dc61552STiezhu Yang * +-------------------------+ 765dc61552STiezhu Yang * | $s2 | 775dc61552STiezhu Yang * +-------------------------+ 785dc61552STiezhu Yang * | $s3 | 795dc61552STiezhu Yang * +-------------------------+ 805dc61552STiezhu Yang * | $s4 | 815dc61552STiezhu Yang * +-------------------------+ 825dc61552STiezhu Yang * | $s5 | 835dc61552STiezhu Yang * +-------------------------+ <--BPF_REG_FP 845dc61552STiezhu Yang * | prog->aux->stack_depth | 855dc61552STiezhu Yang * | (optional) | 865dc61552STiezhu Yang * current $sp -------------> +-------------------------+ 875dc61552STiezhu Yang * low 885dc61552STiezhu Yang */ 895dc61552STiezhu Yang static void build_prologue(struct jit_ctx *ctx) 905dc61552STiezhu Yang { 915dc61552STiezhu Yang int stack_adjust = 0, store_offset, bpf_stack_adjust; 925dc61552STiezhu Yang 935dc61552STiezhu Yang bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); 945dc61552STiezhu Yang 955dc61552STiezhu Yang /* To store ra, fp, s0, s1, s2, s3, s4 and s5. */ 965dc61552STiezhu Yang stack_adjust += sizeof(long) * 8; 975dc61552STiezhu Yang 985dc61552STiezhu Yang stack_adjust = round_up(stack_adjust, 16); 995dc61552STiezhu Yang stack_adjust += bpf_stack_adjust; 1005dc61552STiezhu Yang 1015dc61552STiezhu Yang /* 1025dc61552STiezhu Yang * First instruction initializes the tail call count (TCC). 1035dc61552STiezhu Yang * On tail call we skip this instruction, and the TCC is 1045dc61552STiezhu Yang * passed in REG_TCC from the caller. 1055dc61552STiezhu Yang */ 1065dc61552STiezhu Yang emit_insn(ctx, addid, REG_TCC, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT); 1075dc61552STiezhu Yang 1085dc61552STiezhu Yang emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, -stack_adjust); 1095dc61552STiezhu Yang 1105dc61552STiezhu Yang store_offset = stack_adjust - sizeof(long); 1115dc61552STiezhu Yang emit_insn(ctx, std, LOONGARCH_GPR_RA, LOONGARCH_GPR_SP, store_offset); 1125dc61552STiezhu Yang 1135dc61552STiezhu Yang store_offset -= sizeof(long); 1145dc61552STiezhu Yang emit_insn(ctx, std, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, store_offset); 1155dc61552STiezhu Yang 1165dc61552STiezhu Yang store_offset -= sizeof(long); 1175dc61552STiezhu Yang emit_insn(ctx, std, LOONGARCH_GPR_S0, LOONGARCH_GPR_SP, store_offset); 1185dc61552STiezhu Yang 1195dc61552STiezhu Yang store_offset -= sizeof(long); 1205dc61552STiezhu Yang emit_insn(ctx, std, LOONGARCH_GPR_S1, LOONGARCH_GPR_SP, store_offset); 1215dc61552STiezhu Yang 1225dc61552STiezhu Yang store_offset -= sizeof(long); 1235dc61552STiezhu Yang emit_insn(ctx, std, LOONGARCH_GPR_S2, LOONGARCH_GPR_SP, store_offset); 1245dc61552STiezhu Yang 1255dc61552STiezhu Yang store_offset -= sizeof(long); 1265dc61552STiezhu Yang emit_insn(ctx, std, LOONGARCH_GPR_S3, LOONGARCH_GPR_SP, store_offset); 1275dc61552STiezhu Yang 1285dc61552STiezhu Yang store_offset -= sizeof(long); 1295dc61552STiezhu Yang emit_insn(ctx, std, LOONGARCH_GPR_S4, LOONGARCH_GPR_SP, store_offset); 1305dc61552STiezhu Yang 1315dc61552STiezhu Yang store_offset -= sizeof(long); 1325dc61552STiezhu Yang emit_insn(ctx, std, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, store_offset); 1335dc61552STiezhu Yang 1345dc61552STiezhu Yang emit_insn(ctx, addid, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_adjust); 1355dc61552STiezhu Yang 1365dc61552STiezhu Yang if (bpf_stack_adjust) 1375dc61552STiezhu Yang emit_insn(ctx, addid, regmap[BPF_REG_FP], LOONGARCH_GPR_SP, bpf_stack_adjust); 1385dc61552STiezhu Yang 1395dc61552STiezhu Yang /* 1405dc61552STiezhu Yang * Program contains calls and tail calls, so REG_TCC need 1415dc61552STiezhu Yang * to be saved across calls. 1425dc61552STiezhu Yang */ 1435dc61552STiezhu Yang if (seen_tail_call(ctx) && seen_call(ctx)) 1445dc61552STiezhu Yang move_reg(ctx, TCC_SAVED, REG_TCC); 1455dc61552STiezhu Yang 1465dc61552STiezhu Yang ctx->stack_size = stack_adjust; 1475dc61552STiezhu Yang } 1485dc61552STiezhu Yang 1495dc61552STiezhu Yang static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call) 1505dc61552STiezhu Yang { 1515dc61552STiezhu Yang int stack_adjust = ctx->stack_size; 1525dc61552STiezhu Yang int load_offset; 1535dc61552STiezhu Yang 1545dc61552STiezhu Yang load_offset = stack_adjust - sizeof(long); 1555dc61552STiezhu Yang emit_insn(ctx, ldd, LOONGARCH_GPR_RA, LOONGARCH_GPR_SP, load_offset); 1565dc61552STiezhu Yang 1575dc61552STiezhu Yang load_offset -= sizeof(long); 1585dc61552STiezhu Yang emit_insn(ctx, ldd, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, load_offset); 1595dc61552STiezhu Yang 1605dc61552STiezhu Yang load_offset -= sizeof(long); 1615dc61552STiezhu Yang emit_insn(ctx, ldd, LOONGARCH_GPR_S0, LOONGARCH_GPR_SP, load_offset); 1625dc61552STiezhu Yang 1635dc61552STiezhu Yang load_offset -= sizeof(long); 1645dc61552STiezhu Yang emit_insn(ctx, ldd, LOONGARCH_GPR_S1, LOONGARCH_GPR_SP, load_offset); 1655dc61552STiezhu Yang 1665dc61552STiezhu Yang load_offset -= sizeof(long); 1675dc61552STiezhu Yang emit_insn(ctx, ldd, LOONGARCH_GPR_S2, LOONGARCH_GPR_SP, load_offset); 1685dc61552STiezhu Yang 1695dc61552STiezhu Yang load_offset -= sizeof(long); 1705dc61552STiezhu Yang emit_insn(ctx, ldd, LOONGARCH_GPR_S3, LOONGARCH_GPR_SP, load_offset); 1715dc61552STiezhu Yang 1725dc61552STiezhu Yang load_offset -= sizeof(long); 1735dc61552STiezhu Yang emit_insn(ctx, ldd, LOONGARCH_GPR_S4, LOONGARCH_GPR_SP, load_offset); 1745dc61552STiezhu Yang 1755dc61552STiezhu Yang load_offset -= sizeof(long); 1765dc61552STiezhu Yang emit_insn(ctx, ldd, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, load_offset); 1775dc61552STiezhu Yang 1785dc61552STiezhu Yang emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, stack_adjust); 1795dc61552STiezhu Yang 1805dc61552STiezhu Yang if (!is_tail_call) { 1815dc61552STiezhu Yang /* Set return value */ 1825dc61552STiezhu Yang move_reg(ctx, LOONGARCH_GPR_A0, regmap[BPF_REG_0]); 1835dc61552STiezhu Yang /* Return to the caller */ 1845dc61552STiezhu Yang emit_insn(ctx, jirl, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0); 1855dc61552STiezhu Yang } else { 1865dc61552STiezhu Yang /* 1875dc61552STiezhu Yang * Call the next bpf prog and skip the first instruction 1885dc61552STiezhu Yang * of TCC initialization. 1895dc61552STiezhu Yang */ 1905dc61552STiezhu Yang emit_insn(ctx, jirl, LOONGARCH_GPR_T3, LOONGARCH_GPR_ZERO, 1); 1915dc61552STiezhu Yang } 1925dc61552STiezhu Yang } 1935dc61552STiezhu Yang 1945dc61552STiezhu Yang static void build_epilogue(struct jit_ctx *ctx) 1955dc61552STiezhu Yang { 1965dc61552STiezhu Yang __build_epilogue(ctx, false); 1975dc61552STiezhu Yang } 1985dc61552STiezhu Yang 1995dc61552STiezhu Yang bool bpf_jit_supports_kfunc_call(void) 2005dc61552STiezhu Yang { 2015dc61552STiezhu Yang return true; 2025dc61552STiezhu Yang } 2035dc61552STiezhu Yang 2045dc61552STiezhu Yang /* initialized on the first pass of build_body() */ 2055dc61552STiezhu Yang static int out_offset = -1; 2065dc61552STiezhu Yang static int emit_bpf_tail_call(struct jit_ctx *ctx) 2075dc61552STiezhu Yang { 2085dc61552STiezhu Yang int off; 2095dc61552STiezhu Yang u8 tcc = tail_call_reg(ctx); 2105dc61552STiezhu Yang u8 a1 = LOONGARCH_GPR_A1; 2115dc61552STiezhu Yang u8 a2 = LOONGARCH_GPR_A2; 2125dc61552STiezhu Yang u8 t1 = LOONGARCH_GPR_T1; 2135dc61552STiezhu Yang u8 t2 = LOONGARCH_GPR_T2; 2145dc61552STiezhu Yang u8 t3 = LOONGARCH_GPR_T3; 2155dc61552STiezhu Yang const int idx0 = ctx->idx; 2165dc61552STiezhu Yang 2175dc61552STiezhu Yang #define cur_offset (ctx->idx - idx0) 2185dc61552STiezhu Yang #define jmp_offset (out_offset - (cur_offset)) 2195dc61552STiezhu Yang 2205dc61552STiezhu Yang /* 2215dc61552STiezhu Yang * a0: &ctx 2225dc61552STiezhu Yang * a1: &array 2235dc61552STiezhu Yang * a2: index 2245dc61552STiezhu Yang * 2255dc61552STiezhu Yang * if (index >= array->map.max_entries) 2265dc61552STiezhu Yang * goto out; 2275dc61552STiezhu Yang */ 2285dc61552STiezhu Yang off = offsetof(struct bpf_array, map.max_entries); 2295dc61552STiezhu Yang emit_insn(ctx, ldwu, t1, a1, off); 2305dc61552STiezhu Yang /* bgeu $a2, $t1, jmp_offset */ 2315dc61552STiezhu Yang if (emit_tailcall_jmp(ctx, BPF_JGE, a2, t1, jmp_offset) < 0) 2325dc61552STiezhu Yang goto toofar; 2335dc61552STiezhu Yang 2345dc61552STiezhu Yang /* 2355dc61552STiezhu Yang * if (--TCC < 0) 2365dc61552STiezhu Yang * goto out; 2375dc61552STiezhu Yang */ 2385dc61552STiezhu Yang emit_insn(ctx, addid, REG_TCC, tcc, -1); 2395dc61552STiezhu Yang if (emit_tailcall_jmp(ctx, BPF_JSLT, REG_TCC, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 2405dc61552STiezhu Yang goto toofar; 2415dc61552STiezhu Yang 2425dc61552STiezhu Yang /* 2435dc61552STiezhu Yang * prog = array->ptrs[index]; 2445dc61552STiezhu Yang * if (!prog) 2455dc61552STiezhu Yang * goto out; 2465dc61552STiezhu Yang */ 2475dc61552STiezhu Yang emit_insn(ctx, alsld, t2, a2, a1, 2); 2485dc61552STiezhu Yang off = offsetof(struct bpf_array, ptrs); 2495dc61552STiezhu Yang emit_insn(ctx, ldd, t2, t2, off); 2505dc61552STiezhu Yang /* beq $t2, $zero, jmp_offset */ 2515dc61552STiezhu Yang if (emit_tailcall_jmp(ctx, BPF_JEQ, t2, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 2525dc61552STiezhu Yang goto toofar; 2535dc61552STiezhu Yang 2545dc61552STiezhu Yang /* goto *(prog->bpf_func + 4); */ 2555dc61552STiezhu Yang off = offsetof(struct bpf_prog, bpf_func); 2565dc61552STiezhu Yang emit_insn(ctx, ldd, t3, t2, off); 2575dc61552STiezhu Yang __build_epilogue(ctx, true); 2585dc61552STiezhu Yang 2595dc61552STiezhu Yang /* out: */ 2605dc61552STiezhu Yang if (out_offset == -1) 2615dc61552STiezhu Yang out_offset = cur_offset; 2625dc61552STiezhu Yang if (cur_offset != out_offset) { 2635dc61552STiezhu Yang pr_err_once("tail_call out_offset = %d, expected %d!\n", 2645dc61552STiezhu Yang cur_offset, out_offset); 2655dc61552STiezhu Yang return -1; 2665dc61552STiezhu Yang } 2675dc61552STiezhu Yang 2685dc61552STiezhu Yang return 0; 2695dc61552STiezhu Yang 2705dc61552STiezhu Yang toofar: 2715dc61552STiezhu Yang pr_info_once("tail_call: jump too far\n"); 2725dc61552STiezhu Yang return -1; 2735dc61552STiezhu Yang #undef cur_offset 2745dc61552STiezhu Yang #undef jmp_offset 2755dc61552STiezhu Yang } 2765dc61552STiezhu Yang 2775dc61552STiezhu Yang static void emit_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 2785dc61552STiezhu Yang { 2795dc61552STiezhu Yang const u8 t1 = LOONGARCH_GPR_T1; 2805dc61552STiezhu Yang const u8 t2 = LOONGARCH_GPR_T2; 2815dc61552STiezhu Yang const u8 t3 = LOONGARCH_GPR_T3; 282*bbfddb90SHuacai Chen const u8 r0 = regmap[BPF_REG_0]; 2835dc61552STiezhu Yang const u8 src = regmap[insn->src_reg]; 2845dc61552STiezhu Yang const u8 dst = regmap[insn->dst_reg]; 2855dc61552STiezhu Yang const s16 off = insn->off; 2865dc61552STiezhu Yang const s32 imm = insn->imm; 2875dc61552STiezhu Yang const bool isdw = BPF_SIZE(insn->code) == BPF_DW; 2885dc61552STiezhu Yang 2895dc61552STiezhu Yang move_imm(ctx, t1, off, false); 2905dc61552STiezhu Yang emit_insn(ctx, addd, t1, dst, t1); 2915dc61552STiezhu Yang move_reg(ctx, t3, src); 2925dc61552STiezhu Yang 2935dc61552STiezhu Yang switch (imm) { 2945dc61552STiezhu Yang /* lock *(size *)(dst + off) <op>= src */ 2955dc61552STiezhu Yang case BPF_ADD: 2965dc61552STiezhu Yang if (isdw) 2975dc61552STiezhu Yang emit_insn(ctx, amaddd, t2, t1, src); 2985dc61552STiezhu Yang else 2995dc61552STiezhu Yang emit_insn(ctx, amaddw, t2, t1, src); 3005dc61552STiezhu Yang break; 3015dc61552STiezhu Yang case BPF_AND: 3025dc61552STiezhu Yang if (isdw) 3035dc61552STiezhu Yang emit_insn(ctx, amandd, t2, t1, src); 3045dc61552STiezhu Yang else 3055dc61552STiezhu Yang emit_insn(ctx, amandw, t2, t1, src); 3065dc61552STiezhu Yang break; 3075dc61552STiezhu Yang case BPF_OR: 3085dc61552STiezhu Yang if (isdw) 3095dc61552STiezhu Yang emit_insn(ctx, amord, t2, t1, src); 3105dc61552STiezhu Yang else 3115dc61552STiezhu Yang emit_insn(ctx, amorw, t2, t1, src); 3125dc61552STiezhu Yang break; 3135dc61552STiezhu Yang case BPF_XOR: 3145dc61552STiezhu Yang if (isdw) 3155dc61552STiezhu Yang emit_insn(ctx, amxord, t2, t1, src); 3165dc61552STiezhu Yang else 3175dc61552STiezhu Yang emit_insn(ctx, amxorw, t2, t1, src); 3185dc61552STiezhu Yang break; 3195dc61552STiezhu Yang /* src = atomic_fetch_<op>(dst + off, src) */ 3205dc61552STiezhu Yang case BPF_ADD | BPF_FETCH: 3215dc61552STiezhu Yang if (isdw) { 3225dc61552STiezhu Yang emit_insn(ctx, amaddd, src, t1, t3); 3235dc61552STiezhu Yang } else { 3245dc61552STiezhu Yang emit_insn(ctx, amaddw, src, t1, t3); 3255dc61552STiezhu Yang emit_zext_32(ctx, src, true); 3265dc61552STiezhu Yang } 3275dc61552STiezhu Yang break; 3285dc61552STiezhu Yang case BPF_AND | BPF_FETCH: 3295dc61552STiezhu Yang if (isdw) { 3305dc61552STiezhu Yang emit_insn(ctx, amandd, src, t1, t3); 3315dc61552STiezhu Yang } else { 3325dc61552STiezhu Yang emit_insn(ctx, amandw, src, t1, t3); 3335dc61552STiezhu Yang emit_zext_32(ctx, src, true); 3345dc61552STiezhu Yang } 3355dc61552STiezhu Yang break; 3365dc61552STiezhu Yang case BPF_OR | BPF_FETCH: 3375dc61552STiezhu Yang if (isdw) { 3385dc61552STiezhu Yang emit_insn(ctx, amord, src, t1, t3); 3395dc61552STiezhu Yang } else { 3405dc61552STiezhu Yang emit_insn(ctx, amorw, src, t1, t3); 3415dc61552STiezhu Yang emit_zext_32(ctx, src, true); 3425dc61552STiezhu Yang } 3435dc61552STiezhu Yang break; 3445dc61552STiezhu Yang case BPF_XOR | BPF_FETCH: 3455dc61552STiezhu Yang if (isdw) { 3465dc61552STiezhu Yang emit_insn(ctx, amxord, src, t1, t3); 3475dc61552STiezhu Yang } else { 3485dc61552STiezhu Yang emit_insn(ctx, amxorw, src, t1, t3); 3495dc61552STiezhu Yang emit_zext_32(ctx, src, true); 3505dc61552STiezhu Yang } 3515dc61552STiezhu Yang break; 3525dc61552STiezhu Yang /* src = atomic_xchg(dst + off, src); */ 3535dc61552STiezhu Yang case BPF_XCHG: 3545dc61552STiezhu Yang if (isdw) { 3555dc61552STiezhu Yang emit_insn(ctx, amswapd, src, t1, t3); 3565dc61552STiezhu Yang } else { 3575dc61552STiezhu Yang emit_insn(ctx, amswapw, src, t1, t3); 3585dc61552STiezhu Yang emit_zext_32(ctx, src, true); 3595dc61552STiezhu Yang } 3605dc61552STiezhu Yang break; 3615dc61552STiezhu Yang /* r0 = atomic_cmpxchg(dst + off, r0, src); */ 3625dc61552STiezhu Yang case BPF_CMPXCHG: 3635dc61552STiezhu Yang move_reg(ctx, t2, r0); 3645dc61552STiezhu Yang if (isdw) { 3655dc61552STiezhu Yang emit_insn(ctx, lld, r0, t1, 0); 3665dc61552STiezhu Yang emit_insn(ctx, bne, t2, r0, 4); 3675dc61552STiezhu Yang move_reg(ctx, t3, src); 3685dc61552STiezhu Yang emit_insn(ctx, scd, t3, t1, 0); 3695dc61552STiezhu Yang emit_insn(ctx, beq, t3, LOONGARCH_GPR_ZERO, -4); 3705dc61552STiezhu Yang } else { 3715dc61552STiezhu Yang emit_insn(ctx, llw, r0, t1, 0); 3725dc61552STiezhu Yang emit_zext_32(ctx, t2, true); 3735dc61552STiezhu Yang emit_zext_32(ctx, r0, true); 3745dc61552STiezhu Yang emit_insn(ctx, bne, t2, r0, 4); 3755dc61552STiezhu Yang move_reg(ctx, t3, src); 3765dc61552STiezhu Yang emit_insn(ctx, scw, t3, t1, 0); 3775dc61552STiezhu Yang emit_insn(ctx, beq, t3, LOONGARCH_GPR_ZERO, -6); 3785dc61552STiezhu Yang emit_zext_32(ctx, r0, true); 3795dc61552STiezhu Yang } 3805dc61552STiezhu Yang break; 3815dc61552STiezhu Yang } 3825dc61552STiezhu Yang } 3835dc61552STiezhu Yang 3845dc61552STiezhu Yang static bool is_signed_bpf_cond(u8 cond) 3855dc61552STiezhu Yang { 3865dc61552STiezhu Yang return cond == BPF_JSGT || cond == BPF_JSLT || 3875dc61552STiezhu Yang cond == BPF_JSGE || cond == BPF_JSLE; 3885dc61552STiezhu Yang } 3895dc61552STiezhu Yang 3905dc61552STiezhu Yang static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool extra_pass) 3915dc61552STiezhu Yang { 392*bbfddb90SHuacai Chen u8 tm = -1; 393*bbfddb90SHuacai Chen u64 func_addr; 394*bbfddb90SHuacai Chen bool func_addr_fixed; 395*bbfddb90SHuacai Chen int i = insn - ctx->prog->insnsi; 396*bbfddb90SHuacai Chen int ret, jmp_offset; 3975dc61552STiezhu Yang const u8 code = insn->code; 3985dc61552STiezhu Yang const u8 cond = BPF_OP(code); 3995dc61552STiezhu Yang const u8 t1 = LOONGARCH_GPR_T1; 4005dc61552STiezhu Yang const u8 t2 = LOONGARCH_GPR_T2; 4015dc61552STiezhu Yang const u8 src = regmap[insn->src_reg]; 4025dc61552STiezhu Yang const u8 dst = regmap[insn->dst_reg]; 4035dc61552STiezhu Yang const s16 off = insn->off; 4045dc61552STiezhu Yang const s32 imm = insn->imm; 405*bbfddb90SHuacai Chen const u64 imm64 = (u64)(insn + 1)->imm << 32 | (u32)insn->imm; 406*bbfddb90SHuacai Chen const bool is32 = BPF_CLASS(insn->code) == BPF_ALU || BPF_CLASS(insn->code) == BPF_JMP32; 4075dc61552STiezhu Yang 4085dc61552STiezhu Yang switch (code) { 4095dc61552STiezhu Yang /* dst = src */ 4105dc61552STiezhu Yang case BPF_ALU | BPF_MOV | BPF_X: 4115dc61552STiezhu Yang case BPF_ALU64 | BPF_MOV | BPF_X: 4125dc61552STiezhu Yang move_reg(ctx, dst, src); 4135dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4145dc61552STiezhu Yang break; 4155dc61552STiezhu Yang 4165dc61552STiezhu Yang /* dst = imm */ 4175dc61552STiezhu Yang case BPF_ALU | BPF_MOV | BPF_K: 4185dc61552STiezhu Yang case BPF_ALU64 | BPF_MOV | BPF_K: 4195dc61552STiezhu Yang move_imm(ctx, dst, imm, is32); 4205dc61552STiezhu Yang break; 4215dc61552STiezhu Yang 4225dc61552STiezhu Yang /* dst = dst + src */ 4235dc61552STiezhu Yang case BPF_ALU | BPF_ADD | BPF_X: 4245dc61552STiezhu Yang case BPF_ALU64 | BPF_ADD | BPF_X: 4255dc61552STiezhu Yang emit_insn(ctx, addd, dst, dst, src); 4265dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4275dc61552STiezhu Yang break; 4285dc61552STiezhu Yang 4295dc61552STiezhu Yang /* dst = dst + imm */ 4305dc61552STiezhu Yang case BPF_ALU | BPF_ADD | BPF_K: 4315dc61552STiezhu Yang case BPF_ALU64 | BPF_ADD | BPF_K: 4325dc61552STiezhu Yang if (is_signed_imm12(imm)) { 4335dc61552STiezhu Yang emit_insn(ctx, addid, dst, dst, imm); 4345dc61552STiezhu Yang } else { 4355dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 4365dc61552STiezhu Yang emit_insn(ctx, addd, dst, dst, t1); 4375dc61552STiezhu Yang } 4385dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4395dc61552STiezhu Yang break; 4405dc61552STiezhu Yang 4415dc61552STiezhu Yang /* dst = dst - src */ 4425dc61552STiezhu Yang case BPF_ALU | BPF_SUB | BPF_X: 4435dc61552STiezhu Yang case BPF_ALU64 | BPF_SUB | BPF_X: 4445dc61552STiezhu Yang emit_insn(ctx, subd, dst, dst, src); 4455dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4465dc61552STiezhu Yang break; 4475dc61552STiezhu Yang 4485dc61552STiezhu Yang /* dst = dst - imm */ 4495dc61552STiezhu Yang case BPF_ALU | BPF_SUB | BPF_K: 4505dc61552STiezhu Yang case BPF_ALU64 | BPF_SUB | BPF_K: 4515dc61552STiezhu Yang if (is_signed_imm12(-imm)) { 4525dc61552STiezhu Yang emit_insn(ctx, addid, dst, dst, -imm); 4535dc61552STiezhu Yang } else { 4545dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 4555dc61552STiezhu Yang emit_insn(ctx, subd, dst, dst, t1); 4565dc61552STiezhu Yang } 4575dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4585dc61552STiezhu Yang break; 4595dc61552STiezhu Yang 4605dc61552STiezhu Yang /* dst = dst * src */ 4615dc61552STiezhu Yang case BPF_ALU | BPF_MUL | BPF_X: 4625dc61552STiezhu Yang case BPF_ALU64 | BPF_MUL | BPF_X: 4635dc61552STiezhu Yang emit_insn(ctx, muld, dst, dst, src); 4645dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4655dc61552STiezhu Yang break; 4665dc61552STiezhu Yang 4675dc61552STiezhu Yang /* dst = dst * imm */ 4685dc61552STiezhu Yang case BPF_ALU | BPF_MUL | BPF_K: 4695dc61552STiezhu Yang case BPF_ALU64 | BPF_MUL | BPF_K: 4705dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 4715dc61552STiezhu Yang emit_insn(ctx, muld, dst, dst, t1); 4725dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4735dc61552STiezhu Yang break; 4745dc61552STiezhu Yang 4755dc61552STiezhu Yang /* dst = dst / src */ 4765dc61552STiezhu Yang case BPF_ALU | BPF_DIV | BPF_X: 4775dc61552STiezhu Yang case BPF_ALU64 | BPF_DIV | BPF_X: 4785dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4795dc61552STiezhu Yang move_reg(ctx, t1, src); 4805dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 4815dc61552STiezhu Yang emit_insn(ctx, divdu, dst, dst, t1); 4825dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4835dc61552STiezhu Yang break; 4845dc61552STiezhu Yang 4855dc61552STiezhu Yang /* dst = dst / imm */ 4865dc61552STiezhu Yang case BPF_ALU | BPF_DIV | BPF_K: 4875dc61552STiezhu Yang case BPF_ALU64 | BPF_DIV | BPF_K: 4885dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 4895dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4905dc61552STiezhu Yang emit_insn(ctx, divdu, dst, dst, t1); 4915dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4925dc61552STiezhu Yang break; 4935dc61552STiezhu Yang 4945dc61552STiezhu Yang /* dst = dst % src */ 4955dc61552STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 4965dc61552STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 4975dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4985dc61552STiezhu Yang move_reg(ctx, t1, src); 4995dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 5005dc61552STiezhu Yang emit_insn(ctx, moddu, dst, dst, t1); 5015dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5025dc61552STiezhu Yang break; 5035dc61552STiezhu Yang 5045dc61552STiezhu Yang /* dst = dst % imm */ 5055dc61552STiezhu Yang case BPF_ALU | BPF_MOD | BPF_K: 5065dc61552STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_K: 5075dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5085dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5095dc61552STiezhu Yang emit_insn(ctx, moddu, dst, dst, t1); 5105dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5115dc61552STiezhu Yang break; 5125dc61552STiezhu Yang 5135dc61552STiezhu Yang /* dst = -dst */ 5145dc61552STiezhu Yang case BPF_ALU | BPF_NEG: 5155dc61552STiezhu Yang case BPF_ALU64 | BPF_NEG: 5165dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5175dc61552STiezhu Yang emit_insn(ctx, subd, dst, LOONGARCH_GPR_ZERO, dst); 5185dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5195dc61552STiezhu Yang break; 5205dc61552STiezhu Yang 5215dc61552STiezhu Yang /* dst = dst & src */ 5225dc61552STiezhu Yang case BPF_ALU | BPF_AND | BPF_X: 5235dc61552STiezhu Yang case BPF_ALU64 | BPF_AND | BPF_X: 5245dc61552STiezhu Yang emit_insn(ctx, and, dst, dst, src); 5255dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5265dc61552STiezhu Yang break; 5275dc61552STiezhu Yang 5285dc61552STiezhu Yang /* dst = dst & imm */ 5295dc61552STiezhu Yang case BPF_ALU | BPF_AND | BPF_K: 5305dc61552STiezhu Yang case BPF_ALU64 | BPF_AND | BPF_K: 5315dc61552STiezhu Yang if (is_unsigned_imm12(imm)) { 5325dc61552STiezhu Yang emit_insn(ctx, andi, dst, dst, imm); 5335dc61552STiezhu Yang } else { 5345dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5355dc61552STiezhu Yang emit_insn(ctx, and, dst, dst, t1); 5365dc61552STiezhu Yang } 5375dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5385dc61552STiezhu Yang break; 5395dc61552STiezhu Yang 5405dc61552STiezhu Yang /* dst = dst | src */ 5415dc61552STiezhu Yang case BPF_ALU | BPF_OR | BPF_X: 5425dc61552STiezhu Yang case BPF_ALU64 | BPF_OR | BPF_X: 5435dc61552STiezhu Yang emit_insn(ctx, or, dst, dst, src); 5445dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5455dc61552STiezhu Yang break; 5465dc61552STiezhu Yang 5475dc61552STiezhu Yang /* dst = dst | imm */ 5485dc61552STiezhu Yang case BPF_ALU | BPF_OR | BPF_K: 5495dc61552STiezhu Yang case BPF_ALU64 | BPF_OR | BPF_K: 5505dc61552STiezhu Yang if (is_unsigned_imm12(imm)) { 5515dc61552STiezhu Yang emit_insn(ctx, ori, dst, dst, imm); 5525dc61552STiezhu Yang } else { 5535dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5545dc61552STiezhu Yang emit_insn(ctx, or, dst, dst, t1); 5555dc61552STiezhu Yang } 5565dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5575dc61552STiezhu Yang break; 5585dc61552STiezhu Yang 5595dc61552STiezhu Yang /* dst = dst ^ src */ 5605dc61552STiezhu Yang case BPF_ALU | BPF_XOR | BPF_X: 5615dc61552STiezhu Yang case BPF_ALU64 | BPF_XOR | BPF_X: 5625dc61552STiezhu Yang emit_insn(ctx, xor, dst, dst, src); 5635dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5645dc61552STiezhu Yang break; 5655dc61552STiezhu Yang 5665dc61552STiezhu Yang /* dst = dst ^ imm */ 5675dc61552STiezhu Yang case BPF_ALU | BPF_XOR | BPF_K: 5685dc61552STiezhu Yang case BPF_ALU64 | BPF_XOR | BPF_K: 5695dc61552STiezhu Yang if (is_unsigned_imm12(imm)) { 5705dc61552STiezhu Yang emit_insn(ctx, xori, dst, dst, imm); 5715dc61552STiezhu Yang } else { 5725dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5735dc61552STiezhu Yang emit_insn(ctx, xor, dst, dst, t1); 5745dc61552STiezhu Yang } 5755dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5765dc61552STiezhu Yang break; 5775dc61552STiezhu Yang 5785dc61552STiezhu Yang /* dst = dst << src (logical) */ 5795dc61552STiezhu Yang case BPF_ALU | BPF_LSH | BPF_X: 5805dc61552STiezhu Yang emit_insn(ctx, sllw, dst, dst, src); 5815dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5825dc61552STiezhu Yang break; 5835dc61552STiezhu Yang 5845dc61552STiezhu Yang case BPF_ALU64 | BPF_LSH | BPF_X: 5855dc61552STiezhu Yang emit_insn(ctx, slld, dst, dst, src); 5865dc61552STiezhu Yang break; 5875dc61552STiezhu Yang 5885dc61552STiezhu Yang /* dst = dst << imm (logical) */ 5895dc61552STiezhu Yang case BPF_ALU | BPF_LSH | BPF_K: 5905dc61552STiezhu Yang emit_insn(ctx, slliw, dst, dst, imm); 5915dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5925dc61552STiezhu Yang break; 5935dc61552STiezhu Yang 5945dc61552STiezhu Yang case BPF_ALU64 | BPF_LSH | BPF_K: 5955dc61552STiezhu Yang emit_insn(ctx, sllid, dst, dst, imm); 5965dc61552STiezhu Yang break; 5975dc61552STiezhu Yang 5985dc61552STiezhu Yang /* dst = dst >> src (logical) */ 5995dc61552STiezhu Yang case BPF_ALU | BPF_RSH | BPF_X: 6005dc61552STiezhu Yang emit_insn(ctx, srlw, dst, dst, src); 6015dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6025dc61552STiezhu Yang break; 6035dc61552STiezhu Yang 6045dc61552STiezhu Yang case BPF_ALU64 | BPF_RSH | BPF_X: 6055dc61552STiezhu Yang emit_insn(ctx, srld, dst, dst, src); 6065dc61552STiezhu Yang break; 6075dc61552STiezhu Yang 6085dc61552STiezhu Yang /* dst = dst >> imm (logical) */ 6095dc61552STiezhu Yang case BPF_ALU | BPF_RSH | BPF_K: 6105dc61552STiezhu Yang emit_insn(ctx, srliw, dst, dst, imm); 6115dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6125dc61552STiezhu Yang break; 6135dc61552STiezhu Yang 6145dc61552STiezhu Yang case BPF_ALU64 | BPF_RSH | BPF_K: 6155dc61552STiezhu Yang emit_insn(ctx, srlid, dst, dst, imm); 6165dc61552STiezhu Yang break; 6175dc61552STiezhu Yang 6185dc61552STiezhu Yang /* dst = dst >> src (arithmetic) */ 6195dc61552STiezhu Yang case BPF_ALU | BPF_ARSH | BPF_X: 6205dc61552STiezhu Yang emit_insn(ctx, sraw, dst, dst, src); 6215dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6225dc61552STiezhu Yang break; 6235dc61552STiezhu Yang 6245dc61552STiezhu Yang case BPF_ALU64 | BPF_ARSH | BPF_X: 6255dc61552STiezhu Yang emit_insn(ctx, srad, dst, dst, src); 6265dc61552STiezhu Yang break; 6275dc61552STiezhu Yang 6285dc61552STiezhu Yang /* dst = dst >> imm (arithmetic) */ 6295dc61552STiezhu Yang case BPF_ALU | BPF_ARSH | BPF_K: 6305dc61552STiezhu Yang emit_insn(ctx, sraiw, dst, dst, imm); 6315dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6325dc61552STiezhu Yang break; 6335dc61552STiezhu Yang 6345dc61552STiezhu Yang case BPF_ALU64 | BPF_ARSH | BPF_K: 6355dc61552STiezhu Yang emit_insn(ctx, sraid, dst, dst, imm); 6365dc61552STiezhu Yang break; 6375dc61552STiezhu Yang 6385dc61552STiezhu Yang /* dst = BSWAP##imm(dst) */ 6395dc61552STiezhu Yang case BPF_ALU | BPF_END | BPF_FROM_LE: 6405dc61552STiezhu Yang switch (imm) { 6415dc61552STiezhu Yang case 16: 6425dc61552STiezhu Yang /* zero-extend 16 bits into 64 bits */ 6435dc61552STiezhu Yang emit_insn(ctx, bstrpickd, dst, dst, 15, 0); 6445dc61552STiezhu Yang break; 6455dc61552STiezhu Yang case 32: 6465dc61552STiezhu Yang /* zero-extend 32 bits into 64 bits */ 6475dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6485dc61552STiezhu Yang break; 6495dc61552STiezhu Yang case 64: 6505dc61552STiezhu Yang /* do nothing */ 6515dc61552STiezhu Yang break; 6525dc61552STiezhu Yang } 6535dc61552STiezhu Yang break; 6545dc61552STiezhu Yang 6555dc61552STiezhu Yang case BPF_ALU | BPF_END | BPF_FROM_BE: 6565dc61552STiezhu Yang switch (imm) { 6575dc61552STiezhu Yang case 16: 6585dc61552STiezhu Yang emit_insn(ctx, revb2h, dst, dst); 6595dc61552STiezhu Yang /* zero-extend 16 bits into 64 bits */ 6605dc61552STiezhu Yang emit_insn(ctx, bstrpickd, dst, dst, 15, 0); 6615dc61552STiezhu Yang break; 6625dc61552STiezhu Yang case 32: 6635dc61552STiezhu Yang emit_insn(ctx, revb2w, dst, dst); 6645dc61552STiezhu Yang /* zero-extend 32 bits into 64 bits */ 6655dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6665dc61552STiezhu Yang break; 6675dc61552STiezhu Yang case 64: 6685dc61552STiezhu Yang emit_insn(ctx, revbd, dst, dst); 6695dc61552STiezhu Yang break; 6705dc61552STiezhu Yang } 6715dc61552STiezhu Yang break; 6725dc61552STiezhu Yang 6735dc61552STiezhu Yang /* PC += off if dst cond src */ 6745dc61552STiezhu Yang case BPF_JMP | BPF_JEQ | BPF_X: 6755dc61552STiezhu Yang case BPF_JMP | BPF_JNE | BPF_X: 6765dc61552STiezhu Yang case BPF_JMP | BPF_JGT | BPF_X: 6775dc61552STiezhu Yang case BPF_JMP | BPF_JGE | BPF_X: 6785dc61552STiezhu Yang case BPF_JMP | BPF_JLT | BPF_X: 6795dc61552STiezhu Yang case BPF_JMP | BPF_JLE | BPF_X: 6805dc61552STiezhu Yang case BPF_JMP | BPF_JSGT | BPF_X: 6815dc61552STiezhu Yang case BPF_JMP | BPF_JSGE | BPF_X: 6825dc61552STiezhu Yang case BPF_JMP | BPF_JSLT | BPF_X: 6835dc61552STiezhu Yang case BPF_JMP | BPF_JSLE | BPF_X: 6845dc61552STiezhu Yang case BPF_JMP32 | BPF_JEQ | BPF_X: 6855dc61552STiezhu Yang case BPF_JMP32 | BPF_JNE | BPF_X: 6865dc61552STiezhu Yang case BPF_JMP32 | BPF_JGT | BPF_X: 6875dc61552STiezhu Yang case BPF_JMP32 | BPF_JGE | BPF_X: 6885dc61552STiezhu Yang case BPF_JMP32 | BPF_JLT | BPF_X: 6895dc61552STiezhu Yang case BPF_JMP32 | BPF_JLE | BPF_X: 6905dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGT | BPF_X: 6915dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGE | BPF_X: 6925dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLT | BPF_X: 6935dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLE | BPF_X: 6945dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 6955dc61552STiezhu Yang move_reg(ctx, t1, dst); 6965dc61552STiezhu Yang move_reg(ctx, t2, src); 6975dc61552STiezhu Yang if (is_signed_bpf_cond(BPF_OP(code))) { 6985dc61552STiezhu Yang emit_sext_32(ctx, t1, is32); 6995dc61552STiezhu Yang emit_sext_32(ctx, t2, is32); 7005dc61552STiezhu Yang } else { 7015dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 7025dc61552STiezhu Yang emit_zext_32(ctx, t2, is32); 7035dc61552STiezhu Yang } 7045dc61552STiezhu Yang if (emit_cond_jmp(ctx, cond, t1, t2, jmp_offset) < 0) 7055dc61552STiezhu Yang goto toofar; 7065dc61552STiezhu Yang break; 7075dc61552STiezhu Yang 7085dc61552STiezhu Yang /* PC += off if dst cond imm */ 7095dc61552STiezhu Yang case BPF_JMP | BPF_JEQ | BPF_K: 7105dc61552STiezhu Yang case BPF_JMP | BPF_JNE | BPF_K: 7115dc61552STiezhu Yang case BPF_JMP | BPF_JGT | BPF_K: 7125dc61552STiezhu Yang case BPF_JMP | BPF_JGE | BPF_K: 7135dc61552STiezhu Yang case BPF_JMP | BPF_JLT | BPF_K: 7145dc61552STiezhu Yang case BPF_JMP | BPF_JLE | BPF_K: 7155dc61552STiezhu Yang case BPF_JMP | BPF_JSGT | BPF_K: 7165dc61552STiezhu Yang case BPF_JMP | BPF_JSGE | BPF_K: 7175dc61552STiezhu Yang case BPF_JMP | BPF_JSLT | BPF_K: 7185dc61552STiezhu Yang case BPF_JMP | BPF_JSLE | BPF_K: 7195dc61552STiezhu Yang case BPF_JMP32 | BPF_JEQ | BPF_K: 7205dc61552STiezhu Yang case BPF_JMP32 | BPF_JNE | BPF_K: 7215dc61552STiezhu Yang case BPF_JMP32 | BPF_JGT | BPF_K: 7225dc61552STiezhu Yang case BPF_JMP32 | BPF_JGE | BPF_K: 7235dc61552STiezhu Yang case BPF_JMP32 | BPF_JLT | BPF_K: 7245dc61552STiezhu Yang case BPF_JMP32 | BPF_JLE | BPF_K: 7255dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGT | BPF_K: 7265dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGE | BPF_K: 7275dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLT | BPF_K: 7285dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLE | BPF_K: 7295dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 7305dc61552STiezhu Yang if (imm) { 7315dc61552STiezhu Yang move_imm(ctx, t1, imm, false); 732*bbfddb90SHuacai Chen tm = t1; 7335dc61552STiezhu Yang } else { 7345dc61552STiezhu Yang /* If imm is 0, simply use zero register. */ 735*bbfddb90SHuacai Chen tm = LOONGARCH_GPR_ZERO; 7365dc61552STiezhu Yang } 7375dc61552STiezhu Yang move_reg(ctx, t2, dst); 7385dc61552STiezhu Yang if (is_signed_bpf_cond(BPF_OP(code))) { 739*bbfddb90SHuacai Chen emit_sext_32(ctx, tm, is32); 7405dc61552STiezhu Yang emit_sext_32(ctx, t2, is32); 7415dc61552STiezhu Yang } else { 742*bbfddb90SHuacai Chen emit_zext_32(ctx, tm, is32); 7435dc61552STiezhu Yang emit_zext_32(ctx, t2, is32); 7445dc61552STiezhu Yang } 745*bbfddb90SHuacai Chen if (emit_cond_jmp(ctx, cond, t2, tm, jmp_offset) < 0) 7465dc61552STiezhu Yang goto toofar; 7475dc61552STiezhu Yang break; 7485dc61552STiezhu Yang 7495dc61552STiezhu Yang /* PC += off if dst & src */ 7505dc61552STiezhu Yang case BPF_JMP | BPF_JSET | BPF_X: 7515dc61552STiezhu Yang case BPF_JMP32 | BPF_JSET | BPF_X: 7525dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 7535dc61552STiezhu Yang emit_insn(ctx, and, t1, dst, src); 7545dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 7555dc61552STiezhu Yang if (emit_cond_jmp(ctx, cond, t1, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 7565dc61552STiezhu Yang goto toofar; 7575dc61552STiezhu Yang break; 7585dc61552STiezhu Yang 7595dc61552STiezhu Yang /* PC += off if dst & imm */ 7605dc61552STiezhu Yang case BPF_JMP | BPF_JSET | BPF_K: 7615dc61552STiezhu Yang case BPF_JMP32 | BPF_JSET | BPF_K: 7625dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 7635dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 7645dc61552STiezhu Yang emit_insn(ctx, and, t1, dst, t1); 7655dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 7665dc61552STiezhu Yang if (emit_cond_jmp(ctx, cond, t1, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 7675dc61552STiezhu Yang goto toofar; 7685dc61552STiezhu Yang break; 7695dc61552STiezhu Yang 7705dc61552STiezhu Yang /* PC += off */ 7715dc61552STiezhu Yang case BPF_JMP | BPF_JA: 7725dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 7735dc61552STiezhu Yang if (emit_uncond_jmp(ctx, jmp_offset) < 0) 7745dc61552STiezhu Yang goto toofar; 7755dc61552STiezhu Yang break; 7765dc61552STiezhu Yang 7775dc61552STiezhu Yang /* function call */ 7785dc61552STiezhu Yang case BPF_JMP | BPF_CALL: 7795dc61552STiezhu Yang mark_call(ctx); 7805dc61552STiezhu Yang ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 7815dc61552STiezhu Yang &func_addr, &func_addr_fixed); 7825dc61552STiezhu Yang if (ret < 0) 7835dc61552STiezhu Yang return ret; 7845dc61552STiezhu Yang 7855dc61552STiezhu Yang move_imm(ctx, t1, func_addr, is32); 7865dc61552STiezhu Yang emit_insn(ctx, jirl, t1, LOONGARCH_GPR_RA, 0); 7875dc61552STiezhu Yang move_reg(ctx, regmap[BPF_REG_0], LOONGARCH_GPR_A0); 7885dc61552STiezhu Yang break; 7895dc61552STiezhu Yang 7905dc61552STiezhu Yang /* tail call */ 7915dc61552STiezhu Yang case BPF_JMP | BPF_TAIL_CALL: 7925dc61552STiezhu Yang mark_tail_call(ctx); 7935dc61552STiezhu Yang if (emit_bpf_tail_call(ctx) < 0) 7945dc61552STiezhu Yang return -EINVAL; 7955dc61552STiezhu Yang break; 7965dc61552STiezhu Yang 7975dc61552STiezhu Yang /* function return */ 7985dc61552STiezhu Yang case BPF_JMP | BPF_EXIT: 7995dc61552STiezhu Yang emit_sext_32(ctx, regmap[BPF_REG_0], true); 8005dc61552STiezhu Yang 8015dc61552STiezhu Yang if (i == ctx->prog->len - 1) 8025dc61552STiezhu Yang break; 8035dc61552STiezhu Yang 8045dc61552STiezhu Yang jmp_offset = epilogue_offset(ctx); 8055dc61552STiezhu Yang if (emit_uncond_jmp(ctx, jmp_offset) < 0) 8065dc61552STiezhu Yang goto toofar; 8075dc61552STiezhu Yang break; 8085dc61552STiezhu Yang 8095dc61552STiezhu Yang /* dst = imm64 */ 8105dc61552STiezhu Yang case BPF_LD | BPF_IMM | BPF_DW: 8115dc61552STiezhu Yang move_imm(ctx, dst, imm64, is32); 8125dc61552STiezhu Yang return 1; 8135dc61552STiezhu Yang 8145dc61552STiezhu Yang /* dst = *(size *)(src + off) */ 8155dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_B: 8165dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_H: 8175dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_W: 8185dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_DW: 8195dc61552STiezhu Yang switch (BPF_SIZE(code)) { 8205dc61552STiezhu Yang case BPF_B: 8215dc61552STiezhu Yang if (is_signed_imm12(off)) { 8225dc61552STiezhu Yang emit_insn(ctx, ldbu, dst, src, off); 8235dc61552STiezhu Yang } else { 8245dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 8255dc61552STiezhu Yang emit_insn(ctx, ldxbu, dst, src, t1); 8265dc61552STiezhu Yang } 8275dc61552STiezhu Yang break; 8285dc61552STiezhu Yang case BPF_H: 8295dc61552STiezhu Yang if (is_signed_imm12(off)) { 8305dc61552STiezhu Yang emit_insn(ctx, ldhu, dst, src, off); 8315dc61552STiezhu Yang } else { 8325dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 8335dc61552STiezhu Yang emit_insn(ctx, ldxhu, dst, src, t1); 8345dc61552STiezhu Yang } 8355dc61552STiezhu Yang break; 8365dc61552STiezhu Yang case BPF_W: 8375dc61552STiezhu Yang if (is_signed_imm12(off)) { 8385dc61552STiezhu Yang emit_insn(ctx, ldwu, dst, src, off); 8395dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 8405dc61552STiezhu Yang emit_insn(ctx, ldptrw, dst, src, off); 8415dc61552STiezhu Yang } else { 8425dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 8435dc61552STiezhu Yang emit_insn(ctx, ldxwu, dst, src, t1); 8445dc61552STiezhu Yang } 8455dc61552STiezhu Yang break; 8465dc61552STiezhu Yang case BPF_DW: 8475dc61552STiezhu Yang if (is_signed_imm12(off)) { 8485dc61552STiezhu Yang emit_insn(ctx, ldd, dst, src, off); 8495dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 8505dc61552STiezhu Yang emit_insn(ctx, ldptrd, dst, src, off); 8515dc61552STiezhu Yang } else { 8525dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 8535dc61552STiezhu Yang emit_insn(ctx, ldxd, dst, src, t1); 8545dc61552STiezhu Yang } 8555dc61552STiezhu Yang break; 8565dc61552STiezhu Yang } 8575dc61552STiezhu Yang break; 8585dc61552STiezhu Yang 8595dc61552STiezhu Yang /* *(size *)(dst + off) = imm */ 8605dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_B: 8615dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_H: 8625dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_W: 8635dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_DW: 8645dc61552STiezhu Yang switch (BPF_SIZE(code)) { 8655dc61552STiezhu Yang case BPF_B: 8665dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 8675dc61552STiezhu Yang if (is_signed_imm12(off)) { 8685dc61552STiezhu Yang emit_insn(ctx, stb, t1, dst, off); 8695dc61552STiezhu Yang } else { 8705dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 8715dc61552STiezhu Yang emit_insn(ctx, stxb, t1, dst, t2); 8725dc61552STiezhu Yang } 8735dc61552STiezhu Yang break; 8745dc61552STiezhu Yang case BPF_H: 8755dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 8765dc61552STiezhu Yang if (is_signed_imm12(off)) { 8775dc61552STiezhu Yang emit_insn(ctx, sth, t1, dst, off); 8785dc61552STiezhu Yang } else { 8795dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 8805dc61552STiezhu Yang emit_insn(ctx, stxh, t1, dst, t2); 8815dc61552STiezhu Yang } 8825dc61552STiezhu Yang break; 8835dc61552STiezhu Yang case BPF_W: 8845dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 8855dc61552STiezhu Yang if (is_signed_imm12(off)) { 8865dc61552STiezhu Yang emit_insn(ctx, stw, t1, dst, off); 8875dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 8885dc61552STiezhu Yang emit_insn(ctx, stptrw, t1, dst, off); 8895dc61552STiezhu Yang } else { 8905dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 8915dc61552STiezhu Yang emit_insn(ctx, stxw, t1, dst, t2); 8925dc61552STiezhu Yang } 8935dc61552STiezhu Yang break; 8945dc61552STiezhu Yang case BPF_DW: 8955dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 8965dc61552STiezhu Yang if (is_signed_imm12(off)) { 8975dc61552STiezhu Yang emit_insn(ctx, std, t1, dst, off); 8985dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 8995dc61552STiezhu Yang emit_insn(ctx, stptrd, t1, dst, off); 9005dc61552STiezhu Yang } else { 9015dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 9025dc61552STiezhu Yang emit_insn(ctx, stxd, t1, dst, t2); 9035dc61552STiezhu Yang } 9045dc61552STiezhu Yang break; 9055dc61552STiezhu Yang } 9065dc61552STiezhu Yang break; 9075dc61552STiezhu Yang 9085dc61552STiezhu Yang /* *(size *)(dst + off) = src */ 9095dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_B: 9105dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_H: 9115dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_W: 9125dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_DW: 9135dc61552STiezhu Yang switch (BPF_SIZE(code)) { 9145dc61552STiezhu Yang case BPF_B: 9155dc61552STiezhu Yang if (is_signed_imm12(off)) { 9165dc61552STiezhu Yang emit_insn(ctx, stb, src, dst, off); 9175dc61552STiezhu Yang } else { 9185dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9195dc61552STiezhu Yang emit_insn(ctx, stxb, src, dst, t1); 9205dc61552STiezhu Yang } 9215dc61552STiezhu Yang break; 9225dc61552STiezhu Yang case BPF_H: 9235dc61552STiezhu Yang if (is_signed_imm12(off)) { 9245dc61552STiezhu Yang emit_insn(ctx, sth, src, dst, off); 9255dc61552STiezhu Yang } else { 9265dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9275dc61552STiezhu Yang emit_insn(ctx, stxh, src, dst, t1); 9285dc61552STiezhu Yang } 9295dc61552STiezhu Yang break; 9305dc61552STiezhu Yang case BPF_W: 9315dc61552STiezhu Yang if (is_signed_imm12(off)) { 9325dc61552STiezhu Yang emit_insn(ctx, stw, src, dst, off); 9335dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9345dc61552STiezhu Yang emit_insn(ctx, stptrw, src, dst, off); 9355dc61552STiezhu Yang } else { 9365dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9375dc61552STiezhu Yang emit_insn(ctx, stxw, src, dst, t1); 9385dc61552STiezhu Yang } 9395dc61552STiezhu Yang break; 9405dc61552STiezhu Yang case BPF_DW: 9415dc61552STiezhu Yang if (is_signed_imm12(off)) { 9425dc61552STiezhu Yang emit_insn(ctx, std, src, dst, off); 9435dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9445dc61552STiezhu Yang emit_insn(ctx, stptrd, src, dst, off); 9455dc61552STiezhu Yang } else { 9465dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9475dc61552STiezhu Yang emit_insn(ctx, stxd, src, dst, t1); 9485dc61552STiezhu Yang } 9495dc61552STiezhu Yang break; 9505dc61552STiezhu Yang } 9515dc61552STiezhu Yang break; 9525dc61552STiezhu Yang 9535dc61552STiezhu Yang case BPF_STX | BPF_ATOMIC | BPF_W: 9545dc61552STiezhu Yang case BPF_STX | BPF_ATOMIC | BPF_DW: 9555dc61552STiezhu Yang emit_atomic(insn, ctx); 9565dc61552STiezhu Yang break; 9575dc61552STiezhu Yang 9585dc61552STiezhu Yang default: 9595dc61552STiezhu Yang pr_err("bpf_jit: unknown opcode %02x\n", code); 9605dc61552STiezhu Yang return -EINVAL; 9615dc61552STiezhu Yang } 9625dc61552STiezhu Yang 9635dc61552STiezhu Yang return 0; 9645dc61552STiezhu Yang 9655dc61552STiezhu Yang toofar: 9665dc61552STiezhu Yang pr_info_once("bpf_jit: opcode %02x, jump too far\n", code); 9675dc61552STiezhu Yang return -E2BIG; 9685dc61552STiezhu Yang } 9695dc61552STiezhu Yang 9705dc61552STiezhu Yang static int build_body(struct jit_ctx *ctx, bool extra_pass) 9715dc61552STiezhu Yang { 9725dc61552STiezhu Yang int i; 9735dc61552STiezhu Yang const struct bpf_prog *prog = ctx->prog; 9745dc61552STiezhu Yang 9755dc61552STiezhu Yang for (i = 0; i < prog->len; i++) { 9765dc61552STiezhu Yang const struct bpf_insn *insn = &prog->insnsi[i]; 9775dc61552STiezhu Yang int ret; 9785dc61552STiezhu Yang 9795dc61552STiezhu Yang if (ctx->image == NULL) 9805dc61552STiezhu Yang ctx->offset[i] = ctx->idx; 9815dc61552STiezhu Yang 9825dc61552STiezhu Yang ret = build_insn(insn, ctx, extra_pass); 9835dc61552STiezhu Yang if (ret > 0) { 9845dc61552STiezhu Yang i++; 9855dc61552STiezhu Yang if (ctx->image == NULL) 9865dc61552STiezhu Yang ctx->offset[i] = ctx->idx; 9875dc61552STiezhu Yang continue; 9885dc61552STiezhu Yang } 9895dc61552STiezhu Yang if (ret) 9905dc61552STiezhu Yang return ret; 9915dc61552STiezhu Yang } 9925dc61552STiezhu Yang 9935dc61552STiezhu Yang if (ctx->image == NULL) 9945dc61552STiezhu Yang ctx->offset[i] = ctx->idx; 9955dc61552STiezhu Yang 9965dc61552STiezhu Yang return 0; 9975dc61552STiezhu Yang } 9985dc61552STiezhu Yang 9995dc61552STiezhu Yang /* Fill space with break instructions */ 10005dc61552STiezhu Yang static void jit_fill_hole(void *area, unsigned int size) 10015dc61552STiezhu Yang { 10025dc61552STiezhu Yang u32 *ptr; 10035dc61552STiezhu Yang 10045dc61552STiezhu Yang /* We are guaranteed to have aligned memory */ 10055dc61552STiezhu Yang for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 10065dc61552STiezhu Yang *ptr++ = INSN_BREAK; 10075dc61552STiezhu Yang } 10085dc61552STiezhu Yang 10095dc61552STiezhu Yang static int validate_code(struct jit_ctx *ctx) 10105dc61552STiezhu Yang { 10115dc61552STiezhu Yang int i; 10125dc61552STiezhu Yang union loongarch_instruction insn; 10135dc61552STiezhu Yang 10145dc61552STiezhu Yang for (i = 0; i < ctx->idx; i++) { 10155dc61552STiezhu Yang insn = ctx->image[i]; 10165dc61552STiezhu Yang /* Check INSN_BREAK */ 10175dc61552STiezhu Yang if (insn.word == INSN_BREAK) 10185dc61552STiezhu Yang return -1; 10195dc61552STiezhu Yang } 10205dc61552STiezhu Yang 10215dc61552STiezhu Yang return 0; 10225dc61552STiezhu Yang } 10235dc61552STiezhu Yang 10245dc61552STiezhu Yang struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 10255dc61552STiezhu Yang { 10265dc61552STiezhu Yang bool tmp_blinded = false, extra_pass = false; 10275dc61552STiezhu Yang u8 *image_ptr; 10285dc61552STiezhu Yang int image_size; 10295dc61552STiezhu Yang struct jit_ctx ctx; 10305dc61552STiezhu Yang struct jit_data *jit_data; 10315dc61552STiezhu Yang struct bpf_binary_header *header; 10325dc61552STiezhu Yang struct bpf_prog *tmp, *orig_prog = prog; 10335dc61552STiezhu Yang 10345dc61552STiezhu Yang /* 10355dc61552STiezhu Yang * If BPF JIT was not enabled then we must fall back to 10365dc61552STiezhu Yang * the interpreter. 10375dc61552STiezhu Yang */ 10385dc61552STiezhu Yang if (!prog->jit_requested) 10395dc61552STiezhu Yang return orig_prog; 10405dc61552STiezhu Yang 10415dc61552STiezhu Yang tmp = bpf_jit_blind_constants(prog); 10425dc61552STiezhu Yang /* 10435dc61552STiezhu Yang * If blinding was requested and we failed during blinding, 10445dc61552STiezhu Yang * we must fall back to the interpreter. Otherwise, we save 10455dc61552STiezhu Yang * the new JITed code. 10465dc61552STiezhu Yang */ 10475dc61552STiezhu Yang if (IS_ERR(tmp)) 10485dc61552STiezhu Yang return orig_prog; 10495dc61552STiezhu Yang 10505dc61552STiezhu Yang if (tmp != prog) { 10515dc61552STiezhu Yang tmp_blinded = true; 10525dc61552STiezhu Yang prog = tmp; 10535dc61552STiezhu Yang } 10545dc61552STiezhu Yang 10555dc61552STiezhu Yang jit_data = prog->aux->jit_data; 10565dc61552STiezhu Yang if (!jit_data) { 10575dc61552STiezhu Yang jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 10585dc61552STiezhu Yang if (!jit_data) { 10595dc61552STiezhu Yang prog = orig_prog; 10605dc61552STiezhu Yang goto out; 10615dc61552STiezhu Yang } 10625dc61552STiezhu Yang prog->aux->jit_data = jit_data; 10635dc61552STiezhu Yang } 10645dc61552STiezhu Yang if (jit_data->ctx.offset) { 10655dc61552STiezhu Yang ctx = jit_data->ctx; 10665dc61552STiezhu Yang image_ptr = jit_data->image; 10675dc61552STiezhu Yang header = jit_data->header; 10685dc61552STiezhu Yang extra_pass = true; 10695dc61552STiezhu Yang image_size = sizeof(u32) * ctx.idx; 10705dc61552STiezhu Yang goto skip_init_ctx; 10715dc61552STiezhu Yang } 10725dc61552STiezhu Yang 10735dc61552STiezhu Yang memset(&ctx, 0, sizeof(ctx)); 10745dc61552STiezhu Yang ctx.prog = prog; 10755dc61552STiezhu Yang 10765dc61552STiezhu Yang ctx.offset = kvcalloc(prog->len + 1, sizeof(u32), GFP_KERNEL); 10775dc61552STiezhu Yang if (ctx.offset == NULL) { 10785dc61552STiezhu Yang prog = orig_prog; 10795dc61552STiezhu Yang goto out_offset; 10805dc61552STiezhu Yang } 10815dc61552STiezhu Yang 10825dc61552STiezhu Yang /* 1. Initial fake pass to compute ctx->idx and set ctx->flags */ 10835dc61552STiezhu Yang build_prologue(&ctx); 10845dc61552STiezhu Yang if (build_body(&ctx, extra_pass)) { 10855dc61552STiezhu Yang prog = orig_prog; 10865dc61552STiezhu Yang goto out_offset; 10875dc61552STiezhu Yang } 10885dc61552STiezhu Yang ctx.epilogue_offset = ctx.idx; 10895dc61552STiezhu Yang build_epilogue(&ctx); 10905dc61552STiezhu Yang 10915dc61552STiezhu Yang /* Now we know the actual image size. 10925dc61552STiezhu Yang * As each LoongArch instruction is of length 32bit, 10935dc61552STiezhu Yang * we are translating number of JITed intructions into 10945dc61552STiezhu Yang * the size required to store these JITed code. 10955dc61552STiezhu Yang */ 10965dc61552STiezhu Yang image_size = sizeof(u32) * ctx.idx; 10975dc61552STiezhu Yang /* Now we know the size of the structure to make */ 10985dc61552STiezhu Yang header = bpf_jit_binary_alloc(image_size, &image_ptr, 10995dc61552STiezhu Yang sizeof(u32), jit_fill_hole); 11005dc61552STiezhu Yang if (header == NULL) { 11015dc61552STiezhu Yang prog = orig_prog; 11025dc61552STiezhu Yang goto out_offset; 11035dc61552STiezhu Yang } 11045dc61552STiezhu Yang 11055dc61552STiezhu Yang /* 2. Now, the actual pass to generate final JIT code */ 11065dc61552STiezhu Yang ctx.image = (union loongarch_instruction *)image_ptr; 11075dc61552STiezhu Yang 11085dc61552STiezhu Yang skip_init_ctx: 11095dc61552STiezhu Yang ctx.idx = 0; 11105dc61552STiezhu Yang 11115dc61552STiezhu Yang build_prologue(&ctx); 11125dc61552STiezhu Yang if (build_body(&ctx, extra_pass)) { 11135dc61552STiezhu Yang bpf_jit_binary_free(header); 11145dc61552STiezhu Yang prog = orig_prog; 11155dc61552STiezhu Yang goto out_offset; 11165dc61552STiezhu Yang } 11175dc61552STiezhu Yang build_epilogue(&ctx); 11185dc61552STiezhu Yang 11195dc61552STiezhu Yang /* 3. Extra pass to validate JITed code */ 11205dc61552STiezhu Yang if (validate_code(&ctx)) { 11215dc61552STiezhu Yang bpf_jit_binary_free(header); 11225dc61552STiezhu Yang prog = orig_prog; 11235dc61552STiezhu Yang goto out_offset; 11245dc61552STiezhu Yang } 11255dc61552STiezhu Yang 11265dc61552STiezhu Yang /* And we're done */ 11275dc61552STiezhu Yang if (bpf_jit_enable > 1) 11285dc61552STiezhu Yang bpf_jit_dump(prog->len, image_size, 2, ctx.image); 11295dc61552STiezhu Yang 11305dc61552STiezhu Yang /* Update the icache */ 11315dc61552STiezhu Yang flush_icache_range((unsigned long)header, (unsigned long)(ctx.image + ctx.idx)); 11325dc61552STiezhu Yang 11335dc61552STiezhu Yang if (!prog->is_func || extra_pass) { 11345dc61552STiezhu Yang if (extra_pass && ctx.idx != jit_data->ctx.idx) { 11355dc61552STiezhu Yang pr_err_once("multi-func JIT bug %d != %d\n", 11365dc61552STiezhu Yang ctx.idx, jit_data->ctx.idx); 11375dc61552STiezhu Yang bpf_jit_binary_free(header); 11385dc61552STiezhu Yang prog->bpf_func = NULL; 11395dc61552STiezhu Yang prog->jited = 0; 11405dc61552STiezhu Yang prog->jited_len = 0; 11415dc61552STiezhu Yang goto out_offset; 11425dc61552STiezhu Yang } 11435dc61552STiezhu Yang bpf_jit_binary_lock_ro(header); 11445dc61552STiezhu Yang } else { 11455dc61552STiezhu Yang jit_data->ctx = ctx; 11465dc61552STiezhu Yang jit_data->image = image_ptr; 11475dc61552STiezhu Yang jit_data->header = header; 11485dc61552STiezhu Yang } 11495dc61552STiezhu Yang prog->jited = 1; 11505dc61552STiezhu Yang prog->jited_len = image_size; 11515dc61552STiezhu Yang prog->bpf_func = (void *)ctx.image; 11525dc61552STiezhu Yang 11535dc61552STiezhu Yang if (!prog->is_func || extra_pass) { 11545dc61552STiezhu Yang int i; 11555dc61552STiezhu Yang 11565dc61552STiezhu Yang /* offset[prog->len] is the size of program */ 11575dc61552STiezhu Yang for (i = 0; i <= prog->len; i++) 11585dc61552STiezhu Yang ctx.offset[i] *= LOONGARCH_INSN_SIZE; 11595dc61552STiezhu Yang bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 11605dc61552STiezhu Yang 11615dc61552STiezhu Yang out_offset: 11625dc61552STiezhu Yang kvfree(ctx.offset); 11635dc61552STiezhu Yang kfree(jit_data); 11645dc61552STiezhu Yang prog->aux->jit_data = NULL; 11655dc61552STiezhu Yang } 11665dc61552STiezhu Yang 11675dc61552STiezhu Yang out: 11685dc61552STiezhu Yang if (tmp_blinded) 11695dc61552STiezhu Yang bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog); 11705dc61552STiezhu Yang 11715dc61552STiezhu Yang out_offset = -1; 11725dc61552STiezhu Yang 11735dc61552STiezhu Yang return prog; 11745dc61552STiezhu Yang } 1175