]>
Commit | Line | Data |
---|---|---|
b26e2ae7 JM |
1 | /* eBPF simulator support code |
2 | Copyright (C) 2020 Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of GDB, the GNU debugger. | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
18 | ||
19 | #define WANT_CPU_BPFBF | |
20 | #define WANT_CPU bpfbf | |
21 | ||
22 | #include "sim-main.h" | |
23 | #include "sim-fpu.h" | |
24 | #include "cgen-mem.h" | |
25 | #include "cgen-ops.h" | |
26 | #include "cpuall.h" | |
27 | #include "decode.h" | |
28 | ||
29 | #include "defs-le.h" /* For SCACHE */ | |
30 | ||
31 | /* It is not possible to include both defs-le.h and defs-be.h due to | |
32 | duplicated definitions, so we need a bunch of forward declarations | |
33 | here. */ | |
34 | extern void bpfbf_ebpfle_init_idesc_table (SIM_CPU *); | |
35 | extern void bpfbf_ebpfbe_init_idesc_table (SIM_CPU *); | |
36 | ||
37 | uint64_t skb_data_offset; | |
38 | ||
39 | IDESC *bpf_idesc_le; | |
40 | IDESC *bpf_idesc_be; | |
41 | ||
42 | ||
43 | int | |
44 | bpfbf_fetch_register (SIM_CPU *current_cpu, | |
45 | int rn, | |
46 | unsigned char *buf, | |
47 | int len) | |
48 | { | |
49 | if (rn == 11) | |
50 | SETTDI (buf, CPU_PC_GET (current_cpu)); | |
51 | else if (0 <= rn && rn < 10) | |
52 | SETTDI (buf, GET_H_GPR (rn)); | |
53 | else | |
54 | return 0; | |
55 | ||
56 | return len; | |
57 | } | |
58 | ||
59 | int | |
60 | bpfbf_store_register (SIM_CPU *current_cpu, | |
61 | int rn, | |
62 | unsigned char *buf, | |
63 | int len) | |
64 | { | |
65 | if (rn == 11) | |
66 | CPU_PC_SET (current_cpu, GETTDI (buf)); | |
67 | else if (0 <= rn && rn < 10) | |
68 | SET_H_GPR (rn, GETTDI (buf)); | |
69 | else | |
70 | return 0; | |
71 | ||
72 | return len; | |
73 | } | |
74 | ||
75 | void | |
76 | bpfbf_model_insn_before (SIM_CPU *current_cpu, int first_p) | |
77 | { | |
78 | /* XXX */ | |
79 | } | |
80 | ||
81 | void | |
82 | bpfbf_model_insn_after (SIM_CPU *current_cpu, int first_p) | |
83 | { | |
84 | /* XXX */ | |
85 | } | |
86 | ||
87 | \f | |
88 | /***** Instruction helpers. *****/ | |
89 | ||
90 | /* The semantic routines for most instructions are expressed in RTL in | |
91 | the cpu/bpf.cpu file, and automatically translated to C in the | |
92 | sem-*.c files in this directory. | |
93 | ||
94 | However, some of the semantic routines make use of helper C | |
95 | functions. This happens when the semantics of the instructions | |
96 | can't be expressed in RTL alone in a satisfactory way, or not at | |
97 | all. | |
98 | ||
99 | The following functions implement these C helpers. */ | |
100 | ||
101 | DI | |
102 | bpfbf_endle (SIM_CPU *current_cpu, DI value, UINT bitsize) | |
103 | { | |
104 | switch (bitsize) | |
105 | { | |
106 | case 16: return endian_h2le_2(endian_t2h_2(value)); | |
107 | case 32: return endian_h2le_4(endian_t2h_4(value)); | |
108 | case 64: return endian_h2le_8(endian_t2h_8(value)); | |
109 | default: assert(0); | |
110 | } | |
111 | return value; | |
112 | } | |
113 | ||
114 | DI | |
115 | bpfbf_endbe (SIM_CPU *current_cpu, DI value, UINT bitsize) | |
116 | { | |
117 | switch (bitsize) | |
118 | { | |
119 | case 16: return endian_h2be_2(endian_t2h_2(value)); | |
120 | case 32: return endian_h2be_4(endian_t2h_4(value)); | |
121 | case 64: return endian_h2be_8(endian_t2h_8(value)); | |
122 | default: assert(0); | |
123 | } | |
124 | return value; | |
125 | } | |
126 | ||
127 | DI | |
128 | bpfbf_skb_data_offset (SIM_CPU *current_cpu) | |
129 | { | |
130 | /* Simply return the user-configured value. | |
131 | This will be 0 if it has not been set. */ | |
132 | return skb_data_offset; | |
133 | } | |
134 | ||
135 | ||
136 | VOID | |
137 | bpfbf_call (SIM_CPU *current_cpu, INT disp32, UINT src) | |
138 | { | |
139 | /* eBPF supports two kind of CALL instructions: the so called pseudo | |
140 | calls ("bpf to bpf") and external calls ("bpf to helper"). | |
141 | ||
142 | Both kind of calls use the same instruction (CALL). However, | |
143 | external calls are constructed by passing a constant argument to | |
144 | the instruction, that identifies the helper, whereas pseudo calls | |
145 | result from expressions involving symbols. | |
146 | ||
147 | We distinguish calls from pseudo-calls with the later having a 1 | |
148 | stored in the SRC field of the instruction. */ | |
149 | ||
150 | if (src == 1) | |
151 | { | |
152 | /* This is a pseudo-call. */ | |
153 | ||
154 | /* XXX allocate a new stack frame and transfer control. For | |
155 | that we need to analyze the target function, like the kernel | |
156 | verifier does. We better populate a cache | |
157 | (function_start_address -> frame_size) so we avoid | |
158 | calculating this more than once. */ | |
159 | /* XXX note that disp32 is PC-relative in number of 64-bit | |
160 | words, _minus one_. */ | |
161 | } | |
162 | else | |
163 | { | |
164 | /* This is a call to a helper. | |
165 | ||
166 | DISP32 contains the helper number. Dispatch to the | |
167 | corresponding helper emulator in bpf-helpers.c. */ | |
168 | ||
169 | switch (disp32) { | |
170 | /* case TRACE_PRINTK: */ | |
171 | case 7: | |
172 | bpf_trace_printk (current_cpu); | |
173 | break; | |
174 | default:; | |
175 | } | |
176 | } | |
177 | } | |
178 | ||
179 | VOID | |
180 | bpfbf_exit (SIM_CPU *current_cpu) | |
181 | { | |
182 | SIM_DESC sd = CPU_STATE (current_cpu); | |
183 | ||
184 | /* r0 holds "return code" */ | |
185 | DI r0 = GET_H_GPR (0); | |
186 | ||
187 | printf ("exit %ld (0x%lx)\n", r0, r0); | |
188 | ||
189 | sim_engine_halt (sd, current_cpu, NULL, CPU_PC_GET (current_cpu), | |
190 | sim_exited, 0 /* sigrc */); | |
191 | } | |
192 | ||
193 | VOID | |
194 | bpfbf_breakpoint (SIM_CPU *current_cpu) | |
195 | { | |
196 | SIM_DESC sd = CPU_STATE (current_cpu); | |
197 | ||
198 | sim_engine_halt (sd, current_cpu, NULL, CPU_PC_GET (current_cpu), | |
199 | sim_stopped, SIM_SIGTRAP); | |
200 | } | |
201 | ||
202 | /* We use the definitions below instead of the cgen-generated model.c, | |
203 | because the later is not really able to work with cpus featuring | |
204 | several ISAs. This should be fixed in CGEN. */ | |
205 | ||
206 | static void | |
207 | bpf_def_model_init () | |
208 | { | |
209 | /* Do nothing. */ | |
210 | } | |
211 | ||
212 | static void | |
213 | bpfbf_prepare_run (SIM_CPU *cpu) | |
214 | { | |
215 | /* Nothing. */ | |
216 | } | |
217 | ||
218 | void | |
219 | bpf_engine_run_full (SIM_CPU *cpu) | |
220 | { | |
221 | if (current_target_byte_order == BFD_ENDIAN_LITTLE) | |
222 | { | |
223 | if (!bpf_idesc_le) | |
224 | { | |
225 | bpfbf_ebpfle_init_idesc_table (cpu); | |
226 | bpf_idesc_le = CPU_IDESC (cpu); | |
227 | } | |
228 | else | |
229 | CPU_IDESC (cpu) = bpf_idesc_le; | |
230 | ||
231 | bpfbf_ebpfle_engine_run_full (cpu); | |
232 | } | |
233 | else | |
234 | { | |
235 | if (!bpf_idesc_be) | |
236 | { | |
237 | bpfbf_ebpfbe_init_idesc_table (cpu); | |
238 | bpf_idesc_be = CPU_IDESC (cpu); | |
239 | } | |
240 | else | |
241 | CPU_IDESC (cpu) = bpf_idesc_be; | |
242 | ||
243 | bpfbf_ebpfbe_engine_run_full (cpu); | |
244 | } | |
245 | } | |
246 | ||
247 | #if WITH_FAST | |
248 | ||
249 | void | |
250 | bpf_engine_run_fast (SIM_CPU *cpu) | |
251 | { | |
252 | if (current_target_byte_order == BFD_ENDIAN_LITTLE) | |
253 | { | |
254 | if (!bpf_idesc_le) | |
255 | { | |
256 | bpfbf_ebpfle_init_idesc_table (cpu); | |
257 | bpf_idesc_le = CPU_IDESC (cpu); | |
258 | } | |
259 | else | |
260 | CPU_IDESC (cpu) = bpf_idesc_le; | |
261 | ||
262 | bpfbf_ebpfle_engine_run_fast (cpu); | |
263 | } | |
264 | else | |
265 | { | |
266 | if (!bpf_idesc_be) | |
267 | { | |
268 | bpfbf_ebpfbe_init_idesc_table (cpu); | |
269 | bpf_idesc_be = CPU_IDESC (cpu); | |
270 | } | |
271 | else | |
272 | CPU_IDESC (cpu) = bpf_idesc_be; | |
273 | ||
274 | bpfbf_ebpfbe_engine_run_fast (cpu); | |
275 | } | |
276 | } | |
277 | ||
278 | #endif /* WITH_FAST */ | |
279 | ||
280 | static const CGEN_INSN * | |
281 | bpfbf_get_idata (SIM_CPU *cpu, int inum) | |
282 | { | |
283 | return CPU_IDESC (cpu) [inum].idata; | |
284 | } | |
285 | ||
286 | static void | |
287 | bpf_init_cpu (SIM_CPU *cpu) | |
288 | { | |
289 | CPU_REG_FETCH (cpu) = bpfbf_fetch_register; | |
290 | CPU_REG_STORE (cpu) = bpfbf_store_register; | |
291 | CPU_PC_FETCH (cpu) = bpfbf_h_pc_get; | |
292 | CPU_PC_STORE (cpu) = bpfbf_h_pc_set; | |
293 | CPU_GET_IDATA (cpu) = bpfbf_get_idata; | |
294 | /* Only used by profiling. 0 disables it. */ | |
295 | CPU_MAX_INSNS (cpu) = 0; | |
296 | CPU_INSN_NAME (cpu) = cgen_insn_name; | |
297 | CPU_FULL_ENGINE_FN (cpu) = bpf_engine_run_full; | |
298 | #if WITH_FAST | |
299 | CPU_FAST_ENGINE_FN (cpu) = bpf_engine_run_fast; | |
300 | #else | |
301 | CPU_FAST_ENGINE_FN (cpu) = bpf_engine_run_full; | |
302 | #endif | |
303 | } | |
304 | ||
305 | static const SIM_MODEL bpf_models[] = | |
306 | { | |
307 | { "bpf-def", & bpf_mach, MODEL_BPF_DEF, NULL, bpf_def_model_init }, | |
308 | { 0 } | |
309 | }; | |
310 | ||
311 | static const SIM_MACH_IMP_PROPERTIES bpfbf_imp_properties = | |
312 | { | |
313 | sizeof (SIM_CPU), | |
314 | #if WITH_SCACHE | |
315 | sizeof (SCACHE) | |
316 | #else | |
317 | 0 | |
318 | #endif | |
319 | }; | |
320 | ||
321 | const SIM_MACH bpf_mach = | |
322 | { | |
323 | "bpf", "bpf", MACH_BPF, | |
324 | 32, 32, & bpf_models[0], & bpfbf_imp_properties, | |
325 | bpf_init_cpu, | |
326 | bpfbf_prepare_run | |
327 | }; |