summaryrefslogtreecommitdiffstats
path: root/arch/arc/net/bpf_jit.h
blob: ec44873c42d15418f129da4519536502a00a65a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * The interface that a back-end should provide to bpf_jit_core.c.
 *
 * Copyright (c) 2024 Synopsys Inc.
 * Author: Shahab Vahedi <shahab@synopsys.com>
 */

#ifndef _ARC_BPF_JIT_H
#define _ARC_BPF_JIT_H

#include <linux/bpf.h>
#include <linux/filter.h>

/* Print debug info and assert. */
//#define ARC_BPF_JIT_DEBUG

/* Determine the address type of the target. */
#ifdef CONFIG_ISA_ARCV2
#define ARC_ADDR u32
#endif

/*
 * For the translation of some BPF instructions, a temporary register
 * might be needed for some interim data.
 */
#define JIT_REG_TMP MAX_BPF_JIT_REG

/*
 * Buffer access: If buffer "b" is not NULL, advance by "n" bytes.
 *
 * This macro must be used in any place that potentially requires a
 * "buf + len". This way, we make sure that the "buf" argument for
 * the underlying "arc_*(buf, ...)" ends up as NULL instead of something
 * like "0+4" or "0+8", etc. Those "arc_*()" functions check their "buf"
 * value to decide if instructions should be emitted or not.
 */
#define BUF(b, n) (((b) != NULL) ? ((b) + (n)) : (b))

/************** Functions that the back-end must provide **************/
/* Extension for 32-bit operations. */
inline u8 zext(u8 *buf, u8 rd);
/***** Moves *****/
u8 mov_r32(u8 *buf, u8 rd, u8 rs, u8 sign_ext);
u8 mov_r32_i32(u8 *buf, u8 reg, s32 imm);
u8 mov_r64(u8 *buf, u8 rd, u8 rs, u8 sign_ext);
u8 mov_r64_i32(u8 *buf, u8 reg, s32 imm);
u8 mov_r64_i64(u8 *buf, u8 reg, u32 lo, u32 hi);
/***** Loads and stores *****/
u8 load_r(u8 *buf, u8 rd, u8 rs, s16 off, u8 size, bool sign_ext);
u8 store_r(u8 *buf, u8 rd, u8 rs, s16 off, u8 size);
u8 store_i(u8 *buf, s32 imm, u8 rd, s16 off, u8 size);
/***** Addition *****/
u8 add_r32(u8 *buf, u8 rd, u8 rs);
u8 add_r32_i32(u8 *buf, u8 rd, s32 imm);
u8 add_r64(u8 *buf, u8 rd, u8 rs);
u8 add_r64_i32(u8 *buf, u8 rd, s32 imm);
/***** Subtraction *****/
u8 sub_r32(u8 *buf, u8 rd, u8 rs);
u8 sub_r32_i32(u8 *buf, u8 rd, s32 imm);
u8 sub_r64(u8 *buf, u8 rd, u8 rs);
u8 sub_r64_i32(u8 *buf, u8 rd, s32 imm);
/***** Multiplication *****/
u8 mul_r32(u8 *buf, u8 rd, u8 rs);
u8 mul_r32_i32(u8 *buf, u8 rd, s32 imm);
u8 mul_r64(u8 *buf, u8 rd, u8 rs);
u8 mul_r64_i32(u8 *buf, u8 rd, s32 imm);
/***** Division *****/
u8 div_r32(u8 *buf, u8 rd, u8 rs, bool sign_ext);
u8 div_r32_i32(u8 *buf, u8 rd, s32 imm, bool sign_ext);
/***** Remainder *****/
u8 mod_r32(u8 *buf, u8 rd, u8 rs, bool sign_ext);
u8 mod_r32_i32(u8 *buf, u8 rd, s32 imm, bool sign_ext);
/***** Bitwise AND *****/
u8 and_r32(u8 *buf, u8 rd, u8 rs);
u8 and_r32_i32(u8 *buf, u8 rd, s32 imm);
u8 and_r64(u8 *buf, u8 rd, u8 rs);
u8 and_r64_i32(u8 *buf, u8 rd, s32 imm);
/***** Bitwise OR *****/
u8 or_r32(u8 *buf, u8 rd, u8 rs);
u8 or_r32_i32(u8 *buf, u8 rd, s32 imm);
u8 or_r64(u8 *buf, u8 rd, u8 rs);
u8 or_r64_i32(u8 *buf, u8 rd, s32 imm);
/***** Bitwise XOR *****/
u8 xor_r32(u8 *buf, u8 rd, u8 rs);
u8 xor_r32_i32(u8 *buf, u8 rd, s32 imm);
u8 xor_r64(u8 *buf, u8 rd, u8 rs);
u8 xor_r64_i32(u8 *buf, u8 rd, s32 imm);
/***** Bitwise Negate *****/
u8 neg_r32(u8 *buf, u8 r);
u8 neg_r64(u8 *buf, u8 r);
/***** Bitwise left shift *****/
u8 lsh_r32(u8 *buf, u8 rd, u8 rs);
u8 lsh_r32_i32(u8 *buf, u8 rd, u8 imm);
u8 lsh_r64(u8 *buf, u8 rd, u8 rs);
u8 lsh_r64_i32(u8 *buf, u8 rd, s32 imm);
/***** Bitwise right shift (logical) *****/
u8 rsh_r32(u8 *buf, u8 rd, u8 rs);
u8 rsh_r32_i32(u8 *buf, u8 rd, u8 imm);
u8 rsh_r64(u8 *buf, u8 rd, u8 rs);
u8 rsh_r64_i32(u8 *buf, u8 rd, s32 imm);
/***** Bitwise right shift (arithmetic) *****/
u8 arsh_r32(u8 *buf, u8 rd, u8 rs);
u8 arsh_r32_i32(u8 *buf, u8 rd, u8 imm);
u8 arsh_r64(u8 *buf, u8 rd, u8 rs);
u8 arsh_r64_i32(u8 *buf, u8 rd, s32 imm);
/***** Frame related *****/
u32 mask_for_used_regs(u8 bpf_reg, bool is_call);
u8 arc_prologue(u8 *buf, u32 usage, u16 frame_size);
u8 arc_epilogue(u8 *buf, u32 usage, u16 frame_size);
/***** Jumps *****/
/*
 * Different sorts of conditions (ARC enum as opposed to BPF_*).
 *
 * Do not change the order of enums here. ARC_CC_SLE+1 is used
 * to determine the number of JCCs.
 */
