bpf_jit_machdep.c revision 181644
131921Sbrian/*- 231921Sbrian * Copyright (c) 2002 - 2003 NetGroup, Politecnico di Torino (Italy) 331921Sbrian * Copyright (c) 2005 Jung-uk Kim <jkim@FreeBSD.org> 431921Sbrian * All rights reserved. 531921Sbrian * 631921Sbrian * Redistribution and use in source and binary forms, with or without 731921Sbrian * modification, are permitted provided that the following conditions 831921Sbrian * are met: 931921Sbrian * 1031921Sbrian * 1. Redistributions of source code must retain the above copyright 1131921Sbrian * notice, this list of conditions and the following disclaimer. 1231921Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1331921Sbrian * notice, this list of conditions and the following disclaimer in the 1431921Sbrian * documentation and/or other materials provided with the distribution. 1531921Sbrian * 3. Neither the name of the Politecnico di Torino nor the names of its 1631921Sbrian * contributors may be used to endorse or promote products derived from 1731921Sbrian * this software without specific prior written permission. 1831921Sbrian * 1931921Sbrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2031921Sbrian * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2131921Sbrian * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2231921Sbrian * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2331921Sbrian * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2431921Sbrian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2531921Sbrian * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2631921Sbrian * DATA, OR PROFITS; OR BUSINESS intERRUPTION) HOWEVER CAUSED AND ON ANY 2735773Scharnier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2835773Scharnier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2950471Speter * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3035773Scharnier */ 3135773Scharnier 3227874Sbrian#include <sys/cdefs.h> 3327874Sbrian__FBSDID("$FreeBSD: head/sys/amd64/amd64/bpf_jit_machdep.c 181644 2008-08-12 20:10:45Z jkim $"); 3427874Sbrian 3527874Sbrian#include "opt_bpf.h" 3627874Sbrian 3727874Sbrian#include <sys/param.h> 3827874Sbrian#include <sys/systm.h> 3927874Sbrian#include <sys/kernel.h> 4027874Sbrian#include <sys/types.h> 4127874Sbrian#include <sys/socket.h> 4227874Sbrian#include <sys/malloc.h> 4327874Sbrian 4457326Salfred#include <net/if.h> 4557326Salfred#include <net/bpf.h> 4627874Sbrian#include <net/bpf_jitter.h> 4727874Sbrian 4827874Sbrian#include <amd64/amd64/bpf_jit_machdep.h> 4927874Sbrian 5027874Sbrianbpf_filter_func bpf_jit_compile(struct bpf_insn *, u_int, int *); 5127874Sbrian 5227874Sbrian/* 5327874Sbrian * emit routine to update the jump table 5427874Sbrian */ 5527874Sbrianstatic void 5659175Sbrianemit_length(bpf_bin_stream *stream, u_int value, u_int len) 5727874Sbrian{ 5827874Sbrian 5959022Sbrian (stream->refs)[stream->bpf_pc] += len; 6059022Sbrian stream->cur_ip += len; 6159175Sbrian} 6259022Sbrian 6359175Sbrian/* 6459175Sbrian * emit routine to output the actual binary code 6559175Sbrian */ 6659022Sbrianstatic void 6759022Sbrianemit_code(bpf_bin_stream *stream, u_int value, u_int len) 6859022Sbrian{ 6959022Sbrian 7059022Sbrian switch (len) { 7127874Sbrian case 1: 7227874Sbrian stream->ibuf[stream->cur_ip] = (u_char)value; 7327874Sbrian stream->cur_ip++; 7427874Sbrian break; 7527874Sbrian 7628025Sbrian case 2: 7728025Sbrian *((u_short *)(stream->ibuf + stream->cur_ip)) = (u_short)value; 7827874Sbrian stream->cur_ip += 2; 7927874Sbrian break; 8027874Sbrian 8127874Sbrian case 4: 8227874Sbrian *((u_int *)(stream->ibuf + stream->cur_ip)) = value; 8327874Sbrian stream->cur_ip += 4; 8428025Sbrian break; 8527874Sbrian } 8627874Sbrian 8727874Sbrian return; 8827874Sbrian} 8927874Sbrian 9027874Sbrian/* 9127874Sbrian * Function that does the real stuff 9227874Sbrian */ 9327874Sbrianbpf_filter_func 9427874Sbrianbpf_jit_compile(struct bpf_insn *prog, u_int nins, int *mem) 9527874Sbrian{ 9627874Sbrian struct bpf_insn *ins; 9727874Sbrian u_int i, pass; 9827874Sbrian bpf_bin_stream stream; 9927874Sbrian 10027874Sbrian /* 10127874Sbrian * NOTE: do not modify the name of this variable, as it's used by 10227874Sbrian * the macros to emit code. 10327874Sbrian */ 10427874Sbrian emit_func emitm; 10527874Sbrian 10627874Sbrian /* Do not compile an empty filter. */ 10727874Sbrian if (nins == 0) 10827874Sbrian return NULL; 10927874Sbrian 11027874Sbrian /* Allocate the reference table for the jumps */ 11127874Sbrian stream.refs = (u_int *)malloc((nins + 1) * sizeof(u_int), 11227874Sbrian M_BPFJIT, M_NOWAIT); 11327874Sbrian if (stream.refs == NULL) 11427874Sbrian return NULL; 11527874Sbrian 11627874Sbrian /* Reset the reference table */ 11727874Sbrian for (i = 0; i < nins + 1; i++) 11827874Sbrian stream.refs[i] = 0; 11927874Sbrian 12027874Sbrian stream.cur_ip = 0; 12127874Sbrian stream.bpf_pc = 0; 12227874Sbrian 12327874Sbrian /* 12427874Sbrian * the first pass will emit the lengths of the instructions 12527874Sbrian * to create the reference table 12627874Sbrian */ 12727874Sbrian emitm = emit_length; 12859175Sbrian 12927874Sbrian pass = 0; 13027874Sbrian for (;;) { 13127874Sbrian ins = prog; 13227874Sbrian 13327874Sbrian /* create the procedure header */ 13427874Sbrian MOVrq2(RBX, R8); 13527874Sbrian MOVrq(RDI, RBX); 13627874Sbrian MOVrd2(ESI, R9D); 13727874Sbrian MOVrd(EDX, EDI); 13827874Sbrian 13927874Sbrian for (i = 0; i < nins; i++) { 14027874Sbrian stream.bpf_pc++; 14127874Sbrian 14227874Sbrian switch (ins->code) { 14327874Sbrian default: 14427874Sbrian return NULL; 14559175Sbrian 14627874Sbrian case BPF_RET|BPF_K: 14727874Sbrian MOVid(ins->k, EAX); 14827874Sbrian MOVrq3(R8, RBX); 14959175Sbrian RET(); 15027874Sbrian break; 15127874Sbrian 15227874Sbrian case BPF_RET|BPF_A: 15327874Sbrian MOVrq3(R8, RBX); 15427874Sbrian RET(); 15527874Sbrian break; 15646073Simp 15727874Sbrian case BPF_LD|BPF_W|BPF_ABS: 15827874Sbrian MOVid(ins->k, ECX); 15927874Sbrian MOVrd(ECX, ESI); 16027874Sbrian ADDib(sizeof(int), ECX); 16146073Simp CMPrd(EDI, ECX); 16259022Sbrian JLEb(6); 16359175Sbrian ZEROrd(EAX); 16459022Sbrian MOVrq3(R8, RBX); 16559022Sbrian RET(); 16659022Sbrian MOVobd(RBX, RSI, EAX); 16759022Sbrian BSWAP(EAX); 16859022Sbrian break; 16959022Sbrian 17027874Sbrian case BPF_LD|BPF_H|BPF_ABS: 17127874Sbrian ZEROrd(EAX); 17227874Sbrian MOVid(ins->k, ECX); 17346073Simp MOVrd(ECX, ESI); 17427874Sbrian ADDib(sizeof(short), ECX); 17527874Sbrian CMPrd(EDI, ECX); 17627874Sbrian JLEb(4); 17727874Sbrian MOVrq3(R8, RBX); 17846073Simp RET(); 17959022Sbrian MOVobw(RBX, RSI, AX); 18059175Sbrian SWAP_AX(); 18127874Sbrian break; 18259022Sbrian 18359022Sbrian case BPF_LD|BPF_B|BPF_ABS: 18459175Sbrian ZEROrd(EAX); 18559022Sbrian MOVid(ins->k, ECX); 18659022Sbrian CMPrd(EDI, ECX); 18759022Sbrian JLEb(4); 18859022Sbrian MOVrq3(R8, RBX); 18927874Sbrian RET(); 19027874Sbrian MOVobb(RBX, RCX, AL); 19127874Sbrian break; 19227874Sbrian 19327874Sbrian case BPF_LD|BPF_W|BPF_LEN: 19427874Sbrian MOVrd3(R9D, EAX); 19527874Sbrian break; 19627874Sbrian 19727874Sbrian case BPF_LDX|BPF_W|BPF_LEN: 19859175Sbrian MOVrd3(R9D, EDX); 19927874Sbrian break; 20027874Sbrian 20127874Sbrian case BPF_LD|BPF_W|BPF_IND: 20259175Sbrian MOVid(ins->k, ECX); 20327874Sbrian ADDrd(EDX, ECX); 20427874Sbrian MOVrd(ECX, ESI); 20559022Sbrian ADDib(sizeof(int), ECX); 20627874Sbrian CMPrd(EDI, ECX); 20727874Sbrian JLEb(6); 20827874Sbrian ZEROrd(EAX); 20927874Sbrian MOVrq3(R8, RBX); 21027874Sbrian RET(); 21127874Sbrian MOVobd(RBX, RSI, EAX); 21227874Sbrian BSWAP(EAX); 21359175Sbrian break; 21427874Sbrian 21527874Sbrian case BPF_LD|BPF_H|BPF_IND: 21627874Sbrian ZEROrd(EAX); 21727874Sbrian MOVid(ins->k, ECX); 21827874Sbrian ADDrd(EDX, ECX); 21927874Sbrian MOVrd(ECX, ESI); 22027874Sbrian ADDib(sizeof(short), ECX); 22127874Sbrian CMPrd(EDI, ECX); 22227874Sbrian JLEb(4); 22327874Sbrian MOVrq3(R8, RBX); 22427874Sbrian RET(); 22527874Sbrian MOVobw(RBX, RSI, AX); 22659175Sbrian SWAP_AX(); 22727874Sbrian break; 22827874Sbrian 22927874Sbrian case BPF_LD|BPF_B|BPF_IND: 23027874Sbrian ZEROrd(EAX); 23127874Sbrian MOVid(ins->k, ECX); 23227874Sbrian ADDrd(EDX, ECX); 23327874Sbrian CMPrd(EDI, ECX); 23427874Sbrian JLEb(4); 23527874Sbrian MOVrq3(R8, RBX); 23627874Sbrian RET(); 23727874Sbrian MOVobb(RBX, RCX, AL); 23827874Sbrian break; 23927874Sbrian 24027874Sbrian case BPF_LDX|BPF_MSH|BPF_B: 24127874Sbrian MOVid(ins->k, ECX); 24259175Sbrian CMPrd(EDI, ECX); 24327874Sbrian JLEb(6); 24427874Sbrian ZEROrd(EAX); 24527874Sbrian MOVrq3(R8, RBX); 24659175Sbrian RET(); 24727874Sbrian ZEROrd(EDX); 24827874Sbrian MOVobb(RBX, RCX, DL); 24927874Sbrian ANDib(0xf, DL); 25027874Sbrian SHLib(2, EDX); 25128025Sbrian break; 25227874Sbrian 25327874Sbrian case BPF_LD|BPF_IMM: 25427874Sbrian MOVid(ins->k, EAX); 25527874Sbrian break; 25627874Sbrian 25727874Sbrian case BPF_LDX|BPF_IMM: 25827874Sbrian MOVid(ins->k, EDX); 25959022Sbrian break; 26060836Sbrian 26127874Sbrian case BPF_LD|BPF_MEM: 26259022Sbrian MOViq((uintptr_t)mem, RCX); 26327874Sbrian MOVid(ins->k * 4, ESI); 26427874Sbrian MOVobd(RCX, RSI, EAX); 26527874Sbrian break; 26627874Sbrian 26759022Sbrian case BPF_LDX|BPF_MEM: 26859022Sbrian MOViq((uintptr_t)mem, RCX); 26960836Sbrian MOVid(ins->k * 4, ESI); 27027874Sbrian MOVobd(RCX, RSI, EDX); 27127874Sbrian break; 27260836Sbrian 27327874Sbrian case BPF_ST: 27427874Sbrian /* 27527874Sbrian * XXX this command and the following could 27660836Sbrian * be optimized if the previous instruction 27727874Sbrian * was already of this type 27827874Sbrian */ 27927874Sbrian MOViq((uintptr_t)mem, RCX); 28027874Sbrian MOVid(ins->k * 4, ESI); 28127874Sbrian MOVomd(EAX, RCX, RSI); 28259175Sbrian break; 28327874Sbrian 28428025Sbrian case BPF_STX: 28528025Sbrian MOViq((uintptr_t)mem, RCX); 28628025Sbrian MOVid(ins->k * 4, ESI); 28728025Sbrian MOVomd(EDX, RCX, RSI); 28827874Sbrian break; 28959022Sbrian 29059022Sbrian case BPF_JMP|BPF_JA: 29159022Sbrian JMP(stream.refs[stream.bpf_pc + ins->k] - 29259022Sbrian stream.refs[stream.bpf_pc]); 29359022Sbrian break; 29459022Sbrian 29559022Sbrian case BPF_JMP|BPF_JGT|BPF_K: 29659175Sbrian CMPid(ins->k, EAX); 29759022Sbrian /* 5 is the size of the following JMP */ 29859022Sbrian JG(stream.refs[stream.bpf_pc + ins->jt] - 29928025Sbrian stream.refs[stream.bpf_pc] + 5 ); 30028025Sbrian JMP(stream.refs[stream.bpf_pc + ins->jf] - 30127874Sbrian stream.refs[stream.bpf_pc]); 30259022Sbrian break; 30359022Sbrian 30459022Sbrian case BPF_JMP|BPF_JGE|BPF_K: 30559022Sbrian CMPid(ins->k, EAX); 30659022Sbrian JGE(stream.refs[stream.bpf_pc + ins->jt] - 30759022Sbrian stream.refs[stream.bpf_pc] + 5); 30859022Sbrian JMP(stream.refs[stream.bpf_pc + ins->jf] - 30959022Sbrian stream.refs[stream.bpf_pc]); 31059022Sbrian break; 31159022Sbrian 31259175Sbrian case BPF_JMP|BPF_JEQ|BPF_K: 31328025Sbrian CMPid(ins->k, EAX); 31428025Sbrian JE(stream.refs[stream.bpf_pc + ins->jt] - 31528025Sbrian stream.refs[stream.bpf_pc] + 5); 31628025Sbrian JMP(stream.refs[stream.bpf_pc + ins->jf] - 31727874Sbrian stream.refs[stream.bpf_pc]); 31828025Sbrian break; 31928025Sbrian 32028025Sbrian case BPF_JMP|BPF_JSET|BPF_K: 32127874Sbrian MOVrd(EAX, ECX); 32228025Sbrian ANDid(ins->k, ECX); 32359175Sbrian JE(stream.refs[stream.bpf_pc + ins->jf] - 32427874Sbrian stream.refs[stream.bpf_pc] + 5); 32527874Sbrian JMP(stream.refs[stream.bpf_pc + ins->jt] - 32628025Sbrian stream.refs[stream.bpf_pc]); 32759175Sbrian break; 32828025Sbrian 32928025Sbrian case BPF_JMP|BPF_JGT|BPF_X: 33028025Sbrian CMPrd(EDX, EAX); 33128025Sbrian JA(stream.refs[stream.bpf_pc + ins->jt] - 33228025Sbrian stream.refs[stream.bpf_pc] + 5); 33328025Sbrian JMP(stream.refs[stream.bpf_pc + ins->jf] - 33459022Sbrian stream.refs[stream.bpf_pc]); 33559175Sbrian break; 33659022Sbrian 33759022Sbrian case BPF_JMP|BPF_JGE|BPF_X: 33859022Sbrian CMPrd(EDX, EAX); 33959022Sbrian JAE(stream.refs[stream.bpf_pc + ins->jt] - 34059022Sbrian stream.refs[stream.bpf_pc] + 5); 34159022Sbrian JMP(stream.refs[stream.bpf_pc + ins->jf] - 34228025Sbrian stream.refs[stream.bpf_pc]); 34328025Sbrian break; 34428025Sbrian 34559022Sbrian case BPF_JMP|BPF_JEQ|BPF_X: 34659175Sbrian CMPrd(EDX, EAX); 34728025Sbrian JE(stream.refs[stream.bpf_pc + ins->jt] - 34859022Sbrian stream.refs[stream.bpf_pc] + 5); 34959022Sbrian JMP(stream.refs[stream.bpf_pc + ins->jf] - 35059175Sbrian stream.refs[stream.bpf_pc]); 35159022Sbrian break; 35259022Sbrian 35359022Sbrian case BPF_JMP|BPF_JSET|BPF_X: 35459022Sbrian MOVrd(EAX, ECX); 35528025Sbrian ANDrd(EDX, ECX); 35628025Sbrian JE(stream.refs[stream.bpf_pc + ins->jf] - 35728025Sbrian stream.refs[stream.bpf_pc] + 5); 35828025Sbrian JMP(stream.refs[stream.bpf_pc + ins->jt] - 35928025Sbrian stream.refs[stream.bpf_pc]); 36028025Sbrian break; 36128025Sbrian 36228025Sbrian case BPF_ALU|BPF_ADD|BPF_X: 36328025Sbrian ADDrd(EDX, EAX); 36459175Sbrian break; 36528025Sbrian 36628025Sbrian case BPF_ALU|BPF_SUB|BPF_X: 36744598Sbrian SUBrd(EDX, EAX); 36859175Sbrian break; 36944598Sbrian 37044598Sbrian case BPF_ALU|BPF_MUL|BPF_X: 37144598Sbrian MOVrd(EDX, ECX); 37244598Sbrian MULrd(EDX); 37344598Sbrian MOVrd(ECX, EDX); 37444598Sbrian break; 37559022Sbrian 37659175Sbrian case BPF_ALU|BPF_DIV|BPF_X: 37759022Sbrian CMPid(0, EDX); 37859022Sbrian JNEb(6); 37959022Sbrian ZEROrd(EAX); 38059022Sbrian MOVrq3(R8, RBX); 38159022Sbrian RET(); 38259022Sbrian MOVrd(EDX, ECX); 38344598Sbrian ZEROrd(EDX); 38444598Sbrian DIVrd(ECX); 38544598Sbrian MOVrd(ECX, EDX); 38659022Sbrian break; 38759175Sbrian 38844598Sbrian case BPF_ALU|BPF_AND|BPF_X: 38959022Sbrian ANDrd(EDX, EAX); 39059022Sbrian break; 39159175Sbrian 39259022Sbrian case BPF_ALU|BPF_OR|BPF_X: 39359022Sbrian ORrd(EDX, EAX); 39459022Sbrian break; 39559022Sbrian 39644598Sbrian case BPF_ALU|BPF_LSH|BPF_X: 39744598Sbrian MOVrd(EDX, ECX); 39844598Sbrian SHL_CLrb(EAX); 39944598Sbrian break; 40044598Sbrian 40144598Sbrian case BPF_ALU|BPF_RSH|BPF_X: 40244598Sbrian MOVrd(EDX, ECX); 40344598Sbrian SHR_CLrb(EAX); 40444598Sbrian break; 40559175Sbrian 40644598Sbrian case BPF_ALU|BPF_ADD|BPF_K: 40744598Sbrian ADD_EAXi(ins->k); 40827874Sbrian break; 40927874Sbrian 41027874Sbrian case BPF_ALU|BPF_SUB|BPF_K: 41128025Sbrian SUB_EAXi(ins->k); 41228025Sbrian break; 41328025Sbrian 41428025Sbrian case BPF_ALU|BPF_MUL|BPF_K: 41528025Sbrian MOVrd(EDX, ECX); 41628025Sbrian MOVid(ins->k, EDX); 41727874Sbrian MULrd(EDX); 41828025Sbrian MOVrd(ECX, EDX); 41928025Sbrian break; 42028025Sbrian 42128025Sbrian case BPF_ALU|BPF_DIV|BPF_K: 42228025Sbrian MOVrd(EDX, ECX); 42328025Sbrian ZEROrd(EDX); 42428025Sbrian MOVid(ins->k, ESI); 42528025Sbrian DIVrd(ESI); 42628025Sbrian MOVrd(ECX, EDX); 42728025Sbrian break; 42859175Sbrian 42959175Sbrian case BPF_ALU|BPF_AND|BPF_K: 43059175Sbrian ANDid(ins->k, EAX); 43128025Sbrian break; 43228025Sbrian 43328025Sbrian case BPF_ALU|BPF_OR|BPF_K: 43459175Sbrian ORid(ins->k, EAX); 43528025Sbrian break; 43628025Sbrian 43728025Sbrian case BPF_ALU|BPF_LSH|BPF_K: 43828025Sbrian SHLib((ins->k) & 0xff, EAX); 43959175Sbrian break; 44028025Sbrian 44128025Sbrian case BPF_ALU|BPF_RSH|BPF_K: 44227874Sbrian SHRib((ins->k) & 0xff, EAX); 44328025Sbrian break; 44428025Sbrian 44528025Sbrian case BPF_ALU|BPF_NEG: 44628025Sbrian NEGd(EAX); 44728025Sbrian break; 44828025Sbrian 44944598Sbrian case BPF_MISC|BPF_TAX: 45059175Sbrian MOVrd(EAX, EDX); 45144598Sbrian break; 45244598Sbrian 45328025Sbrian case BPF_MISC|BPF_TXA: 45459175Sbrian MOVrd(EDX, EAX); 45528025Sbrian break; 45628025Sbrian } 45728025Sbrian ins++; 45859175Sbrian } 45928025Sbrian 46028025Sbrian pass++; 46128025Sbrian if (pass == 2) 46259175Sbrian break; 46359175Sbrian 46428025Sbrian stream.ibuf = (char *)malloc(stream.cur_ip, M_BPFJIT, M_NOWAIT); 46528025Sbrian if (stream.ibuf == NULL) { 46628025Sbrian free(stream.refs, M_BPFJIT); 46759175Sbrian return NULL; 46859175Sbrian } 46928025Sbrian 47028025Sbrian /* 47128025Sbrian * modify the reference table to contain the offsets and 47259175Sbrian * not the lengths of the instructions 47359175Sbrian */ 47428025Sbrian for (i = 1; i < nins + 1; i++) 47528025Sbrian stream.refs[i] += stream.refs[i - 1]; 47628025Sbrian 47759175Sbrian /* Reset the counters */ 47859175Sbrian stream.cur_ip = 0; 47928025Sbrian stream.bpf_pc = 0; 48028025Sbrian 48128025Sbrian /* the second pass creates the actual code */ 48227874Sbrian emitm = emit_code; 48328025Sbrian } 48427874Sbrian 48527874Sbrian /* 48627874Sbrian * the reference table is needed only during compilation, 48727874Sbrian * now we can free it 48827874Sbrian */ 48927874Sbrian free(stream.refs, M_BPFJIT); 49027874Sbrian 49127874Sbrian return (bpf_filter_func)stream.ibuf; 49227874Sbrian} 49327874Sbrian