xref: /openbmc/linux/arch/loongarch/net/bpf_jit.c (revision 9aeb09f4)
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 */
1825dc61552STiezhu Yang 		move_reg(ctx, LOONGARCH_GPR_A0, regmap[BPF_REG_0]);
1835dc61552STiezhu Yang 		/* Return to the caller */
1845dc61552STiezhu Yang 		emit_insn(ctx, jirl, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);
1855dc61552STiezhu Yang 	} else {
1865dc61552STiezhu Yang 		/*
1875dc61552STiezhu Yang 		 * Call the next bpf prog and skip the first instruction
1885dc61552STiezhu Yang 		 * of TCC initialization.
1895dc61552STiezhu Yang 		 */
1905dc61552STiezhu Yang 		emit_insn(ctx, jirl, LOONGARCH_GPR_T3, LOONGARCH_GPR_ZERO, 1);
1915dc61552STiezhu Yang 	}
1925dc61552STiezhu Yang }
1935dc61552STiezhu Yang 
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);
8445dc61552STiezhu Yang 		emit_insn(ctx, jirl, t1, LOONGARCH_GPR_RA, 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:
867*9aeb09f4SHengqi Chen 	{
868*9aeb09f4SHengqi Chen 		const u64 imm64 = (u64)(insn + 1)->imm << 32 | (u32)insn->imm;
869*9aeb09f4SHengqi Chen 
8705dc61552STiezhu Yang 		move_imm(ctx, dst, imm64, is32);
8715dc61552STiezhu Yang 		return 1;
872*9aeb09f4SHengqi 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);
12125dc61552STiezhu Yang 			bpf_jit_binary_free(header);
12135dc61552STiezhu Yang 			prog->bpf_func = NULL;
12145dc61552STiezhu Yang 			prog->jited = 0;
12155dc61552STiezhu Yang 			prog->jited_len = 0;
12165dc61552STiezhu Yang 			goto out_offset;
12175dc61552STiezhu Yang 		}
12185dc61552STiezhu Yang 		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