enum ARC_CC {
	ARC_CC_UGT = 0,		/* unsigned >  */
	ARC_CC_UGE,		/* unsigned >= */
	ARC_CC_ULT,		/* unsigned <  */
	ARC_CC_ULE,		/* unsigned <= */
	ARC_CC_SGT,		/*   signed >  */
	ARC_CC_SGE,		/*   signed >= */
	ARC_CC_SLT,		/*   signed <  */
	ARC_CC_SLE,		/*   signed <= */
	ARC_CC_AL,		/* always      */
	ARC_CC_EQ,		/*          == */
	ARC_CC_NE,		/*          != */
	ARC_CC_SET,		/* test        */
	ARC_CC_LAST
};

/*
 * A few notes:
 *
 * - check_jmp_*() are prerequisites before calling the gen_jmp_*().
 *   They return "true" if the jump is possible and "false" otherwise.
 *
 * - The notion of "*_off" is to emphasize that these parameters are
 *   merely offsets in the JIT stream and not absolute addresses. One
 *   can look at them as addresses if the JIT code would start from
 *   address 0x0000_0000. Nonetheless, since the buffer address for the
 *   JIT is on a word-aligned address, this works and actually makes
 *   things simpler (offsets are in the range of u32 which is more than
 *   enough).
 */
bool check_jmp_32(u32 curr_off, u32 targ_off, u8 cond);
bool check_jmp_64(u32 curr_off, u32 targ_off, u8 cond);
u8 gen_jmp_32(u8 *buf, u8 rd, u8 rs, u8 cond, u32 c_off, u32 t_off);
u8 gen_jmp_64(u8 *buf, u8 rd, u8 rs, u8 cond, u32 c_off, u32 t_off);
/***** Miscellaneous *****/
u8 gen_func_call(u8 *buf, ARC_ADDR func_addr, bool external_func);
u8 arc_to_bpf_return(u8 *buf);
/*
 * - Perform byte swaps on "rd" based on the "size".
 * - If "force" is set, do it unconditionally. Otherwise, consider the
 *   desired "endian"ness and the host endianness.
 * - For data "size"s up to 32 bits, perform a zero-extension if asked
 *   by the "do_zext" boolean.
 */
u8 gen_swap(u8 *buf, u8 rd, u8 size, u8 endian, bool force, bool do_zext);

#endif /* _ARC_BPF_JIT_H */