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; 282bbfddb90SHuacai 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 390dbcd7f5fSYouling Tang #define BPF_FIXUP_REG_MASK GENMASK(31, 27) 391dbcd7f5fSYouling Tang #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) 392dbcd7f5fSYouling Tang 393dbcd7f5fSYouling Tang bool ex_handler_bpf(const struct exception_table_entry *ex, 394dbcd7f5fSYouling Tang struct pt_regs *regs) 395dbcd7f5fSYouling Tang { 396dbcd7f5fSYouling Tang int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup); 397dbcd7f5fSYouling Tang off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup); 398dbcd7f5fSYouling Tang 399dbcd7f5fSYouling Tang regs->regs[dst_reg] = 0; 400dbcd7f5fSYouling Tang regs->csr_era = (unsigned long)&ex->fixup - offset; 401dbcd7f5fSYouling Tang 402dbcd7f5fSYouling Tang return true; 403dbcd7f5fSYouling Tang } 404dbcd7f5fSYouling Tang 405dbcd7f5fSYouling Tang /* For accesses to BTF pointers, add an entry to the exception table */ 406dbcd7f5fSYouling Tang static int add_exception_handler(const struct bpf_insn *insn, 407dbcd7f5fSYouling Tang struct jit_ctx *ctx, 408dbcd7f5fSYouling Tang int dst_reg) 409dbcd7f5fSYouling Tang { 410dbcd7f5fSYouling Tang unsigned long pc; 411dbcd7f5fSYouling Tang off_t offset; 412dbcd7f5fSYouling Tang struct exception_table_entry *ex; 413dbcd7f5fSYouling Tang 414dbcd7f5fSYouling Tang if (!ctx->image || !ctx->prog->aux->extable || BPF_MODE(insn->code) != BPF_PROBE_MEM) 415dbcd7f5fSYouling Tang return 0; 416dbcd7f5fSYouling Tang 417dbcd7f5fSYouling Tang if (WARN_ON_ONCE(ctx->num_exentries >= ctx->prog->aux->num_exentries)) 418dbcd7f5fSYouling Tang return -EINVAL; 419dbcd7f5fSYouling Tang 420dbcd7f5fSYouling Tang ex = &ctx->prog->aux->extable[ctx->num_exentries]; 421dbcd7f5fSYouling Tang pc = (unsigned long)&ctx->image[ctx->idx - 1]; 422dbcd7f5fSYouling Tang 423dbcd7f5fSYouling Tang offset = pc - (long)&ex->insn; 424dbcd7f5fSYouling Tang if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN)) 425dbcd7f5fSYouling Tang return -ERANGE; 426dbcd7f5fSYouling Tang 427dbcd7f5fSYouling Tang ex->insn = offset; 428dbcd7f5fSYouling Tang 429dbcd7f5fSYouling Tang /* 430dbcd7f5fSYouling Tang * Since the extable follows the program, the fixup offset is always 431dbcd7f5fSYouling Tang * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value 432dbcd7f5fSYouling Tang * to keep things simple, and put the destination register in the upper 433dbcd7f5fSYouling Tang * bits. We don't need to worry about buildtime or runtime sort 434dbcd7f5fSYouling Tang * modifying the upper bits because the table is already sorted, and 435dbcd7f5fSYouling Tang * isn't part of the main exception table. 436dbcd7f5fSYouling Tang */ 437dbcd7f5fSYouling Tang offset = (long)&ex->fixup - (pc + LOONGARCH_INSN_SIZE); 438dbcd7f5fSYouling Tang if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset)) 439dbcd7f5fSYouling Tang return -ERANGE; 440dbcd7f5fSYouling Tang 441dbcd7f5fSYouling Tang ex->type = EX_TYPE_BPF; 442dbcd7f5fSYouling Tang ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) | FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); 443dbcd7f5fSYouling Tang 444dbcd7f5fSYouling Tang ctx->num_exentries++; 445dbcd7f5fSYouling Tang 446dbcd7f5fSYouling Tang return 0; 447dbcd7f5fSYouling Tang } 448dbcd7f5fSYouling Tang 4495dc61552STiezhu Yang static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool extra_pass) 4505dc61552STiezhu Yang { 451bbfddb90SHuacai Chen u8 tm = -1; 452bbfddb90SHuacai Chen u64 func_addr; 453bbfddb90SHuacai Chen bool func_addr_fixed; 454bbfddb90SHuacai Chen int i = insn - ctx->prog->insnsi; 455bbfddb90SHuacai Chen int ret, jmp_offset; 4565dc61552STiezhu Yang const u8 code = insn->code; 4575dc61552STiezhu Yang const u8 cond = BPF_OP(code); 4585dc61552STiezhu Yang const u8 t1 = LOONGARCH_GPR_T1; 4595dc61552STiezhu Yang const u8 t2 = LOONGARCH_GPR_T2; 4605dc61552STiezhu Yang const u8 src = regmap[insn->src_reg]; 4615dc61552STiezhu Yang const u8 dst = regmap[insn->dst_reg]; 4625dc61552STiezhu Yang const s16 off = insn->off; 4635dc61552STiezhu Yang const s32 imm = insn->imm; 464bbfddb90SHuacai Chen const u64 imm64 = (u64)(insn + 1)->imm << 32 | (u32)insn->imm; 465bbfddb90SHuacai Chen const bool is32 = BPF_CLASS(insn->code) == BPF_ALU || BPF_CLASS(insn->code) == BPF_JMP32; 4665dc61552STiezhu Yang 4675dc61552STiezhu Yang switch (code) { 4685dc61552STiezhu Yang /* dst = src */ 4695dc61552STiezhu Yang case BPF_ALU | BPF_MOV | BPF_X: 4705dc61552STiezhu Yang case BPF_ALU64 | BPF_MOV | BPF_X: 4715dc61552STiezhu Yang move_reg(ctx, dst, src); 4725dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4735dc61552STiezhu Yang break; 4745dc61552STiezhu Yang 4755dc61552STiezhu Yang /* dst = imm */ 4765dc61552STiezhu Yang case BPF_ALU | BPF_MOV | BPF_K: 4775dc61552STiezhu Yang case BPF_ALU64 | BPF_MOV | BPF_K: 4785dc61552STiezhu Yang move_imm(ctx, dst, imm, is32); 4795dc61552STiezhu Yang break; 4805dc61552STiezhu Yang 4815dc61552STiezhu Yang /* dst = dst + src */ 4825dc61552STiezhu Yang case BPF_ALU | BPF_ADD | BPF_X: 4835dc61552STiezhu Yang case BPF_ALU64 | BPF_ADD | BPF_X: 4845dc61552STiezhu Yang emit_insn(ctx, addd, dst, dst, src); 4855dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4865dc61552STiezhu Yang break; 4875dc61552STiezhu Yang 4885dc61552STiezhu Yang /* dst = dst + imm */ 4895dc61552STiezhu Yang case BPF_ALU | BPF_ADD | BPF_K: 4905dc61552STiezhu Yang case BPF_ALU64 | BPF_ADD | BPF_K: 4915dc61552STiezhu Yang if (is_signed_imm12(imm)) { 4925dc61552STiezhu Yang emit_insn(ctx, addid, dst, dst, imm); 4935dc61552STiezhu Yang } else { 4945dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 4955dc61552STiezhu Yang emit_insn(ctx, addd, dst, dst, t1); 4965dc61552STiezhu Yang } 4975dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4985dc61552STiezhu Yang break; 4995dc61552STiezhu Yang 5005dc61552STiezhu Yang /* dst = dst - src */ 5015dc61552STiezhu Yang case BPF_ALU | BPF_SUB | BPF_X: 5025dc61552STiezhu Yang case BPF_ALU64 | BPF_SUB | BPF_X: 5035dc61552STiezhu Yang emit_insn(ctx, subd, dst, dst, src); 5045dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5055dc61552STiezhu Yang break; 5065dc61552STiezhu Yang 5075dc61552STiezhu Yang /* dst = dst - imm */ 5085dc61552STiezhu Yang case BPF_ALU | BPF_SUB | BPF_K: 5095dc61552STiezhu Yang case BPF_ALU64 | BPF_SUB | BPF_K: 5105dc61552STiezhu Yang if (is_signed_imm12(-imm)) { 5115dc61552STiezhu Yang emit_insn(ctx, addid, dst, dst, -imm); 5125dc61552STiezhu Yang } else { 5135dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5145dc61552STiezhu Yang emit_insn(ctx, subd, dst, dst, t1); 5155dc61552STiezhu Yang } 5165dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5175dc61552STiezhu Yang break; 5185dc61552STiezhu Yang 5195dc61552STiezhu Yang /* dst = dst * src */ 5205dc61552STiezhu Yang case BPF_ALU | BPF_MUL | BPF_X: 5215dc61552STiezhu Yang case BPF_ALU64 | BPF_MUL | BPF_X: 5225dc61552STiezhu Yang emit_insn(ctx, muld, dst, dst, src); 5235dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5245dc61552STiezhu Yang break; 5255dc61552STiezhu Yang 5265dc61552STiezhu Yang /* dst = dst * imm */ 5275dc61552STiezhu Yang case BPF_ALU | BPF_MUL | BPF_K: 5285dc61552STiezhu Yang case BPF_ALU64 | BPF_MUL | BPF_K: 5295dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5305dc61552STiezhu Yang emit_insn(ctx, muld, dst, dst, t1); 5315dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5325dc61552STiezhu Yang break; 5335dc61552STiezhu Yang 5345dc61552STiezhu Yang /* dst = dst / src */ 5355dc61552STiezhu Yang case BPF_ALU | BPF_DIV | BPF_X: 5365dc61552STiezhu Yang case BPF_ALU64 | BPF_DIV | BPF_X: 5375dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5385dc61552STiezhu Yang move_reg(ctx, t1, src); 5395dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 5405dc61552STiezhu Yang emit_insn(ctx, divdu, dst, dst, t1); 5415dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5425dc61552STiezhu Yang break; 5435dc61552STiezhu Yang 5445dc61552STiezhu Yang /* dst = dst / imm */ 5455dc61552STiezhu Yang case BPF_ALU | BPF_DIV | BPF_K: 5465dc61552STiezhu Yang case BPF_ALU64 | BPF_DIV | BPF_K: 5475dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5485dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5495dc61552STiezhu Yang emit_insn(ctx, divdu, dst, dst, t1); 5505dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5515dc61552STiezhu Yang break; 5525dc61552STiezhu Yang 5535dc61552STiezhu Yang /* dst = dst % src */ 5545dc61552STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 5555dc61552STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 5565dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5575dc61552STiezhu Yang move_reg(ctx, t1, src); 5585dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 5595dc61552STiezhu Yang emit_insn(ctx, moddu, dst, dst, t1); 5605dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5615dc61552STiezhu Yang break; 5625dc61552STiezhu Yang 5635dc61552STiezhu Yang /* dst = dst % imm */ 5645dc61552STiezhu Yang case BPF_ALU | BPF_MOD | BPF_K: 5655dc61552STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_K: 5665dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5675dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5685dc61552STiezhu Yang emit_insn(ctx, moddu, dst, dst, t1); 5695dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5705dc61552STiezhu Yang break; 5715dc61552STiezhu Yang 5725dc61552STiezhu Yang /* dst = -dst */ 5735dc61552STiezhu Yang case BPF_ALU | BPF_NEG: 5745dc61552STiezhu Yang case BPF_ALU64 | BPF_NEG: 5755dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5765dc61552STiezhu Yang emit_insn(ctx, subd, dst, LOONGARCH_GPR_ZERO, dst); 5775dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5785dc61552STiezhu Yang break; 5795dc61552STiezhu Yang 5805dc61552STiezhu Yang /* dst = dst & src */ 5815dc61552STiezhu Yang case BPF_ALU | BPF_AND | BPF_X: 5825dc61552STiezhu Yang case BPF_ALU64 | BPF_AND | BPF_X: 5835dc61552STiezhu Yang emit_insn(ctx, and, dst, dst, src); 5845dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5855dc61552STiezhu Yang break; 5865dc61552STiezhu Yang 5875dc61552STiezhu Yang /* dst = dst & imm */ 5885dc61552STiezhu Yang case BPF_ALU | BPF_AND | BPF_K: 5895dc61552STiezhu Yang case BPF_ALU64 | BPF_AND | BPF_K: 5905dc61552STiezhu Yang if (is_unsigned_imm12(imm)) { 5915dc61552STiezhu Yang emit_insn(ctx, andi, dst, dst, imm); 5925dc61552STiezhu Yang } else { 5935dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5945dc61552STiezhu Yang emit_insn(ctx, and, dst, dst, t1); 5955dc61552STiezhu Yang } 5965dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5975dc61552STiezhu Yang break; 5985dc61552STiezhu Yang 5995dc61552STiezhu Yang /* dst = dst | src */ 6005dc61552STiezhu Yang case BPF_ALU | BPF_OR | BPF_X: 6015dc61552STiezhu Yang case BPF_ALU64 | BPF_OR | BPF_X: 6025dc61552STiezhu Yang emit_insn(ctx, or, dst, dst, src); 6035dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6045dc61552STiezhu Yang break; 6055dc61552STiezhu Yang 6065dc61552STiezhu Yang /* dst = dst | imm */ 6075dc61552STiezhu Yang case BPF_ALU | BPF_OR | BPF_K: 6085dc61552STiezhu Yang case BPF_ALU64 | BPF_OR | BPF_K: 6095dc61552STiezhu Yang if (is_unsigned_imm12(imm)) { 6105dc61552STiezhu Yang emit_insn(ctx, ori, dst, dst, imm); 6115dc61552STiezhu Yang } else { 6125dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 6135dc61552STiezhu Yang emit_insn(ctx, or, dst, dst, t1); 6145dc61552STiezhu Yang } 6155dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6165dc61552STiezhu Yang break; 6175dc61552STiezhu Yang 6185dc61552STiezhu Yang /* dst = dst ^ src */ 6195dc61552STiezhu Yang case BPF_ALU | BPF_XOR | BPF_X: 6205dc61552STiezhu Yang case BPF_ALU64 | BPF_XOR | BPF_X: 6215dc61552STiezhu Yang emit_insn(ctx, xor, dst, dst, src); 6225dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6235dc61552STiezhu Yang break; 6245dc61552STiezhu Yang 6255dc61552STiezhu Yang /* dst = dst ^ imm */ 6265dc61552STiezhu Yang case BPF_ALU | BPF_XOR | BPF_K: 6275dc61552STiezhu Yang case BPF_ALU64 | BPF_XOR | BPF_K: 6285dc61552STiezhu Yang if (is_unsigned_imm12(imm)) { 6295dc61552STiezhu Yang emit_insn(ctx, xori, dst, dst, imm); 6305dc61552STiezhu Yang } else { 6315dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 6325dc61552STiezhu Yang emit_insn(ctx, xor, dst, dst, t1); 6335dc61552STiezhu Yang } 6345dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6355dc61552STiezhu Yang break; 6365dc61552STiezhu Yang 6375dc61552STiezhu Yang /* dst = dst << src (logical) */ 6385dc61552STiezhu Yang case BPF_ALU | BPF_LSH | BPF_X: 6395dc61552STiezhu Yang emit_insn(ctx, sllw, dst, dst, src); 6405dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6415dc61552STiezhu Yang break; 6425dc61552STiezhu Yang 6435dc61552STiezhu Yang case BPF_ALU64 | BPF_LSH | BPF_X: 6445dc61552STiezhu Yang emit_insn(ctx, slld, dst, dst, src); 6455dc61552STiezhu Yang break; 6465dc61552STiezhu Yang 6475dc61552STiezhu Yang /* dst = dst << imm (logical) */ 6485dc61552STiezhu Yang case BPF_ALU | BPF_LSH | BPF_K: 6495dc61552STiezhu Yang emit_insn(ctx, slliw, dst, dst, imm); 6505dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6515dc61552STiezhu Yang break; 6525dc61552STiezhu Yang 6535dc61552STiezhu Yang case BPF_ALU64 | BPF_LSH | BPF_K: 6545dc61552STiezhu Yang emit_insn(ctx, sllid, dst, dst, imm); 6555dc61552STiezhu Yang break; 6565dc61552STiezhu Yang 6575dc61552STiezhu Yang /* dst = dst >> src (logical) */ 6585dc61552STiezhu Yang case BPF_ALU | BPF_RSH | BPF_X: 6595dc61552STiezhu Yang emit_insn(ctx, srlw, dst, dst, src); 6605dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6615dc61552STiezhu Yang break; 6625dc61552STiezhu Yang 6635dc61552STiezhu Yang case BPF_ALU64 | BPF_RSH | BPF_X: 6645dc61552STiezhu Yang emit_insn(ctx, srld, dst, dst, src); 6655dc61552STiezhu Yang break; 6665dc61552STiezhu Yang 6675dc61552STiezhu Yang /* dst = dst >> imm (logical) */ 6685dc61552STiezhu Yang case BPF_ALU | BPF_RSH | BPF_K: 6695dc61552STiezhu Yang emit_insn(ctx, srliw, dst, dst, imm); 6705dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6715dc61552STiezhu Yang break; 6725dc61552STiezhu Yang 6735dc61552STiezhu Yang case BPF_ALU64 | BPF_RSH | BPF_K: 6745dc61552STiezhu Yang emit_insn(ctx, srlid, dst, dst, imm); 6755dc61552STiezhu Yang break; 6765dc61552STiezhu Yang 6775dc61552STiezhu Yang /* dst = dst >> src (arithmetic) */ 6785dc61552STiezhu Yang case BPF_ALU | BPF_ARSH | BPF_X: 6795dc61552STiezhu Yang emit_insn(ctx, sraw, dst, dst, src); 6805dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6815dc61552STiezhu Yang break; 6825dc61552STiezhu Yang 6835dc61552STiezhu Yang case BPF_ALU64 | BPF_ARSH | BPF_X: 6845dc61552STiezhu Yang emit_insn(ctx, srad, dst, dst, src); 6855dc61552STiezhu Yang break; 6865dc61552STiezhu Yang 6875dc61552STiezhu Yang /* dst = dst >> imm (arithmetic) */ 6885dc61552STiezhu Yang case BPF_ALU | BPF_ARSH | BPF_K: 6895dc61552STiezhu Yang emit_insn(ctx, sraiw, dst, dst, imm); 6905dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6915dc61552STiezhu Yang break; 6925dc61552STiezhu Yang 6935dc61552STiezhu Yang case BPF_ALU64 | BPF_ARSH | BPF_K: 6945dc61552STiezhu Yang emit_insn(ctx, sraid, dst, dst, imm); 6955dc61552STiezhu Yang break; 6965dc61552STiezhu Yang 6975dc61552STiezhu Yang /* dst = BSWAP##imm(dst) */ 6985dc61552STiezhu Yang case BPF_ALU | BPF_END | BPF_FROM_LE: 6995dc61552STiezhu Yang switch (imm) { 7005dc61552STiezhu Yang case 16: 7015dc61552STiezhu Yang /* zero-extend 16 bits into 64 bits */ 7025dc61552STiezhu Yang emit_insn(ctx, bstrpickd, dst, dst, 15, 0); 7035dc61552STiezhu Yang break; 7045dc61552STiezhu Yang case 32: 7055dc61552STiezhu Yang /* zero-extend 32 bits into 64 bits */ 7065dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 7075dc61552STiezhu Yang break; 7085dc61552STiezhu Yang case 64: 7095dc61552STiezhu Yang /* do nothing */ 7105dc61552STiezhu Yang break; 7115dc61552STiezhu Yang } 7125dc61552STiezhu Yang break; 7135dc61552STiezhu Yang 7145dc61552STiezhu Yang case BPF_ALU | BPF_END | BPF_FROM_BE: 7155dc61552STiezhu Yang switch (imm) { 7165dc61552STiezhu Yang case 16: 7175dc61552STiezhu Yang emit_insn(ctx, revb2h, dst, dst); 7185dc61552STiezhu Yang /* zero-extend 16 bits into 64 bits */ 7195dc61552STiezhu Yang emit_insn(ctx, bstrpickd, dst, dst, 15, 0); 7205dc61552STiezhu Yang break; 7215dc61552STiezhu Yang case 32: 7225dc61552STiezhu Yang emit_insn(ctx, revb2w, dst, dst); 7235dc61552STiezhu Yang /* zero-extend 32 bits into 64 bits */ 7245dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 7255dc61552STiezhu Yang break; 7265dc61552STiezhu Yang case 64: 7275dc61552STiezhu Yang emit_insn(ctx, revbd, dst, dst); 7285dc61552STiezhu Yang break; 7295dc61552STiezhu Yang } 7305dc61552STiezhu Yang break; 7315dc61552STiezhu Yang 7325dc61552STiezhu Yang /* PC += off if dst cond src */ 7335dc61552STiezhu Yang case BPF_JMP | BPF_JEQ | BPF_X: 7345dc61552STiezhu Yang case BPF_JMP | BPF_JNE | BPF_X: 7355dc61552STiezhu Yang case BPF_JMP | BPF_JGT | BPF_X: 7365dc61552STiezhu Yang case BPF_JMP | BPF_JGE | BPF_X: 7375dc61552STiezhu Yang case BPF_JMP | BPF_JLT | BPF_X: 7385dc61552STiezhu Yang case BPF_JMP | BPF_JLE | BPF_X: 7395dc61552STiezhu Yang case BPF_JMP | BPF_JSGT | BPF_X: 7405dc61552STiezhu Yang case BPF_JMP | BPF_JSGE | BPF_X: 7415dc61552STiezhu Yang case BPF_JMP | BPF_JSLT | BPF_X: 7425dc61552STiezhu Yang case BPF_JMP | BPF_JSLE | BPF_X: 7435dc61552STiezhu Yang case BPF_JMP32 | BPF_JEQ | BPF_X: 7445dc61552STiezhu Yang case BPF_JMP32 | BPF_JNE | BPF_X: 7455dc61552STiezhu Yang case BPF_JMP32 | BPF_JGT | BPF_X: 7465dc61552STiezhu Yang case BPF_JMP32 | BPF_JGE | BPF_X: 7475dc61552STiezhu Yang case BPF_JMP32 | BPF_JLT | BPF_X: 7485dc61552STiezhu Yang case BPF_JMP32 | BPF_JLE | BPF_X: 7495dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGT | BPF_X: 7505dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGE | BPF_X: 7515dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLT | BPF_X: 7525dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLE | BPF_X: 7535dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 7545dc61552STiezhu Yang move_reg(ctx, t1, dst); 7555dc61552STiezhu Yang move_reg(ctx, t2, src); 7565dc61552STiezhu Yang if (is_signed_bpf_cond(BPF_OP(code))) { 7575dc61552STiezhu Yang emit_sext_32(ctx, t1, is32); 7585dc61552STiezhu Yang emit_sext_32(ctx, t2, is32); 7595dc61552STiezhu Yang } else { 7605dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 7615dc61552STiezhu Yang emit_zext_32(ctx, t2, is32); 7625dc61552STiezhu Yang } 7635dc61552STiezhu Yang if (emit_cond_jmp(ctx, cond, t1, t2, jmp_offset) < 0) 7645dc61552STiezhu Yang goto toofar; 7655dc61552STiezhu Yang break; 7665dc61552STiezhu Yang 7675dc61552STiezhu Yang /* PC += off if dst cond imm */ 7685dc61552STiezhu Yang case BPF_JMP | BPF_JEQ | BPF_K: 7695dc61552STiezhu Yang case BPF_JMP | BPF_JNE | BPF_K: 7705dc61552STiezhu Yang case BPF_JMP | BPF_JGT | BPF_K: 7715dc61552STiezhu Yang case BPF_JMP | BPF_JGE | BPF_K: 7725dc61552STiezhu Yang case BPF_JMP | BPF_JLT | BPF_K: 7735dc61552STiezhu Yang case BPF_JMP | BPF_JLE | BPF_K: 7745dc61552STiezhu Yang case BPF_JMP | BPF_JSGT | BPF_K: 7755dc61552STiezhu Yang case BPF_JMP | BPF_JSGE | BPF_K: 7765dc61552STiezhu Yang case BPF_JMP | BPF_JSLT | BPF_K: 7775dc61552STiezhu Yang case BPF_JMP | BPF_JSLE | BPF_K: 7785dc61552STiezhu Yang case BPF_JMP32 | BPF_JEQ | BPF_K: 7795dc61552STiezhu Yang case BPF_JMP32 | BPF_JNE | BPF_K: 7805dc61552STiezhu Yang case BPF_JMP32 | BPF_JGT | BPF_K: 7815dc61552STiezhu Yang case BPF_JMP32 | BPF_JGE | BPF_K: 7825dc61552STiezhu Yang case BPF_JMP32 | BPF_JLT | BPF_K: 7835dc61552STiezhu Yang case BPF_JMP32 | BPF_JLE | BPF_K: 7845dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGT | BPF_K: 7855dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGE | BPF_K: 7865dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLT | BPF_K: 7875dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLE | BPF_K: 7885dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 7895dc61552STiezhu Yang if (imm) { 7905dc61552STiezhu Yang move_imm(ctx, t1, imm, false); 791bbfddb90SHuacai Chen tm = t1; 7925dc61552STiezhu Yang } else { 7935dc61552STiezhu Yang /* If imm is 0, simply use zero register. */ 794bbfddb90SHuacai Chen tm = LOONGARCH_GPR_ZERO; 7955dc61552STiezhu Yang } 7965dc61552STiezhu Yang move_reg(ctx, t2, dst); 7975dc61552STiezhu Yang if (is_signed_bpf_cond(BPF_OP(code))) { 798bbfddb90SHuacai Chen emit_sext_32(ctx, tm, is32); 7995dc61552STiezhu Yang emit_sext_32(ctx, t2, is32); 8005dc61552STiezhu Yang } else { 801bbfddb90SHuacai Chen emit_zext_32(ctx, tm, is32); 8025dc61552STiezhu Yang emit_zext_32(ctx, t2, is32); 8035dc61552STiezhu Yang } 804bbfddb90SHuacai Chen if (emit_cond_jmp(ctx, cond, t2, tm, jmp_offset) < 0) 8055dc61552STiezhu Yang goto toofar; 8065dc61552STiezhu Yang break; 8075dc61552STiezhu Yang 8085dc61552STiezhu Yang /* PC += off if dst & src */ 8095dc61552STiezhu Yang case BPF_JMP | BPF_JSET | BPF_X: 8105dc61552STiezhu Yang case BPF_JMP32 | BPF_JSET | BPF_X: 8115dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 8125dc61552STiezhu Yang emit_insn(ctx, and, t1, dst, src); 8135dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 8145dc61552STiezhu Yang if (emit_cond_jmp(ctx, cond, t1, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 8155dc61552STiezhu Yang goto toofar; 8165dc61552STiezhu Yang break; 8175dc61552STiezhu Yang 8185dc61552STiezhu Yang /* PC += off if dst & imm */ 8195dc61552STiezhu Yang case BPF_JMP | BPF_JSET | BPF_K: 8205dc61552STiezhu Yang case BPF_JMP32 | BPF_JSET | BPF_K: 8215dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 8225dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 8235dc61552STiezhu Yang emit_insn(ctx, and, t1, dst, t1); 8245dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 8255dc61552STiezhu Yang if (emit_cond_jmp(ctx, cond, t1, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 8265dc61552STiezhu Yang goto toofar; 8275dc61552STiezhu Yang break; 8285dc61552STiezhu Yang 8295dc61552STiezhu Yang /* PC += off */ 8305dc61552STiezhu Yang case BPF_JMP | BPF_JA: 8315dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 8325dc61552STiezhu Yang if (emit_uncond_jmp(ctx, jmp_offset) < 0) 8335dc61552STiezhu Yang goto toofar; 8345dc61552STiezhu Yang break; 8355dc61552STiezhu Yang 8365dc61552STiezhu Yang /* function call */ 8375dc61552STiezhu Yang case BPF_JMP | BPF_CALL: 8385dc61552STiezhu Yang mark_call(ctx); 8395dc61552STiezhu Yang ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 8405dc61552STiezhu Yang &func_addr, &func_addr_fixed); 8415dc61552STiezhu Yang if (ret < 0) 8425dc61552STiezhu Yang return ret; 8435dc61552STiezhu Yang 844*64f50f65SHengqi Chen move_addr(ctx, t1, func_addr); 8455dc61552STiezhu Yang emit_insn(ctx, jirl, t1, LOONGARCH_GPR_RA, 0); 8465dc61552STiezhu Yang move_reg(ctx, regmap[BPF_REG_0], LOONGARCH_GPR_A0); 8475dc61552STiezhu Yang break; 8485dc61552STiezhu Yang 8495dc61552STiezhu Yang /* tail call */ 8505dc61552STiezhu Yang case BPF_JMP | BPF_TAIL_CALL: 8515dc61552STiezhu Yang mark_tail_call(ctx); 8525dc61552STiezhu Yang if (emit_bpf_tail_call(ctx) < 0) 8535dc61552STiezhu Yang return -EINVAL; 8545dc61552STiezhu Yang break; 8555dc61552STiezhu Yang 8565dc61552STiezhu Yang /* function return */ 8575dc61552STiezhu Yang case BPF_JMP | BPF_EXIT: 8585dc61552STiezhu Yang emit_sext_32(ctx, regmap[BPF_REG_0], true); 8595dc61552STiezhu Yang 8605dc61552STiezhu Yang if (i == ctx->prog->len - 1) 8615dc61552STiezhu Yang break; 8625dc61552STiezhu Yang 8635dc61552STiezhu Yang jmp_offset = epilogue_offset(ctx); 8645dc61552STiezhu Yang if (emit_uncond_jmp(ctx, jmp_offset) < 0) 8655dc61552STiezhu Yang goto toofar; 8665dc61552STiezhu Yang break; 8675dc61552STiezhu Yang 8685dc61552STiezhu Yang /* dst = imm64 */ 8695dc61552STiezhu Yang case BPF_LD | BPF_IMM | BPF_DW: 8705dc61552STiezhu Yang move_imm(ctx, dst, imm64, is32); 8715dc61552STiezhu Yang return 1; 8725dc61552STiezhu Yang 8735dc61552STiezhu Yang /* dst = *(size *)(src + off) */ 8745dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_B: 8755dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_H: 8765dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_W: 8775dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_DW: 878dbcd7f5fSYouling Tang case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 879dbcd7f5fSYouling Tang case BPF_LDX | BPF_PROBE_MEM | BPF_W: 880dbcd7f5fSYouling Tang case BPF_LDX | BPF_PROBE_MEM | BPF_H: 881dbcd7f5fSYouling Tang case BPF_LDX | BPF_PROBE_MEM | BPF_B: 8825dc61552STiezhu Yang switch (BPF_SIZE(code)) { 8835dc61552STiezhu Yang case BPF_B: 8845dc61552STiezhu Yang if (is_signed_imm12(off)) { 8855dc61552STiezhu Yang emit_insn(ctx, ldbu, dst, src, off); 8865dc61552STiezhu Yang } else { 8875dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 8885dc61552STiezhu Yang emit_insn(ctx, ldxbu, dst, src, t1); 8895dc61552STiezhu Yang } 8905dc61552STiezhu Yang break; 8915dc61552STiezhu Yang case BPF_H: 8925dc61552STiezhu Yang if (is_signed_imm12(off)) { 8935dc61552STiezhu Yang emit_insn(ctx, ldhu, dst, src, off); 8945dc61552STiezhu Yang } else { 8955dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 8965dc61552STiezhu Yang emit_insn(ctx, ldxhu, dst, src, t1); 8975dc61552STiezhu Yang } 8985dc61552STiezhu Yang break; 8995dc61552STiezhu Yang case BPF_W: 9005dc61552STiezhu Yang if (is_signed_imm12(off)) { 9015dc61552STiezhu Yang emit_insn(ctx, ldwu, dst, src, off); 9025dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9035dc61552STiezhu Yang emit_insn(ctx, ldptrw, dst, src, off); 9045dc61552STiezhu Yang } else { 9055dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9065dc61552STiezhu Yang emit_insn(ctx, ldxwu, dst, src, t1); 9075dc61552STiezhu Yang } 9085dc61552STiezhu Yang break; 9095dc61552STiezhu Yang case BPF_DW: 9105dc61552STiezhu Yang if (is_signed_imm12(off)) { 9115dc61552STiezhu Yang emit_insn(ctx, ldd, dst, src, off); 9125dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9135dc61552STiezhu Yang emit_insn(ctx, ldptrd, dst, src, off); 9145dc61552STiezhu Yang } else { 9155dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9165dc61552STiezhu Yang emit_insn(ctx, ldxd, dst, src, t1); 9175dc61552STiezhu Yang } 9185dc61552STiezhu Yang break; 9195dc61552STiezhu Yang } 920dbcd7f5fSYouling Tang 921dbcd7f5fSYouling Tang ret = add_exception_handler(insn, ctx, dst); 922dbcd7f5fSYouling Tang if (ret) 923dbcd7f5fSYouling Tang return ret; 9245dc61552STiezhu Yang break; 9255dc61552STiezhu Yang 9265dc61552STiezhu Yang /* *(size *)(dst + off) = imm */ 9275dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_B: 9285dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_H: 9295dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_W: 9305dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_DW: 9315dc61552STiezhu Yang switch (BPF_SIZE(code)) { 9325dc61552STiezhu Yang case BPF_B: 9335dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 9345dc61552STiezhu Yang if (is_signed_imm12(off)) { 9355dc61552STiezhu Yang emit_insn(ctx, stb, t1, dst, off); 9365dc61552STiezhu Yang } else { 9375dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 9385dc61552STiezhu Yang emit_insn(ctx, stxb, t1, dst, t2); 9395dc61552STiezhu Yang } 9405dc61552STiezhu Yang break; 9415dc61552STiezhu Yang case BPF_H: 9425dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 9435dc61552STiezhu Yang if (is_signed_imm12(off)) { 9445dc61552STiezhu Yang emit_insn(ctx, sth, t1, dst, off); 9455dc61552STiezhu Yang } else { 9465dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 9475dc61552STiezhu Yang emit_insn(ctx, stxh, t1, dst, t2); 9485dc61552STiezhu Yang } 9495dc61552STiezhu Yang break; 9505dc61552STiezhu Yang case BPF_W: 9515dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 9525dc61552STiezhu Yang if (is_signed_imm12(off)) { 9535dc61552STiezhu Yang emit_insn(ctx, stw, t1, dst, off); 9545dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9555dc61552STiezhu Yang emit_insn(ctx, stptrw, t1, dst, off); 9565dc61552STiezhu Yang } else { 9575dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 9585dc61552STiezhu Yang emit_insn(ctx, stxw, t1, dst, t2); 9595dc61552STiezhu Yang } 9605dc61552STiezhu Yang break; 9615dc61552STiezhu Yang case BPF_DW: 9625dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 9635dc61552STiezhu Yang if (is_signed_imm12(off)) { 9645dc61552STiezhu Yang emit_insn(ctx, std, t1, dst, off); 9655dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9665dc61552STiezhu Yang emit_insn(ctx, stptrd, t1, dst, off); 9675dc61552STiezhu Yang } else { 9685dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 9695dc61552STiezhu Yang emit_insn(ctx, stxd, t1, dst, t2); 9705dc61552STiezhu Yang } 9715dc61552STiezhu Yang break; 9725dc61552STiezhu Yang } 9735dc61552STiezhu Yang break; 9745dc61552STiezhu Yang 9755dc61552STiezhu Yang /* *(size *)(dst + off) = src */ 9765dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_B: 9775dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_H: 9785dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_W: 9795dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_DW: 9805dc61552STiezhu Yang switch (BPF_SIZE(code)) { 9815dc61552STiezhu Yang case BPF_B: 9825dc61552STiezhu Yang if (is_signed_imm12(off)) { 9835dc61552STiezhu Yang emit_insn(ctx, stb, src, dst, off); 9845dc61552STiezhu Yang } else { 9855dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9865dc61552STiezhu Yang emit_insn(ctx, stxb, src, dst, t1); 9875dc61552STiezhu Yang } 9885dc61552STiezhu Yang break; 9895dc61552STiezhu Yang case BPF_H: 9905dc61552STiezhu Yang if (is_signed_imm12(off)) { 9915dc61552STiezhu Yang emit_insn(ctx, sth, src, dst, off); 9925dc61552STiezhu Yang } else { 9935dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9945dc61552STiezhu Yang emit_insn(ctx, stxh, src, dst, t1); 9955dc61552STiezhu Yang } 9965dc61552STiezhu Yang break; 9975dc61552STiezhu Yang case BPF_W: 9985dc61552STiezhu Yang if (is_signed_imm12(off)) { 9995dc61552STiezhu Yang emit_insn(ctx, stw, src, dst, off); 10005dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 10015dc61552STiezhu Yang emit_insn(ctx, stptrw, src, dst, off); 10025dc61552STiezhu Yang } else { 10035dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 10045dc61552STiezhu Yang emit_insn(ctx, stxw, src, dst, t1); 10055dc61552STiezhu Yang } 10065dc61552STiezhu Yang break; 10075dc61552STiezhu Yang case BPF_DW: 10085dc61552STiezhu Yang if (is_signed_imm12(off)) { 10095dc61552STiezhu Yang emit_insn(ctx, std, src, dst, off); 10105dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 10115dc61552STiezhu Yang emit_insn(ctx, stptrd, src, dst, off); 10125dc61552STiezhu Yang } else { 10135dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 10145dc61552STiezhu Yang emit_insn(ctx, stxd, src, dst, t1); 10155dc61552STiezhu Yang } 10165dc61552STiezhu Yang break; 10175dc61552STiezhu Yang } 10185dc61552STiezhu Yang break; 10195dc61552STiezhu Yang 10205dc61552STiezhu Yang case BPF_STX | BPF_ATOMIC | BPF_W: 10215dc61552STiezhu Yang case BPF_STX | BPF_ATOMIC | BPF_DW: 10225dc61552STiezhu Yang emit_atomic(insn, ctx); 10235dc61552STiezhu Yang break; 10245dc61552STiezhu Yang 10255dc61552STiezhu Yang default: 10265dc61552STiezhu Yang pr_err("bpf_jit: unknown opcode %02x\n", code); 10275dc61552STiezhu Yang return -EINVAL; 10285dc61552STiezhu Yang } 10295dc61552STiezhu Yang 10305dc61552STiezhu Yang return 0; 10315dc61552STiezhu Yang 10325dc61552STiezhu Yang toofar: 10335dc61552STiezhu Yang pr_info_once("bpf_jit: opcode %02x, jump too far\n", code); 10345dc61552STiezhu Yang return -E2BIG; 10355dc61552STiezhu Yang } 10365dc61552STiezhu Yang 10375dc61552STiezhu Yang static int build_body(struct jit_ctx *ctx, bool extra_pass) 10385dc61552STiezhu Yang { 10395dc61552STiezhu Yang int i; 10405dc61552STiezhu Yang const struct bpf_prog *prog = ctx->prog; 10415dc61552STiezhu Yang 10425dc61552STiezhu Yang for (i = 0; i < prog->len; i++) { 10435dc61552STiezhu Yang const struct bpf_insn *insn = &prog->insnsi[i]; 10445dc61552STiezhu Yang int ret; 10455dc61552STiezhu Yang 10465dc61552STiezhu Yang if (ctx->image == NULL) 10475dc61552STiezhu Yang ctx->offset[i] = ctx->idx; 10485dc61552STiezhu Yang 10495dc61552STiezhu Yang ret = build_insn(insn, ctx, extra_pass); 10505dc61552STiezhu Yang if (ret > 0) { 10515dc61552STiezhu Yang i++; 10525dc61552STiezhu Yang if (ctx->image == NULL) 10535dc61552STiezhu Yang ctx->offset[i] = ctx->idx; 10545dc61552STiezhu Yang continue; 10555dc61552STiezhu Yang } 10565dc61552STiezhu Yang if (ret) 10575dc61552STiezhu Yang return ret; 10585dc61552STiezhu Yang } 10595dc61552STiezhu Yang 10605dc61552STiezhu Yang if (ctx->image == NULL) 10615dc61552STiezhu Yang ctx->offset[i] = ctx->idx; 10625dc61552STiezhu Yang 10635dc61552STiezhu Yang return 0; 10645dc61552STiezhu Yang } 10655dc61552STiezhu Yang 10665dc61552STiezhu Yang /* Fill space with break instructions */ 10675dc61552STiezhu Yang static void jit_fill_hole(void *area, unsigned int size) 10685dc61552STiezhu Yang { 10695dc61552STiezhu Yang u32 *ptr; 10705dc61552STiezhu Yang 10715dc61552STiezhu Yang /* We are guaranteed to have aligned memory */ 10725dc61552STiezhu Yang for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 10735dc61552STiezhu Yang *ptr++ = INSN_BREAK; 10745dc61552STiezhu Yang } 10755dc61552STiezhu Yang 10765dc61552STiezhu Yang static int validate_code(struct jit_ctx *ctx) 10775dc61552STiezhu Yang { 10785dc61552STiezhu Yang int i; 10795dc61552STiezhu Yang union loongarch_instruction insn; 10805dc61552STiezhu Yang 10815dc61552STiezhu Yang for (i = 0; i < ctx->idx; i++) { 10825dc61552STiezhu Yang insn = ctx->image[i]; 10835dc61552STiezhu Yang /* Check INSN_BREAK */ 10845dc61552STiezhu Yang if (insn.word == INSN_BREAK) 10855dc61552STiezhu Yang return -1; 10865dc61552STiezhu Yang } 10875dc61552STiezhu Yang 1088dbcd7f5fSYouling Tang if (WARN_ON_ONCE(ctx->num_exentries != ctx->prog->aux->num_exentries)) 1089dbcd7f5fSYouling Tang return -1; 1090dbcd7f5fSYouling Tang 10915dc61552STiezhu Yang return 0; 10925dc61552STiezhu Yang } 10935dc61552STiezhu Yang 10945dc61552STiezhu Yang struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 10955dc61552STiezhu Yang { 10965dc61552STiezhu Yang bool tmp_blinded = false, extra_pass = false; 10975dc61552STiezhu Yang u8 *image_ptr; 1098dbcd7f5fSYouling Tang int image_size, prog_size, extable_size; 10995dc61552STiezhu Yang struct jit_ctx ctx; 11005dc61552STiezhu Yang struct jit_data *jit_data; 11015dc61552STiezhu Yang struct bpf_binary_header *header; 11025dc61552STiezhu Yang struct bpf_prog *tmp, *orig_prog = prog; 11035dc61552STiezhu Yang 11045dc61552STiezhu Yang /* 11055dc61552STiezhu Yang * If BPF JIT was not enabled then we must fall back to 11065dc61552STiezhu Yang * the interpreter. 11075dc61552STiezhu Yang */ 11085dc61552STiezhu Yang if (!prog->jit_requested) 11095dc61552STiezhu Yang return orig_prog; 11105dc61552STiezhu Yang 11115dc61552STiezhu Yang tmp = bpf_jit_blind_constants(prog); 11125dc61552STiezhu Yang /* 11135dc61552STiezhu Yang * If blinding was requested and we failed during blinding, 11145dc61552STiezhu Yang * we must fall back to the interpreter. Otherwise, we save 11155dc61552STiezhu Yang * the new JITed code. 11165dc61552STiezhu Yang */ 11175dc61552STiezhu Yang if (IS_ERR(tmp)) 11185dc61552STiezhu Yang return orig_prog; 11195dc61552STiezhu Yang 11205dc61552STiezhu Yang if (tmp != prog) { 11215dc61552STiezhu Yang tmp_blinded = true; 11225dc61552STiezhu Yang prog = tmp; 11235dc61552STiezhu Yang } 11245dc61552STiezhu Yang 11255dc61552STiezhu Yang jit_data = prog->aux->jit_data; 11265dc61552STiezhu Yang if (!jit_data) { 11275dc61552STiezhu Yang jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 11285dc61552STiezhu Yang if (!jit_data) { 11295dc61552STiezhu Yang prog = orig_prog; 11305dc61552STiezhu Yang goto out; 11315dc61552STiezhu Yang } 11325dc61552STiezhu Yang prog->aux->jit_data = jit_data; 11335dc61552STiezhu Yang } 11345dc61552STiezhu Yang if (jit_data->ctx.offset) { 11355dc61552STiezhu Yang ctx = jit_data->ctx; 11365dc61552STiezhu Yang image_ptr = jit_data->image; 11375dc61552STiezhu Yang header = jit_data->header; 11385dc61552STiezhu Yang extra_pass = true; 1139dbcd7f5fSYouling Tang prog_size = sizeof(u32) * ctx.idx; 11405dc61552STiezhu Yang goto skip_init_ctx; 11415dc61552STiezhu Yang } 11425dc61552STiezhu Yang 11435dc61552STiezhu Yang memset(&ctx, 0, sizeof(ctx)); 11445dc61552STiezhu Yang ctx.prog = prog; 11455dc61552STiezhu Yang 11465dc61552STiezhu Yang ctx.offset = kvcalloc(prog->len + 1, sizeof(u32), GFP_KERNEL); 11475dc61552STiezhu Yang if (ctx.offset == NULL) { 11485dc61552STiezhu Yang prog = orig_prog; 11495dc61552STiezhu Yang goto out_offset; 11505dc61552STiezhu Yang } 11515dc61552STiezhu Yang 11525dc61552STiezhu Yang /* 1. Initial fake pass to compute ctx->idx and set ctx->flags */ 11535dc61552STiezhu Yang build_prologue(&ctx); 11545dc61552STiezhu Yang if (build_body(&ctx, extra_pass)) { 11555dc61552STiezhu Yang prog = orig_prog; 11565dc61552STiezhu Yang goto out_offset; 11575dc61552STiezhu Yang } 11585dc61552STiezhu Yang ctx.epilogue_offset = ctx.idx; 11595dc61552STiezhu Yang build_epilogue(&ctx); 11605dc61552STiezhu Yang 1161dbcd7f5fSYouling Tang extable_size = prog->aux->num_exentries * sizeof(struct exception_table_entry); 1162dbcd7f5fSYouling Tang 11635dc61552STiezhu Yang /* Now we know the actual image size. 11645dc61552STiezhu Yang * As each LoongArch instruction is of length 32bit, 11655dc61552STiezhu Yang * we are translating number of JITed intructions into 11665dc61552STiezhu Yang * the size required to store these JITed code. 11675dc61552STiezhu Yang */ 1168dbcd7f5fSYouling Tang prog_size = sizeof(u32) * ctx.idx; 1169dbcd7f5fSYouling Tang image_size = prog_size + extable_size; 11705dc61552STiezhu Yang /* Now we know the size of the structure to make */ 11715dc61552STiezhu Yang header = bpf_jit_binary_alloc(image_size, &image_ptr, 11725dc61552STiezhu Yang sizeof(u32), jit_fill_hole); 11735dc61552STiezhu Yang if (header == NULL) { 11745dc61552STiezhu Yang prog = orig_prog; 11755dc61552STiezhu Yang goto out_offset; 11765dc61552STiezhu Yang } 11775dc61552STiezhu Yang 11785dc61552STiezhu Yang /* 2. Now, the actual pass to generate final JIT code */ 11795dc61552STiezhu Yang ctx.image = (union loongarch_instruction *)image_ptr; 1180dbcd7f5fSYouling Tang if (extable_size) 1181dbcd7f5fSYouling Tang prog->aux->extable = (void *)image_ptr + prog_size; 11825dc61552STiezhu Yang 11835dc61552STiezhu Yang skip_init_ctx: 11845dc61552STiezhu Yang ctx.idx = 0; 1185dbcd7f5fSYouling Tang ctx.num_exentries = 0; 11865dc61552STiezhu Yang 11875dc61552STiezhu Yang build_prologue(&ctx); 11885dc61552STiezhu Yang if (build_body(&ctx, extra_pass)) { 11895dc61552STiezhu Yang bpf_jit_binary_free(header); 11905dc61552STiezhu Yang prog = orig_prog; 11915dc61552STiezhu Yang goto out_offset; 11925dc61552STiezhu Yang } 11935dc61552STiezhu Yang build_epilogue(&ctx); 11945dc61552STiezhu Yang 11955dc61552STiezhu Yang /* 3. Extra pass to validate JITed code */ 11965dc61552STiezhu Yang if (validate_code(&ctx)) { 11975dc61552STiezhu Yang bpf_jit_binary_free(header); 11985dc61552STiezhu Yang prog = orig_prog; 11995dc61552STiezhu Yang goto out_offset; 12005dc61552STiezhu Yang } 12015dc61552STiezhu Yang 12025dc61552STiezhu Yang /* And we're done */ 12035dc61552STiezhu Yang if (bpf_jit_enable > 1) 1204dbcd7f5fSYouling Tang bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 12055dc61552STiezhu Yang 12065dc61552STiezhu Yang /* Update the icache */ 12075dc61552STiezhu Yang flush_icache_range((unsigned long)header, (unsigned long)(ctx.image + ctx.idx)); 12085dc61552STiezhu Yang 12095dc61552STiezhu Yang if (!prog->is_func || extra_pass) { 12105dc61552STiezhu Yang if (extra_pass && ctx.idx != jit_data->ctx.idx) { 12115dc61552STiezhu Yang pr_err_once("multi-func JIT bug %d != %d\n", 12125dc61552STiezhu Yang ctx.idx, jit_data->ctx.idx); 12135dc61552STiezhu Yang bpf_jit_binary_free(header); 12145dc61552STiezhu Yang prog->bpf_func = NULL; 12155dc61552STiezhu Yang prog->jited = 0; 12165dc61552STiezhu Yang prog->jited_len = 0; 12175dc61552STiezhu Yang goto out_offset; 12185dc61552STiezhu Yang } 12195dc61552STiezhu Yang bpf_jit_binary_lock_ro(header); 12205dc61552STiezhu Yang } else { 12215dc61552STiezhu Yang jit_data->ctx = ctx; 12225dc61552STiezhu Yang jit_data->image = image_ptr; 12235dc61552STiezhu Yang jit_data->header = header; 12245dc61552STiezhu Yang } 12255dc61552STiezhu Yang prog->jited = 1; 1226dbcd7f5fSYouling Tang prog->jited_len = prog_size; 12275dc61552STiezhu Yang prog->bpf_func = (void *)ctx.image; 12285dc61552STiezhu Yang 12295dc61552STiezhu Yang if (!prog->is_func || extra_pass) { 12305dc61552STiezhu Yang int i; 12315dc61552STiezhu Yang 12325dc61552STiezhu Yang /* offset[prog->len] is the size of program */ 12335dc61552STiezhu Yang for (i = 0; i <= prog->len; i++) 12345dc61552STiezhu Yang ctx.offset[i] *= LOONGARCH_INSN_SIZE; 12355dc61552STiezhu Yang bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 12365dc61552STiezhu Yang 12375dc61552STiezhu Yang out_offset: 12385dc61552STiezhu Yang kvfree(ctx.offset); 12395dc61552STiezhu Yang kfree(jit_data); 12405dc61552STiezhu Yang prog->aux->jit_data = NULL; 12415dc61552STiezhu Yang } 12425dc61552STiezhu Yang 12435dc61552STiezhu Yang out: 12445dc61552STiezhu Yang if (tmp_blinded) 12455dc61552STiezhu Yang bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog); 12465dc61552STiezhu Yang 12475dc61552STiezhu Yang out_offset = -1; 12485dc61552STiezhu Yang 12495dc61552STiezhu Yang return prog; 12505dc61552STiezhu Yang } 1251