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
mark_call(struct jit_ctx * ctx)355dc61552STiezhu Yang static void mark_call(struct jit_ctx *ctx)
365dc61552STiezhu Yang {
375dc61552STiezhu Yang ctx->flags |= SAVE_RA;
385dc61552STiezhu Yang }
395dc61552STiezhu Yang
mark_tail_call(struct jit_ctx * ctx)405dc61552STiezhu Yang static void mark_tail_call(struct jit_ctx *ctx)
415dc61552STiezhu Yang {
425dc61552STiezhu Yang ctx->flags |= SAVE_TCC;
435dc61552STiezhu Yang }
445dc61552STiezhu Yang
seen_call(struct jit_ctx * ctx)455dc61552STiezhu Yang static bool seen_call(struct jit_ctx *ctx)
465dc61552STiezhu Yang {
475dc61552STiezhu Yang return (ctx->flags & SAVE_RA);
485dc61552STiezhu Yang }
495dc61552STiezhu Yang
seen_tail_call(struct jit_ctx * ctx)505dc61552STiezhu Yang static bool seen_tail_call(struct jit_ctx *ctx)
515dc61552STiezhu Yang {
525dc61552STiezhu Yang return (ctx->flags & SAVE_TCC);
535dc61552STiezhu Yang }
545dc61552STiezhu Yang
tail_call_reg(struct jit_ctx * ctx)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 */
build_prologue(struct jit_ctx * ctx)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
__build_epilogue(struct jit_ctx * ctx,bool is_tail_call)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
build_epilogue(struct jit_ctx * ctx)1945dc61552STiezhu Yang static void build_epilogue(struct jit_ctx *ctx)
1955dc61552STiezhu Yang {
1965dc61552STiezhu Yang __build_epilogue(ctx, false);
1975dc61552STiezhu Yang }
1985dc61552STiezhu Yang
bpf_jit_supports_kfunc_call(void)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;
emit_bpf_tail_call(struct jit_ctx * ctx)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
emit_atomic(const struct bpf_insn * insn,struct jit_ctx * ctx)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
is_signed_bpf_cond(u8 cond)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
ex_handler_bpf(const struct exception_table_entry * ex,struct pt_regs * regs)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 */
add_exception_handler(const struct bpf_insn * insn,struct jit_ctx * ctx,int dst_reg)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
build_insn(const struct bpf_insn * insn,struct jit_ctx * ctx,bool extra_pass)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
build_body(struct jit_ctx * ctx,bool extra_pass)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 */
jit_fill_hole(void * area,unsigned int size)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
validate_code(struct jit_ctx * ctx)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
bpf_int_jit_compile(struct bpf_prog * prog)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. */
bpf_jit_supports_subprog_tailcalls(void)1252bb035ef0SHengqi Chen bool bpf_jit_supports_subprog_tailcalls(void)
1253bb035ef0SHengqi Chen {
1254bb035ef0SHengqi Chen return true;
1255bb035ef0SHengqi Chen }
1256