bpf_jit_machdep.c revision 182173
1115013Smarcel/*-
2160157Smarcel * Copyright (C) 2002-2003 NetGroup, Politecnico di Torino (Italy)
3121642Smarcel * Copyright (C) 2005-2008 Jung-uk Kim <jkim@FreeBSD.org>
4121642Smarcel * All rights reserved.
5121642Smarcel *
6121642Smarcel * Redistribution and use in source and binary forms, with or without
7121642Smarcel * modification, are permitted provided that the following conditions
8121642Smarcel * are met:
9121642Smarcel *
10121642Smarcel * 1. Redistributions of source code must retain the above copyright
11115013Smarcel * notice, this list of conditions and the following disclaimer.
12121642Smarcel * 2. Redistributions in binary form must reproduce the above copyright
13121642Smarcel * notice, this list of conditions and the following disclaimer in the
14121642Smarcel * documentation and/or other materials provided with the distribution.
15121642Smarcel * 3. Neither the name of the Politecnico di Torino nor the names of its
16121642Smarcel * contributors may be used to endorse or promote products derived from
17121642Smarcel * this software without specific prior written permission.
18121642Smarcel *
19121642Smarcel * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20121642Smarcel * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21121642Smarcel * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22121642Smarcel * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23121642Smarcel * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24121642Smarcel * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25160163Smarcel * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26115013Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27160163Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28115013Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29115013Smarcel * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30115013Smarcel */
31115013Smarcel
32115013Smarcel#include <sys/cdefs.h>
33160163Smarcel__FBSDID("$FreeBSD: head/sys/i386/i386/bpf_jit_machdep.c 182173 2008-08-25 20:43:13Z jkim $");
34160163Smarcel
35160163Smarcel#ifdef _KERNEL
36115013Smarcel#include "opt_bpf.h"
37160163Smarcel#include <sys/param.h>
38160163Smarcel#include <sys/systm.h>
39160163Smarcel#include <sys/kernel.h>
40160163Smarcel#include <sys/socket.h>
41160163Smarcel#include <sys/malloc.h>
42160163Smarcel#include <net/if.h>
43160163Smarcel#else
44160163Smarcel#include <stdlib.h>
45160163Smarcel#endif
46160163Smarcel
47160163Smarcel#include <sys/types.h>
48160163Smarcel
49160163Smarcel#include <net/bpf.h>
50160163Smarcel#include <net/bpf_jitter.h>
51160163Smarcel
52160163Smarcel#include <i386/i386/bpf_jit_machdep.h>
53160163Smarcel
54160163Smarcelbpf_filter_func	bpf_jit_compile(struct bpf_insn *, u_int, int *);
55160163Smarcel
56160163Smarcel/*
57160163Smarcel * emit routine to update the jump table
58160163Smarcel */
59160163Smarcelstatic void
60160163Smarcelemit_length(bpf_bin_stream *stream, __unused u_int value, u_int len)
61160157Smarcel{
62129059Smarcel
63160157Smarcel	(stream->refs)[stream->bpf_pc] += len;
64160157Smarcel	stream->cur_ip += len;
65160157Smarcel}
66160157Smarcel
67160157Smarcel/*
68129059Smarcel * emit routine to output the actual binary code
69129059Smarcel */
70115013Smarcelstatic void
71115013Smarcelemit_code(bpf_bin_stream *stream, u_int value, u_int len)
72115013Smarcel{
73115013Smarcel
74115013Smarcel	switch (len) {
75115013Smarcel	case 1:
76115013Smarcel		stream->ibuf[stream->cur_ip] = (u_char)value;
77115013Smarcel		stream->cur_ip++;
78115013Smarcel		break;
79115013Smarcel
80115013Smarcel	case 2:
81115013Smarcel		*((u_short *)(stream->ibuf + stream->cur_ip)) = (u_short)value;
82115013Smarcel		stream->cur_ip += 2;
83115013Smarcel		break;
84115013Smarcel
85115013Smarcel	case 4:
86115013Smarcel		*((u_int *)(stream->ibuf + stream->cur_ip)) = value;
87115013Smarcel		stream->cur_ip += 4;
88115013Smarcel		break;
89115013Smarcel	}
90115013Smarcel
91160157Smarcel	return;
92115013Smarcel}
93115013Smarcel
94115013Smarcel/*
95115013Smarcel * Function that does the real stuff
96115013Smarcel */
97115013Smarcelbpf_filter_func
98115013Smarcelbpf_jit_compile(struct bpf_insn *prog, u_int nins, int *mem)
99115013Smarcel{
100115013Smarcel	struct bpf_insn *ins;
101115013Smarcel	u_int i, pass;
102120925Smarcel	bpf_bin_stream stream;
103120925Smarcel
104115013Smarcel	/*
105115013Smarcel	 * NOTE: do not modify the name of this variable, as it's used by
106115013Smarcel	 * the macros to emit code.
107115013Smarcel	 */
108160163Smarcel	emit_func emitm;
109115013Smarcel
110115013Smarcel	/* Do not compile an empty filter. */
111115013Smarcel	if (nins == 0)
112115013Smarcel		return (NULL);
113115013Smarcel
114115013Smarcel	/* Allocate the reference table for the jumps */
115115013Smarcel#ifdef _KERNEL
116115013Smarcel	stream.refs = (u_int *)malloc((nins + 1) * sizeof(u_int),
117115013Smarcel	    M_BPFJIT, M_NOWAIT);
118115013Smarcel#else
119115013Smarcel	stream.refs = (u_int *)malloc((nins + 1) * sizeof(u_int));
120115013Smarcel#endif
121115013Smarcel	if (stream.refs == NULL)
122115013Smarcel		return (NULL);
123115013Smarcel
124115013Smarcel	/* Reset the reference table */
125115013Smarcel	for (i = 0; i < nins + 1; i++)
126115013Smarcel		stream.refs[i] = 0;
127115013Smarcel
128115013Smarcel	stream.cur_ip = 0;
129115013Smarcel	stream.bpf_pc = 0;
130115013Smarcel
131115013Smarcel	/*
132115013Smarcel	 * the first pass will emit the lengths of the instructions
133115013Smarcel	 * to create the reference table
134115013Smarcel	 */
135115013Smarcel	emitm = emit_length;
136115013Smarcel
137115013Smarcel	pass = 0;
138115013Smarcel	for (;;) {
139115013Smarcel		ins = prog;
140115013Smarcel
141115013Smarcel		/* create the procedure header */
142115013Smarcel		PUSH(EBP);
143115013Smarcel		MOVrd(ESP, EBP);
144115013Smarcel		PUSH(EDI);
145115013Smarcel		PUSH(ESI);
146115013Smarcel		PUSH(EBX);
147115013Smarcel		MOVodd(8, EBP, EBX);
148115013Smarcel		MOVodd(16, EBP, EDI);
149115013Smarcel
150115013Smarcel		for (i = 0; i < nins; i++) {
151115013Smarcel			stream.bpf_pc++;
152115013Smarcel
153115013Smarcel			switch (ins->code) {
154115013Smarcel			default:
155115013Smarcel#ifdef _KERNEL
156115013Smarcel				return (NULL);
157115013Smarcel#else
158115013Smarcel				abort();
159115013Smarcel#endif
160115013Smarcel
161115013Smarcel			case BPF_RET|BPF_K:
162115013Smarcel				MOVid(ins->k, EAX);
163115013Smarcel				POP(EBX);
164115013Smarcel				POP(ESI);
165115013Smarcel				POP(EDI);
166115013Smarcel				LEAVE_RET();
167115013Smarcel				break;
168115013Smarcel
169115013Smarcel			case BPF_RET|BPF_A:
170115013Smarcel				POP(EBX);
171115013Smarcel				POP(ESI);
172115013Smarcel				POP(EDI);
173115013Smarcel				LEAVE_RET();
174115013Smarcel				break;
175115013Smarcel
176115013Smarcel			case BPF_LD|BPF_W|BPF_ABS:
177115013Smarcel				MOVid(ins->k, ESI);
178115013Smarcel				CMPrd(EDI, ESI);
179115013Smarcel				JAb(12);
180115013Smarcel				MOVrd(EDI, ECX);
181115013Smarcel				SUBrd(ESI, ECX);
182115013Smarcel				CMPid(sizeof(int32_t), ECX);
183160163Smarcel				JAEb(7);
184115013Smarcel				ZEROrd(EAX);
185115013Smarcel				POP(EBX);
186115013Smarcel				POP(ESI);
187115013Smarcel				POP(EDI);
188115013Smarcel				LEAVE_RET();
189115013Smarcel				MOVobd(EBX, ESI, EAX);
190115013Smarcel				BSWAP(EAX);
191115013Smarcel				break;
192115013Smarcel
193115013Smarcel			case BPF_LD|BPF_H|BPF_ABS:
194115013Smarcel				ZEROrd(EAX);
195115013Smarcel				MOVid(ins->k, ESI);
196115013Smarcel				CMPrd(EDI, ESI);
197115013Smarcel				JAb(12);
198115013Smarcel				MOVrd(EDI, ECX);
199115013Smarcel				SUBrd(ESI, ECX);
200115013Smarcel				CMPid(sizeof(int16_t), ECX);
201115013Smarcel				JAEb(5);
202115013Smarcel				POP(EBX);
203115013Smarcel				POP(ESI);
204115013Smarcel				POP(EDI);
205115013Smarcel				LEAVE_RET();
206115013Smarcel				MOVobw(EBX, ESI, AX);
207115013Smarcel				SWAP_AX();
208115013Smarcel				break;
209115013Smarcel
210115013Smarcel			case BPF_LD|BPF_B|BPF_ABS:
211115013Smarcel				ZEROrd(EAX);
212115013Smarcel				MOVid(ins->k, ESI);
213115013Smarcel				CMPrd(EDI, ESI);
214115013Smarcel				JBb(5);
215115013Smarcel				POP(EBX);
216115013Smarcel				POP(ESI);
217115013Smarcel				POP(EDI);
218115013Smarcel				LEAVE_RET();
219115013Smarcel				MOVobb(EBX, ESI, AL);
220115013Smarcel				break;
221115013Smarcel
222115013Smarcel			case BPF_LD|BPF_W|BPF_LEN:
223115013Smarcel				MOVodd(12, EBP, EAX);
224115013Smarcel				break;
225115013Smarcel
226115013Smarcel			case BPF_LDX|BPF_W|BPF_LEN:
227115013Smarcel				MOVodd(12, EBP, EDX);
228115013Smarcel				break;
229115013Smarcel
230115013Smarcel			case BPF_LD|BPF_W|BPF_IND:
231115013Smarcel				CMPrd(EDI, EDX);
232115013Smarcel				JAb(27);
233115013Smarcel				MOVid(ins->k, ESI);
234115013Smarcel				MOVrd(EDI, ECX);
235115013Smarcel				SUBrd(EDX, ECX);
236115013Smarcel				CMPrd(ESI, ECX);
237115013Smarcel				JBb(14);
238115013Smarcel				ADDrd(EDX, ESI);
239115013Smarcel				MOVrd(EDI, ECX);
240115013Smarcel				SUBrd(ESI, ECX);
241115013Smarcel				CMPid(sizeof(int32_t), ECX);
242115013Smarcel				JAEb(7);
243115013Smarcel				ZEROrd(EAX);
244115013Smarcel				POP(EBX);
245115013Smarcel				POP(ESI);
246115013Smarcel				POP(EDI);
247115013Smarcel				LEAVE_RET();
248115013Smarcel				MOVobd(EBX, ESI, EAX);
249115013Smarcel				BSWAP(EAX);
250115013Smarcel				break;
251115013Smarcel
252115013Smarcel			case BPF_LD|BPF_H|BPF_IND:
253115013Smarcel				ZEROrd(EAX);
254115013Smarcel				CMPrd(EDI, EDX);
255115013Smarcel				JAb(27);
256115013Smarcel				MOVid(ins->k, ESI);
257115013Smarcel				MOVrd(EDI, ECX);
258115013Smarcel				SUBrd(EDX, ECX);
259115013Smarcel				CMPrd(ESI, ECX);
260115013Smarcel				JBb(14);
261115013Smarcel				ADDrd(EDX, ESI);
262115013Smarcel				MOVrd(EDI, ECX);
263115013Smarcel				SUBrd(ESI, ECX);
264115013Smarcel				CMPid(sizeof(int16_t), ECX);
265115013Smarcel				JAEb(5);
266115013Smarcel				POP(EBX);
267160163Smarcel				POP(ESI);
268115013Smarcel				POP(EDI);
269115013Smarcel				LEAVE_RET();
270115013Smarcel				MOVobw(EBX, ESI, AX);
271115013Smarcel				SWAP_AX();
272115013Smarcel				break;
273115013Smarcel
274115013Smarcel			case BPF_LD|BPF_B|BPF_IND:
275115013Smarcel				ZEROrd(EAX);
276115013Smarcel				CMPrd(EDI, EDX);
277115013Smarcel				JAEb(13);
278115013Smarcel				MOVid(ins->k, ESI);
279115013Smarcel				MOVrd(EDI, ECX);
280115013Smarcel				SUBrd(EDX, ECX);
281115013Smarcel				CMPrd(ESI, ECX);
282115013Smarcel				JAb(5);
283115013Smarcel				POP(EBX);
284115013Smarcel				POP(ESI);
285115013Smarcel				POP(EDI);
286115013Smarcel				LEAVE_RET();
287115013Smarcel				ADDrd(EDX, ESI);
288115013Smarcel				MOVobb(EBX, ESI, AL);
289115013Smarcel				break;
290115013Smarcel
291115013Smarcel			case BPF_LDX|BPF_MSH|BPF_B:
292115013Smarcel				MOVid(ins->k, ESI);
293115013Smarcel				CMPrd(EDI, ESI);
294115013Smarcel				JBb(7);
295115013Smarcel				ZEROrd(EAX);
296115013Smarcel				POP(EBX);
297115013Smarcel				POP(ESI);
298115013Smarcel				POP(EDI);
299115013Smarcel				LEAVE_RET();
300115013Smarcel				ZEROrd(EDX);
301115013Smarcel				MOVobb(EBX, ESI, DL);
302115013Smarcel				ANDib(0x0f, DL);
303115013Smarcel				SHLib(2, EDX);
304160157Smarcel				break;
305160157Smarcel
306160157Smarcel			case BPF_LD|BPF_IMM:
307160157Smarcel				MOVid(ins->k, EAX);
308160157Smarcel				break;
309160157Smarcel
310115013Smarcel			case BPF_LDX|BPF_IMM:
311115013Smarcel				MOVid(ins->k, EDX);
312115013Smarcel				break;
313115013Smarcel
314115013Smarcel			case BPF_LD|BPF_MEM:
315115013Smarcel				MOVid((uintptr_t)mem, ECX);
316				MOVid(ins->k * 4, ESI);
317				MOVobd(ECX, ESI, EAX);
318				break;
319
320			case BPF_LDX|BPF_MEM:
321				MOVid((uintptr_t)mem, ECX);
322				MOVid(ins->k * 4, ESI);
323				MOVobd(ECX, ESI, EDX);
324				break;
325
326			case BPF_ST:
327				/*
328				 * XXX this command and the following could
329				 * be optimized if the previous instruction
330				 * was already of this type
331				 */
332				MOVid((uintptr_t)mem, ECX);
333				MOVid(ins->k * 4, ESI);
334				MOVomd(EAX, ECX, ESI);
335				break;
336
337			case BPF_STX:
338				MOVid((uintptr_t)mem, ECX);
339				MOVid(ins->k * 4, ESI);
340				MOVomd(EDX, ECX, ESI);
341				break;
342
343			case BPF_JMP|BPF_JA:
344				JMP(stream.refs[stream.bpf_pc + ins->k] -
345				    stream.refs[stream.bpf_pc]);
346				break;
347
348			case BPF_JMP|BPF_JGT|BPF_K:
349				if (ins->jt == 0 && ins->jf == 0)
350					break;
351				CMPid(ins->k, EAX);
352				JCC(JA, JBE);
353				break;
354
355			case BPF_JMP|BPF_JGE|BPF_K:
356				if (ins->jt == 0 && ins->jf == 0)
357					break;
358				CMPid(ins->k, EAX);
359				JCC(JAE, JB);
360				break;
361
362			case BPF_JMP|BPF_JEQ|BPF_K:
363				if (ins->jt == 0 && ins->jf == 0)
364					break;
365				CMPid(ins->k, EAX);
366				JCC(JE, JNE);
367				break;
368
369			case BPF_JMP|BPF_JSET|BPF_K:
370				if (ins->jt == 0 && ins->jf == 0)
371					break;
372				TESTid(ins->k, EAX);
373				JCC(JNE, JE);
374				break;
375
376			case BPF_JMP|BPF_JGT|BPF_X:
377				if (ins->jt == 0 && ins->jf == 0)
378					break;
379				CMPrd(EDX, EAX);
380				JCC(JA, JBE);
381				break;
382
383			case BPF_JMP|BPF_JGE|BPF_X:
384				if (ins->jt == 0 && ins->jf == 0)
385					break;
386				CMPrd(EDX, EAX);
387				JCC(JAE, JB);
388				break;
389
390			case BPF_JMP|BPF_JEQ|BPF_X:
391				if (ins->jt == 0 && ins->jf == 0)
392					break;
393				CMPrd(EDX, EAX);
394				JCC(JE, JNE);
395				break;
396
397			case BPF_JMP|BPF_JSET|BPF_X:
398				if (ins->jt == 0 && ins->jf == 0)
399					break;
400				TESTrd(EDX, EAX);
401				JCC(JNE, JE);
402				break;
403
404			case BPF_ALU|BPF_ADD|BPF_X:
405				ADDrd(EDX, EAX);
406				break;
407
408			case BPF_ALU|BPF_SUB|BPF_X:
409				SUBrd(EDX, EAX);
410				break;
411
412			case BPF_ALU|BPF_MUL|BPF_X:
413				MOVrd(EDX, ECX);
414				MULrd(EDX);
415				MOVrd(ECX, EDX);
416				break;
417
418			case BPF_ALU|BPF_DIV|BPF_X:
419				TESTrd(EDX, EDX);
420				JNEb(7);
421				ZEROrd(EAX);
422				POP(EBX);
423				POP(ESI);
424				POP(EDI);
425				LEAVE_RET();
426				MOVrd(EDX, ECX);
427				ZEROrd(EDX);
428				DIVrd(ECX);
429				MOVrd(ECX, EDX);
430				break;
431
432			case BPF_ALU|BPF_AND|BPF_X:
433				ANDrd(EDX, EAX);
434				break;
435
436			case BPF_ALU|BPF_OR|BPF_X:
437				ORrd(EDX, EAX);
438				break;
439
440			case BPF_ALU|BPF_LSH|BPF_X:
441				MOVrd(EDX, ECX);
442				SHL_CLrb(EAX);
443				break;
444
445			case BPF_ALU|BPF_RSH|BPF_X:
446				MOVrd(EDX, ECX);
447				SHR_CLrb(EAX);
448				break;
449
450			case BPF_ALU|BPF_ADD|BPF_K:
451				ADD_EAXi(ins->k);
452				break;
453
454			case BPF_ALU|BPF_SUB|BPF_K:
455				SUB_EAXi(ins->k);
456				break;
457
458			case BPF_ALU|BPF_MUL|BPF_K:
459				MOVrd(EDX, ECX);
460				MOVid(ins->k, EDX);
461				MULrd(EDX);
462				MOVrd(ECX, EDX);
463				break;
464
465			case BPF_ALU|BPF_DIV|BPF_K:
466				MOVrd(EDX, ECX);
467				ZEROrd(EDX);
468				MOVid(ins->k, ESI);
469				DIVrd(ESI);
470				MOVrd(ECX, EDX);
471				break;
472
473			case BPF_ALU|BPF_AND|BPF_K:
474				ANDid(ins->k, EAX);
475				break;
476
477			case BPF_ALU|BPF_OR|BPF_K:
478				ORid(ins->k, EAX);
479				break;
480
481			case BPF_ALU|BPF_LSH|BPF_K:
482				SHLib((ins->k) & 0xff, EAX);
483				break;
484
485			case BPF_ALU|BPF_RSH|BPF_K:
486				SHRib((ins->k) & 0xff, EAX);
487				break;
488
489			case BPF_ALU|BPF_NEG:
490				NEGd(EAX);
491				break;
492
493			case BPF_MISC|BPF_TAX:
494				MOVrd(EAX, EDX);
495				break;
496
497			case BPF_MISC|BPF_TXA:
498				MOVrd(EDX, EAX);
499				break;
500			}
501			ins++;
502		}
503
504		pass++;
505		if (pass == 2)
506			break;
507
508#ifdef _KERNEL
509		stream.ibuf = (char *)malloc(stream.cur_ip, M_BPFJIT, M_NOWAIT);
510		if (stream.ibuf == NULL) {
511			free(stream.refs, M_BPFJIT);
512			return (NULL);
513		}
514#else
515		stream.ibuf = (char *)malloc(stream.cur_ip);
516		if (stream.ibuf == NULL) {
517			free(stream.refs);
518			return (NULL);
519		}
520#endif
521
522		/*
523		 * modify the reference table to contain the offsets and
524		 * not the lengths of the instructions
525		 */
526		for (i = 1; i < nins + 1; i++)
527			stream.refs[i] += stream.refs[i - 1];
528
529		/* Reset the counters */
530		stream.cur_ip = 0;
531		stream.bpf_pc = 0;
532
533		/* the second pass creates the actual code */
534		emitm = emit_code;
535	}
536
537	/*
538	 * the reference table is needed only during compilation,
539	 * now we can free it
540	 */
541#ifdef _KERNEL
542	free(stream.refs, M_BPFJIT);
543#else
544	free(stream.refs);
545#endif
546
547	return ((bpf_filter_func)stream.ibuf);
548}
549