Merge tag 'docs-5.11-2' of git://git.lwn.net/linux
[linux-2.6-microblaze.git] / kernel / bpf / disasm.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
3  * Copyright (c) 2016 Facebook
4  */
5
6 #include <linux/bpf.h>
7
8 #include "disasm.h"
9
10 #define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x)
11 static const char * const func_id_str[] = {
12         __BPF_FUNC_MAPPER(__BPF_FUNC_STR_FN)
13 };
14 #undef __BPF_FUNC_STR_FN
15
16 static const char *__func_get_name(const struct bpf_insn_cbs *cbs,
17                                    const struct bpf_insn *insn,
18                                    char *buff, size_t len)
19 {
20         BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_ID);
21
22         if (insn->src_reg != BPF_PSEUDO_CALL &&
23             insn->imm >= 0 && insn->imm < __BPF_FUNC_MAX_ID &&
24             func_id_str[insn->imm])
25                 return func_id_str[insn->imm];
26
27         if (cbs && cbs->cb_call)
28                 return cbs->cb_call(cbs->private_data, insn);
29
30         if (insn->src_reg == BPF_PSEUDO_CALL)
31                 snprintf(buff, len, "%+d", insn->imm);
32
33         return buff;
34 }
35
36 static const char *__func_imm_name(const struct bpf_insn_cbs *cbs,
37                                    const struct bpf_insn *insn,
38                                    u64 full_imm, char *buff, size_t len)
39 {
40         if (cbs && cbs->cb_imm)
41                 return cbs->cb_imm(cbs->private_data, insn, full_imm);
42
43         snprintf(buff, len, "0x%llx", (unsigned long long)full_imm);
44         return buff;
45 }
46
47 const char *func_id_name(int id)
48 {
49         if (id >= 0 && id < __BPF_FUNC_MAX_ID && func_id_str[id])
50                 return func_id_str[id];
51         else
52                 return "unknown";
53 }
54
55 const char *const bpf_class_string[8] = {
56         [BPF_LD]    = "ld",
57         [BPF_LDX]   = "ldx",
58         [BPF_ST]    = "st",
59         [BPF_STX]   = "stx",
60         [BPF_ALU]   = "alu",
61         [BPF_JMP]   = "jmp",
62         [BPF_JMP32] = "jmp32",
63         [BPF_ALU64] = "alu64",
64 };
65
66 const char *const bpf_alu_string[16] = {
67         [BPF_ADD >> 4]  = "+=",
68         [BPF_SUB >> 4]  = "-=",
69         [BPF_MUL >> 4]  = "*=",
70         [BPF_DIV >> 4]  = "/=",
71         [BPF_OR  >> 4]  = "|=",
72         [BPF_AND >> 4]  = "&=",
73         [BPF_LSH >> 4]  = "<<=",
74         [BPF_RSH >> 4]  = ">>=",
75         [BPF_NEG >> 4]  = "neg",
76         [BPF_MOD >> 4]  = "%=",
77         [BPF_XOR >> 4]  = "^=",
78         [BPF_MOV >> 4]  = "=",
79         [BPF_ARSH >> 4] = "s>>=",
80         [BPF_END >> 4]  = "endian",
81 };
82
83 static const char *const bpf_ldst_string[] = {
84         [BPF_W >> 3]  = "u32",
85         [BPF_H >> 3]  = "u16",
86         [BPF_B >> 3]  = "u8",
87         [BPF_DW >> 3] = "u64",
88 };
89
90 static const char *const bpf_jmp_string[16] = {
91         [BPF_JA >> 4]   = "jmp",
92         [BPF_JEQ >> 4]  = "==",
93         [BPF_JGT >> 4]  = ">",
94         [BPF_JLT >> 4]  = "<",
95         [BPF_JGE >> 4]  = ">=",
96         [BPF_JLE >> 4]  = "<=",
97         [BPF_JSET >> 4] = "&",
98         [BPF_JNE >> 4]  = "!=",
99         [BPF_JSGT >> 4] = "s>",
100         [BPF_JSLT >> 4] = "s<",
101         [BPF_JSGE >> 4] = "s>=",
102         [BPF_JSLE >> 4] = "s<=",
103         [BPF_CALL >> 4] = "call",
104         [BPF_EXIT >> 4] = "exit",
105 };
106
107 static void print_bpf_end_insn(bpf_insn_print_t verbose,
108                                void *private_data,
109                                const struct bpf_insn *insn)
110 {
111         verbose(private_data, "(%02x) r%d = %s%d r%d\n",
112                 insn->code, insn->dst_reg,
113                 BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le",
114                 insn->imm, insn->dst_reg);
115 }
116
117 void print_bpf_insn(const struct bpf_insn_cbs *cbs,
118                     const struct bpf_insn *insn,
119                     bool allow_ptr_leaks)
120 {
121         const bpf_insn_print_t verbose = cbs->cb_print;
122         u8 class = BPF_CLASS(insn->code);
123
124         if (class == BPF_ALU || class == BPF_ALU64) {
125                 if (BPF_OP(insn->code) == BPF_END) {
126                         if (class == BPF_ALU64)
127                                 verbose(cbs->private_data, "BUG_alu64_%02x\n", insn->code);
128                         else
129                                 print_bpf_end_insn(verbose, cbs->private_data, insn);
130                 } else if (BPF_OP(insn->code) == BPF_NEG) {
131                         verbose(cbs->private_data, "(%02x) %c%d = -%c%d\n",
132                                 insn->code, class == BPF_ALU ? 'w' : 'r',
133                                 insn->dst_reg, class == BPF_ALU ? 'w' : 'r',
134                                 insn->dst_reg);
135                 } else if (BPF_SRC(insn->code) == BPF_X) {
136                         verbose(cbs->private_data, "(%02x) %c%d %s %c%d\n",
137                                 insn->code, class == BPF_ALU ? 'w' : 'r',
138                                 insn->dst_reg,
139                                 bpf_alu_string[BPF_OP(insn->code) >> 4],
140                                 class == BPF_ALU ? 'w' : 'r',
141                                 insn->src_reg);
142                 } else {
143                         verbose(cbs->private_data, "(%02x) %c%d %s %d\n",
144                                 insn->code, class == BPF_ALU ? 'w' : 'r',
145                                 insn->dst_reg,
146                                 bpf_alu_string[BPF_OP(insn->code) >> 4],
147                                 insn->imm);
148                 }
149         } else if (class == BPF_STX) {
150                 if (BPF_MODE(insn->code) == BPF_MEM)
151                         verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d\n",
152                                 insn->code,
153                                 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
154                                 insn->dst_reg,
155                                 insn->off, insn->src_reg);
156                 else if (BPF_MODE(insn->code) == BPF_XADD)
157                         verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) += r%d\n",
158                                 insn->code,
159                                 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
160                                 insn->dst_reg, insn->off,
161                                 insn->src_reg);
162                 else
163                         verbose(cbs->private_data, "BUG_%02x\n", insn->code);
164         } else if (class == BPF_ST) {
165                 if (BPF_MODE(insn->code) != BPF_MEM) {
166                         verbose(cbs->private_data, "BUG_st_%02x\n", insn->code);
167                         return;
168                 }
169                 verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = %d\n",
170                         insn->code,
171                         bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
172                         insn->dst_reg,
173                         insn->off, insn->imm);
174         } else if (class == BPF_LDX) {
175                 if (BPF_MODE(insn->code) != BPF_MEM) {
176                         verbose(cbs->private_data, "BUG_ldx_%02x\n", insn->code);
177                         return;
178                 }
179                 verbose(cbs->private_data, "(%02x) r%d = *(%s *)(r%d %+d)\n",
180                         insn->code, insn->dst_reg,
181                         bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
182                         insn->src_reg, insn->off);
183         } else if (class == BPF_LD) {
184                 if (BPF_MODE(insn->code) == BPF_ABS) {
185                         verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[%d]\n",
186                                 insn->code,
187                                 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
188                                 insn->imm);
189                 } else if (BPF_MODE(insn->code) == BPF_IND) {
190                         verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[r%d + %d]\n",
191                                 insn->code,
192                                 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
193                                 insn->src_reg, insn->imm);
194                 } else if (BPF_MODE(insn->code) == BPF_IMM &&
195                            BPF_SIZE(insn->code) == BPF_DW) {
196                         /* At this point, we already made sure that the second
197                          * part of the ldimm64 insn is accessible.
198                          */
199                         u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm;
200                         bool is_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD ||
201                                       insn->src_reg == BPF_PSEUDO_MAP_VALUE;
202                         char tmp[64];
203
204                         if (is_ptr && !allow_ptr_leaks)
205                                 imm = 0;
206
207                         verbose(cbs->private_data, "(%02x) r%d = %s\n",
208                                 insn->code, insn->dst_reg,
209                                 __func_imm_name(cbs, insn, imm,
210                                                 tmp, sizeof(tmp)));
211                 } else {
212                         verbose(cbs->private_data, "BUG_ld_%02x\n", insn->code);
213                         return;
214                 }
215         } else if (class == BPF_JMP32 || class == BPF_JMP) {
216                 u8 opcode = BPF_OP(insn->code);
217
218                 if (opcode == BPF_CALL) {
219                         char tmp[64];
220
221                         if (insn->src_reg == BPF_PSEUDO_CALL) {
222                                 verbose(cbs->private_data, "(%02x) call pc%s\n",
223                                         insn->code,
224                                         __func_get_name(cbs, insn,
225                                                         tmp, sizeof(tmp)));
226                         } else {
227                                 strcpy(tmp, "unknown");
228                                 verbose(cbs->private_data, "(%02x) call %s#%d\n", insn->code,
229                                         __func_get_name(cbs, insn,
230                                                         tmp, sizeof(tmp)),
231                                         insn->imm);
232                         }
233                 } else if (insn->code == (BPF_JMP | BPF_JA)) {
234                         verbose(cbs->private_data, "(%02x) goto pc%+d\n",
235                                 insn->code, insn->off);
236                 } else if (insn->code == (BPF_JMP | BPF_EXIT)) {
237                         verbose(cbs->private_data, "(%02x) exit\n", insn->code);
238                 } else if (BPF_SRC(insn->code) == BPF_X) {
239                         verbose(cbs->private_data,
240                                 "(%02x) if %c%d %s %c%d goto pc%+d\n",
241                                 insn->code, class == BPF_JMP32 ? 'w' : 'r',
242                                 insn->dst_reg,
243                                 bpf_jmp_string[BPF_OP(insn->code) >> 4],
244                                 class == BPF_JMP32 ? 'w' : 'r',
245                                 insn->src_reg, insn->off);
246                 } else {
247                         verbose(cbs->private_data,
248                                 "(%02x) if %c%d %s 0x%x goto pc%+d\n",
249                                 insn->code, class == BPF_JMP32 ? 'w' : 'r',
250                                 insn->dst_reg,
251                                 bpf_jmp_string[BPF_OP(insn->code) >> 4],
252                                 insn->imm, insn->off);
253                 }
254         } else {
255                 verbose(cbs->private_data, "(%02x) %s\n",
256                         insn->code, bpf_class_string[class]);
257         }
258 }