xref: /freebsd/sys/amd64/amd64/bpf_jit_machdep.c (revision 39beb93c)
1 /*-
2  * Copyright (C) 2002-2003 NetGroup, Politecnico di Torino (Italy)
3  * Copyright (C) 2005-2008 Jung-uk Kim <jkim@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the Politecnico di Torino nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #ifdef _KERNEL
36 #include "opt_bpf.h"
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/socket.h>
41 #include <sys/malloc.h>
42 #include <net/if.h>
43 #else
44 #include <stdlib.h>
45 #endif
46 
47 #include <sys/types.h>
48 
49 #include <net/bpf.h>
50 #include <net/bpf_jitter.h>
51 
52 #include <amd64/amd64/bpf_jit_machdep.h>
53 
54 bpf_filter_func	bpf_jit_compile(struct bpf_insn *, u_int, int *);
55 
56 /*
57  * emit routine to update the jump table
58  */
59 static void
60 emit_length(bpf_bin_stream *stream, __unused u_int value, u_int len)
61 {
62 
63 	(stream->refs)[stream->bpf_pc] += len;
64 	stream->cur_ip += len;
65 }
66 
67 /*
68  * emit routine to output the actual binary code
69  */
70 static void
71 emit_code(bpf_bin_stream *stream, u_int value, u_int len)
72 {
73 
74 	switch (len) {
75 	case 1:
76 		stream->ibuf[stream->cur_ip] = (u_char)value;
77 		stream->cur_ip++;
78 		break;
79 
80 	case 2:
81 		*((u_short *)(stream->ibuf + stream->cur_ip)) = (u_short)value;
82 		stream->cur_ip += 2;
83 		break;
84 
85 	case 4:
86 		*((u_int *)(stream->ibuf + stream->cur_ip)) = value;
87 		stream->cur_ip += 4;
88 		break;
89 	}
90 
91 	return;
92 }
93 
94 /*
95  * Function that does the real stuff
96  */
97 bpf_filter_func
98 bpf_jit_compile(struct bpf_insn *prog, u_int nins, int *mem)
99 {
100 	struct bpf_insn *ins;
101 	u_int i, pass;
102 	bpf_bin_stream stream;
103 
104 	/*
105 	 * NOTE: do not modify the name of this variable, as it's used by
106 	 * the macros to emit code.
107 	 */
108 	emit_func emitm;
109 
110 	/* Allocate the reference table for the jumps */
111 #ifdef _KERNEL
112 	stream.refs = (u_int *)malloc((nins + 1) * sizeof(u_int),
113 	    M_BPFJIT, M_NOWAIT);
114 #else
115 	stream.refs = (u_int *)malloc((nins + 1) * sizeof(u_int));
116 #endif
117 	if (stream.refs == NULL)
118 		return (NULL);
119 
120 	/* Reset the reference table */
121 	for (i = 0; i < nins + 1; i++)
122 		stream.refs[i] = 0;
123 
124 	stream.cur_ip = 0;
125 	stream.bpf_pc = 0;
126 
127 	/*
128 	 * the first pass will emit the lengths of the instructions
129 	 * to create the reference table
130 	 */
131 	emitm = emit_length;
132 
133 	pass = 0;
134 	for (;;) {
135 		ins = prog;
136 
137 		/* create the procedure header */
138 		MOVrq2(RBX, R8);
139 		MOVrq(RDI, RBX);
140 		MOVrd2(ESI, R9D);
141 		MOVrd(EDX, EDI);
142 
143 		for (i = 0; i < nins; i++) {
144 			stream.bpf_pc++;
145 
146 			switch (ins->code) {
147 			default:
148 #ifdef _KERNEL
149 				return (NULL);
150 #else
151 				abort();
152 #endif
153 
154 			case BPF_RET|BPF_K:
155 				MOVid(ins->k, EAX);
156 				MOVrq3(R8, RBX);
157 				RET();
158 				break;
159 
160 			case BPF_RET|BPF_A:
161 				MOVrq3(R8, RBX);
162 				RET();
163 				break;
164 
165 			case BPF_LD|BPF_W|BPF_ABS:
166 				MOVid(ins->k, ESI);
167 				CMPrd(EDI, ESI);
168 				JAb(12);
169 				MOVrd(EDI, ECX);
170 				SUBrd(ESI, ECX);
171 				CMPid(sizeof(int32_t), ECX);
172 				JAEb(6);
173 				ZEROrd(EAX);
174 				MOVrq3(R8, RBX);
175 				RET();
176 				MOVobd(RBX, RSI, EAX);
177 				BSWAP(EAX);
178 				break;
179 
180 			case BPF_LD|BPF_H|BPF_ABS:
181 				ZEROrd(EAX);
182 				MOVid(ins->k, ESI);
183 				CMPrd(EDI, ESI);
184 				JAb(12);
185 				MOVrd(EDI, ECX);
186 				SUBrd(ESI, ECX);
187 				CMPid(sizeof(int16_t), ECX);
188 				JAEb(4);
189 				MOVrq3(R8, RBX);
190 				RET();
191 				MOVobw(RBX, RSI, AX);
192 				SWAP_AX();
193 				break;
194 
195 			case BPF_LD|BPF_B|BPF_ABS:
196 				ZEROrd(EAX);
197 				MOVid(ins->k, ESI);
198 				CMPrd(EDI, ESI);
199 				JBb(4);
200 				MOVrq3(R8, RBX);
201 				RET();
202 				MOVobb(RBX, RSI, AL);
203 				break;
204 
205 			case BPF_LD|BPF_W|BPF_LEN:
206 				MOVrd3(R9D, EAX);
207 				break;
208 
209 			case BPF_LDX|BPF_W|BPF_LEN:
210 				MOVrd3(R9D, EDX);
211 				break;
212 
213 			case BPF_LD|BPF_W|BPF_IND:
214 				CMPrd(EDI, EDX);
215 				JAb(27);
216 				MOVid(ins->k, ESI);
217 				MOVrd(EDI, ECX);
218 				SUBrd(EDX, ECX);
219 				CMPrd(ESI, ECX);
220 				JBb(14);
221 				ADDrd(EDX, ESI);
222 				MOVrd(EDI, ECX);
223 				SUBrd(ESI, ECX);
224 				CMPid(sizeof(int32_t), ECX);
225 				JAEb(6);
226 				ZEROrd(EAX);
227 				MOVrq3(R8, RBX);
228 				RET();
229 				MOVobd(RBX, RSI, EAX);
230 				BSWAP(EAX);
231 				break;
232 
233 			case BPF_LD|BPF_H|BPF_IND:
234 				ZEROrd(EAX);
235 				CMPrd(EDI, EDX);
236 				JAb(27);
237 				MOVid(ins->k, ESI);
238 				MOVrd(EDI, ECX);
239 				SUBrd(EDX, ECX);
240 				CMPrd(ESI, ECX);
241 				JBb(14);
242 				ADDrd(EDX, ESI);
243 				MOVrd(EDI, ECX);
244 				SUBrd(ESI, ECX);
245 				CMPid(sizeof(int16_t), ECX);
246 				JAEb(4);
247 				MOVrq3(R8, RBX);
248 				RET();
249 				MOVobw(RBX, RSI, AX);
250 				SWAP_AX();
251 				break;
252 
253 			case BPF_LD|BPF_B|BPF_IND:
254 				ZEROrd(EAX);
255 				CMPrd(EDI, EDX);
256 				JAEb(13);
257 				MOVid(ins->k, ESI);
258 				MOVrd(EDI, ECX);
259 				SUBrd(EDX, ECX);
260 				CMPrd(ESI, ECX);
261 				JAb(4);
262 				MOVrq3(R8, RBX);
263 				RET();
264 				ADDrd(EDX, ESI);
265 				MOVobb(RBX, RSI, AL);
266 				break;
267 
268 			case BPF_LDX|BPF_MSH|BPF_B:
269 				MOVid(ins->k, ESI);
270 				CMPrd(EDI, ESI);
271 				JBb(6);
272 				ZEROrd(EAX);
273 				MOVrq3(R8, RBX);
274 				RET();
275 				ZEROrd(EDX);
276 				MOVobb(RBX, RSI, DL);
277 				ANDib(0x0f, DL);
278 				SHLib(2, EDX);
279 				break;
280 
281 			case BPF_LD|BPF_IMM:
282 				MOVid(ins->k, EAX);
283 				break;
284 
285 			case BPF_LDX|BPF_IMM:
286 				MOVid(ins->k, EDX);
287 				break;
288 
289 			case BPF_LD|BPF_MEM:
290 				MOViq((uintptr_t)mem, RCX);
291 				MOVid(ins->k * 4, ESI);
292 				MOVobd(RCX, RSI, EAX);
293 				break;
294 
295 			case BPF_LDX|BPF_MEM:
296 				MOViq((uintptr_t)mem, RCX);
297 				MOVid(ins->k * 4, ESI);
298 				MOVobd(RCX, RSI, EDX);
299 				break;
300 
301 			case BPF_ST:
302 				/*
303 				 * XXX this command and the following could
304 				 * be optimized if the previous instruction
305 				 * was already of this type
306 				 */
307 				MOViq((uintptr_t)mem, RCX);
308 				MOVid(ins->k * 4, ESI);
309 				MOVomd(EAX, RCX, RSI);
310 				break;
311 
312 			case BPF_STX:
313 				MOViq((uintptr_t)mem, RCX);
314 				MOVid(ins->k * 4, ESI);
315 				MOVomd(EDX, RCX, RSI);
316 				break;
317 
318 			case BPF_JMP|BPF_JA:
319 				JMP(stream.refs[stream.bpf_pc + ins->k] -
320 				    stream.refs[stream.bpf_pc]);
321 				break;
322 
323 			case BPF_JMP|BPF_JGT|BPF_K:
324 				if (ins->jt == 0 && ins->jf == 0)
325 					break;
326 				CMPid(ins->k, EAX);
327 				JCC(JA, JBE);
328 				break;
329 
330 			case BPF_JMP|BPF_JGE|BPF_K:
331 				if (ins->jt == 0 && ins->jf == 0)
332 					break;
333 				CMPid(ins->k, EAX);
334 				JCC(JAE, JB);
335 				break;
336 
337 			case BPF_JMP|BPF_JEQ|BPF_K:
338 				if (ins->jt == 0 && ins->jf == 0)
339 					break;
340 				CMPid(ins->k, EAX);
341 				JCC(JE, JNE);
342 				break;
343 
344 			case BPF_JMP|BPF_JSET|BPF_K:
345 				if (ins->jt == 0 && ins->jf == 0)
346 					break;
347 				TESTid(ins->k, EAX);
348 				JCC(JNE, JE);
349 				break;
350 
351 			case BPF_JMP|BPF_JGT|BPF_X:
352 				if (ins->jt == 0 && ins->jf == 0)
353 					break;
354 				CMPrd(EDX, EAX);
355 				JCC(JA, JBE);
356 				break;
357 
358 			case BPF_JMP|BPF_JGE|BPF_X:
359 				if (ins->jt == 0 && ins->jf == 0)
360 					break;
361 				CMPrd(EDX, EAX);
362 				JCC(JAE, JB);
363 				break;
364 
365 			case BPF_JMP|BPF_JEQ|BPF_X:
366 				if (ins->jt == 0 && ins->jf == 0)
367 					break;
368 				CMPrd(EDX, EAX);
369 				JCC(JE, JNE);
370 				break;
371 
372 			case BPF_JMP|BPF_JSET|BPF_X:
373 				if (ins->jt == 0 && ins->jf == 0)
374 					break;
375 				TESTrd(EDX, EAX);
376 				JCC(JNE, JE);
377 				break;
378 
379 			case BPF_ALU|BPF_ADD|BPF_X:
380 				ADDrd(EDX, EAX);
381 				break;
382 
383 			case BPF_ALU|BPF_SUB|BPF_X:
384 				SUBrd(EDX, EAX);
385 				break;
386 
387 			case BPF_ALU|BPF_MUL|BPF_X:
388 				MOVrd(EDX, ECX);
389 				MULrd(EDX);
390 				MOVrd(ECX, EDX);
391 				break;
392 
393 			case BPF_ALU|BPF_DIV|BPF_X:
394 				TESTrd(EDX, EDX);
395 				JNEb(6);
396 				ZEROrd(EAX);
397 				MOVrq3(R8, RBX);
398 				RET();
399 				MOVrd(EDX, ECX);
400 				ZEROrd(EDX);
401 				DIVrd(ECX);
402 				MOVrd(ECX, EDX);
403 				break;
404 
405 			case BPF_ALU|BPF_AND|BPF_X:
406 				ANDrd(EDX, EAX);
407 				break;
408 
409 			case BPF_ALU|BPF_OR|BPF_X:
410 				ORrd(EDX, EAX);
411 				break;
412 
413 			case BPF_ALU|BPF_LSH|BPF_X:
414 				MOVrd(EDX, ECX);
415 				SHL_CLrb(EAX);
416 				break;
417 
418 			case BPF_ALU|BPF_RSH|BPF_X:
419 				MOVrd(EDX, ECX);
420 				SHR_CLrb(EAX);
421 				break;
422 
423 			case BPF_ALU|BPF_ADD|BPF_K:
424 				ADD_EAXi(ins->k);
425 				break;
426 
427 			case BPF_ALU|BPF_SUB|BPF_K:
428 				SUB_EAXi(ins->k);
429 				break;
430 
431 			case BPF_ALU|BPF_MUL|BPF_K:
432 				MOVrd(EDX, ECX);
433 				MOVid(ins->k, EDX);
434 				MULrd(EDX);
435 				MOVrd(ECX, EDX);
436 				break;
437 
438 			case BPF_ALU|BPF_DIV|BPF_K:
439 				MOVrd(EDX, ECX);
440 				ZEROrd(EDX);
441 				MOVid(ins->k, ESI);
442 				DIVrd(ESI);
443 				MOVrd(ECX, EDX);
444 				break;
445 
446 			case BPF_ALU|BPF_AND|BPF_K:
447 				ANDid(ins->k, EAX);
448 				break;
449 
450 			case BPF_ALU|BPF_OR|BPF_K:
451 				ORid(ins->k, EAX);
452 				break;
453 
454 			case BPF_ALU|BPF_LSH|BPF_K:
455 				SHLib((ins->k) & 0xff, EAX);
456 				break;
457 
458 			case BPF_ALU|BPF_RSH|BPF_K:
459 				SHRib((ins->k) & 0xff, EAX);
460 				break;
461 
462 			case BPF_ALU|BPF_NEG:
463 				NEGd(EAX);
464 				break;
465 
466 			case BPF_MISC|BPF_TAX:
467 				MOVrd(EAX, EDX);
468 				break;
469 
470 			case BPF_MISC|BPF_TXA:
471 				MOVrd(EDX, EAX);
472 				break;
473 			}
474 			ins++;
475 		}
476 
477 		pass++;
478 		if (pass == 2)
479 			break;
480 
481 #ifdef _KERNEL
482 		stream.ibuf = (char *)malloc(stream.cur_ip, M_BPFJIT, M_NOWAIT);
483 		if (stream.ibuf == NULL) {
484 			free(stream.refs, M_BPFJIT);
485 			return (NULL);
486 		}
487 #else
488 		stream.ibuf = (char *)malloc(stream.cur_ip);
489 		if (stream.ibuf == NULL) {
490 			free(stream.refs);
491 			return (NULL);
492 		}
493 #endif
494 
495 		/*
496 		 * modify the reference table to contain the offsets and
497 		 * not the lengths of the instructions
498 		 */
499 		for (i = 1; i < nins + 1; i++)
500 			stream.refs[i] += stream.refs[i - 1];
501 
502 		/* Reset the counters */
503 		stream.cur_ip = 0;
504 		stream.bpf_pc = 0;
505 
506 		/* the second pass creates the actual code */
507 		emitm = emit_code;
508 	}
509 
510 	/*
511 	 * the reference table is needed only during compilation,
512 	 * now we can free it
513 	 */
514 #ifdef _KERNEL
515 	free(stream.refs, M_BPFJIT);
516 #else
517 	free(stream.refs);
518 #endif
519 
520 	return ((bpf_filter_func)stream.ibuf);
521 }
522