xref: /openbmc/linux/arch/riscv/net/bpf_jit_comp64.c (revision 7112cd26)
1ca6cb544SLuke Nelson // SPDX-License-Identifier: GPL-2.0
2ca6cb544SLuke Nelson /* BPF JIT compiler for RV64G
3ca6cb544SLuke Nelson  *
4ca6cb544SLuke Nelson  * Copyright(c) 2019 Björn Töpel <bjorn.topel@gmail.com>
5ca6cb544SLuke Nelson  *
6ca6cb544SLuke Nelson  */
7ca6cb544SLuke Nelson 
8252c765bSTong Tiangen #include <linux/bitfield.h>
9ca6cb544SLuke Nelson #include <linux/bpf.h>
10ca6cb544SLuke Nelson #include <linux/filter.h>
11596f2e6fSPu Lehui #include <linux/memory.h>
12596f2e6fSPu Lehui #include <linux/stop_machine.h>
132d311f48SRandy Dunlap #include <asm/patch.h>
14ca6cb544SLuke Nelson #include "bpf_jit.h"
15ca6cb544SLuke Nelson 
1625ad1065SPu Lehui #define RV_FENTRY_NINSNS 2
1725ad1065SPu Lehui 
18ca6cb544SLuke Nelson #define RV_REG_TCC RV_REG_A6
19ca6cb544SLuke Nelson #define RV_REG_TCC_SAVED RV_REG_S6 /* Store A6 in S6 if program do calls */
20ca6cb544SLuke Nelson 
21ca6cb544SLuke Nelson static const int regmap[] = {
22ca6cb544SLuke Nelson 	[BPF_REG_0] =	RV_REG_A5,
23ca6cb544SLuke Nelson 	[BPF_REG_1] =	RV_REG_A0,
24ca6cb544SLuke Nelson 	[BPF_REG_2] =	RV_REG_A1,
25ca6cb544SLuke Nelson 	[BPF_REG_3] =	RV_REG_A2,
26ca6cb544SLuke Nelson 	[BPF_REG_4] =	RV_REG_A3,
27ca6cb544SLuke Nelson 	[BPF_REG_5] =	RV_REG_A4,
28ca6cb544SLuke Nelson 	[BPF_REG_6] =	RV_REG_S1,
29ca6cb544SLuke Nelson 	[BPF_REG_7] =	RV_REG_S2,
30ca6cb544SLuke Nelson 	[BPF_REG_8] =	RV_REG_S3,
31ca6cb544SLuke Nelson 	[BPF_REG_9] =	RV_REG_S4,
32ca6cb544SLuke Nelson 	[BPF_REG_FP] =	RV_REG_S5,
33ca6cb544SLuke Nelson 	[BPF_REG_AX] =	RV_REG_T0,
34ca6cb544SLuke Nelson };
35ca6cb544SLuke Nelson 
36252c765bSTong Tiangen static const int pt_regmap[] = {
37252c765bSTong Tiangen 	[RV_REG_A0] = offsetof(struct pt_regs, a0),
38252c765bSTong Tiangen 	[RV_REG_A1] = offsetof(struct pt_regs, a1),
39252c765bSTong Tiangen 	[RV_REG_A2] = offsetof(struct pt_regs, a2),
40252c765bSTong Tiangen 	[RV_REG_A3] = offsetof(struct pt_regs, a3),
41252c765bSTong Tiangen 	[RV_REG_A4] = offsetof(struct pt_regs, a4),
42252c765bSTong Tiangen 	[RV_REG_A5] = offsetof(struct pt_regs, a5),
43252c765bSTong Tiangen 	[RV_REG_S1] = offsetof(struct pt_regs, s1),
44252c765bSTong Tiangen 	[RV_REG_S2] = offsetof(struct pt_regs, s2),
45252c765bSTong Tiangen 	[RV_REG_S3] = offsetof(struct pt_regs, s3),
46252c765bSTong Tiangen 	[RV_REG_S4] = offsetof(struct pt_regs, s4),
47252c765bSTong Tiangen 	[RV_REG_S5] = offsetof(struct pt_regs, s5),
48252c765bSTong Tiangen 	[RV_REG_T0] = offsetof(struct pt_regs, t0),
49252c765bSTong Tiangen };
50252c765bSTong Tiangen 
51ca6cb544SLuke Nelson enum {
52ca6cb544SLuke Nelson 	RV_CTX_F_SEEN_TAIL_CALL =	0,
53ca6cb544SLuke Nelson 	RV_CTX_F_SEEN_CALL =		RV_REG_RA,
54ca6cb544SLuke Nelson 	RV_CTX_F_SEEN_S1 =		RV_REG_S1,
55ca6cb544SLuke Nelson 	RV_CTX_F_SEEN_S2 =		RV_REG_S2,
56ca6cb544SLuke Nelson 	RV_CTX_F_SEEN_S3 =		RV_REG_S3,
57ca6cb544SLuke Nelson 	RV_CTX_F_SEEN_S4 =		RV_REG_S4,
58ca6cb544SLuke Nelson 	RV_CTX_F_SEEN_S5 =		RV_REG_S5,
59ca6cb544SLuke Nelson 	RV_CTX_F_SEEN_S6 =		RV_REG_S6,
60ca6cb544SLuke Nelson };
61ca6cb544SLuke Nelson 
bpf_to_rv_reg(int bpf_reg,struct rv_jit_context * ctx)62ca6cb544SLuke Nelson static u8 bpf_to_rv_reg(int bpf_reg, struct rv_jit_context *ctx)
63ca6cb544SLuke Nelson {
64ca6cb544SLuke Nelson 	u8 reg = regmap[bpf_reg];
65ca6cb544SLuke Nelson 
66ca6cb544SLuke Nelson 	switch (reg) {
67ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S1:
68ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S2:
69ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S3:
70ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S4:
71ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S5:
72ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S6:
73ca6cb544SLuke Nelson 		__set_bit(reg, &ctx->flags);
74ca6cb544SLuke Nelson 	}
75ca6cb544SLuke Nelson 	return reg;
76ca6cb544SLuke Nelson };
77ca6cb544SLuke Nelson 
seen_reg(int reg,struct rv_jit_context * ctx)78ca6cb544SLuke Nelson static bool seen_reg(int reg, struct rv_jit_context *ctx)
79ca6cb544SLuke Nelson {
80ca6cb544SLuke Nelson 	switch (reg) {
81ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_CALL:
82ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S1:
83ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S2:
84ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S3:
85ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S4:
86ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S5:
87ca6cb544SLuke Nelson 	case RV_CTX_F_SEEN_S6:
88ca6cb544SLuke Nelson 		return test_bit(reg, &ctx->flags);
89ca6cb544SLuke Nelson 	}
90ca6cb544SLuke Nelson 	return false;
91ca6cb544SLuke Nelson }
92ca6cb544SLuke Nelson 
mark_fp(struct rv_jit_context * ctx)93ca6cb544SLuke Nelson static void mark_fp(struct rv_jit_context *ctx)
94ca6cb544SLuke Nelson {
95ca6cb544SLuke Nelson 	__set_bit(RV_CTX_F_SEEN_S5, &ctx->flags);
96ca6cb544SLuke Nelson }
97ca6cb544SLuke Nelson 
mark_call(struct rv_jit_context * ctx)98ca6cb544SLuke Nelson static void mark_call(struct rv_jit_context *ctx)
99ca6cb544SLuke Nelson {
100ca6cb544SLuke Nelson 	__set_bit(RV_CTX_F_SEEN_CALL, &ctx->flags);
101ca6cb544SLuke Nelson }
102ca6cb544SLuke Nelson 
seen_call(struct rv_jit_context * ctx)103ca6cb544SLuke Nelson static bool seen_call(struct rv_jit_context *ctx)
104ca6cb544SLuke Nelson {
105ca6cb544SLuke Nelson 	return test_bit(RV_CTX_F_SEEN_CALL, &ctx->flags);
106ca6cb544SLuke Nelson }
107ca6cb544SLuke Nelson 
mark_tail_call(struct rv_jit_context * ctx)108ca6cb544SLuke Nelson static void mark_tail_call(struct rv_jit_context *ctx)
109ca6cb544SLuke Nelson {
110ca6cb544SLuke Nelson 	__set_bit(RV_CTX_F_SEEN_TAIL_CALL, &ctx->flags);
111ca6cb544SLuke Nelson }
112ca6cb544SLuke Nelson 
seen_tail_call(struct rv_jit_context * ctx)113ca6cb544SLuke Nelson static bool seen_tail_call(struct rv_jit_context *ctx)
114ca6cb544SLuke Nelson {
115ca6cb544SLuke Nelson 	return test_bit(RV_CTX_F_SEEN_TAIL_CALL, &ctx->flags);
116ca6cb544SLuke Nelson }
117ca6cb544SLuke Nelson 
rv_tail_call_reg(struct rv_jit_context * ctx)118ca6cb544SLuke Nelson static u8 rv_tail_call_reg(struct rv_jit_context *ctx)
119ca6cb544SLuke Nelson {
120ca6cb544SLuke Nelson 	mark_tail_call(ctx);
121ca6cb544SLuke Nelson 
122ca6cb544SLuke Nelson 	if (seen_call(ctx)) {
123ca6cb544SLuke Nelson 		__set_bit(RV_CTX_F_SEEN_S6, &ctx->flags);
124ca6cb544SLuke Nelson 		return RV_REG_S6;
125ca6cb544SLuke Nelson 	}
126ca6cb544SLuke Nelson 	return RV_REG_A6;
127ca6cb544SLuke Nelson }
128ca6cb544SLuke Nelson 
is_32b_int(s64 val)129ca6cb544SLuke Nelson static bool is_32b_int(s64 val)
130ca6cb544SLuke Nelson {
131ca6cb544SLuke Nelson 	return -(1L << 31) <= val && val < (1L << 31);
132ca6cb544SLuke Nelson }
133ca6cb544SLuke Nelson 
in_auipc_jalr_range(s64 val)134489553ddSLuke Nelson static bool in_auipc_jalr_range(s64 val)
135489553ddSLuke Nelson {
136489553ddSLuke Nelson 	/*
137489553ddSLuke Nelson 	 * auipc+jalr can reach any signed PC-relative offset in the range
138489553ddSLuke Nelson 	 * [-2^31 - 2^11, 2^31 - 2^11).
139489553ddSLuke Nelson 	 */
140489553ddSLuke Nelson 	return (-(1L << 31) - (1L << 11)) <= val &&
141489553ddSLuke Nelson 		val < ((1L << 31) - (1L << 11));
142489553ddSLuke Nelson }
143489553ddSLuke Nelson 
144b54b6003SPu Lehui /* Emit fixed-length instructions for address */
emit_addr(u8 rd,u64 addr,bool extra_pass,struct rv_jit_context * ctx)145b54b6003SPu Lehui static int emit_addr(u8 rd, u64 addr, bool extra_pass, struct rv_jit_context *ctx)
146b54b6003SPu Lehui {
14748a8f78cSPuranjay Mohan 	/*
14848a8f78cSPuranjay Mohan 	 * Use the ro_insns(RX) to calculate the offset as the BPF program will
14948a8f78cSPuranjay Mohan 	 * finally run from this memory region.
15048a8f78cSPuranjay Mohan 	 */
15148a8f78cSPuranjay Mohan 	u64 ip = (u64)(ctx->ro_insns + ctx->ninsns);
152b54b6003SPu Lehui 	s64 off = addr - ip;
153b54b6003SPu Lehui 	s64 upper = (off + (1 << 11)) >> 12;
154b54b6003SPu Lehui 	s64 lower = off & 0xfff;
155b54b6003SPu Lehui 
156b54b6003SPu Lehui 	if (extra_pass && !in_auipc_jalr_range(off)) {
157b54b6003SPu Lehui 		pr_err("bpf-jit: target offset 0x%llx is out of range\n", off);
158b54b6003SPu Lehui 		return -ERANGE;
159b54b6003SPu Lehui 	}
160b54b6003SPu Lehui 
161b54b6003SPu Lehui 	emit(rv_auipc(rd, upper), ctx);
162b54b6003SPu Lehui 	emit(rv_addi(rd, rd, lower), ctx);
163b54b6003SPu Lehui 	return 0;
164b54b6003SPu Lehui }
165b54b6003SPu Lehui 
166b54b6003SPu Lehui /* Emit variable-length instructions for 32-bit and 64-bit imm */
emit_imm(u8 rd,s64 val,struct rv_jit_context * ctx)167ca6cb544SLuke Nelson static void emit_imm(u8 rd, s64 val, struct rv_jit_context *ctx)
168ca6cb544SLuke Nelson {
169ca6cb544SLuke Nelson 	/* Note that the immediate from the add is sign-extended,
170ca6cb544SLuke Nelson 	 * which means that we need to compensate this by adding 2^12,
171ca6cb544SLuke Nelson 	 * when the 12th bit is set. A simpler way of doing this, and
172ca6cb544SLuke Nelson 	 * getting rid of the check, is to just add 2**11 before the
173ca6cb544SLuke Nelson 	 * shift. The "Loading a 32-Bit constant" example from the
174ca6cb544SLuke Nelson 	 * "Computer Organization and Design, RISC-V edition" book by
175ca6cb544SLuke Nelson 	 * Patterson/Hennessy highlights this fact.
176ca6cb544SLuke Nelson 	 *
177ca6cb544SLuke Nelson 	 * This also means that we need to process LSB to MSB.
178ca6cb544SLuke Nelson 	 */
17918a4d8c9SLuke Nelson 	s64 upper = (val + (1 << 11)) >> 12;
18018a4d8c9SLuke Nelson 	/* Sign-extend lower 12 bits to 64 bits since immediates for li, addiw,
18118a4d8c9SLuke Nelson 	 * and addi are signed and RVC checks will perform signed comparisons.
18218a4d8c9SLuke Nelson 	 */
18318a4d8c9SLuke Nelson 	s64 lower = ((val & 0xfff) << 52) >> 52;
184ca6cb544SLuke Nelson 	int shift;
185ca6cb544SLuke Nelson 
186ca6cb544SLuke Nelson 	if (is_32b_int(val)) {
187ca6cb544SLuke Nelson 		if (upper)
18818a4d8c9SLuke Nelson 			emit_lui(rd, upper, ctx);
189ca6cb544SLuke Nelson 
190ca6cb544SLuke Nelson 		if (!upper) {
19118a4d8c9SLuke Nelson 			emit_li(rd, lower, ctx);
192ca6cb544SLuke Nelson 			return;
193ca6cb544SLuke Nelson 		}
194ca6cb544SLuke Nelson 
19518a4d8c9SLuke Nelson 		emit_addiw(rd, rd, lower, ctx);
196ca6cb544SLuke Nelson 		return;
197ca6cb544SLuke Nelson 	}
198ca6cb544SLuke Nelson 
199ca6cb544SLuke Nelson 	shift = __ffs(upper);
200ca6cb544SLuke Nelson 	upper >>= shift;
201ca6cb544SLuke Nelson 	shift += 12;
202ca6cb544SLuke Nelson 
203ca6cb544SLuke Nelson 	emit_imm(rd, upper, ctx);
204ca6cb544SLuke Nelson 
20518a4d8c9SLuke Nelson 	emit_slli(rd, rd, shift, ctx);
206ca6cb544SLuke Nelson 	if (lower)
20718a4d8c9SLuke Nelson 		emit_addi(rd, rd, lower, ctx);
208ca6cb544SLuke Nelson }
209ca6cb544SLuke Nelson 
__build_epilogue(bool is_tail_call,struct rv_jit_context * ctx)210ca6cb544SLuke Nelson static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx)
211ca6cb544SLuke Nelson {
212ca6cb544SLuke Nelson 	int stack_adjust = ctx->stack_size, store_offset = stack_adjust - 8;
213ca6cb544SLuke Nelson 
214ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_RA, ctx)) {
21518a4d8c9SLuke Nelson 		emit_ld(RV_REG_RA, store_offset, RV_REG_SP, ctx);
216ca6cb544SLuke Nelson 		store_offset -= 8;
217ca6cb544SLuke Nelson 	}
21818a4d8c9SLuke Nelson 	emit_ld(RV_REG_FP, store_offset, RV_REG_SP, ctx);
219ca6cb544SLuke Nelson 	store_offset -= 8;
220ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S1, ctx)) {
22118a4d8c9SLuke Nelson 		emit_ld(RV_REG_S1, store_offset, RV_REG_SP, ctx);
222ca6cb544SLuke Nelson 		store_offset -= 8;
223ca6cb544SLuke Nelson 	}
224ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S2, ctx)) {
22518a4d8c9SLuke Nelson 		emit_ld(RV_REG_S2, store_offset, RV_REG_SP, ctx);
226ca6cb544SLuke Nelson 		store_offset -= 8;
227ca6cb544SLuke Nelson 	}
228ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S3, ctx)) {
22918a4d8c9SLuke Nelson 		emit_ld(RV_REG_S3, store_offset, RV_REG_SP, ctx);
230ca6cb544SLuke Nelson 		store_offset -= 8;
231ca6cb544SLuke Nelson 	}
232ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S4, ctx)) {
23318a4d8c9SLuke Nelson 		emit_ld(RV_REG_S4, store_offset, RV_REG_SP, ctx);
234ca6cb544SLuke Nelson 		store_offset -= 8;
235ca6cb544SLuke Nelson 	}
236ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S5, ctx)) {
23718a4d8c9SLuke Nelson 		emit_ld(RV_REG_S5, store_offset, RV_REG_SP, ctx);
238ca6cb544SLuke Nelson 		store_offset -= 8;
239ca6cb544SLuke Nelson 	}
240ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S6, ctx)) {
24118a4d8c9SLuke Nelson 		emit_ld(RV_REG_S6, store_offset, RV_REG_SP, ctx);
242ca6cb544SLuke Nelson 		store_offset -= 8;
243ca6cb544SLuke Nelson 	}
244ca6cb544SLuke Nelson 
24518a4d8c9SLuke Nelson 	emit_addi(RV_REG_SP, RV_REG_SP, stack_adjust, ctx);
246ca6cb544SLuke Nelson 	/* Set return value. */
247ca6cb544SLuke Nelson 	if (!is_tail_call)
2482f1b0d3dSBjörn Töpel 		emit_addiw(RV_REG_A0, RV_REG_A5, 0, ctx);
24918a4d8c9SLuke Nelson 	emit_jalr(RV_REG_ZERO, is_tail_call ? RV_REG_T3 : RV_REG_RA,
25025ad1065SPu Lehui 		  is_tail_call ? (RV_FENTRY_NINSNS + 1) * 4 : 0, /* skip reserved nops and TCC init */
251ca6cb544SLuke Nelson 		  ctx);
252ca6cb544SLuke Nelson }
253ca6cb544SLuke Nelson 
emit_bcc(u8 cond,u8 rd,u8 rs,int rvoff,struct rv_jit_context * ctx)254ca6cb544SLuke Nelson static void emit_bcc(u8 cond, u8 rd, u8 rs, int rvoff,
255ca6cb544SLuke Nelson 		     struct rv_jit_context *ctx)
256ca6cb544SLuke Nelson {
257ca6cb544SLuke Nelson 	switch (cond) {
258ca6cb544SLuke Nelson 	case BPF_JEQ:
259ca6cb544SLuke Nelson 		emit(rv_beq(rd, rs, rvoff >> 1), ctx);
260ca6cb544SLuke Nelson 		return;
261ca6cb544SLuke Nelson 	case BPF_JGT:
262ca6cb544SLuke Nelson 		emit(rv_bltu(rs, rd, rvoff >> 1), ctx);
263ca6cb544SLuke Nelson 		return;
264ca6cb544SLuke Nelson 	case BPF_JLT:
265ca6cb544SLuke Nelson 		emit(rv_bltu(rd, rs, rvoff >> 1), ctx);
266ca6cb544SLuke Nelson 		return;
267ca6cb544SLuke Nelson 	case BPF_JGE:
268ca6cb544SLuke Nelson 		emit(rv_bgeu(rd, rs, rvoff >> 1), ctx);
269ca6cb544SLuke Nelson 		return;
270ca6cb544SLuke Nelson 	case BPF_JLE:
271ca6cb544SLuke Nelson 		emit(rv_bgeu(rs, rd, rvoff >> 1), ctx);
272ca6cb544SLuke Nelson 		return;
273ca6cb544SLuke Nelson 	case BPF_JNE:
274ca6cb544SLuke Nelson 		emit(rv_bne(rd, rs, rvoff >> 1), ctx);
275ca6cb544SLuke Nelson 		return;
276ca6cb544SLuke Nelson 	case BPF_JSGT:
277ca6cb544SLuke Nelson 		emit(rv_blt(rs, rd, rvoff >> 1), ctx);
278ca6cb544SLuke Nelson 		return;
279ca6cb544SLuke Nelson 	case BPF_JSLT:
280ca6cb544SLuke Nelson 		emit(rv_blt(rd, rs, rvoff >> 1), ctx);
281ca6cb544SLuke Nelson 		return;
282ca6cb544SLuke Nelson 	case BPF_JSGE:
283ca6cb544SLuke Nelson 		emit(rv_bge(rd, rs, rvoff >> 1), ctx);
284ca6cb544SLuke Nelson 		return;
285ca6cb544SLuke Nelson 	case BPF_JSLE:
286ca6cb544SLuke Nelson 		emit(rv_bge(rs, rd, rvoff >> 1), ctx);
287ca6cb544SLuke Nelson 	}
288ca6cb544SLuke Nelson }
289ca6cb544SLuke Nelson 
emit_branch(u8 cond,u8 rd,u8 rs,int rvoff,struct rv_jit_context * ctx)290ca6cb544SLuke Nelson static void emit_branch(u8 cond, u8 rd, u8 rs, int rvoff,
291ca6cb544SLuke Nelson 			struct rv_jit_context *ctx)
292ca6cb544SLuke Nelson {
293ca6cb544SLuke Nelson 	s64 upper, lower;
294ca6cb544SLuke Nelson 
295ca6cb544SLuke Nelson 	if (is_13b_int(rvoff)) {
296ca6cb544SLuke Nelson 		emit_bcc(cond, rd, rs, rvoff, ctx);
297ca6cb544SLuke Nelson 		return;
298ca6cb544SLuke Nelson 	}
299ca6cb544SLuke Nelson 
300ca6cb544SLuke Nelson 	/* Adjust for jal */
301ca6cb544SLuke Nelson 	rvoff -= 4;
302ca6cb544SLuke Nelson 
303ca6cb544SLuke Nelson 	/* Transform, e.g.:
304ca6cb544SLuke Nelson 	 *   bne rd,rs,foo
305ca6cb544SLuke Nelson 	 * to
306ca6cb544SLuke Nelson 	 *   beq rd,rs,<.L1>
307ca6cb544SLuke Nelson 	 *   (auipc foo)
308ca6cb544SLuke Nelson 	 *   jal(r) foo
309ca6cb544SLuke Nelson 	 * .L1
310ca6cb544SLuke Nelson 	 */
311ca6cb544SLuke Nelson 	cond = invert_bpf_cond(cond);
312ca6cb544SLuke Nelson 	if (is_21b_int(rvoff)) {
313ca6cb544SLuke Nelson 		emit_bcc(cond, rd, rs, 8, ctx);
314ca6cb544SLuke Nelson 		emit(rv_jal(RV_REG_ZERO, rvoff >> 1), ctx);
315ca6cb544SLuke Nelson 		return;
316ca6cb544SLuke Nelson 	}
317ca6cb544SLuke Nelson 
318ca6cb544SLuke Nelson 	/* 32b No need for an additional rvoff adjustment, since we
319ca6cb544SLuke Nelson 	 * get that from the auipc at PC', where PC = PC' + 4.
320ca6cb544SLuke Nelson 	 */
321ca6cb544SLuke Nelson 	upper = (rvoff + (1 << 11)) >> 12;
322ca6cb544SLuke Nelson 	lower = rvoff & 0xfff;
323ca6cb544SLuke Nelson 
324ca6cb544SLuke Nelson 	emit_bcc(cond, rd, rs, 12, ctx);
325ca6cb544SLuke Nelson 	emit(rv_auipc(RV_REG_T1, upper), ctx);
326ca6cb544SLuke Nelson 	emit(rv_jalr(RV_REG_ZERO, RV_REG_T1, lower), ctx);
327ca6cb544SLuke Nelson }
328ca6cb544SLuke Nelson 
emit_zext_32(u8 reg,struct rv_jit_context * ctx)329ca6cb544SLuke Nelson static void emit_zext_32(u8 reg, struct rv_jit_context *ctx)
330ca6cb544SLuke Nelson {
33118a4d8c9SLuke Nelson 	emit_slli(reg, reg, 32, ctx);
33218a4d8c9SLuke Nelson 	emit_srli(reg, reg, 32, ctx);
333ca6cb544SLuke Nelson }
334ca6cb544SLuke Nelson 
emit_bpf_tail_call(int insn,struct rv_jit_context * ctx)335ca6cb544SLuke Nelson static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx)
336ca6cb544SLuke Nelson {
337ca6cb544SLuke Nelson 	int tc_ninsn, off, start_insn = ctx->ninsns;
338ca6cb544SLuke Nelson 	u8 tcc = rv_tail_call_reg(ctx);
339ca6cb544SLuke Nelson 
340ca6cb544SLuke Nelson 	/* a0: &ctx
341ca6cb544SLuke Nelson 	 * a1: &array
342ca6cb544SLuke Nelson 	 * a2: index
343ca6cb544SLuke Nelson 	 *
344ca6cb544SLuke Nelson 	 * if (index >= array->map.max_entries)
345ca6cb544SLuke Nelson 	 *	goto out;
346ca6cb544SLuke Nelson 	 */
347ca6cb544SLuke Nelson 	tc_ninsn = insn ? ctx->offset[insn] - ctx->offset[insn - 1] :
348ca6cb544SLuke Nelson 		   ctx->offset[0];
349ca6cb544SLuke Nelson 	emit_zext_32(RV_REG_A2, ctx);
350ca6cb544SLuke Nelson 
351ca6cb544SLuke Nelson 	off = offsetof(struct bpf_array, map.max_entries);
352ca6cb544SLuke Nelson 	if (is_12b_check(off, insn))
353ca6cb544SLuke Nelson 		return -1;
354ca6cb544SLuke Nelson 	emit(rv_lwu(RV_REG_T1, off, RV_REG_A1), ctx);
355bfabff3cSLuke Nelson 	off = ninsns_rvoff(tc_ninsn - (ctx->ninsns - start_insn));
356ca6cb544SLuke Nelson 	emit_branch(BPF_JGE, RV_REG_A2, RV_REG_T1, off, ctx);
357ca6cb544SLuke Nelson 
358ebf7f6f0STiezhu Yang 	/* if (--TCC < 0)
359ca6cb544SLuke Nelson 	 *     goto out;
360ca6cb544SLuke Nelson 	 */
361ebf7f6f0STiezhu Yang 	emit_addi(RV_REG_TCC, tcc, -1, ctx);
362bfabff3cSLuke Nelson 	off = ninsns_rvoff(tc_ninsn - (ctx->ninsns - start_insn));
363ebf7f6f0STiezhu Yang 	emit_branch(BPF_JSLT, RV_REG_TCC, RV_REG_ZERO, off, ctx);
364ca6cb544SLuke Nelson 
365ca6cb544SLuke Nelson 	/* prog = array->ptrs[index];
366ca6cb544SLuke Nelson 	 * if (!prog)
367ca6cb544SLuke Nelson 	 *     goto out;
368ca6cb544SLuke Nelson 	 */
36918a4d8c9SLuke Nelson 	emit_slli(RV_REG_T2, RV_REG_A2, 3, ctx);
37018a4d8c9SLuke Nelson 	emit_add(RV_REG_T2, RV_REG_T2, RV_REG_A1, ctx);
371ca6cb544SLuke Nelson 	off = offsetof(struct bpf_array, ptrs);
372ca6cb544SLuke Nelson 	if (is_12b_check(off, insn))
373ca6cb544SLuke Nelson 		return -1;
37418a4d8c9SLuke Nelson 	emit_ld(RV_REG_T2, off, RV_REG_T2, ctx);
375bfabff3cSLuke Nelson 	off = ninsns_rvoff(tc_ninsn - (ctx->ninsns - start_insn));
376ca6cb544SLuke Nelson 	emit_branch(BPF_JEQ, RV_REG_T2, RV_REG_ZERO, off, ctx);
377ca6cb544SLuke Nelson 
378ca6cb544SLuke Nelson 	/* goto *(prog->bpf_func + 4); */
379ca6cb544SLuke Nelson 	off = offsetof(struct bpf_prog, bpf_func);
380ca6cb544SLuke Nelson 	if (is_12b_check(off, insn))
381ca6cb544SLuke Nelson 		return -1;
38218a4d8c9SLuke Nelson 	emit_ld(RV_REG_T3, off, RV_REG_T2, ctx);
383ca6cb544SLuke Nelson 	__build_epilogue(true, ctx);
384ca6cb544SLuke Nelson 	return 0;
385ca6cb544SLuke Nelson }
386ca6cb544SLuke Nelson 
init_regs(u8 * rd,u8 * rs,const struct bpf_insn * insn,struct rv_jit_context * ctx)387ca6cb544SLuke Nelson static void init_regs(u8 *rd, u8 *rs, const struct bpf_insn *insn,
388ca6cb544SLuke Nelson 		      struct rv_jit_context *ctx)
389ca6cb544SLuke Nelson {
390ca6cb544SLuke Nelson 	u8 code = insn->code;
391ca6cb544SLuke Nelson 
392ca6cb544SLuke Nelson 	switch (code) {
393ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JA:
394ca6cb544SLuke Nelson 	case BPF_JMP | BPF_CALL:
395ca6cb544SLuke Nelson 	case BPF_JMP | BPF_EXIT:
396ca6cb544SLuke Nelson 	case BPF_JMP | BPF_TAIL_CALL:
397ca6cb544SLuke Nelson 		break;
398ca6cb544SLuke Nelson 	default:
399ca6cb544SLuke Nelson 		*rd = bpf_to_rv_reg(insn->dst_reg, ctx);
400ca6cb544SLuke Nelson 	}
401ca6cb544SLuke Nelson 
402ca6cb544SLuke Nelson 	if (code & (BPF_ALU | BPF_X) || code & (BPF_ALU64 | BPF_X) ||
403ca6cb544SLuke Nelson 	    code & (BPF_JMP | BPF_X) || code & (BPF_JMP32 | BPF_X) ||
404ca6cb544SLuke Nelson 	    code & BPF_LDX || code & BPF_STX)
405ca6cb544SLuke Nelson 		*rs = bpf_to_rv_reg(insn->src_reg, ctx);
406ca6cb544SLuke Nelson }
407ca6cb544SLuke Nelson 
emit_zext_32_rd_rs(u8 * rd,u8 * rs,struct rv_jit_context * ctx)408ca6cb544SLuke Nelson static void emit_zext_32_rd_rs(u8 *rd, u8 *rs, struct rv_jit_context *ctx)
409ca6cb544SLuke Nelson {
41018a4d8c9SLuke Nelson 	emit_mv(RV_REG_T2, *rd, ctx);
411ca6cb544SLuke Nelson 	emit_zext_32(RV_REG_T2, ctx);
41218a4d8c9SLuke Nelson 	emit_mv(RV_REG_T1, *rs, ctx);
413ca6cb544SLuke Nelson 	emit_zext_32(RV_REG_T1, ctx);
414ca6cb544SLuke Nelson 	*rd = RV_REG_T2;
415ca6cb544SLuke Nelson 	*rs = RV_REG_T1;
416ca6cb544SLuke Nelson }
417ca6cb544SLuke Nelson 
emit_sext_32_rd_rs(u8 * rd,u8 * rs,struct rv_jit_context * ctx)418ca6cb544SLuke Nelson static void emit_sext_32_rd_rs(u8 *rd, u8 *rs, struct rv_jit_context *ctx)
419ca6cb544SLuke Nelson {
42018a4d8c9SLuke Nelson 	emit_addiw(RV_REG_T2, *rd, 0, ctx);
42118a4d8c9SLuke Nelson 	emit_addiw(RV_REG_T1, *rs, 0, ctx);
422ca6cb544SLuke Nelson 	*rd = RV_REG_T2;
423ca6cb544SLuke Nelson 	*rs = RV_REG_T1;
424ca6cb544SLuke Nelson }
425ca6cb544SLuke Nelson 
emit_zext_32_rd_t1(u8 * rd,struct rv_jit_context * ctx)426ca6cb544SLuke Nelson static void emit_zext_32_rd_t1(u8 *rd, struct rv_jit_context *ctx)
427ca6cb544SLuke Nelson {
42818a4d8c9SLuke Nelson 	emit_mv(RV_REG_T2, *rd, ctx);
429ca6cb544SLuke Nelson 	emit_zext_32(RV_REG_T2, ctx);
430ca6cb544SLuke Nelson 	emit_zext_32(RV_REG_T1, ctx);
431ca6cb544SLuke Nelson 	*rd = RV_REG_T2;
432ca6cb544SLuke Nelson }
433ca6cb544SLuke Nelson 
emit_sext_32_rd(u8 * rd,struct rv_jit_context * ctx)434ca6cb544SLuke Nelson static void emit_sext_32_rd(u8 *rd, struct rv_jit_context *ctx)
435ca6cb544SLuke Nelson {
43618a4d8c9SLuke Nelson 	emit_addiw(RV_REG_T2, *rd, 0, ctx);
437ca6cb544SLuke Nelson 	*rd = RV_REG_T2;
438ca6cb544SLuke Nelson }
439ca6cb544SLuke Nelson 
emit_jump_and_link(u8 rd,s64 rvoff,bool fixed_addr,struct rv_jit_context * ctx)4400fd1fd01SPu Lehui static int emit_jump_and_link(u8 rd, s64 rvoff, bool fixed_addr,
441ca6cb544SLuke Nelson 			      struct rv_jit_context *ctx)
442ca6cb544SLuke Nelson {
443ca6cb544SLuke Nelson 	s64 upper, lower;
444ca6cb544SLuke Nelson 
4450fd1fd01SPu Lehui 	if (rvoff && fixed_addr && is_21b_int(rvoff)) {
446ca6cb544SLuke Nelson 		emit(rv_jal(rd, rvoff >> 1), ctx);
447489553ddSLuke Nelson 		return 0;
448489553ddSLuke Nelson 	} else if (in_auipc_jalr_range(rvoff)) {
449ca6cb544SLuke Nelson 		upper = (rvoff + (1 << 11)) >> 12;
450ca6cb544SLuke Nelson 		lower = rvoff & 0xfff;
451ca6cb544SLuke Nelson 		emit(rv_auipc(RV_REG_T1, upper), ctx);
452ca6cb544SLuke Nelson 		emit(rv_jalr(rd, RV_REG_T1, lower), ctx);
453489553ddSLuke Nelson 		return 0;
454489553ddSLuke Nelson 	}
455489553ddSLuke Nelson 
456489553ddSLuke Nelson 	pr_err("bpf-jit: target offset 0x%llx is out of range\n", rvoff);
457489553ddSLuke Nelson 	return -ERANGE;
458ca6cb544SLuke Nelson }
459ca6cb544SLuke Nelson 
is_signed_bpf_cond(u8 cond)460ca6cb544SLuke Nelson static bool is_signed_bpf_cond(u8 cond)
461ca6cb544SLuke Nelson {
462ca6cb544SLuke Nelson 	return cond == BPF_JSGT || cond == BPF_JSLT ||
463ca6cb544SLuke Nelson 		cond == BPF_JSGE || cond == BPF_JSLE;
464ca6cb544SLuke Nelson }
465ca6cb544SLuke Nelson 
emit_call(u64 addr,bool fixed_addr,struct rv_jit_context * ctx)4660fd1fd01SPu Lehui static int emit_call(u64 addr, bool fixed_addr, struct rv_jit_context *ctx)
467ca6cb544SLuke Nelson {
468ca6cb544SLuke Nelson 	s64 off = 0;
469ca6cb544SLuke Nelson 	u64 ip;
470ca6cb544SLuke Nelson 
47148a8f78cSPuranjay Mohan 	if (addr && ctx->insns && ctx->ro_insns) {
47248a8f78cSPuranjay Mohan 		/*
47348a8f78cSPuranjay Mohan 		 * Use the ro_insns(RX) to calculate the offset as the BPF
47448a8f78cSPuranjay Mohan 		 * program will finally run from this memory region.
47548a8f78cSPuranjay Mohan 		 */
47648a8f78cSPuranjay Mohan 		ip = (u64)(long)(ctx->ro_insns + ctx->ninsns);
477ca6cb544SLuke Nelson 		off = addr - ip;
478ca6cb544SLuke Nelson 	}
479ca6cb544SLuke Nelson 
4800fd1fd01SPu Lehui 	return emit_jump_and_link(RV_REG_RA, off, fixed_addr, ctx);
481ca6cb544SLuke Nelson }
482ca6cb544SLuke Nelson 
emit_atomic(u8 rd,u8 rs,s16 off,s32 imm,bool is64,struct rv_jit_context * ctx)483dd642ccbSPu Lehui static void emit_atomic(u8 rd, u8 rs, s16 off, s32 imm, bool is64,
484dd642ccbSPu Lehui 			struct rv_jit_context *ctx)
485dd642ccbSPu Lehui {
486dd642ccbSPu Lehui 	u8 r0;
487dd642ccbSPu Lehui 	int jmp_offset;
488dd642ccbSPu Lehui 
489dd642ccbSPu Lehui 	if (off) {
490dd642ccbSPu Lehui 		if (is_12b_int(off)) {
491dd642ccbSPu Lehui 			emit_addi(RV_REG_T1, rd, off, ctx);
492dd642ccbSPu Lehui 		} else {
493dd642ccbSPu Lehui 			emit_imm(RV_REG_T1, off, ctx);
494dd642ccbSPu Lehui 			emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
495dd642ccbSPu Lehui 		}
496dd642ccbSPu Lehui 		rd = RV_REG_T1;
497dd642ccbSPu Lehui 	}
498dd642ccbSPu Lehui 
499dd642ccbSPu Lehui 	switch (imm) {
500dd642ccbSPu Lehui 	/* lock *(u32/u64 *)(dst_reg + off16) <op>= src_reg */
501dd642ccbSPu Lehui 	case BPF_ADD:
502dd642ccbSPu Lehui 		emit(is64 ? rv_amoadd_d(RV_REG_ZERO, rs, rd, 0, 0) :
503dd642ccbSPu Lehui 		     rv_amoadd_w(RV_REG_ZERO, rs, rd, 0, 0), ctx);
504dd642ccbSPu Lehui 		break;
505dd642ccbSPu Lehui 	case BPF_AND:
506dd642ccbSPu Lehui 		emit(is64 ? rv_amoand_d(RV_REG_ZERO, rs, rd, 0, 0) :
507dd642ccbSPu Lehui 		     rv_amoand_w(RV_REG_ZERO, rs, rd, 0, 0), ctx);
508dd642ccbSPu Lehui 		break;
509dd642ccbSPu Lehui 	case BPF_OR:
510dd642ccbSPu Lehui 		emit(is64 ? rv_amoor_d(RV_REG_ZERO, rs, rd, 0, 0) :
511dd642ccbSPu Lehui 		     rv_amoor_w(RV_REG_ZERO, rs, rd, 0, 0), ctx);
512dd642ccbSPu Lehui 		break;
513dd642ccbSPu Lehui 	case BPF_XOR:
514dd642ccbSPu Lehui 		emit(is64 ? rv_amoxor_d(RV_REG_ZERO, rs, rd, 0, 0) :
515dd642ccbSPu Lehui 		     rv_amoxor_w(RV_REG_ZERO, rs, rd, 0, 0), ctx);
516dd642ccbSPu Lehui 		break;
517dd642ccbSPu Lehui 	/* src_reg = atomic_fetch_<op>(dst_reg + off16, src_reg) */
518dd642ccbSPu Lehui 	case BPF_ADD | BPF_FETCH:
519dd642ccbSPu Lehui 		emit(is64 ? rv_amoadd_d(rs, rs, rd, 0, 0) :
520dd642ccbSPu Lehui 		     rv_amoadd_w(rs, rs, rd, 0, 0), ctx);
521dd642ccbSPu Lehui 		if (!is64)
522dd642ccbSPu Lehui 			emit_zext_32(rs, ctx);
523dd642ccbSPu Lehui 		break;
524dd642ccbSPu Lehui 	case BPF_AND | BPF_FETCH:
525dd642ccbSPu Lehui 		emit(is64 ? rv_amoand_d(rs, rs, rd, 0, 0) :
526dd642ccbSPu Lehui 		     rv_amoand_w(rs, rs, rd, 0, 0), ctx);
527dd642ccbSPu Lehui 		if (!is64)
528dd642ccbSPu Lehui 			emit_zext_32(rs, ctx);
529dd642ccbSPu Lehui 		break;
530dd642ccbSPu Lehui 	case BPF_OR | BPF_FETCH:
531dd642ccbSPu Lehui 		emit(is64 ? rv_amoor_d(rs, rs, rd, 0, 0) :
532dd642ccbSPu Lehui 		     rv_amoor_w(rs, rs, rd, 0, 0), ctx);
533dd642ccbSPu Lehui 		if (!is64)
534dd642ccbSPu Lehui 			emit_zext_32(rs, ctx);
535dd642ccbSPu Lehui 		break;
536dd642ccbSPu Lehui 	case BPF_XOR | BPF_FETCH:
537dd642ccbSPu Lehui 		emit(is64 ? rv_amoxor_d(rs, rs, rd, 0, 0) :
538dd642ccbSPu Lehui 		     rv_amoxor_w(rs, rs, rd, 0, 0), ctx);
539dd642ccbSPu Lehui 		if (!is64)
540dd642ccbSPu Lehui 			emit_zext_32(rs, ctx);
541dd642ccbSPu Lehui 		break;
542dd642ccbSPu Lehui 	/* src_reg = atomic_xchg(dst_reg + off16, src_reg); */
543dd642ccbSPu Lehui 	case BPF_XCHG:
544dd642ccbSPu Lehui 		emit(is64 ? rv_amoswap_d(rs, rs, rd, 0, 0) :
545dd642ccbSPu Lehui 		     rv_amoswap_w(rs, rs, rd, 0, 0), ctx);
546dd642ccbSPu Lehui 		if (!is64)
547dd642ccbSPu Lehui 			emit_zext_32(rs, ctx);
548dd642ccbSPu Lehui 		break;
549dd642ccbSPu Lehui 	/* r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg); */
550dd642ccbSPu Lehui 	case BPF_CMPXCHG:
551dd642ccbSPu Lehui 		r0 = bpf_to_rv_reg(BPF_REG_0, ctx);
552dd642ccbSPu Lehui 		emit(is64 ? rv_addi(RV_REG_T2, r0, 0) :
553dd642ccbSPu Lehui 		     rv_addiw(RV_REG_T2, r0, 0), ctx);
554dd642ccbSPu Lehui 		emit(is64 ? rv_lr_d(r0, 0, rd, 0, 0) :
555dd642ccbSPu Lehui 		     rv_lr_w(r0, 0, rd, 0, 0), ctx);
556dd642ccbSPu Lehui 		jmp_offset = ninsns_rvoff(8);
557dd642ccbSPu Lehui 		emit(rv_bne(RV_REG_T2, r0, jmp_offset >> 1), ctx);
558dd642ccbSPu Lehui 		emit(is64 ? rv_sc_d(RV_REG_T3, rs, rd, 0, 0) :
559dd642ccbSPu Lehui 		     rv_sc_w(RV_REG_T3, rs, rd, 0, 0), ctx);
560dd642ccbSPu Lehui 		jmp_offset = ninsns_rvoff(-6);
561dd642ccbSPu Lehui 		emit(rv_bne(RV_REG_T3, 0, jmp_offset >> 1), ctx);
562dd642ccbSPu Lehui 		emit(rv_fence(0x3, 0x3), ctx);
563dd642ccbSPu Lehui 		break;
564dd642ccbSPu Lehui 	}
565dd642ccbSPu Lehui }
566dd642ccbSPu Lehui 
567252c765bSTong Tiangen #define BPF_FIXUP_OFFSET_MASK   GENMASK(26, 0)
568252c765bSTong Tiangen #define BPF_FIXUP_REG_MASK      GENMASK(31, 27)
569252c765bSTong Tiangen 
ex_handler_bpf(const struct exception_table_entry * ex,struct pt_regs * regs)5702bf847dbSJisheng Zhang bool ex_handler_bpf(const struct exception_table_entry *ex,
571252c765bSTong Tiangen 		    struct pt_regs *regs)
572252c765bSTong Tiangen {
573252c765bSTong Tiangen 	off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup);
574252c765bSTong Tiangen 	int regs_offset = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup);
575252c765bSTong Tiangen 
576252c765bSTong Tiangen 	*(unsigned long *)((void *)regs + pt_regmap[regs_offset]) = 0;
577252c765bSTong Tiangen 	regs->epc = (unsigned long)&ex->fixup - offset;
578252c765bSTong Tiangen 
579ef127bcaSJisheng Zhang 	return true;
580252c765bSTong Tiangen }
581252c765bSTong Tiangen 
582252c765bSTong Tiangen /* For accesses to BTF pointers, add an entry to the exception table */
add_exception_handler(const struct bpf_insn * insn,struct rv_jit_context * ctx,int dst_reg,int insn_len)583252c765bSTong Tiangen static int add_exception_handler(const struct bpf_insn *insn,
584252c765bSTong Tiangen 				 struct rv_jit_context *ctx,
585252c765bSTong Tiangen 				 int dst_reg, int insn_len)
586252c765bSTong Tiangen {
587252c765bSTong Tiangen 	struct exception_table_entry *ex;
588252c765bSTong Tiangen 	unsigned long pc;
58948a8f78cSPuranjay Mohan 	off_t ins_offset;
59048a8f78cSPuranjay Mohan 	off_t fixup_offset;
591252c765bSTong Tiangen 
59248a8f78cSPuranjay Mohan 	if (!ctx->insns || !ctx->ro_insns || !ctx->prog->aux->extable ||
5933d06d816SPu Lehui 	    (BPF_MODE(insn->code) != BPF_PROBE_MEM && BPF_MODE(insn->code) != BPF_PROBE_MEMSX))
594252c765bSTong Tiangen 		return 0;
595252c765bSTong Tiangen 
596252c765bSTong Tiangen 	if (WARN_ON_ONCE(ctx->nexentries >= ctx->prog->aux->num_exentries))
597252c765bSTong Tiangen 		return -EINVAL;
598252c765bSTong Tiangen 
599252c765bSTong Tiangen 	if (WARN_ON_ONCE(insn_len > ctx->ninsns))
600252c765bSTong Tiangen 		return -EINVAL;
601252c765bSTong Tiangen 
602252c765bSTong Tiangen 	if (WARN_ON_ONCE(!rvc_enabled() && insn_len == 1))
603252c765bSTong Tiangen 		return -EINVAL;
604252c765bSTong Tiangen 
605252c765bSTong Tiangen 	ex = &ctx->prog->aux->extable[ctx->nexentries];
60648a8f78cSPuranjay Mohan 	pc = (unsigned long)&ctx->ro_insns[ctx->ninsns - insn_len];
607252c765bSTong Tiangen 
60848a8f78cSPuranjay Mohan 	/*
60948a8f78cSPuranjay Mohan 	 * This is the relative offset of the instruction that may fault from
61048a8f78cSPuranjay Mohan 	 * the exception table itself. This will be written to the exception
61148a8f78cSPuranjay Mohan 	 * table and if this instruction faults, the destination register will
61248a8f78cSPuranjay Mohan 	 * be set to '0' and the execution will jump to the next instruction.
61348a8f78cSPuranjay Mohan 	 */
61448a8f78cSPuranjay Mohan 	ins_offset = pc - (long)&ex->insn;
61548a8f78cSPuranjay Mohan 	if (WARN_ON_ONCE(ins_offset >= 0 || ins_offset < INT_MIN))
616252c765bSTong Tiangen 		return -ERANGE;
617252c765bSTong Tiangen 
618252c765bSTong Tiangen 	/*
619252c765bSTong Tiangen 	 * Since the extable follows the program, the fixup offset is always
620252c765bSTong Tiangen 	 * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value
621252c765bSTong Tiangen 	 * to keep things simple, and put the destination register in the upper
622252c765bSTong Tiangen 	 * bits. We don't need to worry about buildtime or runtime sort
623252c765bSTong Tiangen 	 * modifying the upper bits because the table is already sorted, and
624252c765bSTong Tiangen 	 * isn't part of the main exception table.
62548a8f78cSPuranjay Mohan 	 *
62648a8f78cSPuranjay Mohan 	 * The fixup_offset is set to the next instruction from the instruction
62748a8f78cSPuranjay Mohan 	 * that may fault. The execution will jump to this after handling the
62848a8f78cSPuranjay Mohan 	 * fault.
629252c765bSTong Tiangen 	 */
63048a8f78cSPuranjay Mohan 	fixup_offset = (long)&ex->fixup - (pc + insn_len * sizeof(u16));
63148a8f78cSPuranjay Mohan 	if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, fixup_offset))
632252c765bSTong Tiangen 		return -ERANGE;
633252c765bSTong Tiangen 
63448a8f78cSPuranjay Mohan 	/*
63548a8f78cSPuranjay Mohan 	 * The offsets above have been calculated using the RO buffer but we
63648a8f78cSPuranjay Mohan 	 * need to use the R/W buffer for writes.
63748a8f78cSPuranjay Mohan 	 * switch ex to rw buffer for writing.
63848a8f78cSPuranjay Mohan 	 */
63948a8f78cSPuranjay Mohan 	ex = (void *)ctx->insns + ((void *)ex - (void *)ctx->ro_insns);
64048a8f78cSPuranjay Mohan 
64148a8f78cSPuranjay Mohan 	ex->insn = ins_offset;
64248a8f78cSPuranjay Mohan 
64348a8f78cSPuranjay Mohan 	ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, fixup_offset) |
644252c765bSTong Tiangen 		FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg);
6452bf847dbSJisheng Zhang 	ex->type = EX_TYPE_BPF;
646252c765bSTong Tiangen 
647252c765bSTong Tiangen 	ctx->nexentries++;
648252c765bSTong Tiangen 	return 0;
649252c765bSTong Tiangen }
650252c765bSTong Tiangen 
gen_jump_or_nops(void * target,void * ip,u32 * insns,bool is_call)65125ad1065SPu Lehui static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call)
652596f2e6fSPu Lehui {
653596f2e6fSPu Lehui 	s64 rvoff;
654596f2e6fSPu Lehui 	struct rv_jit_context ctx;
655596f2e6fSPu Lehui 
656596f2e6fSPu Lehui 	ctx.ninsns = 0;
657596f2e6fSPu Lehui 	ctx.insns = (u16 *)insns;
658596f2e6fSPu Lehui 
659596f2e6fSPu Lehui 	if (!target) {
660596f2e6fSPu Lehui 		emit(rv_nop(), &ctx);
661596f2e6fSPu Lehui 		emit(rv_nop(), &ctx);
662596f2e6fSPu Lehui 		return 0;
663596f2e6fSPu Lehui 	}
664596f2e6fSPu Lehui 
665596f2e6fSPu Lehui 	rvoff = (s64)(target - ip);
66625ad1065SPu Lehui 	return emit_jump_and_link(is_call ? RV_REG_T0 : RV_REG_ZERO, rvoff, false, &ctx);
667596f2e6fSPu Lehui }
668596f2e6fSPu Lehui 
bpf_arch_text_poke(void * ip,enum bpf_text_poke_type poke_type,void * old_addr,void * new_addr)669596f2e6fSPu Lehui int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
670596f2e6fSPu Lehui 		       void *old_addr, void *new_addr)
671596f2e6fSPu Lehui {
67225ad1065SPu Lehui 	u32 old_insns[RV_FENTRY_NINSNS], new_insns[RV_FENTRY_NINSNS];
673596f2e6fSPu Lehui 	bool is_call = poke_type == BPF_MOD_CALL;
674596f2e6fSPu Lehui 	int ret;
675596f2e6fSPu Lehui 
67625ad1065SPu Lehui 	if (!is_kernel_text((unsigned long)ip) &&
67725ad1065SPu Lehui 	    !is_bpf_text_address((unsigned long)ip))
678596f2e6fSPu Lehui 		return -ENOTSUPP;
679596f2e6fSPu Lehui 
68025ad1065SPu Lehui 	ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call);
681596f2e6fSPu Lehui 	if (ret)
682596f2e6fSPu Lehui 		return ret;
683596f2e6fSPu Lehui 
68425ad1065SPu Lehui 	if (memcmp(ip, old_insns, RV_FENTRY_NINSNS * 4))
685596f2e6fSPu Lehui 		return -EFAULT;
686596f2e6fSPu Lehui 
68725ad1065SPu Lehui 	ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call);
688596f2e6fSPu Lehui 	if (ret)
689596f2e6fSPu Lehui 		return ret;
690596f2e6fSPu Lehui 
691596f2e6fSPu Lehui 	cpus_read_lock();
692596f2e6fSPu Lehui 	mutex_lock(&text_mutex);
69325ad1065SPu Lehui 	if (memcmp(ip, new_insns, RV_FENTRY_NINSNS * 4))
69425ad1065SPu Lehui 		ret = patch_text(ip, new_insns, RV_FENTRY_NINSNS);
695596f2e6fSPu Lehui 	mutex_unlock(&text_mutex);
696596f2e6fSPu Lehui 	cpus_read_unlock();
697596f2e6fSPu Lehui 
698596f2e6fSPu Lehui 	return ret;
699596f2e6fSPu Lehui }
700596f2e6fSPu Lehui 
store_args(int nregs,int args_off,struct rv_jit_context * ctx)70149b5e77aSPu Lehui static void store_args(int nregs, int args_off, struct rv_jit_context *ctx)
70249b5e77aSPu Lehui {
70349b5e77aSPu Lehui 	int i;
70449b5e77aSPu Lehui 
70549b5e77aSPu Lehui 	for (i = 0; i < nregs; i++) {
70649b5e77aSPu Lehui 		emit_sd(RV_REG_FP, -args_off, RV_REG_A0 + i, ctx);
70749b5e77aSPu Lehui 		args_off -= 8;
70849b5e77aSPu Lehui 	}
70949b5e77aSPu Lehui }
71049b5e77aSPu Lehui 
restore_args(int nregs,int args_off,struct rv_jit_context * ctx)71149b5e77aSPu Lehui static void restore_args(int nregs, int args_off, struct rv_jit_context *ctx)
71249b5e77aSPu Lehui {
71349b5e77aSPu Lehui 	int i;
71449b5e77aSPu Lehui 
71549b5e77aSPu Lehui 	for (i = 0; i < nregs; i++) {
71649b5e77aSPu Lehui 		emit_ld(RV_REG_A0 + i, -args_off, RV_REG_FP, ctx);
71749b5e77aSPu Lehui 		args_off -= 8;
71849b5e77aSPu Lehui 	}
71949b5e77aSPu Lehui }
72049b5e77aSPu Lehui 
invoke_bpf_prog(struct bpf_tramp_link * l,int args_off,int retval_off,int run_ctx_off,bool save_ret,struct rv_jit_context * ctx)72149b5e77aSPu Lehui static int invoke_bpf_prog(struct bpf_tramp_link *l, int args_off, int retval_off,
72249b5e77aSPu Lehui 			   int run_ctx_off, bool save_ret, struct rv_jit_context *ctx)
72349b5e77aSPu Lehui {
72449b5e77aSPu Lehui 	int ret, branch_off;
72549b5e77aSPu Lehui 	struct bpf_prog *p = l->link.prog;
72649b5e77aSPu Lehui 	int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie);
72749b5e77aSPu Lehui 
72849b5e77aSPu Lehui 	if (l->cookie) {
72949b5e77aSPu Lehui 		emit_imm(RV_REG_T1, l->cookie, ctx);
73049b5e77aSPu Lehui 		emit_sd(RV_REG_FP, -run_ctx_off + cookie_off, RV_REG_T1, ctx);
73149b5e77aSPu Lehui 	} else {
73249b5e77aSPu Lehui 		emit_sd(RV_REG_FP, -run_ctx_off + cookie_off, RV_REG_ZERO, ctx);
73349b5e77aSPu Lehui 	}
73449b5e77aSPu Lehui 
73549b5e77aSPu Lehui 	/* arg1: prog */
73649b5e77aSPu Lehui 	emit_imm(RV_REG_A0, (const s64)p, ctx);
73749b5e77aSPu Lehui 	/* arg2: &run_ctx */
73849b5e77aSPu Lehui 	emit_addi(RV_REG_A1, RV_REG_FP, -run_ctx_off, ctx);
73949b5e77aSPu Lehui 	ret = emit_call((const u64)bpf_trampoline_enter(p), true, ctx);
74049b5e77aSPu Lehui 	if (ret)
74149b5e77aSPu Lehui 		return ret;
74249b5e77aSPu Lehui 
74349b5e77aSPu Lehui 	/* if (__bpf_prog_enter(prog) == 0)
74449b5e77aSPu Lehui 	 *	goto skip_exec_of_prog;
74549b5e77aSPu Lehui 	 */
74649b5e77aSPu Lehui 	branch_off = ctx->ninsns;
74749b5e77aSPu Lehui 	/* nop reserved for conditional jump */
74849b5e77aSPu Lehui 	emit(rv_nop(), ctx);
74949b5e77aSPu Lehui 
75049b5e77aSPu Lehui 	/* store prog start time */
75149b5e77aSPu Lehui 	emit_mv(RV_REG_S1, RV_REG_A0, ctx);
75249b5e77aSPu Lehui 
75349b5e77aSPu Lehui 	/* arg1: &args_off */
75449b5e77aSPu Lehui 	emit_addi(RV_REG_A0, RV_REG_FP, -args_off, ctx);
75549b5e77aSPu Lehui 	if (!p->jited)
75649b5e77aSPu Lehui 		/* arg2: progs[i]->insnsi for interpreter */
75749b5e77aSPu Lehui 		emit_imm(RV_REG_A1, (const s64)p->insnsi, ctx);
75849b5e77aSPu Lehui 	ret = emit_call((const u64)p->bpf_func, true, ctx);
75949b5e77aSPu Lehui 	if (ret)
76049b5e77aSPu Lehui 		return ret;
76149b5e77aSPu Lehui 
762*7112cd26SBjörn Töpel 	if (save_ret) {
763*7112cd26SBjörn Töpel 		emit_sd(RV_REG_FP, -retval_off, RV_REG_A0, ctx);
764*7112cd26SBjörn Töpel 		emit_sd(RV_REG_FP, -(retval_off - 8), regmap[BPF_REG_0], ctx);
765*7112cd26SBjörn Töpel 	}
76649b5e77aSPu Lehui 
76749b5e77aSPu Lehui 	/* update branch with beqz */
76849b5e77aSPu Lehui 	if (ctx->insns) {
76949b5e77aSPu Lehui 		int offset = ninsns_rvoff(ctx->ninsns - branch_off);
77049b5e77aSPu Lehui 		u32 insn = rv_beq(RV_REG_A0, RV_REG_ZERO, offset >> 1);
77149b5e77aSPu Lehui 		*(u32 *)(ctx->insns + branch_off) = insn;
77249b5e77aSPu Lehui 	}
77349b5e77aSPu Lehui 
77449b5e77aSPu Lehui 	/* arg1: prog */
77549b5e77aSPu Lehui 	emit_imm(RV_REG_A0, (const s64)p, ctx);
77649b5e77aSPu Lehui 	/* arg2: prog start time */
77749b5e77aSPu Lehui 	emit_mv(RV_REG_A1, RV_REG_S1, ctx);
77849b5e77aSPu Lehui 	/* arg3: &run_ctx */
77949b5e77aSPu Lehui 	emit_addi(RV_REG_A2, RV_REG_FP, -run_ctx_off, ctx);
78049b5e77aSPu Lehui 	ret = emit_call((const u64)bpf_trampoline_exit(p), true, ctx);
78149b5e77aSPu Lehui 
78249b5e77aSPu Lehui 	return ret;
78349b5e77aSPu Lehui }
78449b5e77aSPu Lehui 
__arch_prepare_bpf_trampoline(struct bpf_tramp_image * im,const struct btf_func_model * m,struct bpf_tramp_links * tlinks,void * func_addr,u32 flags,struct rv_jit_context * ctx)78549b5e77aSPu Lehui static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
78649b5e77aSPu Lehui 					 const struct btf_func_model *m,
78749b5e77aSPu Lehui 					 struct bpf_tramp_links *tlinks,
78849b5e77aSPu Lehui 					 void *func_addr, u32 flags,
78949b5e77aSPu Lehui 					 struct rv_jit_context *ctx)
79049b5e77aSPu Lehui {
79149b5e77aSPu Lehui 	int i, ret, offset;
79249b5e77aSPu Lehui 	int *branches_off = NULL;
79349b5e77aSPu Lehui 	int stack_size = 0, nregs = m->nr_args;
79425ad1065SPu Lehui 	int retval_off, args_off, nregs_off, ip_off, run_ctx_off, sreg_off;
79549b5e77aSPu Lehui 	struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY];
79649b5e77aSPu Lehui 	struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT];
79749b5e77aSPu Lehui 	struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN];
79849b5e77aSPu Lehui 	void *orig_call = func_addr;
79949b5e77aSPu Lehui 	bool save_ret;
80049b5e77aSPu Lehui 	u32 insn;
80149b5e77aSPu Lehui 
80225ad1065SPu Lehui 	/* Two types of generated trampoline stack layout:
80349b5e77aSPu Lehui 	 *
80425ad1065SPu Lehui 	 * 1. trampoline called from function entry
80525ad1065SPu Lehui 	 * --------------------------------------
80625ad1065SPu Lehui 	 * FP + 8	    [ RA to parent func	] return address to parent
80749b5e77aSPu Lehui 	 *					  function
80825ad1065SPu Lehui 	 * FP + 0	    [ FP of parent func ] frame pointer of parent
80949b5e77aSPu Lehui 	 *					  function
81025ad1065SPu Lehui 	 * FP - 8           [ T0 to traced func ] return address of traced
81125ad1065SPu Lehui 	 *					  function
81225ad1065SPu Lehui 	 * FP - 16	    [ FP of traced func ] frame pointer of traced
81325ad1065SPu Lehui 	 *					  function
81425ad1065SPu Lehui 	 * --------------------------------------
81525ad1065SPu Lehui 	 *
81625ad1065SPu Lehui 	 * 2. trampoline called directly
81725ad1065SPu Lehui 	 * --------------------------------------
81825ad1065SPu Lehui 	 * FP - 8	    [ RA to caller func ] return address to caller
81925ad1065SPu Lehui 	 *					  function
82025ad1065SPu Lehui 	 * FP - 16	    [ FP of caller func	] frame pointer of caller
82125ad1065SPu Lehui 	 *					  function
82225ad1065SPu Lehui 	 * --------------------------------------
82349b5e77aSPu Lehui 	 *
82449b5e77aSPu Lehui 	 * FP - retval_off  [ return value      ] BPF_TRAMP_F_CALL_ORIG or
82549b5e77aSPu Lehui 	 *					  BPF_TRAMP_F_RET_FENTRY_RET
82649b5e77aSPu Lehui 	 *                  [ argN              ]
82749b5e77aSPu Lehui 	 *                  [ ...               ]
82849b5e77aSPu Lehui 	 * FP - args_off    [ arg1              ]
82949b5e77aSPu Lehui 	 *
83049b5e77aSPu Lehui 	 * FP - nregs_off   [ regs count        ]
83149b5e77aSPu Lehui 	 *
83249b5e77aSPu Lehui 	 * FP - ip_off      [ traced func	] BPF_TRAMP_F_IP_ARG
83349b5e77aSPu Lehui 	 *
83449b5e77aSPu Lehui 	 * FP - run_ctx_off [ bpf_tramp_run_ctx ]
83549b5e77aSPu Lehui 	 *
83649b5e77aSPu Lehui 	 * FP - sreg_off    [ callee saved reg	]
83749b5e77aSPu Lehui 	 *
83849b5e77aSPu Lehui 	 *		    [ pads              ] pads for 16 bytes alignment
83949b5e77aSPu Lehui 	 */
84049b5e77aSPu Lehui 
84149b5e77aSPu Lehui 	if (flags & (BPF_TRAMP_F_ORIG_STACK | BPF_TRAMP_F_SHARE_IPMODIFY))
84249b5e77aSPu Lehui 		return -ENOTSUPP;
84349b5e77aSPu Lehui 
84449b5e77aSPu Lehui 	/* extra regiters for struct arguments */
84549b5e77aSPu Lehui 	for (i = 0; i < m->nr_args; i++)
84649b5e77aSPu Lehui 		if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
84749b5e77aSPu Lehui 			nregs += round_up(m->arg_size[i], 8) / 8 - 1;
84849b5e77aSPu Lehui 
84949b5e77aSPu Lehui 	/* 8 arguments passed by registers */
85049b5e77aSPu Lehui 	if (nregs > 8)
85149b5e77aSPu Lehui 		return -ENOTSUPP;
85249b5e77aSPu Lehui 
85325ad1065SPu Lehui 	/* room of trampoline frame to store return address and frame pointer */
85425ad1065SPu Lehui 	stack_size += 16;
85549b5e77aSPu Lehui 
85649b5e77aSPu Lehui 	save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET);
85749b5e77aSPu Lehui 	if (save_ret) {
858*7112cd26SBjörn Töpel 		stack_size += 16; /* Save both A5 (BPF R0) and A0 */
85949b5e77aSPu Lehui 		retval_off = stack_size;
86049b5e77aSPu Lehui 	}
86149b5e77aSPu Lehui 
86249b5e77aSPu Lehui 	stack_size += nregs * 8;
86349b5e77aSPu Lehui 	args_off = stack_size;
86449b5e77aSPu Lehui 
86549b5e77aSPu Lehui 	stack_size += 8;
86649b5e77aSPu Lehui 	nregs_off = stack_size;
86749b5e77aSPu Lehui 
86849b5e77aSPu Lehui 	if (flags & BPF_TRAMP_F_IP_ARG) {
86949b5e77aSPu Lehui 		stack_size += 8;
87049b5e77aSPu Lehui 		ip_off = stack_size;
87149b5e77aSPu Lehui 	}
87249b5e77aSPu Lehui 
87349b5e77aSPu Lehui 	stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8);
87449b5e77aSPu Lehui 	run_ctx_off = stack_size;
87549b5e77aSPu Lehui 
87649b5e77aSPu Lehui 	stack_size += 8;
87749b5e77aSPu Lehui 	sreg_off = stack_size;
87849b5e77aSPu Lehui 
87949b5e77aSPu Lehui 	stack_size = round_up(stack_size, 16);
88049b5e77aSPu Lehui 
88125ad1065SPu Lehui 	if (func_addr) {
88225ad1065SPu Lehui 		/* For the trampoline called from function entry,
88325ad1065SPu Lehui 		 * the frame of traced function and the frame of
88425ad1065SPu Lehui 		 * trampoline need to be considered.
88525ad1065SPu Lehui 		 */
88625ad1065SPu Lehui 		emit_addi(RV_REG_SP, RV_REG_SP, -16, ctx);
88725ad1065SPu Lehui 		emit_sd(RV_REG_SP, 8, RV_REG_RA, ctx);
88825ad1065SPu Lehui 		emit_sd(RV_REG_SP, 0, RV_REG_FP, ctx);
88925ad1065SPu Lehui 		emit_addi(RV_REG_FP, RV_REG_SP, 16, ctx);
89025ad1065SPu Lehui 
89149b5e77aSPu Lehui 		emit_addi(RV_REG_SP, RV_REG_SP, -stack_size, ctx);
89225ad1065SPu Lehui 		emit_sd(RV_REG_SP, stack_size - 8, RV_REG_T0, ctx);
89325ad1065SPu Lehui 		emit_sd(RV_REG_SP, stack_size - 16, RV_REG_FP, ctx);
89449b5e77aSPu Lehui 		emit_addi(RV_REG_FP, RV_REG_SP, stack_size, ctx);
89525ad1065SPu Lehui 	} else {
89625ad1065SPu Lehui 		/* For the trampoline called directly, just handle
89725ad1065SPu Lehui 		 * the frame of trampoline.
89825ad1065SPu Lehui 		 */
89925ad1065SPu Lehui 		emit_addi(RV_REG_SP, RV_REG_SP, -stack_size, ctx);
90025ad1065SPu Lehui 		emit_sd(RV_REG_SP, stack_size - 8, RV_REG_RA, ctx);
90125ad1065SPu Lehui 		emit_sd(RV_REG_SP, stack_size - 16, RV_REG_FP, ctx);
90225ad1065SPu Lehui 		emit_addi(RV_REG_FP, RV_REG_SP, stack_size, ctx);
90325ad1065SPu Lehui 	}
90449b5e77aSPu Lehui 
90549b5e77aSPu Lehui 	/* callee saved register S1 to pass start time */
90649b5e77aSPu Lehui 	emit_sd(RV_REG_FP, -sreg_off, RV_REG_S1, ctx);
90749b5e77aSPu Lehui 
90849b5e77aSPu Lehui 	/* store ip address of the traced function */
90949b5e77aSPu Lehui 	if (flags & BPF_TRAMP_F_IP_ARG) {
91049b5e77aSPu Lehui 		emit_imm(RV_REG_T1, (const s64)func_addr, ctx);
91149b5e77aSPu Lehui 		emit_sd(RV_REG_FP, -ip_off, RV_REG_T1, ctx);
91249b5e77aSPu Lehui 	}
91349b5e77aSPu Lehui 
91449b5e77aSPu Lehui 	emit_li(RV_REG_T1, nregs, ctx);
91549b5e77aSPu Lehui 	emit_sd(RV_REG_FP, -nregs_off, RV_REG_T1, ctx);
91649b5e77aSPu Lehui 
91749b5e77aSPu Lehui 	store_args(nregs, args_off, ctx);
91849b5e77aSPu Lehui 
91949b5e77aSPu Lehui 	/* skip to actual body of traced function */
92049b5e77aSPu Lehui 	if (flags & BPF_TRAMP_F_SKIP_FRAME)
92125ad1065SPu Lehui 		orig_call += RV_FENTRY_NINSNS * 4;
92249b5e77aSPu Lehui 
92349b5e77aSPu Lehui 	if (flags & BPF_TRAMP_F_CALL_ORIG) {
92449b5e77aSPu Lehui 		emit_imm(RV_REG_A0, (const s64)im, ctx);
92549b5e77aSPu Lehui 		ret = emit_call((const u64)__bpf_tramp_enter, true, ctx);
92649b5e77aSPu Lehui 		if (ret)
92749b5e77aSPu Lehui 			return ret;
92849b5e77aSPu Lehui 	}
92949b5e77aSPu Lehui 
93049b5e77aSPu Lehui 	for (i = 0; i < fentry->nr_links; i++) {
93149b5e77aSPu Lehui 		ret = invoke_bpf_prog(fentry->links[i], args_off, retval_off, run_ctx_off,
93249b5e77aSPu Lehui 				      flags & BPF_TRAMP_F_RET_FENTRY_RET, ctx);
93349b5e77aSPu Lehui 		if (ret)
93449b5e77aSPu Lehui 			return ret;
93549b5e77aSPu Lehui 	}
93649b5e77aSPu Lehui 
93749b5e77aSPu Lehui 	if (fmod_ret->nr_links) {
93849b5e77aSPu Lehui 		branches_off = kcalloc(fmod_ret->nr_links, sizeof(int), GFP_KERNEL);
93949b5e77aSPu Lehui 		if (!branches_off)
94049b5e77aSPu Lehui 			return -ENOMEM;
94149b5e77aSPu Lehui 
94249b5e77aSPu Lehui 		/* cleanup to avoid garbage return value confusion */
94349b5e77aSPu Lehui 		emit_sd(RV_REG_FP, -retval_off, RV_REG_ZERO, ctx);
94449b5e77aSPu Lehui 		for (i = 0; i < fmod_ret->nr_links; i++) {
94549b5e77aSPu Lehui 			ret = invoke_bpf_prog(fmod_ret->links[i], args_off, retval_off,
94649b5e77aSPu Lehui 					      run_ctx_off, true, ctx);
94749b5e77aSPu Lehui 			if (ret)
94849b5e77aSPu Lehui 				goto out;
94949b5e77aSPu Lehui 			emit_ld(RV_REG_T1, -retval_off, RV_REG_FP, ctx);
95049b5e77aSPu Lehui 			branches_off[i] = ctx->ninsns;
95149b5e77aSPu Lehui 			/* nop reserved for conditional jump */
95249b5e77aSPu Lehui 			emit(rv_nop(), ctx);
95349b5e77aSPu Lehui 		}
95449b5e77aSPu Lehui 	}
95549b5e77aSPu Lehui 
95649b5e77aSPu Lehui 	if (flags & BPF_TRAMP_F_CALL_ORIG) {
95749b5e77aSPu Lehui 		restore_args(nregs, args_off, ctx);
95849b5e77aSPu Lehui 		ret = emit_call((const u64)orig_call, true, ctx);
95949b5e77aSPu Lehui 		if (ret)
96049b5e77aSPu Lehui 			goto out;
96149b5e77aSPu Lehui 		emit_sd(RV_REG_FP, -retval_off, RV_REG_A0, ctx);
962*7112cd26SBjörn Töpel 		emit_sd(RV_REG_FP, -(retval_off - 8), regmap[BPF_REG_0], ctx);
96349b5e77aSPu Lehui 		im->ip_after_call = ctx->insns + ctx->ninsns;
96449b5e77aSPu Lehui 		/* 2 nops reserved for auipc+jalr pair */
96549b5e77aSPu Lehui 		emit(rv_nop(), ctx);
96649b5e77aSPu Lehui 		emit(rv_nop(), ctx);
96749b5e77aSPu Lehui 	}
96849b5e77aSPu Lehui 
96949b5e77aSPu Lehui 	/* update branches saved in invoke_bpf_mod_ret with bnez */
97049b5e77aSPu Lehui 	for (i = 0; ctx->insns && i < fmod_ret->nr_links; i++) {
97149b5e77aSPu Lehui 		offset = ninsns_rvoff(ctx->ninsns - branches_off[i]);
97249b5e77aSPu Lehui 		insn = rv_bne(RV_REG_T1, RV_REG_ZERO, offset >> 1);
97349b5e77aSPu Lehui 		*(u32 *)(ctx->insns + branches_off[i]) = insn;
97449b5e77aSPu Lehui 	}
97549b5e77aSPu Lehui 
97649b5e77aSPu Lehui 	for (i = 0; i < fexit->nr_links; i++) {
97749b5e77aSPu Lehui 		ret = invoke_bpf_prog(fexit->links[i], args_off, retval_off,
97849b5e77aSPu Lehui 				      run_ctx_off, false, ctx);
97949b5e77aSPu Lehui 		if (ret)
98049b5e77aSPu Lehui 			goto out;
98149b5e77aSPu Lehui 	}
98249b5e77aSPu Lehui 
98349b5e77aSPu Lehui 	if (flags & BPF_TRAMP_F_CALL_ORIG) {
98449b5e77aSPu Lehui 		im->ip_epilogue = ctx->insns + ctx->ninsns;
98549b5e77aSPu Lehui 		emit_imm(RV_REG_A0, (const s64)im, ctx);
98649b5e77aSPu Lehui 		ret = emit_call((const u64)__bpf_tramp_exit, true, ctx);
98749b5e77aSPu Lehui 		if (ret)
98849b5e77aSPu Lehui 			goto out;
98949b5e77aSPu Lehui 	}
99049b5e77aSPu Lehui 
99149b5e77aSPu Lehui 	if (flags & BPF_TRAMP_F_RESTORE_REGS)
99249b5e77aSPu Lehui 		restore_args(nregs, args_off, ctx);
99349b5e77aSPu Lehui 
994*7112cd26SBjörn Töpel 	if (save_ret) {
99549b5e77aSPu Lehui 		emit_ld(RV_REG_A0, -retval_off, RV_REG_FP, ctx);
996*7112cd26SBjörn Töpel 		emit_ld(regmap[BPF_REG_0], -(retval_off - 8), RV_REG_FP, ctx);
997*7112cd26SBjörn Töpel 	}
99849b5e77aSPu Lehui 
99949b5e77aSPu Lehui 	emit_ld(RV_REG_S1, -sreg_off, RV_REG_FP, ctx);
100049b5e77aSPu Lehui 
100125ad1065SPu Lehui 	if (func_addr) {
100225ad1065SPu Lehui 		/* trampoline called from function entry */
100325ad1065SPu Lehui 		emit_ld(RV_REG_T0, stack_size - 8, RV_REG_SP, ctx);
100425ad1065SPu Lehui 		emit_ld(RV_REG_FP, stack_size - 16, RV_REG_SP, ctx);
100525ad1065SPu Lehui 		emit_addi(RV_REG_SP, RV_REG_SP, stack_size, ctx);
100649b5e77aSPu Lehui 
100725ad1065SPu Lehui 		emit_ld(RV_REG_RA, 8, RV_REG_SP, ctx);
100825ad1065SPu Lehui 		emit_ld(RV_REG_FP, 0, RV_REG_SP, ctx);
100925ad1065SPu Lehui 		emit_addi(RV_REG_SP, RV_REG_SP, 16, ctx);
101025ad1065SPu Lehui 
101125ad1065SPu Lehui 		if (flags & BPF_TRAMP_F_SKIP_FRAME)
101225ad1065SPu Lehui 			/* return to parent function */
101325ad1065SPu Lehui 			emit_jalr(RV_REG_ZERO, RV_REG_RA, 0, ctx);
101425ad1065SPu Lehui 		else
101525ad1065SPu Lehui 			/* return to traced function */
101625ad1065SPu Lehui 			emit_jalr(RV_REG_ZERO, RV_REG_T0, 0, ctx);
101725ad1065SPu Lehui 	} else {
101825ad1065SPu Lehui 		/* trampoline called directly */
101925ad1065SPu Lehui 		emit_ld(RV_REG_RA, stack_size - 8, RV_REG_SP, ctx);
102025ad1065SPu Lehui 		emit_ld(RV_REG_FP, stack_size - 16, RV_REG_SP, ctx);
102149b5e77aSPu Lehui 		emit_addi(RV_REG_SP, RV_REG_SP, stack_size, ctx);
102249b5e77aSPu Lehui 
102349b5e77aSPu Lehui 		emit_jalr(RV_REG_ZERO, RV_REG_RA, 0, ctx);
102425ad1065SPu Lehui 	}
102549b5e77aSPu Lehui 
102649b5e77aSPu Lehui 	ret = ctx->ninsns;
102749b5e77aSPu Lehui out:
102849b5e77aSPu Lehui 	kfree(branches_off);
102949b5e77aSPu Lehui 	return ret;
103049b5e77aSPu Lehui }
103149b5e77aSPu Lehui 
arch_prepare_bpf_trampoline(struct bpf_tramp_image * im,void * image,void * image_end,const struct btf_func_model * m,u32 flags,struct bpf_tramp_links * tlinks,void * func_addr)103249b5e77aSPu Lehui int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image,
103349b5e77aSPu Lehui 				void *image_end, const struct btf_func_model *m,
103449b5e77aSPu Lehui 				u32 flags, struct bpf_tramp_links *tlinks,
103549b5e77aSPu Lehui 				void *func_addr)
103649b5e77aSPu Lehui {
103749b5e77aSPu Lehui 	int ret;
103849b5e77aSPu Lehui 	struct rv_jit_context ctx;
103949b5e77aSPu Lehui 
104049b5e77aSPu Lehui 	ctx.ninsns = 0;
104149b5e77aSPu Lehui 	ctx.insns = NULL;
104248a8f78cSPuranjay Mohan 	ctx.ro_insns = NULL;
104349b5e77aSPu Lehui 	ret = __arch_prepare_bpf_trampoline(im, m, tlinks, func_addr, flags, &ctx);
104449b5e77aSPu Lehui 	if (ret < 0)
104549b5e77aSPu Lehui 		return ret;
104649b5e77aSPu Lehui 
104749b5e77aSPu Lehui 	if (ninsns_rvoff(ret) > (long)image_end - (long)image)
104849b5e77aSPu Lehui 		return -EFBIG;
104949b5e77aSPu Lehui 
105049b5e77aSPu Lehui 	ctx.ninsns = 0;
105148a8f78cSPuranjay Mohan 	/*
105248a8f78cSPuranjay Mohan 	 * The bpf_int_jit_compile() uses a RW buffer (ctx.insns) to write the
105348a8f78cSPuranjay Mohan 	 * JITed instructions and later copies it to a RX region (ctx.ro_insns).
105448a8f78cSPuranjay Mohan 	 * It also uses ctx.ro_insns to calculate offsets for jumps etc. As the
105548a8f78cSPuranjay Mohan 	 * trampoline image uses the same memory area for writing and execution,
105648a8f78cSPuranjay Mohan 	 * both ctx.insns and ctx.ro_insns can be set to image.
105748a8f78cSPuranjay Mohan 	 */
105849b5e77aSPu Lehui 	ctx.insns = image;
105948a8f78cSPuranjay Mohan 	ctx.ro_insns = image;
106049b5e77aSPu Lehui 	ret = __arch_prepare_bpf_trampoline(im, m, tlinks, func_addr, flags, &ctx);
106149b5e77aSPu Lehui 	if (ret < 0)
106249b5e77aSPu Lehui 		return ret;
106349b5e77aSPu Lehui 
106449b5e77aSPu Lehui 	bpf_flush_icache(ctx.insns, ctx.insns + ctx.ninsns);
106549b5e77aSPu Lehui 
106649b5e77aSPu Lehui 	return ninsns_rvoff(ret);
106749b5e77aSPu Lehui }
106849b5e77aSPu Lehui 
bpf_jit_emit_insn(const struct bpf_insn * insn,struct rv_jit_context * ctx,bool extra_pass)1069ca6cb544SLuke Nelson int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
1070ca6cb544SLuke Nelson 		      bool extra_pass)
1071ca6cb544SLuke Nelson {
1072ca6cb544SLuke Nelson 	bool is64 = BPF_CLASS(insn->code) == BPF_ALU64 ||
1073ca6cb544SLuke Nelson 		    BPF_CLASS(insn->code) == BPF_JMP;
1074489553ddSLuke Nelson 	int s, e, rvoff, ret, i = insn - ctx->prog->insnsi;
1075ca6cb544SLuke Nelson 	struct bpf_prog_aux *aux = ctx->prog->aux;
1076ca6cb544SLuke Nelson 	u8 rd = -1, rs = -1, code = insn->code;
1077ca6cb544SLuke Nelson 	s16 off = insn->off;
1078ca6cb544SLuke Nelson 	s32 imm = insn->imm;
1079ca6cb544SLuke Nelson 
1080ca6cb544SLuke Nelson 	init_regs(&rd, &rs, insn, ctx);
1081ca6cb544SLuke Nelson 
1082ca6cb544SLuke Nelson 	switch (code) {
1083ca6cb544SLuke Nelson 	/* dst = src */
1084ca6cb544SLuke Nelson 	case BPF_ALU | BPF_MOV | BPF_X:
1085ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_MOV | BPF_X:
1086ca6cb544SLuke Nelson 		if (imm == 1) {
1087ca6cb544SLuke Nelson 			/* Special mov32 for zext */
1088ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1089ca6cb544SLuke Nelson 			break;
1090ca6cb544SLuke Nelson 		}
1091694896adSPu Lehui 		switch (insn->off) {
1092694896adSPu Lehui 		case 0:
109318a4d8c9SLuke Nelson 			emit_mv(rd, rs, ctx);
1094694896adSPu Lehui 			break;
1095694896adSPu Lehui 		case 8:
1096694896adSPu Lehui 		case 16:
1097694896adSPu Lehui 			emit_slli(RV_REG_T1, rs, 64 - insn->off, ctx);
1098694896adSPu Lehui 			emit_srai(rd, RV_REG_T1, 64 - insn->off, ctx);
1099694896adSPu Lehui 			break;
1100694896adSPu Lehui 		case 32:
1101694896adSPu Lehui 			emit_addiw(rd, rs, 0, ctx);
1102694896adSPu Lehui 			break;
1103694896adSPu Lehui 		}
1104ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1105ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1106ca6cb544SLuke Nelson 		break;
1107ca6cb544SLuke Nelson 
1108ca6cb544SLuke Nelson 	/* dst = dst OP src */
1109ca6cb544SLuke Nelson 	case BPF_ALU | BPF_ADD | BPF_X:
1110ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_ADD | BPF_X:
111118a4d8c9SLuke Nelson 		emit_add(rd, rd, rs, ctx);
1112ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1113ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1114ca6cb544SLuke Nelson 		break;
1115ca6cb544SLuke Nelson 	case BPF_ALU | BPF_SUB | BPF_X:
1116ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_SUB | BPF_X:
111718a4d8c9SLuke Nelson 		if (is64)
111818a4d8c9SLuke Nelson 			emit_sub(rd, rd, rs, ctx);
111918a4d8c9SLuke Nelson 		else
112018a4d8c9SLuke Nelson 			emit_subw(rd, rd, rs, ctx);
112118a4d8c9SLuke Nelson 
1122ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1123ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1124ca6cb544SLuke Nelson 		break;
1125ca6cb544SLuke Nelson 	case BPF_ALU | BPF_AND | BPF_X:
1126ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_AND | BPF_X:
112718a4d8c9SLuke Nelson 		emit_and(rd, rd, rs, ctx);
1128ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1129ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1130ca6cb544SLuke Nelson 		break;
1131ca6cb544SLuke Nelson 	case BPF_ALU | BPF_OR | BPF_X:
1132ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_OR | BPF_X:
113318a4d8c9SLuke Nelson 		emit_or(rd, rd, rs, ctx);
1134ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1135ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1136ca6cb544SLuke Nelson 		break;
1137ca6cb544SLuke Nelson 	case BPF_ALU | BPF_XOR | BPF_X:
1138ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_XOR | BPF_X:
113918a4d8c9SLuke Nelson 		emit_xor(rd, rd, rs, ctx);
1140ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1141ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1142ca6cb544SLuke Nelson 		break;
1143ca6cb544SLuke Nelson 	case BPF_ALU | BPF_MUL | BPF_X:
1144ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_MUL | BPF_X:
1145ca6cb544SLuke Nelson 		emit(is64 ? rv_mul(rd, rd, rs) : rv_mulw(rd, rd, rs), ctx);
1146ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1147ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1148ca6cb544SLuke Nelson 		break;
1149ca6cb544SLuke Nelson 	case BPF_ALU | BPF_DIV | BPF_X:
1150ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_DIV | BPF_X:
11513e18ff4bSPu Lehui 		if (off)
11523e18ff4bSPu Lehui 			emit(is64 ? rv_div(rd, rd, rs) : rv_divw(rd, rd, rs), ctx);
11533e18ff4bSPu Lehui 		else
1154ca6cb544SLuke Nelson 			emit(is64 ? rv_divu(rd, rd, rs) : rv_divuw(rd, rd, rs), ctx);
1155ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1156ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1157ca6cb544SLuke Nelson 		break;
1158ca6cb544SLuke Nelson 	case BPF_ALU | BPF_MOD | BPF_X:
1159ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_MOD | BPF_X:
11603e18ff4bSPu Lehui 		if (off)
11613e18ff4bSPu Lehui 			emit(is64 ? rv_rem(rd, rd, rs) : rv_remw(rd, rd, rs), ctx);
11623e18ff4bSPu Lehui 		else
1163ca6cb544SLuke Nelson 			emit(is64 ? rv_remu(rd, rd, rs) : rv_remuw(rd, rd, rs), ctx);
1164ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1165ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1166ca6cb544SLuke Nelson 		break;
1167ca6cb544SLuke Nelson 	case BPF_ALU | BPF_LSH | BPF_X:
1168ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_LSH | BPF_X:
1169ca6cb544SLuke Nelson 		emit(is64 ? rv_sll(rd, rd, rs) : rv_sllw(rd, rd, rs), ctx);
11700224b2acSLuke Nelson 		if (!is64 && !aux->verifier_zext)
1171ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1172ca6cb544SLuke Nelson 		break;
1173ca6cb544SLuke Nelson 	case BPF_ALU | BPF_RSH | BPF_X:
1174ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_RSH | BPF_X:
1175ca6cb544SLuke Nelson 		emit(is64 ? rv_srl(rd, rd, rs) : rv_srlw(rd, rd, rs), ctx);
1176ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1177ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1178ca6cb544SLuke Nelson 		break;
1179ca6cb544SLuke Nelson 	case BPF_ALU | BPF_ARSH | BPF_X:
1180ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_ARSH | BPF_X:
1181ca6cb544SLuke Nelson 		emit(is64 ? rv_sra(rd, rd, rs) : rv_sraw(rd, rd, rs), ctx);
1182ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1183ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1184ca6cb544SLuke Nelson 		break;
1185ca6cb544SLuke Nelson 
1186ca6cb544SLuke Nelson 	/* dst = -dst */
1187ca6cb544SLuke Nelson 	case BPF_ALU | BPF_NEG:
1188ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_NEG:
118918a4d8c9SLuke Nelson 		emit_sub(rd, RV_REG_ZERO, rd, ctx);
1190ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1191ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1192ca6cb544SLuke Nelson 		break;
1193ca6cb544SLuke Nelson 
1194ca6cb544SLuke Nelson 	/* dst = BSWAP##imm(dst) */
1195ca6cb544SLuke Nelson 	case BPF_ALU | BPF_END | BPF_FROM_LE:
119621a099abSLuke Nelson 		switch (imm) {
119721a099abSLuke Nelson 		case 16:
119818a4d8c9SLuke Nelson 			emit_slli(rd, rd, 48, ctx);
119918a4d8c9SLuke Nelson 			emit_srli(rd, rd, 48, ctx);
120021a099abSLuke Nelson 			break;
120121a099abSLuke Nelson 		case 32:
120221a099abSLuke Nelson 			if (!aux->verifier_zext)
120321a099abSLuke Nelson 				emit_zext_32(rd, ctx);
120421a099abSLuke Nelson 			break;
120521a099abSLuke Nelson 		case 64:
120621a099abSLuke Nelson 			/* Do nothing */
1207ca6cb544SLuke Nelson 			break;
1208ca6cb544SLuke Nelson 		}
120921a099abSLuke Nelson 		break;
121021a099abSLuke Nelson 
1211ca6cb544SLuke Nelson 	case BPF_ALU | BPF_END | BPF_FROM_BE:
121283cc63afSPu Lehui 	case BPF_ALU64 | BPF_END | BPF_FROM_LE:
121318a4d8c9SLuke Nelson 		emit_li(RV_REG_T2, 0, ctx);
1214ca6cb544SLuke Nelson 
121518a4d8c9SLuke Nelson 		emit_andi(RV_REG_T1, rd, 0xff, ctx);
121618a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, RV_REG_T1, ctx);
121718a4d8c9SLuke Nelson 		emit_slli(RV_REG_T2, RV_REG_T2, 8, ctx);
121818a4d8c9SLuke Nelson 		emit_srli(rd, rd, 8, ctx);
1219ca6cb544SLuke Nelson 		if (imm == 16)
1220ca6cb544SLuke Nelson 			goto out_be;
1221ca6cb544SLuke Nelson 
122218a4d8c9SLuke Nelson 		emit_andi(RV_REG_T1, rd, 0xff, ctx);
122318a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, RV_REG_T1, ctx);
122418a4d8c9SLuke Nelson 		emit_slli(RV_REG_T2, RV_REG_T2, 8, ctx);
122518a4d8c9SLuke Nelson 		emit_srli(rd, rd, 8, ctx);
1226ca6cb544SLuke Nelson 
122718a4d8c9SLuke Nelson 		emit_andi(RV_REG_T1, rd, 0xff, ctx);
122818a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, RV_REG_T1, ctx);
122918a4d8c9SLuke Nelson 		emit_slli(RV_REG_T2, RV_REG_T2, 8, ctx);
123018a4d8c9SLuke Nelson 		emit_srli(rd, rd, 8, ctx);
1231ca6cb544SLuke Nelson 		if (imm == 32)
1232ca6cb544SLuke Nelson 			goto out_be;
1233ca6cb544SLuke Nelson 
123418a4d8c9SLuke Nelson 		emit_andi(RV_REG_T1, rd, 0xff, ctx);
123518a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, RV_REG_T1, ctx);
123618a4d8c9SLuke Nelson 		emit_slli(RV_REG_T2, RV_REG_T2, 8, ctx);
123718a4d8c9SLuke Nelson 		emit_srli(rd, rd, 8, ctx);
1238ca6cb544SLuke Nelson 
123918a4d8c9SLuke Nelson 		emit_andi(RV_REG_T1, rd, 0xff, ctx);
124018a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, RV_REG_T1, ctx);
124118a4d8c9SLuke Nelson 		emit_slli(RV_REG_T2, RV_REG_T2, 8, ctx);
124218a4d8c9SLuke Nelson 		emit_srli(rd, rd, 8, ctx);
1243ca6cb544SLuke Nelson 
124418a4d8c9SLuke Nelson 		emit_andi(RV_REG_T1, rd, 0xff, ctx);
124518a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, RV_REG_T1, ctx);
124618a4d8c9SLuke Nelson 		emit_slli(RV_REG_T2, RV_REG_T2, 8, ctx);
124718a4d8c9SLuke Nelson 		emit_srli(rd, rd, 8, ctx);
1248ca6cb544SLuke Nelson 
124918a4d8c9SLuke Nelson 		emit_andi(RV_REG_T1, rd, 0xff, ctx);
125018a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, RV_REG_T1, ctx);
125118a4d8c9SLuke Nelson 		emit_slli(RV_REG_T2, RV_REG_T2, 8, ctx);
125218a4d8c9SLuke Nelson 		emit_srli(rd, rd, 8, ctx);
1253ca6cb544SLuke Nelson out_be:
125418a4d8c9SLuke Nelson 		emit_andi(RV_REG_T1, rd, 0xff, ctx);
125518a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, RV_REG_T1, ctx);
1256ca6cb544SLuke Nelson 
125718a4d8c9SLuke Nelson 		emit_mv(rd, RV_REG_T2, ctx);
1258ca6cb544SLuke Nelson 		break;
1259ca6cb544SLuke Nelson 
1260ca6cb544SLuke Nelson 	/* dst = imm */
1261ca6cb544SLuke Nelson 	case BPF_ALU | BPF_MOV | BPF_K:
1262ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_MOV | BPF_K:
1263ca6cb544SLuke Nelson 		emit_imm(rd, imm, ctx);
1264ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1265ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1266ca6cb544SLuke Nelson 		break;
1267ca6cb544SLuke Nelson 
1268ca6cb544SLuke Nelson 	/* dst = dst OP imm */
1269ca6cb544SLuke Nelson 	case BPF_ALU | BPF_ADD | BPF_K:
1270ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_ADD | BPF_K:
1271ca6cb544SLuke Nelson 		if (is_12b_int(imm)) {
127218a4d8c9SLuke Nelson 			emit_addi(rd, rd, imm, ctx);
1273ca6cb544SLuke Nelson 		} else {
1274ca6cb544SLuke Nelson 			emit_imm(RV_REG_T1, imm, ctx);
127518a4d8c9SLuke Nelson 			emit_add(rd, rd, RV_REG_T1, ctx);
1276ca6cb544SLuke Nelson 		}
1277ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1278ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1279ca6cb544SLuke Nelson 		break;
1280ca6cb544SLuke Nelson 	case BPF_ALU | BPF_SUB | BPF_K:
1281ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_SUB | BPF_K:
1282ca6cb544SLuke Nelson 		if (is_12b_int(-imm)) {
128318a4d8c9SLuke Nelson 			emit_addi(rd, rd, -imm, ctx);
1284ca6cb544SLuke Nelson 		} else {
1285ca6cb544SLuke Nelson 			emit_imm(RV_REG_T1, imm, ctx);
128618a4d8c9SLuke Nelson 			emit_sub(rd, rd, RV_REG_T1, ctx);
1287ca6cb544SLuke Nelson 		}
1288ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1289ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1290ca6cb544SLuke Nelson 		break;
1291ca6cb544SLuke Nelson 	case BPF_ALU | BPF_AND | BPF_K:
1292ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_AND | BPF_K:
1293ca6cb544SLuke Nelson 		if (is_12b_int(imm)) {
129418a4d8c9SLuke Nelson 			emit_andi(rd, rd, imm, ctx);
1295ca6cb544SLuke Nelson 		} else {
1296ca6cb544SLuke Nelson 			emit_imm(RV_REG_T1, imm, ctx);
129718a4d8c9SLuke Nelson 			emit_and(rd, rd, RV_REG_T1, ctx);
1298ca6cb544SLuke Nelson 		}
1299ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1300ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1301ca6cb544SLuke Nelson 		break;
1302ca6cb544SLuke Nelson 	case BPF_ALU | BPF_OR | BPF_K:
1303ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_OR | BPF_K:
1304ca6cb544SLuke Nelson 		if (is_12b_int(imm)) {
1305ca6cb544SLuke Nelson 			emit(rv_ori(rd, rd, imm), ctx);
1306ca6cb544SLuke Nelson 		} else {
1307ca6cb544SLuke Nelson 			emit_imm(RV_REG_T1, imm, ctx);
130818a4d8c9SLuke Nelson 			emit_or(rd, rd, RV_REG_T1, ctx);
1309ca6cb544SLuke Nelson 		}
1310ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1311ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1312ca6cb544SLuke Nelson 		break;
1313ca6cb544SLuke Nelson 	case BPF_ALU | BPF_XOR | BPF_K:
1314ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_XOR | BPF_K:
1315ca6cb544SLuke Nelson 		if (is_12b_int(imm)) {
1316ca6cb544SLuke Nelson 			emit(rv_xori(rd, rd, imm), ctx);
1317ca6cb544SLuke Nelson 		} else {
1318ca6cb544SLuke Nelson 			emit_imm(RV_REG_T1, imm, ctx);
131918a4d8c9SLuke Nelson 			emit_xor(rd, rd, RV_REG_T1, ctx);
1320ca6cb544SLuke Nelson 		}
1321ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1322ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1323ca6cb544SLuke Nelson 		break;
1324ca6cb544SLuke Nelson 	case BPF_ALU | BPF_MUL | BPF_K:
1325ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_MUL | BPF_K:
1326ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, imm, ctx);
1327ca6cb544SLuke Nelson 		emit(is64 ? rv_mul(rd, rd, RV_REG_T1) :
1328ca6cb544SLuke Nelson 		     rv_mulw(rd, rd, RV_REG_T1), ctx);
1329ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1330ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1331ca6cb544SLuke Nelson 		break;
1332ca6cb544SLuke Nelson 	case BPF_ALU | BPF_DIV | BPF_K:
1333ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_DIV | BPF_K:
1334ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, imm, ctx);
13353e18ff4bSPu Lehui 		if (off)
13363e18ff4bSPu Lehui 			emit(is64 ? rv_div(rd, rd, RV_REG_T1) :
13373e18ff4bSPu Lehui 			     rv_divw(rd, rd, RV_REG_T1), ctx);
13383e18ff4bSPu Lehui 		else
1339ca6cb544SLuke Nelson 			emit(is64 ? rv_divu(rd, rd, RV_REG_T1) :
1340ca6cb544SLuke Nelson 			     rv_divuw(rd, rd, RV_REG_T1), ctx);
1341ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1342ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1343ca6cb544SLuke Nelson 		break;
1344ca6cb544SLuke Nelson 	case BPF_ALU | BPF_MOD | BPF_K:
1345ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_MOD | BPF_K:
1346ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, imm, ctx);
13473e18ff4bSPu Lehui 		if (off)
13483e18ff4bSPu Lehui 			emit(is64 ? rv_rem(rd, rd, RV_REG_T1) :
13493e18ff4bSPu Lehui 			     rv_remw(rd, rd, RV_REG_T1), ctx);
13503e18ff4bSPu Lehui 		else
1351ca6cb544SLuke Nelson 			emit(is64 ? rv_remu(rd, rd, RV_REG_T1) :
1352ca6cb544SLuke Nelson 			     rv_remuw(rd, rd, RV_REG_T1), ctx);
1353ca6cb544SLuke Nelson 		if (!is64 && !aux->verifier_zext)
1354ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1355ca6cb544SLuke Nelson 		break;
1356ca6cb544SLuke Nelson 	case BPF_ALU | BPF_LSH | BPF_K:
1357ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_LSH | BPF_K:
135818a4d8c9SLuke Nelson 		emit_slli(rd, rd, imm, ctx);
135918a4d8c9SLuke Nelson 
13600224b2acSLuke Nelson 		if (!is64 && !aux->verifier_zext)
1361ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1362ca6cb544SLuke Nelson 		break;
1363ca6cb544SLuke Nelson 	case BPF_ALU | BPF_RSH | BPF_K:
1364ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_RSH | BPF_K:
136518a4d8c9SLuke Nelson 		if (is64)
136618a4d8c9SLuke Nelson 			emit_srli(rd, rd, imm, ctx);
136718a4d8c9SLuke Nelson 		else
136818a4d8c9SLuke Nelson 			emit(rv_srliw(rd, rd, imm), ctx);
136918a4d8c9SLuke Nelson 
13700224b2acSLuke Nelson 		if (!is64 && !aux->verifier_zext)
1371ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1372ca6cb544SLuke Nelson 		break;
1373ca6cb544SLuke Nelson 	case BPF_ALU | BPF_ARSH | BPF_K:
1374ca6cb544SLuke Nelson 	case BPF_ALU64 | BPF_ARSH | BPF_K:
137518a4d8c9SLuke Nelson 		if (is64)
137618a4d8c9SLuke Nelson 			emit_srai(rd, rd, imm, ctx);
137718a4d8c9SLuke Nelson 		else
137818a4d8c9SLuke Nelson 			emit(rv_sraiw(rd, rd, imm), ctx);
137918a4d8c9SLuke Nelson 
13800224b2acSLuke Nelson 		if (!is64 && !aux->verifier_zext)
1381ca6cb544SLuke Nelson 			emit_zext_32(rd, ctx);
1382ca6cb544SLuke Nelson 		break;
1383ca6cb544SLuke Nelson 
1384ca6cb544SLuke Nelson 	/* JUMP off */
1385ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JA:
1386d9839f16SPu Lehui 	case BPF_JMP32 | BPF_JA:
1387d9839f16SPu Lehui 		if (BPF_CLASS(code) == BPF_JMP)
1388ca6cb544SLuke Nelson 			rvoff = rv_offset(i, off, ctx);
1389d9839f16SPu Lehui 		else
1390d9839f16SPu Lehui 			rvoff = rv_offset(i, imm, ctx);
13910fd1fd01SPu Lehui 		ret = emit_jump_and_link(RV_REG_ZERO, rvoff, true, ctx);
1392489553ddSLuke Nelson 		if (ret)
1393489553ddSLuke Nelson 			return ret;
1394ca6cb544SLuke Nelson 		break;
1395ca6cb544SLuke Nelson 
1396ca6cb544SLuke Nelson 	/* IF (dst COND src) JUMP off */
1397ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JEQ | BPF_X:
1398ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JEQ | BPF_X:
1399ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JGT | BPF_X:
1400ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JGT | BPF_X:
1401ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JLT | BPF_X:
1402ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JLT | BPF_X:
1403ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JGE | BPF_X:
1404ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JGE | BPF_X:
1405ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JLE | BPF_X:
1406ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JLE | BPF_X:
1407ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JNE | BPF_X:
1408ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JNE | BPF_X:
1409ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JSGT | BPF_X:
1410ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JSGT | BPF_X:
1411ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JSLT | BPF_X:
1412ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JSLT | BPF_X:
1413ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JSGE | BPF_X:
1414ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JSGE | BPF_X:
1415ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JSLE | BPF_X:
1416ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JSLE | BPF_X:
1417ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JSET | BPF_X:
1418ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JSET | BPF_X:
1419ca6cb544SLuke Nelson 		rvoff = rv_offset(i, off, ctx);
1420ca6cb544SLuke Nelson 		if (!is64) {
1421ca6cb544SLuke Nelson 			s = ctx->ninsns;
1422ca6cb544SLuke Nelson 			if (is_signed_bpf_cond(BPF_OP(code)))
1423ca6cb544SLuke Nelson 				emit_sext_32_rd_rs(&rd, &rs, ctx);
1424ca6cb544SLuke Nelson 			else
1425ca6cb544SLuke Nelson 				emit_zext_32_rd_rs(&rd, &rs, ctx);
1426ca6cb544SLuke Nelson 			e = ctx->ninsns;
1427ca6cb544SLuke Nelson 
1428ca6cb544SLuke Nelson 			/* Adjust for extra insns */
1429bfabff3cSLuke Nelson 			rvoff -= ninsns_rvoff(e - s);
1430ca6cb544SLuke Nelson 		}
1431ca6cb544SLuke Nelson 
1432ca6cb544SLuke Nelson 		if (BPF_OP(code) == BPF_JSET) {
1433ca6cb544SLuke Nelson 			/* Adjust for and */
1434ca6cb544SLuke Nelson 			rvoff -= 4;
143518a4d8c9SLuke Nelson 			emit_and(RV_REG_T1, rd, rs, ctx);
1436ca6cb544SLuke Nelson 			emit_branch(BPF_JNE, RV_REG_T1, RV_REG_ZERO, rvoff,
1437ca6cb544SLuke Nelson 				    ctx);
1438ca6cb544SLuke Nelson 		} else {
1439ca6cb544SLuke Nelson 			emit_branch(BPF_OP(code), rd, rs, rvoff, ctx);
1440ca6cb544SLuke Nelson 		}
1441ca6cb544SLuke Nelson 		break;
1442ca6cb544SLuke Nelson 
1443ca6cb544SLuke Nelson 	/* IF (dst COND imm) JUMP off */
1444ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JEQ | BPF_K:
1445ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JEQ | BPF_K:
1446ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JGT | BPF_K:
1447ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JGT | BPF_K:
1448ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JLT | BPF_K:
1449ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JLT | BPF_K:
1450ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JGE | BPF_K:
1451ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JGE | BPF_K:
1452ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JLE | BPF_K:
1453ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JLE | BPF_K:
1454ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JNE | BPF_K:
1455ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JNE | BPF_K:
1456ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JSGT | BPF_K:
1457ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JSGT | BPF_K:
1458ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JSLT | BPF_K:
1459ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JSLT | BPF_K:
1460ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JSGE | BPF_K:
1461ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JSGE | BPF_K:
1462ca6cb544SLuke Nelson 	case BPF_JMP | BPF_JSLE | BPF_K:
1463ca6cb544SLuke Nelson 	case BPF_JMP32 | BPF_JSLE | BPF_K:
1464ca6cb544SLuke Nelson 		rvoff = rv_offset(i, off, ctx);
1465ca6cb544SLuke Nelson 		s = ctx->ninsns;
1466ca349a6aSLuke Nelson 		if (imm) {
1467ca6cb544SLuke Nelson 			emit_imm(RV_REG_T1, imm, ctx);
1468ca349a6aSLuke Nelson 			rs = RV_REG_T1;
1469ca349a6aSLuke Nelson 		} else {
1470ca349a6aSLuke Nelson 			/* If imm is 0, simply use zero register. */
1471ca349a6aSLuke Nelson 			rs = RV_REG_ZERO;
1472ca349a6aSLuke Nelson 		}
1473ca6cb544SLuke Nelson 		if (!is64) {
1474ca6cb544SLuke Nelson 			if (is_signed_bpf_cond(BPF_OP(code)))
1475ca6cb544SLuke Nelson 				emit_sext_32_rd(&rd, ctx);
1476ca6cb544SLuke Nelson 			else
1477ca6cb544SLuke Nelson 				emit_zext_32_rd_t1(&rd, ctx);
1478ca6cb544SLuke Nelson 		}
1479ca6cb544SLuke Nelson 		e = ctx->ninsns;
1480ca6cb544SLuke Nelson 
1481ca6cb544SLuke Nelson 		/* Adjust for extra insns */
1482bfabff3cSLuke Nelson 		rvoff -= ninsns_rvoff(e - s);
1483ca349a6aSLuke Nelson 		emit_branch(BPF_OP(code), rd, rs, rvoff, ctx);
1484073ca6a0SLuke Nelson 		break;
1485073ca6a0SLuke Nelson 
1486073ca6a0SLuke Nelson 	case BPF_JMP | BPF_JSET | BPF_K:
1487073ca6a0SLuke Nelson 	case BPF_JMP32 | BPF_JSET | BPF_K:
1488073ca6a0SLuke Nelson 		rvoff = rv_offset(i, off, ctx);
1489073ca6a0SLuke Nelson 		s = ctx->ninsns;
1490073ca6a0SLuke Nelson 		if (is_12b_int(imm)) {
149118a4d8c9SLuke Nelson 			emit_andi(RV_REG_T1, rd, imm, ctx);
1492073ca6a0SLuke Nelson 		} else {
1493073ca6a0SLuke Nelson 			emit_imm(RV_REG_T1, imm, ctx);
149418a4d8c9SLuke Nelson 			emit_and(RV_REG_T1, rd, RV_REG_T1, ctx);
1495ca6cb544SLuke Nelson 		}
1496073ca6a0SLuke Nelson 		/* For jset32, we should clear the upper 32 bits of t1, but
1497073ca6a0SLuke Nelson 		 * sign-extension is sufficient here and saves one instruction,
1498073ca6a0SLuke Nelson 		 * as t1 is used only in comparison against zero.
1499073ca6a0SLuke Nelson 		 */
1500073ca6a0SLuke Nelson 		if (!is64 && imm < 0)
150118a4d8c9SLuke Nelson 			emit_addiw(RV_REG_T1, RV_REG_T1, 0, ctx);
1502073ca6a0SLuke Nelson 		e = ctx->ninsns;
1503bfabff3cSLuke Nelson 		rvoff -= ninsns_rvoff(e - s);
1504073ca6a0SLuke Nelson 		emit_branch(BPF_JNE, RV_REG_T1, RV_REG_ZERO, rvoff, ctx);
1505ca6cb544SLuke Nelson 		break;
1506ca6cb544SLuke Nelson 
1507ca6cb544SLuke Nelson 	/* function call */
1508ca6cb544SLuke Nelson 	case BPF_JMP | BPF_CALL:
1509ca6cb544SLuke Nelson 	{
15100fd1fd01SPu Lehui 		bool fixed_addr;
1511ca6cb544SLuke Nelson 		u64 addr;
1512ca6cb544SLuke Nelson 
1513ca6cb544SLuke Nelson 		mark_call(ctx);
15140fd1fd01SPu Lehui 		ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass,
15150fd1fd01SPu Lehui 					    &addr, &fixed_addr);
1516ca6cb544SLuke Nelson 		if (ret < 0)
1517ca6cb544SLuke Nelson 			return ret;
15180fd1fd01SPu Lehui 
15190fd1fd01SPu Lehui 		ret = emit_call(addr, fixed_addr, ctx);
1520ca6cb544SLuke Nelson 		if (ret)
1521ca6cb544SLuke Nelson 			return ret;
15220fd1fd01SPu Lehui 
15232f1b0d3dSBjörn Töpel 		if (insn->src_reg != BPF_PSEUDO_CALL)
15240fd1fd01SPu Lehui 			emit_mv(bpf_to_rv_reg(BPF_REG_0, ctx), RV_REG_A0, ctx);
1525ca6cb544SLuke Nelson 		break;
1526ca6cb544SLuke Nelson 	}
1527ca6cb544SLuke Nelson 	/* tail call */
1528ca6cb544SLuke Nelson 	case BPF_JMP | BPF_TAIL_CALL:
1529ca6cb544SLuke Nelson 		if (emit_bpf_tail_call(i, ctx))
1530ca6cb544SLuke Nelson 			return -1;
1531ca6cb544SLuke Nelson 		break;
1532ca6cb544SLuke Nelson 
1533ca6cb544SLuke Nelson 	/* function return */
1534ca6cb544SLuke Nelson 	case BPF_JMP | BPF_EXIT:
1535ca6cb544SLuke Nelson 		if (i == ctx->prog->len - 1)
1536ca6cb544SLuke Nelson 			break;
1537ca6cb544SLuke Nelson 
1538ca6cb544SLuke Nelson 		rvoff = epilogue_offset(ctx);
15390fd1fd01SPu Lehui 		ret = emit_jump_and_link(RV_REG_ZERO, rvoff, true, ctx);
1540489553ddSLuke Nelson 		if (ret)
1541489553ddSLuke Nelson 			return ret;
1542ca6cb544SLuke Nelson 		break;
1543ca6cb544SLuke Nelson 
1544ca6cb544SLuke Nelson 	/* dst = imm64 */
1545ca6cb544SLuke Nelson 	case BPF_LD | BPF_IMM | BPF_DW:
1546ca6cb544SLuke Nelson 	{
1547ca6cb544SLuke Nelson 		struct bpf_insn insn1 = insn[1];
1548ca6cb544SLuke Nelson 		u64 imm64;
1549ca6cb544SLuke Nelson 
1550ca6cb544SLuke Nelson 		imm64 = (u64)insn1.imm << 32 | (u32)imm;
1551b54b6003SPu Lehui 		if (bpf_pseudo_func(insn)) {
1552b54b6003SPu Lehui 			/* fixed-length insns for extra jit pass */
1553b54b6003SPu Lehui 			ret = emit_addr(rd, imm64, extra_pass, ctx);
1554b54b6003SPu Lehui 			if (ret)
1555b54b6003SPu Lehui 				return ret;
1556b54b6003SPu Lehui 		} else {
1557ca6cb544SLuke Nelson 			emit_imm(rd, imm64, ctx);
1558b54b6003SPu Lehui 		}
1559b54b6003SPu Lehui 
1560ca6cb544SLuke Nelson 		return 1;
1561ca6cb544SLuke Nelson 	}
1562ca6cb544SLuke Nelson 
15633d06d816SPu Lehui 	/* LDX: dst = *(unsigned size *)(src + off) */
1564ca6cb544SLuke Nelson 	case BPF_LDX | BPF_MEM | BPF_B:
1565ca6cb544SLuke Nelson 	case BPF_LDX | BPF_MEM | BPF_H:
1566ca6cb544SLuke Nelson 	case BPF_LDX | BPF_MEM | BPF_W:
1567252c765bSTong Tiangen 	case BPF_LDX | BPF_MEM | BPF_DW:
1568252c765bSTong Tiangen 	case BPF_LDX | BPF_PROBE_MEM | BPF_B:
1569252c765bSTong Tiangen 	case BPF_LDX | BPF_PROBE_MEM | BPF_H:
1570252c765bSTong Tiangen 	case BPF_LDX | BPF_PROBE_MEM | BPF_W:
1571252c765bSTong Tiangen 	case BPF_LDX | BPF_PROBE_MEM | BPF_DW:
15723d06d816SPu Lehui 	/* LDSX: dst = *(signed size *)(src + off) */
15733d06d816SPu Lehui 	case BPF_LDX | BPF_MEMSX | BPF_B:
15743d06d816SPu Lehui 	case BPF_LDX | BPF_MEMSX | BPF_H:
15753d06d816SPu Lehui 	case BPF_LDX | BPF_MEMSX | BPF_W:
15763d06d816SPu Lehui 	case BPF_LDX | BPF_PROBE_MEMSX | BPF_B:
15773d06d816SPu Lehui 	case BPF_LDX | BPF_PROBE_MEMSX | BPF_H:
15783d06d816SPu Lehui 	case BPF_LDX | BPF_PROBE_MEMSX | BPF_W:
1579252c765bSTong Tiangen 	{
1580252c765bSTong Tiangen 		int insn_len, insns_start;
15813d06d816SPu Lehui 		bool sign_ext;
15823d06d816SPu Lehui 
15833d06d816SPu Lehui 		sign_ext = BPF_MODE(insn->code) == BPF_MEMSX ||
15843d06d816SPu Lehui 			   BPF_MODE(insn->code) == BPF_PROBE_MEMSX;
1585252c765bSTong Tiangen 
1586252c765bSTong Tiangen 		switch (BPF_SIZE(code)) {
1587252c765bSTong Tiangen 		case BPF_B:
1588ca6cb544SLuke Nelson 			if (is_12b_int(off)) {
1589252c765bSTong Tiangen 				insns_start = ctx->ninsns;
15903d06d816SPu Lehui 				if (sign_ext)
15913d06d816SPu Lehui 					emit(rv_lb(rd, off, rs), ctx);
15923d06d816SPu Lehui 				else
1593252c765bSTong Tiangen 					emit(rv_lbu(rd, off, rs), ctx);
1594252c765bSTong Tiangen 				insn_len = ctx->ninsns - insns_start;
1595ca6cb544SLuke Nelson 				break;
1596ca6cb544SLuke Nelson 			}
1597ca6cb544SLuke Nelson 
1598ca6cb544SLuke Nelson 			emit_imm(RV_REG_T1, off, ctx);
159918a4d8c9SLuke Nelson 			emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
1600252c765bSTong Tiangen 			insns_start = ctx->ninsns;
16013d06d816SPu Lehui 			if (sign_ext)
16023d06d816SPu Lehui 				emit(rv_lb(rd, 0, RV_REG_T1), ctx);
16033d06d816SPu Lehui 			else
1604252c765bSTong Tiangen 				emit(rv_lbu(rd, 0, RV_REG_T1), ctx);
1605252c765bSTong Tiangen 			insn_len = ctx->ninsns - insns_start;
1606ca6cb544SLuke Nelson 			break;
1607252c765bSTong Tiangen 		case BPF_H:
1608ca6cb544SLuke Nelson 			if (is_12b_int(off)) {
1609252c765bSTong Tiangen 				insns_start = ctx->ninsns;
16103d06d816SPu Lehui 				if (sign_ext)
16113d06d816SPu Lehui 					emit(rv_lh(rd, off, rs), ctx);
16123d06d816SPu Lehui 				else
1613252c765bSTong Tiangen 					emit(rv_lhu(rd, off, rs), ctx);
1614252c765bSTong Tiangen 				insn_len = ctx->ninsns - insns_start;
1615ca6cb544SLuke Nelson 				break;
1616ca6cb544SLuke Nelson 			}
1617ca6cb544SLuke Nelson 
1618ca6cb544SLuke Nelson 			emit_imm(RV_REG_T1, off, ctx);
161918a4d8c9SLuke Nelson 			emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
1620252c765bSTong Tiangen 			insns_start = ctx->ninsns;
16213d06d816SPu Lehui 			if (sign_ext)
16223d06d816SPu Lehui 				emit(rv_lh(rd, 0, RV_REG_T1), ctx);
16233d06d816SPu Lehui 			else
1624252c765bSTong Tiangen 				emit(rv_lhu(rd, 0, RV_REG_T1), ctx);
1625252c765bSTong Tiangen 			insn_len = ctx->ninsns - insns_start;
1626ca6cb544SLuke Nelson 			break;
1627252c765bSTong Tiangen 		case BPF_W:
1628252c765bSTong Tiangen 			if (is_12b_int(off)) {
1629252c765bSTong Tiangen 				insns_start = ctx->ninsns;
16303d06d816SPu Lehui 				if (sign_ext)
16313d06d816SPu Lehui 					emit(rv_lw(rd, off, rs), ctx);
16323d06d816SPu Lehui 				else
1633252c765bSTong Tiangen 					emit(rv_lwu(rd, off, rs), ctx);
1634252c765bSTong Tiangen 				insn_len = ctx->ninsns - insns_start;
1635252c765bSTong Tiangen 				break;
1636252c765bSTong Tiangen 			}
1637ca6cb544SLuke Nelson 
1638252c765bSTong Tiangen 			emit_imm(RV_REG_T1, off, ctx);
1639252c765bSTong Tiangen 			emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
1640252c765bSTong Tiangen 			insns_start = ctx->ninsns;
16413d06d816SPu Lehui 			if (sign_ext)
16423d06d816SPu Lehui 				emit(rv_lw(rd, 0, RV_REG_T1), ctx);
16433d06d816SPu Lehui 			else
1644252c765bSTong Tiangen 				emit(rv_lwu(rd, 0, RV_REG_T1), ctx);
1645252c765bSTong Tiangen 			insn_len = ctx->ninsns - insns_start;
1646252c765bSTong Tiangen 			break;
1647252c765bSTong Tiangen 		case BPF_DW:
1648252c765bSTong Tiangen 			if (is_12b_int(off)) {
1649252c765bSTong Tiangen 				insns_start = ctx->ninsns;
1650252c765bSTong Tiangen 				emit_ld(rd, off, rs, ctx);
1651252c765bSTong Tiangen 				insn_len = ctx->ninsns - insns_start;
1652252c765bSTong Tiangen 				break;
1653252c765bSTong Tiangen 			}
1654252c765bSTong Tiangen 
1655252c765bSTong Tiangen 			emit_imm(RV_REG_T1, off, ctx);
1656252c765bSTong Tiangen 			emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
1657252c765bSTong Tiangen 			insns_start = ctx->ninsns;
1658252c765bSTong Tiangen 			emit_ld(rd, 0, RV_REG_T1, ctx);
1659252c765bSTong Tiangen 			insn_len = ctx->ninsns - insns_start;
1660252c765bSTong Tiangen 			break;
1661252c765bSTong Tiangen 		}
1662252c765bSTong Tiangen 
1663252c765bSTong Tiangen 		ret = add_exception_handler(insn, ctx, rd, insn_len);
1664252c765bSTong Tiangen 		if (ret)
1665252c765bSTong Tiangen 			return ret;
1666469fb2c3SPu Lehui 
1667469fb2c3SPu Lehui 		if (BPF_SIZE(code) != BPF_DW && insn_is_zext(&insn[1]))
1668469fb2c3SPu Lehui 			return 1;
1669252c765bSTong Tiangen 		break;
1670252c765bSTong Tiangen 	}
1671f5e81d11SDaniel Borkmann 	/* speculation barrier */
1672f5e81d11SDaniel Borkmann 	case BPF_ST | BPF_NOSPEC:
1673f5e81d11SDaniel Borkmann 		break;
1674f5e81d11SDaniel Borkmann 
1675ca6cb544SLuke Nelson 	/* ST: *(size *)(dst + off) = imm */
1676ca6cb544SLuke Nelson 	case BPF_ST | BPF_MEM | BPF_B:
1677ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, imm, ctx);
1678ca6cb544SLuke Nelson 		if (is_12b_int(off)) {
1679ca6cb544SLuke Nelson 			emit(rv_sb(rd, off, RV_REG_T1), ctx);
1680ca6cb544SLuke Nelson 			break;
1681ca6cb544SLuke Nelson 		}
1682ca6cb544SLuke Nelson 
1683ca6cb544SLuke Nelson 		emit_imm(RV_REG_T2, off, ctx);
168418a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, rd, ctx);
1685ca6cb544SLuke Nelson 		emit(rv_sb(RV_REG_T2, 0, RV_REG_T1), ctx);
1686ca6cb544SLuke Nelson 		break;
1687ca6cb544SLuke Nelson 
1688ca6cb544SLuke Nelson 	case BPF_ST | BPF_MEM | BPF_H:
1689ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, imm, ctx);
1690ca6cb544SLuke Nelson 		if (is_12b_int(off)) {
1691ca6cb544SLuke Nelson 			emit(rv_sh(rd, off, RV_REG_T1), ctx);
1692ca6cb544SLuke Nelson 			break;
1693ca6cb544SLuke Nelson 		}
1694ca6cb544SLuke Nelson 
1695ca6cb544SLuke Nelson 		emit_imm(RV_REG_T2, off, ctx);
169618a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, rd, ctx);
1697ca6cb544SLuke Nelson 		emit(rv_sh(RV_REG_T2, 0, RV_REG_T1), ctx);
1698ca6cb544SLuke Nelson 		break;
1699ca6cb544SLuke Nelson 	case BPF_ST | BPF_MEM | BPF_W:
1700ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, imm, ctx);
1701ca6cb544SLuke Nelson 		if (is_12b_int(off)) {
170218a4d8c9SLuke Nelson 			emit_sw(rd, off, RV_REG_T1, ctx);
1703ca6cb544SLuke Nelson 			break;
1704ca6cb544SLuke Nelson 		}
1705ca6cb544SLuke Nelson 
1706ca6cb544SLuke Nelson 		emit_imm(RV_REG_T2, off, ctx);
170718a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, rd, ctx);
170818a4d8c9SLuke Nelson 		emit_sw(RV_REG_T2, 0, RV_REG_T1, ctx);
1709ca6cb544SLuke Nelson 		break;
1710ca6cb544SLuke Nelson 	case BPF_ST | BPF_MEM | BPF_DW:
1711ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, imm, ctx);
1712ca6cb544SLuke Nelson 		if (is_12b_int(off)) {
171318a4d8c9SLuke Nelson 			emit_sd(rd, off, RV_REG_T1, ctx);
1714ca6cb544SLuke Nelson 			break;
1715ca6cb544SLuke Nelson 		}
1716ca6cb544SLuke Nelson 
1717ca6cb544SLuke Nelson 		emit_imm(RV_REG_T2, off, ctx);
171818a4d8c9SLuke Nelson 		emit_add(RV_REG_T2, RV_REG_T2, rd, ctx);
171918a4d8c9SLuke Nelson 		emit_sd(RV_REG_T2, 0, RV_REG_T1, ctx);
1720ca6cb544SLuke Nelson 		break;
1721ca6cb544SLuke Nelson 
1722ca6cb544SLuke Nelson 	/* STX: *(size *)(dst + off) = src */
1723ca6cb544SLuke Nelson 	case BPF_STX | BPF_MEM | BPF_B:
1724ca6cb544SLuke Nelson 		if (is_12b_int(off)) {
1725ca6cb544SLuke Nelson 			emit(rv_sb(rd, off, rs), ctx);
1726ca6cb544SLuke Nelson 			break;
1727ca6cb544SLuke Nelson 		}
1728ca6cb544SLuke Nelson 
1729ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, off, ctx);
173018a4d8c9SLuke Nelson 		emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
1731ca6cb544SLuke Nelson 		emit(rv_sb(RV_REG_T1, 0, rs), ctx);
1732ca6cb544SLuke Nelson 		break;
1733ca6cb544SLuke Nelson 	case BPF_STX | BPF_MEM | BPF_H:
1734ca6cb544SLuke Nelson 		if (is_12b_int(off)) {
1735ca6cb544SLuke Nelson 			emit(rv_sh(rd, off, rs), ctx);
1736ca6cb544SLuke Nelson 			break;
1737ca6cb544SLuke Nelson 		}
1738ca6cb544SLuke Nelson 
1739ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, off, ctx);
174018a4d8c9SLuke Nelson 		emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
1741ca6cb544SLuke Nelson 		emit(rv_sh(RV_REG_T1, 0, rs), ctx);
1742ca6cb544SLuke Nelson 		break;
1743ca6cb544SLuke Nelson 	case BPF_STX | BPF_MEM | BPF_W:
1744ca6cb544SLuke Nelson 		if (is_12b_int(off)) {
174518a4d8c9SLuke Nelson 			emit_sw(rd, off, rs, ctx);
1746ca6cb544SLuke Nelson 			break;
1747ca6cb544SLuke Nelson 		}
1748ca6cb544SLuke Nelson 
1749ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, off, ctx);
175018a4d8c9SLuke Nelson 		emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
175118a4d8c9SLuke Nelson 		emit_sw(RV_REG_T1, 0, rs, ctx);
1752ca6cb544SLuke Nelson 		break;
1753ca6cb544SLuke Nelson 	case BPF_STX | BPF_MEM | BPF_DW:
1754ca6cb544SLuke Nelson 		if (is_12b_int(off)) {
175518a4d8c9SLuke Nelson 			emit_sd(rd, off, rs, ctx);
1756ca6cb544SLuke Nelson 			break;
1757ca6cb544SLuke Nelson 		}
1758ca6cb544SLuke Nelson 
1759ca6cb544SLuke Nelson 		emit_imm(RV_REG_T1, off, ctx);
176018a4d8c9SLuke Nelson 		emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
176118a4d8c9SLuke Nelson 		emit_sd(RV_REG_T1, 0, rs, ctx);
1762ca6cb544SLuke Nelson 		break;
176391c960b0SBrendan Jackman 	case BPF_STX | BPF_ATOMIC | BPF_W:
176491c960b0SBrendan Jackman 	case BPF_STX | BPF_ATOMIC | BPF_DW:
1765dd642ccbSPu Lehui 		emit_atomic(rd, rs, off, imm,
1766dd642ccbSPu Lehui 			    BPF_SIZE(code) == BPF_DW, ctx);
1767ca6cb544SLuke Nelson 		break;
1768ca6cb544SLuke Nelson 	default:
1769ca6cb544SLuke Nelson 		pr_err("bpf-jit: unknown opcode %02x\n", code);
1770ca6cb544SLuke Nelson 		return -EINVAL;
1771ca6cb544SLuke Nelson 	}
1772ca6cb544SLuke Nelson 
1773ca6cb544SLuke Nelson 	return 0;
1774ca6cb544SLuke Nelson }
1775ca6cb544SLuke Nelson 
bpf_jit_build_prologue(struct rv_jit_context * ctx)1776ca6cb544SLuke Nelson void bpf_jit_build_prologue(struct rv_jit_context *ctx)
1777ca6cb544SLuke Nelson {
1778596f2e6fSPu Lehui 	int i, stack_adjust = 0, store_offset, bpf_stack_adjust;
1779ca6cb544SLuke Nelson 
1780ca6cb544SLuke Nelson 	bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16);
1781ca6cb544SLuke Nelson 	if (bpf_stack_adjust)
1782ca6cb544SLuke Nelson 		mark_fp(ctx);
1783ca6cb544SLuke Nelson 
1784ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_RA, ctx))
1785ca6cb544SLuke Nelson 		stack_adjust += 8;
1786ca6cb544SLuke Nelson 	stack_adjust += 8; /* RV_REG_FP */
1787ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S1, ctx))
1788ca6cb544SLuke Nelson 		stack_adjust += 8;
1789ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S2, ctx))
1790ca6cb544SLuke Nelson 		stack_adjust += 8;
1791ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S3, ctx))
1792ca6cb544SLuke Nelson 		stack_adjust += 8;
1793ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S4, ctx))
1794ca6cb544SLuke Nelson 		stack_adjust += 8;
1795ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S5, ctx))
1796ca6cb544SLuke Nelson 		stack_adjust += 8;
1797ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S6, ctx))
1798ca6cb544SLuke Nelson 		stack_adjust += 8;
1799ca6cb544SLuke Nelson 
1800ca6cb544SLuke Nelson 	stack_adjust = round_up(stack_adjust, 16);
1801ca6cb544SLuke Nelson 	stack_adjust += bpf_stack_adjust;
1802ca6cb544SLuke Nelson 
1803ca6cb544SLuke Nelson 	store_offset = stack_adjust - 8;
1804ca6cb544SLuke Nelson 
180525ad1065SPu Lehui 	/* nops reserved for auipc+jalr pair */
180625ad1065SPu Lehui 	for (i = 0; i < RV_FENTRY_NINSNS; i++)
1807596f2e6fSPu Lehui 		emit(rv_nop(), ctx);
1808596f2e6fSPu Lehui 
1809ca6cb544SLuke Nelson 	/* First instruction is always setting the tail-call-counter
1810ca6cb544SLuke Nelson 	 * (TCC) register. This instruction is skipped for tail calls.
181118a4d8c9SLuke Nelson 	 * Force using a 4-byte (non-compressed) instruction.
1812ca6cb544SLuke Nelson 	 */
1813ca6cb544SLuke Nelson 	emit(rv_addi(RV_REG_TCC, RV_REG_ZERO, MAX_TAIL_CALL_CNT), ctx);
1814ca6cb544SLuke Nelson 
181518a4d8c9SLuke Nelson 	emit_addi(RV_REG_SP, RV_REG_SP, -stack_adjust, ctx);
1816ca6cb544SLuke Nelson 
1817ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_RA, ctx)) {
181818a4d8c9SLuke Nelson 		emit_sd(RV_REG_SP, store_offset, RV_REG_RA, ctx);
1819ca6cb544SLuke Nelson 		store_offset -= 8;
1820ca6cb544SLuke Nelson 	}
182118a4d8c9SLuke Nelson 	emit_sd(RV_REG_SP, store_offset, RV_REG_FP, ctx);
1822ca6cb544SLuke Nelson 	store_offset -= 8;
1823ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S1, ctx)) {
182418a4d8c9SLuke Nelson 		emit_sd(RV_REG_SP, store_offset, RV_REG_S1, ctx);
1825ca6cb544SLuke Nelson 		store_offset -= 8;
1826ca6cb544SLuke Nelson 	}
1827ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S2, ctx)) {
182818a4d8c9SLuke Nelson 		emit_sd(RV_REG_SP, store_offset, RV_REG_S2, ctx);
1829ca6cb544SLuke Nelson 		store_offset -= 8;
1830ca6cb544SLuke Nelson 	}
1831ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S3, ctx)) {
183218a4d8c9SLuke Nelson 		emit_sd(RV_REG_SP, store_offset, RV_REG_S3, ctx);
1833ca6cb544SLuke Nelson 		store_offset -= 8;
1834ca6cb544SLuke Nelson 	}
1835ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S4, ctx)) {
183618a4d8c9SLuke Nelson 		emit_sd(RV_REG_SP, store_offset, RV_REG_S4, ctx);
1837ca6cb544SLuke Nelson 		store_offset -= 8;
1838ca6cb544SLuke Nelson 	}
1839ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S5, ctx)) {
184018a4d8c9SLuke Nelson 		emit_sd(RV_REG_SP, store_offset, RV_REG_S5, ctx);
1841ca6cb544SLuke Nelson 		store_offset -= 8;
1842ca6cb544SLuke Nelson 	}
1843ca6cb544SLuke Nelson 	if (seen_reg(RV_REG_S6, ctx)) {
184418a4d8c9SLuke Nelson 		emit_sd(RV_REG_SP, store_offset, RV_REG_S6, ctx);
1845ca6cb544SLuke Nelson 		store_offset -= 8;
1846ca6cb544SLuke Nelson 	}
1847ca6cb544SLuke Nelson 
184818a4d8c9SLuke Nelson 	emit_addi(RV_REG_FP, RV_REG_SP, stack_adjust, ctx);
1849ca6cb544SLuke Nelson 
1850ca6cb544SLuke Nelson 	if (bpf_stack_adjust)
185118a4d8c9SLuke Nelson 		emit_addi(RV_REG_S5, RV_REG_SP, bpf_stack_adjust, ctx);
1852ca6cb544SLuke Nelson 
1853ca6cb544SLuke Nelson 	/* Program contains calls and tail calls, so RV_REG_TCC need
1854ca6cb544SLuke Nelson 	 * to be saved across calls.
1855ca6cb544SLuke Nelson 	 */
1856ca6cb544SLuke Nelson 	if (seen_tail_call(ctx) && seen_call(ctx))
185718a4d8c9SLuke Nelson 		emit_mv(RV_REG_TCC_SAVED, RV_REG_TCC, ctx);
1858ca6cb544SLuke Nelson 
1859ca6cb544SLuke Nelson 	ctx->stack_size = stack_adjust;
1860ca6cb544SLuke Nelson }
1861ca6cb544SLuke Nelson 
bpf_jit_build_epilogue(struct rv_jit_context * ctx)1862ca6cb544SLuke Nelson void bpf_jit_build_epilogue(struct rv_jit_context *ctx)
1863ca6cb544SLuke Nelson {
1864ca6cb544SLuke Nelson 	__build_epilogue(false, ctx);
1865ca6cb544SLuke Nelson }
1866d40c3847SPu Lehui 
bpf_jit_supports_kfunc_call(void)1867d40c3847SPu Lehui bool bpf_jit_supports_kfunc_call(void)
1868d40c3847SPu Lehui {
1869d40c3847SPu Lehui 	return true;
1870d40c3847SPu Lehui }
1871