Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2021 Stephen Hemminger
3 : : * Based on filter2xdp
4 : : * Copyright (C) 2017 Tobias Klauser
5 : : */
6 : :
7 : : #include <stdio.h>
8 : : #include <stdint.h>
9 : :
10 : : #include <eal_export.h>
11 : : #include "rte_bpf.h"
12 : :
13 : : #define BPF_OP_INDEX(x) (BPF_OP(x) >> 4)
14 : : #define BPF_SIZE_INDEX(x) (BPF_SIZE(x) >> 3)
15 : :
16 : : static const char *const class_tbl[] = {
17 : : [BPF_LD] = "ld", [BPF_LDX] = "ldx", [BPF_ST] = "st",
18 : : [BPF_STX] = "stx", [BPF_ALU] = "alu", [BPF_JMP] = "jmp",
19 : : [BPF_RET] = "ret", [BPF_MISC] = "alu64",
20 : : };
21 : :
22 : : static const char *const alu_op_tbl[16] = {
23 : : [BPF_ADD >> 4] = "add", [BPF_SUB >> 4] = "sub",
24 : : [BPF_MUL >> 4] = "mul", [BPF_DIV >> 4] = "div",
25 : : [BPF_OR >> 4] = "or", [BPF_AND >> 4] = "and",
26 : : [BPF_LSH >> 4] = "lsh", [BPF_RSH >> 4] = "rsh",
27 : : [BPF_NEG >> 4] = "neg", [BPF_MOD >> 4] = "mod",
28 : : [BPF_XOR >> 4] = "xor", [EBPF_MOV >> 4] = "mov",
29 : : [EBPF_ARSH >> 4] = "arsh", [EBPF_END >> 4] = "endian",
30 : : };
31 : :
32 : : static const char *const size_tbl[] = {
33 : : [BPF_W >> 3] = "w",
34 : : [BPF_H >> 3] = "h",
35 : : [BPF_B >> 3] = "b",
36 : : [EBPF_DW >> 3] = "dw",
37 : : };
38 : :
39 : : static const char *const jump_tbl[16] = {
40 : : [BPF_JA >> 4] = "ja", [BPF_JEQ >> 4] = "jeq",
41 : : [BPF_JGT >> 4] = "jgt", [BPF_JGE >> 4] = "jge",
42 : : [BPF_JSET >> 4] = "jset", [EBPF_JNE >> 4] = "jne",
43 : : [EBPF_JSGT >> 4] = "jsgt", [EBPF_JSGE >> 4] = "jsge",
44 : : [EBPF_CALL >> 4] = "call", [EBPF_EXIT >> 4] = "exit",
45 : : [EBPF_JLT >> 4] = "jlt", [EBPF_JLE >> 4] = "jle",
46 : : [EBPF_JSLT >> 4] = "jslt", [EBPF_JSLE >> 4] = "jsle",
47 : : };
48 : :
49 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_insn_is_wide, 26.07)
50 : : bool
51 : 1758 : rte_bpf_insn_is_wide(const struct ebpf_insn *ins)
52 : : {
53 : 1758 : return ins->code == (BPF_LD | BPF_IMM | EBPF_DW);
54 : : }
55 : :
56 : :
57 : : /* Format one (possibly wide) eBPF command as hexadecimal in objdump format. */
58 : : static int
59 : 0 : format_hexadecimal(char *buffer, size_t bufsz, const struct ebpf_insn *ins,
60 : : uint32_t flags)
61 : : {
62 : : const char *const b = (const char *)ins;
63 : :
64 : : RTE_ASSERT((flags & RTE_BPF_FORMAT_FLAG_HEXADECIMAL) != 0);
65 : :
66 : : RTE_BUILD_BUG_ON(sizeof(*ins) != 8);
67 : :
68 [ # # # # ]: 0 : if ((flags & RTE_BPF_FORMAT_FLAG_NEVER_WIDE) == 0 && rte_bpf_insn_is_wide(ins))
69 : 0 : return snprintf(buffer, bufsz,
70 : : "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx "
71 : : "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx",
72 : 0 : b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
73 : 0 : b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
74 : : else
75 : 0 : return snprintf(buffer, bufsz,
76 : : "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx",
77 : 0 : b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
78 : : }
79 : :
80 : : /* Return atomic subcommand mnemonic based on BPF_STX immediate. */
81 : : static inline const char *
82 : : atomic_op(int32_t imm)
83 : : {
84 : 0 : switch (imm) {
85 : : case BPF_ATOMIC_ADD:
86 : : return "xadd";
87 : 0 : case BPF_ATOMIC_XCHG:
88 : 0 : return "xchg";
89 : : default:
90 : : return NULL;
91 : : }
92 : : }
93 : :
94 : : /* Format one (possibly wide) eBPF command as assembler. */
95 : : static int
96 : 1758 : format_disassembly(char *buffer, size_t bufsz, const struct ebpf_insn *ins,
97 : : uint32_t pc, uint32_t flags)
98 : : {
99 : 1758 : uint8_t cls = BPF_CLASS(ins->code);
100 : : const char *op, *postfix = "", *warning = "";
101 : : char jump[16];
102 : :
103 : : RTE_ASSERT((flags & RTE_BPF_FORMAT_FLAG_HEXADECIMAL) == 0);
104 : :
105 [ + + + + : 1758 : switch (cls) {
- + + - ]
106 : : default:
107 : : return snprintf(buffer, bufsz, "unimp 0x%x // class: %s",
108 : : ins->code, class_tbl[cls]);
109 : 316 : case BPF_ALU:
110 : : postfix = "32";
111 : : /* fall through */
112 : 584 : case EBPF_ALU64:
113 : 584 : op = alu_op_tbl[BPF_OP_INDEX(ins->code)];
114 [ - + ]: 584 : if (ins->off != 0)
115 : : /* Not yet supported variation with non-zero offset. */
116 : : warning = ", off != 0";
117 [ + + ]: 584 : if (BPF_SRC(ins->code) == BPF_X)
118 : 284 : return snprintf(buffer, bufsz, "%s%s r%u, r%u%s", op, postfix, ins->dst_reg,
119 : 284 : ins->src_reg, warning);
120 : : else
121 : 300 : return snprintf(buffer, bufsz, "%s%s r%u, #0x%x%s", op, postfix,
122 : 300 : ins->dst_reg, ins->imm, warning);
123 : 470 : case BPF_LD:
124 : : op = "ld";
125 : 470 : postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
126 [ - + ]: 470 : if (ins->code == (BPF_LD | BPF_IMM | EBPF_DW)) {
127 : : uint64_t val;
128 : :
129 [ # # ]: 0 : if (ins->src_reg != 0)
130 : : /* Not yet supported variation with non-zero src. */
131 : : warning = ", src != 0";
132 : 0 : val = (uint32_t)ins[0].imm |
133 : 0 : (uint64_t)(uint32_t)ins[1].imm << 32;
134 : 0 : return snprintf(buffer, bufsz, "%s%s r%d, #0x%"PRIx64"%s",
135 : 0 : op, postfix, ins->dst_reg, val, warning);
136 : : }
137 [ - + + - ]: 470 : switch (BPF_MODE(ins->code)) {
138 : 0 : case BPF_IMM:
139 : 0 : return snprintf(buffer, bufsz, "%s%s r%d, #0x%x", op, postfix,
140 : 0 : ins->dst_reg, ins->imm);
141 : 416 : case BPF_ABS:
142 : 416 : return snprintf(buffer, bufsz, "%s%s r%d, [%d]", op, postfix,
143 : 416 : ins->dst_reg, ins->imm);
144 : 54 : case BPF_IND:
145 : 54 : return snprintf(buffer, bufsz, "%s%s r%d, [r%u + %d]", op, postfix,
146 : 54 : ins->dst_reg, ins->src_reg, ins->imm);
147 : 0 : default:
148 : 0 : return snprintf(buffer, bufsz, "// BUG: LD opcode 0x%02x in eBPF insns",
149 : : ins->code);
150 : : }
151 : 6 : case BPF_LDX:
152 : : op = "ldx";
153 : 6 : postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
154 [ + - ]: 6 : if (BPF_MODE(ins->code) == BPF_MEM)
155 : 6 : return snprintf(buffer, bufsz, "%s%s r%d, [r%u + %d]", op, postfix,
156 : 6 : ins->dst_reg, ins->src_reg, ins->off);
157 : : else
158 : 0 : return snprintf(buffer, bufsz, "// BUG: LDX opcode 0x%02x in eBPF insns",
159 : : ins->code);
160 : 0 : case BPF_ST:
161 : : op = "st";
162 : 0 : postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
163 [ # # ]: 0 : if (BPF_MODE(ins->code) == BPF_MEM)
164 : 0 : return snprintf(buffer, bufsz, "%s%s [r%d + %d], #0x%x", op, postfix,
165 : 0 : ins->dst_reg, ins->off, ins->imm);
166 : : else
167 : 0 : return snprintf(buffer, bufsz, "// BUG: ST opcode 0x%02x in eBPF insns",
168 : : ins->code);
169 : 4 : case BPF_STX:
170 [ - - + ]: 4 : switch (BPF_MODE(ins->code)) {
171 : : case BPF_MEM:
172 : : op = "stx";
173 : : break;
174 : 0 : case EBPF_ATOMIC:
175 [ # # # ]: 0 : op = atomic_op(ins->imm);
176 : : if (op == NULL)
177 : 0 : return snprintf(buffer, bufsz,
178 : : "// BUG: ATOMIC operation 0x%x in eBPF insns", ins->imm);
179 : : break;
180 : 0 : default:
181 : 0 : return snprintf(buffer, bufsz, "// BUG: STX opcode 0x%02x in eBPF insns",
182 : : ins->code);
183 : : }
184 : 4 : postfix = size_tbl[BPF_SIZE_INDEX(ins->code)];
185 : 4 : return snprintf(buffer, bufsz, "%s%s [r%d + %d], r%u", op, postfix,
186 : 4 : ins->dst_reg, ins->off, ins->src_reg);
187 : 694 : case BPF_JMP:
188 : 694 : op = jump_tbl[BPF_OP_INDEX(ins->code)];
189 [ - + ]: 694 : if (op == NULL)
190 : 0 : return snprintf(buffer, bufsz, "invalid jump opcode: %#x", ins->code);
191 : :
192 [ + - ]: 694 : if ((flags & RTE_BPF_FORMAT_FLAG_ABSOLUTE_JUMPS) != 0)
193 : 694 : snprintf(jump, sizeof(jump), "L%d", pc + 1 + ins->off);
194 : : else
195 : 0 : snprintf(jump, sizeof(jump), "%+d", (int)ins->off);
196 : :
197 [ + + ]: 694 : if (ins->src_reg != 0)
198 : : /* Not yet supported variation with non-zero src w/o condition. */
199 : : warning = ", src != 0";
200 [ + - + + ]: 694 : switch (BPF_OP(ins->code)) {
201 : : case BPF_JA:
202 : 40 : return snprintf(buffer, bufsz, "%s %s%s", op, jump, warning);
203 : 0 : case EBPF_CALL:
204 : : /* Call of helper function with index in immediate. */
205 : 0 : return snprintf(buffer, bufsz, "%s #%u%s", op, ins->imm, warning);
206 : : case EBPF_EXIT:
207 : 112 : return snprintf(buffer, bufsz, "%s%s", op, warning);
208 : : }
209 : :
210 [ + + ]: 542 : if (BPF_SRC(ins->code) == BPF_X)
211 : 70 : return snprintf(buffer, bufsz, "%s r%u, r%u, %s", op, ins->dst_reg,
212 : 70 : ins->src_reg, jump);
213 : : else
214 : 472 : return snprintf(buffer, bufsz, "%s r%u, #0x%x, %s", op, ins->dst_reg,
215 : 472 : ins->imm, jump);
216 : 0 : case BPF_RET:
217 : 0 : return snprintf(buffer, bufsz, "// BUG: RET opcode 0x%02x in eBPF insns",
218 : : ins->code);
219 : : }
220 : : }
221 : :
222 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_format, 26.07)
223 : : int
224 : 0 : rte_bpf_format(char *buffer, size_t bufsz, const struct ebpf_insn *ins,
225 : : uint32_t pc, uint32_t flags)
226 : : {
227 [ # # ]: 0 : if ((flags & RTE_BPF_FORMAT_FLAG_HEXADECIMAL) != 0)
228 : 0 : return format_hexadecimal(buffer, bufsz, ins, flags);
229 : : else
230 : 0 : return format_disassembly(buffer, bufsz, ins, pc, flags);
231 : : }
232 : :
233 : : RTE_EXPORT_SYMBOL(rte_bpf_dump)
234 : 56 : void rte_bpf_dump(FILE *f, const struct ebpf_insn *buf, uint32_t len)
235 : : {
236 : : uint32_t i;
237 : : char buffer[256];
238 : :
239 [ + + ]: 1814 : for (i = 0; i < len; ++i) {
240 : 1758 : const struct ebpf_insn *ins = buf + i;
241 : :
242 : 1758 : format_disassembly(buffer, sizeof(buffer), ins, i,
243 : : RTE_BPF_FORMAT_FLAG_DISASSEMBLY |
244 : : RTE_BPF_FORMAT_FLAG_ABSOLUTE_JUMPS);
245 : : fprintf(f, " L%u:\t%s\n", i, buffer);
246 : 1758 : i += rte_bpf_insn_is_wide(ins);
247 : : }
248 : 56 : }
|