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 */ 1820c8d5050STiezhu Yang emit_insn(ctx, addiw, LOONGARCH_GPR_A0, regmap[BPF_REG_0], 0); 1835dc61552STiezhu Yang /* Return to the caller */ 184*4eb54230STiezhu Yang emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_RA, 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 */ 190*4eb54230STiezhu Yang emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T3, 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 bool is32 = BPF_CLASS(insn->code) == BPF_ALU || BPF_CLASS(insn->code) == BPF_JMP32; 4655dc61552STiezhu Yang 4665dc61552STiezhu Yang switch (code) { 4675dc61552STiezhu Yang /* dst = src */ 4685dc61552STiezhu Yang case BPF_ALU | BPF_MOV | BPF_X: 4695dc61552STiezhu Yang case BPF_ALU64 | BPF_MOV | BPF_X: 4705dc61552STiezhu Yang move_reg(ctx, dst, src); 4715dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4725dc61552STiezhu Yang break; 4735dc61552STiezhu Yang 4745dc61552STiezhu Yang /* dst = imm */ 4755dc61552STiezhu Yang case BPF_ALU | BPF_MOV | BPF_K: 4765dc61552STiezhu Yang case BPF_ALU64 | BPF_MOV | BPF_K: 4775dc61552STiezhu Yang move_imm(ctx, dst, imm, is32); 4785dc61552STiezhu Yang break; 4795dc61552STiezhu Yang 4805dc61552STiezhu Yang /* dst = dst + src */ 4815dc61552STiezhu Yang case BPF_ALU | BPF_ADD | BPF_X: 4825dc61552STiezhu Yang case BPF_ALU64 | BPF_ADD | BPF_X: 4835dc61552STiezhu Yang emit_insn(ctx, addd, dst, dst, src); 4845dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4855dc61552STiezhu Yang break; 4865dc61552STiezhu Yang 4875dc61552STiezhu Yang /* dst = dst + imm */ 4885dc61552STiezhu Yang case BPF_ALU | BPF_ADD | BPF_K: 4895dc61552STiezhu Yang case BPF_ALU64 | BPF_ADD | BPF_K: 4905dc61552STiezhu Yang if (is_signed_imm12(imm)) { 4915dc61552STiezhu Yang emit_insn(ctx, addid, dst, dst, imm); 4925dc61552STiezhu Yang } else { 4935dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 4945dc61552STiezhu Yang emit_insn(ctx, addd, dst, dst, t1); 4955dc61552STiezhu Yang } 4965dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 4975dc61552STiezhu Yang break; 4985dc61552STiezhu Yang 4995dc61552STiezhu Yang /* dst = dst - src */ 5005dc61552STiezhu Yang case BPF_ALU | BPF_SUB | BPF_X: 5015dc61552STiezhu Yang case BPF_ALU64 | BPF_SUB | BPF_X: 5025dc61552STiezhu Yang emit_insn(ctx, subd, dst, dst, src); 5035dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5045dc61552STiezhu Yang break; 5055dc61552STiezhu Yang 5065dc61552STiezhu Yang /* dst = dst - imm */ 5075dc61552STiezhu Yang case BPF_ALU | BPF_SUB | BPF_K: 5085dc61552STiezhu Yang case BPF_ALU64 | BPF_SUB | BPF_K: 5095dc61552STiezhu Yang if (is_signed_imm12(-imm)) { 5105dc61552STiezhu Yang emit_insn(ctx, addid, dst, dst, -imm); 5115dc61552STiezhu Yang } else { 5125dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5135dc61552STiezhu Yang emit_insn(ctx, subd, dst, dst, t1); 5145dc61552STiezhu Yang } 5155dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5165dc61552STiezhu Yang break; 5175dc61552STiezhu Yang 5185dc61552STiezhu Yang /* dst = dst * src */ 5195dc61552STiezhu Yang case BPF_ALU | BPF_MUL | BPF_X: 5205dc61552STiezhu Yang case BPF_ALU64 | BPF_MUL | BPF_X: 5215dc61552STiezhu Yang emit_insn(ctx, muld, dst, dst, src); 5225dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5235dc61552STiezhu Yang break; 5245dc61552STiezhu Yang 5255dc61552STiezhu Yang /* dst = dst * imm */ 5265dc61552STiezhu Yang case BPF_ALU | BPF_MUL | BPF_K: 5275dc61552STiezhu Yang case BPF_ALU64 | BPF_MUL | BPF_K: 5285dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5295dc61552STiezhu Yang emit_insn(ctx, muld, dst, dst, t1); 5305dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5315dc61552STiezhu Yang break; 5325dc61552STiezhu Yang 5335dc61552STiezhu Yang /* dst = dst / src */ 5345dc61552STiezhu Yang case BPF_ALU | BPF_DIV | BPF_X: 5355dc61552STiezhu Yang case BPF_ALU64 | BPF_DIV | BPF_X: 5365dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5375dc61552STiezhu Yang move_reg(ctx, t1, src); 5385dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 5395dc61552STiezhu Yang emit_insn(ctx, divdu, dst, dst, t1); 5405dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5415dc61552STiezhu Yang break; 5425dc61552STiezhu Yang 5435dc61552STiezhu Yang /* dst = dst / imm */ 5445dc61552STiezhu Yang case BPF_ALU | BPF_DIV | BPF_K: 5455dc61552STiezhu Yang case BPF_ALU64 | BPF_DIV | BPF_K: 5465dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5475dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5485dc61552STiezhu Yang emit_insn(ctx, divdu, dst, dst, t1); 5495dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5505dc61552STiezhu Yang break; 5515dc61552STiezhu Yang 5525dc61552STiezhu Yang /* dst = dst % src */ 5535dc61552STiezhu Yang case BPF_ALU | BPF_MOD | BPF_X: 5545dc61552STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_X: 5555dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5565dc61552STiezhu Yang move_reg(ctx, t1, src); 5575dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 5585dc61552STiezhu Yang emit_insn(ctx, moddu, dst, dst, t1); 5595dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5605dc61552STiezhu Yang break; 5615dc61552STiezhu Yang 5625dc61552STiezhu Yang /* dst = dst % imm */ 5635dc61552STiezhu Yang case BPF_ALU | BPF_MOD | BPF_K: 5645dc61552STiezhu Yang case BPF_ALU64 | BPF_MOD | BPF_K: 5655dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5665dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5675dc61552STiezhu Yang emit_insn(ctx, moddu, dst, dst, t1); 5685dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5695dc61552STiezhu Yang break; 5705dc61552STiezhu Yang 5715dc61552STiezhu Yang /* dst = -dst */ 5725dc61552STiezhu Yang case BPF_ALU | BPF_NEG: 5735dc61552STiezhu Yang case BPF_ALU64 | BPF_NEG: 5745dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5755dc61552STiezhu Yang emit_insn(ctx, subd, dst, LOONGARCH_GPR_ZERO, dst); 5765dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5775dc61552STiezhu Yang break; 5785dc61552STiezhu Yang 5795dc61552STiezhu Yang /* dst = dst & src */ 5805dc61552STiezhu Yang case BPF_ALU | BPF_AND | BPF_X: 5815dc61552STiezhu Yang case BPF_ALU64 | BPF_AND | BPF_X: 5825dc61552STiezhu Yang emit_insn(ctx, and, dst, dst, src); 5835dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5845dc61552STiezhu Yang break; 5855dc61552STiezhu Yang 5865dc61552STiezhu Yang /* dst = dst & imm */ 5875dc61552STiezhu Yang case BPF_ALU | BPF_AND | BPF_K: 5885dc61552STiezhu Yang case BPF_ALU64 | BPF_AND | BPF_K: 5895dc61552STiezhu Yang if (is_unsigned_imm12(imm)) { 5905dc61552STiezhu Yang emit_insn(ctx, andi, dst, dst, imm); 5915dc61552STiezhu Yang } else { 5925dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 5935dc61552STiezhu Yang emit_insn(ctx, and, dst, dst, t1); 5945dc61552STiezhu Yang } 5955dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 5965dc61552STiezhu Yang break; 5975dc61552STiezhu Yang 5985dc61552STiezhu Yang /* dst = dst | src */ 5995dc61552STiezhu Yang case BPF_ALU | BPF_OR | BPF_X: 6005dc61552STiezhu Yang case BPF_ALU64 | BPF_OR | BPF_X: 6015dc61552STiezhu Yang emit_insn(ctx, or, dst, dst, src); 6025dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6035dc61552STiezhu Yang break; 6045dc61552STiezhu Yang 6055dc61552STiezhu Yang /* dst = dst | imm */ 6065dc61552STiezhu Yang case BPF_ALU | BPF_OR | BPF_K: 6075dc61552STiezhu Yang case BPF_ALU64 | BPF_OR | BPF_K: 6085dc61552STiezhu Yang if (is_unsigned_imm12(imm)) { 6095dc61552STiezhu Yang emit_insn(ctx, ori, dst, dst, imm); 6105dc61552STiezhu Yang } else { 6115dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 6125dc61552STiezhu Yang emit_insn(ctx, or, dst, dst, t1); 6135dc61552STiezhu Yang } 6145dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6155dc61552STiezhu Yang break; 6165dc61552STiezhu Yang 6175dc61552STiezhu Yang /* dst = dst ^ src */ 6185dc61552STiezhu Yang case BPF_ALU | BPF_XOR | BPF_X: 6195dc61552STiezhu Yang case BPF_ALU64 | BPF_XOR | BPF_X: 6205dc61552STiezhu Yang emit_insn(ctx, xor, dst, dst, src); 6215dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6225dc61552STiezhu Yang break; 6235dc61552STiezhu Yang 6245dc61552STiezhu Yang /* dst = dst ^ imm */ 6255dc61552STiezhu Yang case BPF_ALU | BPF_XOR | BPF_K: 6265dc61552STiezhu Yang case BPF_ALU64 | BPF_XOR | BPF_K: 6275dc61552STiezhu Yang if (is_unsigned_imm12(imm)) { 6285dc61552STiezhu Yang emit_insn(ctx, xori, dst, dst, imm); 6295dc61552STiezhu Yang } else { 6305dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 6315dc61552STiezhu Yang emit_insn(ctx, xor, dst, dst, t1); 6325dc61552STiezhu Yang } 6335dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6345dc61552STiezhu Yang break; 6355dc61552STiezhu Yang 6365dc61552STiezhu Yang /* dst = dst << src (logical) */ 6375dc61552STiezhu Yang case BPF_ALU | BPF_LSH | BPF_X: 6385dc61552STiezhu Yang emit_insn(ctx, sllw, dst, dst, src); 6395dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6405dc61552STiezhu Yang break; 6415dc61552STiezhu Yang 6425dc61552STiezhu Yang case BPF_ALU64 | BPF_LSH | BPF_X: 6435dc61552STiezhu Yang emit_insn(ctx, slld, dst, dst, src); 6445dc61552STiezhu Yang break; 6455dc61552STiezhu Yang 6465dc61552STiezhu Yang /* dst = dst << imm (logical) */ 6475dc61552STiezhu Yang case BPF_ALU | BPF_LSH | BPF_K: 6485dc61552STiezhu Yang emit_insn(ctx, slliw, dst, dst, imm); 6495dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6505dc61552STiezhu Yang break; 6515dc61552STiezhu Yang 6525dc61552STiezhu Yang case BPF_ALU64 | BPF_LSH | BPF_K: 6535dc61552STiezhu Yang emit_insn(ctx, sllid, dst, dst, imm); 6545dc61552STiezhu Yang break; 6555dc61552STiezhu Yang 6565dc61552STiezhu Yang /* dst = dst >> src (logical) */ 6575dc61552STiezhu Yang case BPF_ALU | BPF_RSH | BPF_X: 6585dc61552STiezhu Yang emit_insn(ctx, srlw, dst, dst, src); 6595dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6605dc61552STiezhu Yang break; 6615dc61552STiezhu Yang 6625dc61552STiezhu Yang case BPF_ALU64 | BPF_RSH | BPF_X: 6635dc61552STiezhu Yang emit_insn(ctx, srld, dst, dst, src); 6645dc61552STiezhu Yang break; 6655dc61552STiezhu Yang 6665dc61552STiezhu Yang /* dst = dst >> imm (logical) */ 6675dc61552STiezhu Yang case BPF_ALU | BPF_RSH | BPF_K: 6685dc61552STiezhu Yang emit_insn(ctx, srliw, dst, dst, imm); 6695dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6705dc61552STiezhu Yang break; 6715dc61552STiezhu Yang 6725dc61552STiezhu Yang case BPF_ALU64 | BPF_RSH | BPF_K: 6735dc61552STiezhu Yang emit_insn(ctx, srlid, dst, dst, imm); 6745dc61552STiezhu Yang break; 6755dc61552STiezhu Yang 6765dc61552STiezhu Yang /* dst = dst >> src (arithmetic) */ 6775dc61552STiezhu Yang case BPF_ALU | BPF_ARSH | BPF_X: 6785dc61552STiezhu Yang emit_insn(ctx, sraw, dst, dst, src); 6795dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6805dc61552STiezhu Yang break; 6815dc61552STiezhu Yang 6825dc61552STiezhu Yang case BPF_ALU64 | BPF_ARSH | BPF_X: 6835dc61552STiezhu Yang emit_insn(ctx, srad, dst, dst, src); 6845dc61552STiezhu Yang break; 6855dc61552STiezhu Yang 6865dc61552STiezhu Yang /* dst = dst >> imm (arithmetic) */ 6875dc61552STiezhu Yang case BPF_ALU | BPF_ARSH | BPF_K: 6885dc61552STiezhu Yang emit_insn(ctx, sraiw, dst, dst, imm); 6895dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 6905dc61552STiezhu Yang break; 6915dc61552STiezhu Yang 6925dc61552STiezhu Yang case BPF_ALU64 | BPF_ARSH | BPF_K: 6935dc61552STiezhu Yang emit_insn(ctx, sraid, dst, dst, imm); 6945dc61552STiezhu Yang break; 6955dc61552STiezhu Yang 6965dc61552STiezhu Yang /* dst = BSWAP##imm(dst) */ 6975dc61552STiezhu Yang case BPF_ALU | BPF_END | BPF_FROM_LE: 6985dc61552STiezhu Yang switch (imm) { 6995dc61552STiezhu Yang case 16: 7005dc61552STiezhu Yang /* zero-extend 16 bits into 64 bits */ 7015dc61552STiezhu Yang emit_insn(ctx, bstrpickd, dst, dst, 15, 0); 7025dc61552STiezhu Yang break; 7035dc61552STiezhu Yang case 32: 7045dc61552STiezhu Yang /* zero-extend 32 bits into 64 bits */ 7055dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 7065dc61552STiezhu Yang break; 7075dc61552STiezhu Yang case 64: 7085dc61552STiezhu Yang /* do nothing */ 7095dc61552STiezhu Yang break; 7105dc61552STiezhu Yang } 7115dc61552STiezhu Yang break; 7125dc61552STiezhu Yang 7135dc61552STiezhu Yang case BPF_ALU | BPF_END | BPF_FROM_BE: 7145dc61552STiezhu Yang switch (imm) { 7155dc61552STiezhu Yang case 16: 7165dc61552STiezhu Yang emit_insn(ctx, revb2h, dst, dst); 7175dc61552STiezhu Yang /* zero-extend 16 bits into 64 bits */ 7185dc61552STiezhu Yang emit_insn(ctx, bstrpickd, dst, dst, 15, 0); 7195dc61552STiezhu Yang break; 7205dc61552STiezhu Yang case 32: 7215dc61552STiezhu Yang emit_insn(ctx, revb2w, dst, dst); 7225dc61552STiezhu Yang /* zero-extend 32 bits into 64 bits */ 7235dc61552STiezhu Yang emit_zext_32(ctx, dst, is32); 7245dc61552STiezhu Yang break; 7255dc61552STiezhu Yang case 64: 7265dc61552STiezhu Yang emit_insn(ctx, revbd, dst, dst); 7275dc61552STiezhu Yang break; 7285dc61552STiezhu Yang } 7295dc61552STiezhu Yang break; 7305dc61552STiezhu Yang 7315dc61552STiezhu Yang /* PC += off if dst cond src */ 7325dc61552STiezhu Yang case BPF_JMP | BPF_JEQ | BPF_X: 7335dc61552STiezhu Yang case BPF_JMP | BPF_JNE | BPF_X: 7345dc61552STiezhu Yang case BPF_JMP | BPF_JGT | BPF_X: 7355dc61552STiezhu Yang case BPF_JMP | BPF_JGE | BPF_X: 7365dc61552STiezhu Yang case BPF_JMP | BPF_JLT | BPF_X: 7375dc61552STiezhu Yang case BPF_JMP | BPF_JLE | BPF_X: 7385dc61552STiezhu Yang case BPF_JMP | BPF_JSGT | BPF_X: 7395dc61552STiezhu Yang case BPF_JMP | BPF_JSGE | BPF_X: 7405dc61552STiezhu Yang case BPF_JMP | BPF_JSLT | BPF_X: 7415dc61552STiezhu Yang case BPF_JMP | BPF_JSLE | BPF_X: 7425dc61552STiezhu Yang case BPF_JMP32 | BPF_JEQ | BPF_X: 7435dc61552STiezhu Yang case BPF_JMP32 | BPF_JNE | BPF_X: 7445dc61552STiezhu Yang case BPF_JMP32 | BPF_JGT | BPF_X: 7455dc61552STiezhu Yang case BPF_JMP32 | BPF_JGE | BPF_X: 7465dc61552STiezhu Yang case BPF_JMP32 | BPF_JLT | BPF_X: 7475dc61552STiezhu Yang case BPF_JMP32 | BPF_JLE | BPF_X: 7485dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGT | BPF_X: 7495dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGE | BPF_X: 7505dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLT | BPF_X: 7515dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLE | BPF_X: 7525dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 7535dc61552STiezhu Yang move_reg(ctx, t1, dst); 7545dc61552STiezhu Yang move_reg(ctx, t2, src); 7555dc61552STiezhu Yang if (is_signed_bpf_cond(BPF_OP(code))) { 7565dc61552STiezhu Yang emit_sext_32(ctx, t1, is32); 7575dc61552STiezhu Yang emit_sext_32(ctx, t2, is32); 7585dc61552STiezhu Yang } else { 7595dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 7605dc61552STiezhu Yang emit_zext_32(ctx, t2, is32); 7615dc61552STiezhu Yang } 7625dc61552STiezhu Yang if (emit_cond_jmp(ctx, cond, t1, t2, jmp_offset) < 0) 7635dc61552STiezhu Yang goto toofar; 7645dc61552STiezhu Yang break; 7655dc61552STiezhu Yang 7665dc61552STiezhu Yang /* PC += off if dst cond imm */ 7675dc61552STiezhu Yang case BPF_JMP | BPF_JEQ | BPF_K: 7685dc61552STiezhu Yang case BPF_JMP | BPF_JNE | BPF_K: 7695dc61552STiezhu Yang case BPF_JMP | BPF_JGT | BPF_K: 7705dc61552STiezhu Yang case BPF_JMP | BPF_JGE | BPF_K: 7715dc61552STiezhu Yang case BPF_JMP | BPF_JLT | BPF_K: 7725dc61552STiezhu Yang case BPF_JMP | BPF_JLE | BPF_K: 7735dc61552STiezhu Yang case BPF_JMP | BPF_JSGT | BPF_K: 7745dc61552STiezhu Yang case BPF_JMP | BPF_JSGE | BPF_K: 7755dc61552STiezhu Yang case BPF_JMP | BPF_JSLT | BPF_K: 7765dc61552STiezhu Yang case BPF_JMP | BPF_JSLE | BPF_K: 7775dc61552STiezhu Yang case BPF_JMP32 | BPF_JEQ | BPF_K: 7785dc61552STiezhu Yang case BPF_JMP32 | BPF_JNE | BPF_K: 7795dc61552STiezhu Yang case BPF_JMP32 | BPF_JGT | BPF_K: 7805dc61552STiezhu Yang case BPF_JMP32 | BPF_JGE | BPF_K: 7815dc61552STiezhu Yang case BPF_JMP32 | BPF_JLT | BPF_K: 7825dc61552STiezhu Yang case BPF_JMP32 | BPF_JLE | BPF_K: 7835dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGT | BPF_K: 7845dc61552STiezhu Yang case BPF_JMP32 | BPF_JSGE | BPF_K: 7855dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLT | BPF_K: 7865dc61552STiezhu Yang case BPF_JMP32 | BPF_JSLE | BPF_K: 7875dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 7885dc61552STiezhu Yang if (imm) { 7895dc61552STiezhu Yang move_imm(ctx, t1, imm, false); 790bbfddb90SHuacai Chen tm = t1; 7915dc61552STiezhu Yang } else { 7925dc61552STiezhu Yang /* If imm is 0, simply use zero register. */ 793bbfddb90SHuacai Chen tm = LOONGARCH_GPR_ZERO; 7945dc61552STiezhu Yang } 7955dc61552STiezhu Yang move_reg(ctx, t2, dst); 7965dc61552STiezhu Yang if (is_signed_bpf_cond(BPF_OP(code))) { 797bbfddb90SHuacai Chen emit_sext_32(ctx, tm, is32); 7985dc61552STiezhu Yang emit_sext_32(ctx, t2, is32); 7995dc61552STiezhu Yang } else { 800bbfddb90SHuacai Chen emit_zext_32(ctx, tm, is32); 8015dc61552STiezhu Yang emit_zext_32(ctx, t2, is32); 8025dc61552STiezhu Yang } 803bbfddb90SHuacai Chen if (emit_cond_jmp(ctx, cond, t2, tm, jmp_offset) < 0) 8045dc61552STiezhu Yang goto toofar; 8055dc61552STiezhu Yang break; 8065dc61552STiezhu Yang 8075dc61552STiezhu Yang /* PC += off if dst & src */ 8085dc61552STiezhu Yang case BPF_JMP | BPF_JSET | BPF_X: 8095dc61552STiezhu Yang case BPF_JMP32 | BPF_JSET | BPF_X: 8105dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 8115dc61552STiezhu Yang emit_insn(ctx, and, t1, dst, src); 8125dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 8135dc61552STiezhu Yang if (emit_cond_jmp(ctx, cond, t1, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 8145dc61552STiezhu Yang goto toofar; 8155dc61552STiezhu Yang break; 8165dc61552STiezhu Yang 8175dc61552STiezhu Yang /* PC += off if dst & imm */ 8185dc61552STiezhu Yang case BPF_JMP | BPF_JSET | BPF_K: 8195dc61552STiezhu Yang case BPF_JMP32 | BPF_JSET | BPF_K: 8205dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 8215dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 8225dc61552STiezhu Yang emit_insn(ctx, and, t1, dst, t1); 8235dc61552STiezhu Yang emit_zext_32(ctx, t1, is32); 8245dc61552STiezhu Yang if (emit_cond_jmp(ctx, cond, t1, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 8255dc61552STiezhu Yang goto toofar; 8265dc61552STiezhu Yang break; 8275dc61552STiezhu Yang 8285dc61552STiezhu Yang /* PC += off */ 8295dc61552STiezhu Yang case BPF_JMP | BPF_JA: 8305dc61552STiezhu Yang jmp_offset = bpf2la_offset(i, off, ctx); 8315dc61552STiezhu Yang if (emit_uncond_jmp(ctx, jmp_offset) < 0) 8325dc61552STiezhu Yang goto toofar; 8335dc61552STiezhu Yang break; 8345dc61552STiezhu Yang 8355dc61552STiezhu Yang /* function call */ 8365dc61552STiezhu Yang case BPF_JMP | BPF_CALL: 8375dc61552STiezhu Yang mark_call(ctx); 8385dc61552STiezhu Yang ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 8395dc61552STiezhu Yang &func_addr, &func_addr_fixed); 8405dc61552STiezhu Yang if (ret < 0) 8415dc61552STiezhu Yang return ret; 8425dc61552STiezhu Yang 84364f50f65SHengqi Chen move_addr(ctx, t1, func_addr); 844*4eb54230STiezhu Yang emit_insn(ctx, jirl, LOONGARCH_GPR_RA, t1, 0); 8455dc61552STiezhu Yang move_reg(ctx, regmap[BPF_REG_0], LOONGARCH_GPR_A0); 8465dc61552STiezhu Yang break; 8475dc61552STiezhu Yang 8485dc61552STiezhu Yang /* tail call */ 8495dc61552STiezhu Yang case BPF_JMP | BPF_TAIL_CALL: 8505dc61552STiezhu Yang mark_tail_call(ctx); 8515dc61552STiezhu Yang if (emit_bpf_tail_call(ctx) < 0) 8525dc61552STiezhu Yang return -EINVAL; 8535dc61552STiezhu Yang break; 8545dc61552STiezhu Yang 8555dc61552STiezhu Yang /* function return */ 8565dc61552STiezhu Yang case BPF_JMP | BPF_EXIT: 8575dc61552STiezhu Yang if (i == ctx->prog->len - 1) 8585dc61552STiezhu Yang break; 8595dc61552STiezhu Yang 8605dc61552STiezhu Yang jmp_offset = epilogue_offset(ctx); 8615dc61552STiezhu Yang if (emit_uncond_jmp(ctx, jmp_offset) < 0) 8625dc61552STiezhu Yang goto toofar; 8635dc61552STiezhu Yang break; 8645dc61552STiezhu Yang 8655dc61552STiezhu Yang /* dst = imm64 */ 8665dc61552STiezhu Yang case BPF_LD | BPF_IMM | BPF_DW: 8679aeb09f4SHengqi Chen { 8689aeb09f4SHengqi Chen const u64 imm64 = (u64)(insn + 1)->imm << 32 | (u32)insn->imm; 8699aeb09f4SHengqi Chen 8705dc61552STiezhu Yang move_imm(ctx, dst, imm64, is32); 8715dc61552STiezhu Yang return 1; 8729aeb09f4SHengqi Chen } 8735dc61552STiezhu Yang 8745dc61552STiezhu Yang /* dst = *(size *)(src + off) */ 8755dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_B: 8765dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_H: 8775dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_W: 8785dc61552STiezhu Yang case BPF_LDX | BPF_MEM | BPF_DW: 879dbcd7f5fSYouling Tang case BPF_LDX | BPF_PROBE_MEM | BPF_DW: 880dbcd7f5fSYouling Tang case BPF_LDX | BPF_PROBE_MEM | BPF_W: 881dbcd7f5fSYouling Tang case BPF_LDX | BPF_PROBE_MEM | BPF_H: 882dbcd7f5fSYouling Tang case BPF_LDX | BPF_PROBE_MEM | BPF_B: 8835dc61552STiezhu Yang switch (BPF_SIZE(code)) { 8845dc61552STiezhu Yang case BPF_B: 8855dc61552STiezhu Yang if (is_signed_imm12(off)) { 8865dc61552STiezhu Yang emit_insn(ctx, ldbu, dst, src, off); 8875dc61552STiezhu Yang } else { 8885dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 8895dc61552STiezhu Yang emit_insn(ctx, ldxbu, dst, src, t1); 8905dc61552STiezhu Yang } 8915dc61552STiezhu Yang break; 8925dc61552STiezhu Yang case BPF_H: 8935dc61552STiezhu Yang if (is_signed_imm12(off)) { 8945dc61552STiezhu Yang emit_insn(ctx, ldhu, dst, src, off); 8955dc61552STiezhu Yang } else { 8965dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 8975dc61552STiezhu Yang emit_insn(ctx, ldxhu, dst, src, t1); 8985dc61552STiezhu Yang } 8995dc61552STiezhu Yang break; 9005dc61552STiezhu Yang case BPF_W: 9015dc61552STiezhu Yang if (is_signed_imm12(off)) { 9025dc61552STiezhu Yang emit_insn(ctx, ldwu, dst, src, off); 9035dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9045dc61552STiezhu Yang emit_insn(ctx, ldptrw, dst, src, off); 9055dc61552STiezhu Yang } else { 9065dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9075dc61552STiezhu Yang emit_insn(ctx, ldxwu, dst, src, t1); 9085dc61552STiezhu Yang } 9095dc61552STiezhu Yang break; 9105dc61552STiezhu Yang case BPF_DW: 9115dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9125dc61552STiezhu Yang emit_insn(ctx, ldxd, dst, src, t1); 9135dc61552STiezhu Yang break; 9145dc61552STiezhu Yang } 915dbcd7f5fSYouling Tang 916dbcd7f5fSYouling Tang ret = add_exception_handler(insn, ctx, dst); 917dbcd7f5fSYouling Tang if (ret) 918dbcd7f5fSYouling Tang return ret; 9195dc61552STiezhu Yang break; 9205dc61552STiezhu Yang 9215dc61552STiezhu Yang /* *(size *)(dst + off) = imm */ 9225dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_B: 9235dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_H: 9245dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_W: 9255dc61552STiezhu Yang case BPF_ST | BPF_MEM | BPF_DW: 9265dc61552STiezhu Yang switch (BPF_SIZE(code)) { 9275dc61552STiezhu Yang case BPF_B: 9285dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 9295dc61552STiezhu Yang if (is_signed_imm12(off)) { 9305dc61552STiezhu Yang emit_insn(ctx, stb, t1, dst, off); 9315dc61552STiezhu Yang } else { 9325dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 9335dc61552STiezhu Yang emit_insn(ctx, stxb, t1, dst, t2); 9345dc61552STiezhu Yang } 9355dc61552STiezhu Yang break; 9365dc61552STiezhu Yang case BPF_H: 9375dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 9385dc61552STiezhu Yang if (is_signed_imm12(off)) { 9395dc61552STiezhu Yang emit_insn(ctx, sth, t1, dst, off); 9405dc61552STiezhu Yang } else { 9415dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 9425dc61552STiezhu Yang emit_insn(ctx, stxh, t1, dst, t2); 9435dc61552STiezhu Yang } 9445dc61552STiezhu Yang break; 9455dc61552STiezhu Yang case BPF_W: 9465dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 9475dc61552STiezhu Yang if (is_signed_imm12(off)) { 9485dc61552STiezhu Yang emit_insn(ctx, stw, t1, dst, off); 9495dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9505dc61552STiezhu Yang emit_insn(ctx, stptrw, t1, dst, off); 9515dc61552STiezhu Yang } else { 9525dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 9535dc61552STiezhu Yang emit_insn(ctx, stxw, t1, dst, t2); 9545dc61552STiezhu Yang } 9555dc61552STiezhu Yang break; 9565dc61552STiezhu Yang case BPF_DW: 9575dc61552STiezhu Yang move_imm(ctx, t1, imm, is32); 9585dc61552STiezhu Yang if (is_signed_imm12(off)) { 9595dc61552STiezhu Yang emit_insn(ctx, std, t1, dst, off); 9605dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9615dc61552STiezhu Yang emit_insn(ctx, stptrd, t1, dst, off); 9625dc61552STiezhu Yang } else { 9635dc61552STiezhu Yang move_imm(ctx, t2, off, is32); 9645dc61552STiezhu Yang emit_insn(ctx, stxd, t1, dst, t2); 9655dc61552STiezhu Yang } 9665dc61552STiezhu Yang break; 9675dc61552STiezhu Yang } 9685dc61552STiezhu Yang break; 9695dc61552STiezhu Yang 9705dc61552STiezhu Yang /* *(size *)(dst + off) = src */ 9715dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_B: 9725dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_H: 9735dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_W: 9745dc61552STiezhu Yang case BPF_STX | BPF_MEM | BPF_DW: 9755dc61552STiezhu Yang switch (BPF_SIZE(code)) { 9765dc61552STiezhu Yang case BPF_B: 9775dc61552STiezhu Yang if (is_signed_imm12(off)) { 9785dc61552STiezhu Yang emit_insn(ctx, stb, src, dst, off); 9795dc61552STiezhu Yang } else { 9805dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9815dc61552STiezhu Yang emit_insn(ctx, stxb, src, dst, t1); 9825dc61552STiezhu Yang } 9835dc61552STiezhu Yang break; 9845dc61552STiezhu Yang case BPF_H: 9855dc61552STiezhu Yang if (is_signed_imm12(off)) { 9865dc61552STiezhu Yang emit_insn(ctx, sth, src, dst, off); 9875dc61552STiezhu Yang } else { 9885dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9895dc61552STiezhu Yang emit_insn(ctx, stxh, src, dst, t1); 9905dc61552STiezhu Yang } 9915dc61552STiezhu Yang break; 9925dc61552STiezhu Yang case BPF_W: 9935dc61552STiezhu Yang if (is_signed_imm12(off)) { 9945dc61552STiezhu Yang emit_insn(ctx, stw, src, dst, off); 9955dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 9965dc61552STiezhu Yang emit_insn(ctx, stptrw, src, dst, off); 9975dc61552STiezhu Yang } else { 9985dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 9995dc61552STiezhu Yang emit_insn(ctx, stxw, src, dst, t1); 10005dc61552STiezhu Yang } 10015dc61552STiezhu Yang break; 10025dc61552STiezhu Yang case BPF_DW: 10035dc61552STiezhu Yang if (is_signed_imm12(off)) { 10045dc61552STiezhu Yang emit_insn(ctx, std, src, dst, off); 10055dc61552STiezhu Yang } else if (is_signed_imm14(off)) { 10065dc61552STiezhu Yang emit_insn(ctx, stptrd, src, dst, off); 10075dc61552STiezhu Yang } else { 10085dc61552STiezhu Yang move_imm(ctx, t1, off, is32); 10095dc61552STiezhu Yang emit_insn(ctx, stxd, src, dst, t1); 10105dc61552STiezhu Yang } 10115dc61552STiezhu Yang break; 10125dc61552STiezhu Yang } 10135dc61552STiezhu Yang break; 10145dc61552STiezhu Yang 10155dc61552STiezhu Yang case BPF_STX | BPF_ATOMIC | BPF_W: 10165dc61552STiezhu Yang case BPF_STX | BPF_ATOMIC | BPF_DW: 10175dc61552STiezhu Yang emit_atomic(insn, ctx); 10185dc61552STiezhu Yang break; 10195dc61552STiezhu Yang 1020a6f6a95fSGeorge Guo /* Speculation barrier */ 1021a6f6a95fSGeorge Guo case BPF_ST | BPF_NOSPEC: 1022a6f6a95fSGeorge Guo break; 1023a6f6a95fSGeorge Guo 10245dc61552STiezhu Yang default: 10255dc61552STiezhu Yang pr_err("bpf_jit: unknown opcode %02x\n", code); 10265dc61552STiezhu Yang return -EINVAL; 10275dc61552STiezhu Yang } 10285dc61552STiezhu Yang 10295dc61552STiezhu Yang return 0; 10305dc61552STiezhu Yang 10315dc61552STiezhu Yang toofar: 10325dc61552STiezhu Yang pr_info_once("bpf_jit: opcode %02x, jump too far\n", code); 10335dc61552STiezhu Yang return -E2BIG; 10345dc61552STiezhu Yang } 10355dc61552STiezhu Yang 10365dc61552STiezhu Yang static int build_body(struct jit_ctx *ctx, bool extra_pass) 10375dc61552STiezhu Yang { 10385dc61552STiezhu Yang int i; 10395dc61552STiezhu Yang const struct bpf_prog *prog = ctx->prog; 10405dc61552STiezhu Yang 10415dc61552STiezhu Yang for (i = 0; i < prog->len; i++) { 10425dc61552STiezhu Yang const struct bpf_insn *insn = &prog->insnsi[i]; 10435dc61552STiezhu Yang int ret; 10445dc61552STiezhu Yang 10455dc61552STiezhu Yang if (ctx->image == NULL) 10465dc61552STiezhu Yang ctx->offset[i] = ctx->idx; 10475dc61552STiezhu Yang 10485dc61552STiezhu Yang ret = build_insn(insn, ctx, extra_pass); 10495dc61552STiezhu Yang if (ret > 0) { 10505dc61552STiezhu Yang i++; 10515dc61552STiezhu Yang if (ctx->image == NULL) 10525dc61552STiezhu Yang ctx->offset[i] = ctx->idx; 10535dc61552STiezhu Yang continue; 10545dc61552STiezhu Yang } 10555dc61552STiezhu Yang if (ret) 10565dc61552STiezhu Yang return ret; 10575dc61552STiezhu Yang } 10585dc61552STiezhu Yang 10595dc61552STiezhu Yang if (ctx->image == NULL) 10605dc61552STiezhu Yang ctx->offset[i] = ctx->idx; 10615dc61552STiezhu Yang 10625dc61552STiezhu Yang return 0; 10635dc61552STiezhu Yang } 10645dc61552STiezhu Yang 10655dc61552STiezhu Yang /* Fill space with break instructions */ 10665dc61552STiezhu Yang static void jit_fill_hole(void *area, unsigned int size) 10675dc61552STiezhu Yang { 10685dc61552STiezhu Yang u32 *ptr; 10695dc61552STiezhu Yang 10705dc61552STiezhu Yang /* We are guaranteed to have aligned memory */ 10715dc61552STiezhu Yang for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 10725dc61552STiezhu Yang *ptr++ = INSN_BREAK; 10735dc61552STiezhu Yang } 10745dc61552STiezhu Yang 10755dc61552STiezhu Yang static int validate_code(struct jit_ctx *ctx) 10765dc61552STiezhu Yang { 10775dc61552STiezhu Yang int i; 10785dc61552STiezhu Yang union loongarch_instruction insn; 10795dc61552STiezhu Yang 10805dc61552STiezhu Yang for (i = 0; i < ctx->idx; i++) { 10815dc61552STiezhu Yang insn = ctx->image[i]; 10825dc61552STiezhu Yang /* Check INSN_BREAK */ 10835dc61552STiezhu Yang if (insn.word == INSN_BREAK) 10845dc61552STiezhu Yang return -1; 10855dc61552STiezhu Yang } 10865dc61552STiezhu Yang 1087dbcd7f5fSYouling Tang if (WARN_ON_ONCE(ctx->num_exentries != ctx->prog->aux->num_exentries)) 1088dbcd7f5fSYouling Tang return -1; 1089dbcd7f5fSYouling Tang 10905dc61552STiezhu Yang return 0; 10915dc61552STiezhu Yang } 10925dc61552STiezhu Yang 10935dc61552STiezhu Yang struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 10945dc61552STiezhu Yang { 10955dc61552STiezhu Yang bool tmp_blinded = false, extra_pass = false; 10965dc61552STiezhu Yang u8 *image_ptr; 1097dbcd7f5fSYouling Tang int image_size, prog_size, extable_size; 10985dc61552STiezhu Yang struct jit_ctx ctx; 10995dc61552STiezhu Yang struct jit_data *jit_data; 11005dc61552STiezhu Yang struct bpf_binary_header *header; 11015dc61552STiezhu Yang struct bpf_prog *tmp, *orig_prog = prog; 11025dc61552STiezhu Yang 11035dc61552STiezhu Yang /* 11045dc61552STiezhu Yang * If BPF JIT was not enabled then we must fall back to 11055dc61552STiezhu Yang * the interpreter. 11065dc61552STiezhu Yang */ 11075dc61552STiezhu Yang if (!prog->jit_requested) 11085dc61552STiezhu Yang return orig_prog; 11095dc61552STiezhu Yang 11105dc61552STiezhu Yang tmp = bpf_jit_blind_constants(prog); 11115dc61552STiezhu Yang /* 11125dc61552STiezhu Yang * If blinding was requested and we failed during blinding, 11135dc61552STiezhu Yang * we must fall back to the interpreter. Otherwise, we save 11145dc61552STiezhu Yang * the new JITed code. 11155dc61552STiezhu Yang */ 11165dc61552STiezhu Yang if (IS_ERR(tmp)) 11175dc61552STiezhu Yang return orig_prog; 11185dc61552STiezhu Yang 11195dc61552STiezhu Yang if (tmp != prog) { 11205dc61552STiezhu Yang tmp_blinded = true; 11215dc61552STiezhu Yang prog = tmp; 11225dc61552STiezhu Yang } 11235dc61552STiezhu Yang 11245dc61552STiezhu Yang jit_data = prog->aux->jit_data; 11255dc61552STiezhu Yang if (!jit_data) { 11265dc61552STiezhu Yang jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 11275dc61552STiezhu Yang if (!jit_data) { 11285dc61552STiezhu Yang prog = orig_prog; 11295dc61552STiezhu Yang goto out; 11305dc61552STiezhu Yang } 11315dc61552STiezhu Yang prog->aux->jit_data = jit_data; 11325dc61552STiezhu Yang } 11335dc61552STiezhu Yang if (jit_data->ctx.offset) { 11345dc61552STiezhu Yang ctx = jit_data->ctx; 11355dc61552STiezhu Yang image_ptr = jit_data->image; 11365dc61552STiezhu Yang header = jit_data->header; 11375dc61552STiezhu Yang extra_pass = true; 1138dbcd7f5fSYouling Tang prog_size = sizeof(u32) * ctx.idx; 11395dc61552STiezhu Yang goto skip_init_ctx; 11405dc61552STiezhu Yang } 11415dc61552STiezhu Yang 11425dc61552STiezhu Yang memset(&ctx, 0, sizeof(ctx)); 11435dc61552STiezhu Yang ctx.prog = prog; 11445dc61552STiezhu Yang 11455dc61552STiezhu Yang ctx.offset = kvcalloc(prog->len + 1, sizeof(u32), GFP_KERNEL); 11465dc61552STiezhu Yang if (ctx.offset == NULL) { 11475dc61552STiezhu Yang prog = orig_prog; 11485dc61552STiezhu Yang goto out_offset; 11495dc61552STiezhu Yang } 11505dc61552STiezhu Yang 11515dc61552STiezhu Yang /* 1. Initial fake pass to compute ctx->idx and set ctx->flags */ 11525dc61552STiezhu Yang build_prologue(&ctx); 11535dc61552STiezhu Yang if (build_body(&ctx, extra_pass)) { 11545dc61552STiezhu Yang prog = orig_prog; 11555dc61552STiezhu Yang goto out_offset; 11565dc61552STiezhu Yang } 11575dc61552STiezhu Yang ctx.epilogue_offset = ctx.idx; 11585dc61552STiezhu Yang build_epilogue(&ctx); 11595dc61552STiezhu Yang 1160dbcd7f5fSYouling Tang extable_size = prog->aux->num_exentries * sizeof(struct exception_table_entry); 1161dbcd7f5fSYouling Tang 11625dc61552STiezhu Yang /* Now we know the actual image size. 11635dc61552STiezhu Yang * As each LoongArch instruction is of length 32bit, 11645dc61552STiezhu Yang * we are translating number of JITed intructions into 11655dc61552STiezhu Yang * the size required to store these JITed code. 11665dc61552STiezhu Yang */ 1167dbcd7f5fSYouling Tang prog_size = sizeof(u32) * ctx.idx; 1168dbcd7f5fSYouling Tang image_size = prog_size + extable_size; 11695dc61552STiezhu Yang /* Now we know the size of the structure to make */ 11705dc61552STiezhu Yang header = bpf_jit_binary_alloc(image_size, &image_ptr, 11715dc61552STiezhu Yang sizeof(u32), jit_fill_hole); 11725dc61552STiezhu Yang if (header == NULL) { 11735dc61552STiezhu Yang prog = orig_prog; 11745dc61552STiezhu Yang goto out_offset; 11755dc61552STiezhu Yang } 11765dc61552STiezhu Yang 11775dc61552STiezhu Yang /* 2. Now, the actual pass to generate final JIT code */ 11785dc61552STiezhu Yang ctx.image = (union loongarch_instruction *)image_ptr; 1179dbcd7f5fSYouling Tang if (extable_size) 1180dbcd7f5fSYouling Tang prog->aux->extable = (void *)image_ptr + prog_size; 11815dc61552STiezhu Yang 11825dc61552STiezhu Yang skip_init_ctx: 11835dc61552STiezhu Yang ctx.idx = 0; 1184dbcd7f5fSYouling Tang ctx.num_exentries = 0; 11855dc61552STiezhu Yang 11865dc61552STiezhu Yang build_prologue(&ctx); 11875dc61552STiezhu Yang if (build_body(&ctx, extra_pass)) { 11885dc61552STiezhu Yang bpf_jit_binary_free(header); 11895dc61552STiezhu Yang prog = orig_prog; 11905dc61552STiezhu Yang goto out_offset; 11915dc61552STiezhu Yang } 11925dc61552STiezhu Yang build_epilogue(&ctx); 11935dc61552STiezhu Yang 11945dc61552STiezhu Yang /* 3. Extra pass to validate JITed code */ 11955dc61552STiezhu Yang if (validate_code(&ctx)) { 11965dc61552STiezhu Yang bpf_jit_binary_free(header); 11975dc61552STiezhu Yang prog = orig_prog; 11985dc61552STiezhu Yang goto out_offset; 11995dc61552STiezhu Yang } 12005dc61552STiezhu Yang 12015dc61552STiezhu Yang /* And we're done */ 12025dc61552STiezhu Yang if (bpf_jit_enable > 1) 1203dbcd7f5fSYouling Tang bpf_jit_dump(prog->len, prog_size, 2, ctx.image); 12045dc61552STiezhu Yang 12055dc61552STiezhu Yang /* Update the icache */ 12065dc61552STiezhu Yang flush_icache_range((unsigned long)header, (unsigned long)(ctx.image + ctx.idx)); 12075dc61552STiezhu Yang 12085dc61552STiezhu Yang if (!prog->is_func || extra_pass) { 12095dc61552STiezhu Yang if (extra_pass && ctx.idx != jit_data->ctx.idx) { 12105dc61552STiezhu Yang pr_err_once("multi-func JIT bug %d != %d\n", 12115dc61552STiezhu Yang ctx.idx, jit_data->ctx.idx); 12129fef36caSGreg Kroah-Hartman bpf_jit_binary_free(header); 12139fef36caSGreg Kroah-Hartman prog->bpf_func = NULL; 12149fef36caSGreg Kroah-Hartman prog->jited = 0; 12159fef36caSGreg Kroah-Hartman prog->jited_len = 0; 12169fef36caSGreg Kroah-Hartman goto out_offset; 12175dc61552STiezhu Yang } 12189fef36caSGreg Kroah-Hartman bpf_jit_binary_lock_ro(header); 12195dc61552STiezhu Yang } else { 12205dc61552STiezhu Yang jit_data->ctx = ctx; 12215dc61552STiezhu Yang jit_data->image = image_ptr; 12225dc61552STiezhu Yang jit_data->header = header; 12235dc61552STiezhu Yang } 12245dc61552STiezhu Yang prog->jited = 1; 1225dbcd7f5fSYouling Tang prog->jited_len = prog_size; 12265dc61552STiezhu Yang prog->bpf_func = (void *)ctx.image; 12275dc61552STiezhu Yang 12285dc61552STiezhu Yang if (!prog->is_func || extra_pass) { 12295dc61552STiezhu Yang int i; 12305dc61552STiezhu Yang 12315dc61552STiezhu Yang /* offset[prog->len] is the size of program */ 12325dc61552STiezhu Yang for (i = 0; i <= prog->len; i++) 12335dc61552STiezhu Yang ctx.offset[i] *= LOONGARCH_INSN_SIZE; 12345dc61552STiezhu Yang bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 12355dc61552STiezhu Yang 12365dc61552STiezhu Yang out_offset: 12375dc61552STiezhu Yang kvfree(ctx.offset); 12385dc61552STiezhu Yang kfree(jit_data); 12395dc61552STiezhu Yang prog->aux->jit_data = NULL; 12405dc61552STiezhu Yang } 12415dc61552STiezhu Yang 12425dc61552STiezhu Yang out: 12435dc61552STiezhu Yang if (tmp_blinded) 12445dc61552STiezhu Yang bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog); 12455dc61552STiezhu Yang 12465dc61552STiezhu Yang out_offset = -1; 12475dc61552STiezhu Yang 12485dc61552STiezhu Yang return prog; 12495dc61552STiezhu Yang } 1250bb035ef0SHengqi Chen 1251bb035ef0SHengqi Chen /* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */ 1252bb035ef0SHengqi Chen bool bpf_jit_supports_subprog_tailcalls(void) 1253bb035ef0SHengqi Chen { 1254bb035ef0SHengqi Chen return true; 1255bb035ef0SHengqi Chen } 1256