bpf_jit_machdep.c revision 199531
1103856Stjr/*- 2103856Stjr * Copyright (C) 2002-2003 NetGroup, Politecnico di Torino (Italy) 3103856Stjr * Copyright (C) 2005-2009 Jung-uk Kim <jkim@FreeBSD.org> 4103856Stjr * All rights reserved. 5103856Stjr * 6103856Stjr * Redistribution and use in source and binary forms, with or without 7103856Stjr * modification, are permitted provided that the following conditions 8227753Stheraven * are met: 9227753Stheraven * 10227753Stheraven * 1. Redistributions of source code must retain the above copyright 11227753Stheraven * notice, this list of conditions and the following disclaimer. 12227753Stheraven * 2. Redistributions in binary form must reproduce the above copyright 13103856Stjr * notice, this list of conditions and the following disclaimer in the 14103856Stjr * documentation and/or other materials provided with the distribution. 15103856Stjr * 3. Neither the name of the Politecnico di Torino nor the names of its 16103856Stjr * contributors may be used to endorse or promote products derived from 17103856Stjr * this software without specific prior written permission. 18103856Stjr * 19103856Stjr * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20103856Stjr * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21249808Semaste * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22103856Stjr * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23103856Stjr * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24103856Stjr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25103856Stjr * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26103856Stjr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27103856Stjr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28103856Stjr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29103856Stjr * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30103856Stjr */ 31103856Stjr 32103856Stjr#include <sys/cdefs.h> 33103856Stjr__FBSDID("$FreeBSD: head/sys/amd64/amd64/bpf_jit_machdep.c 199531 2009-11-19 15:45:24Z jkim $"); 34103856Stjr 35103856Stjr#ifdef _KERNEL 36103856Stjr#include "opt_bpf.h" 37103856Stjr#include <sys/param.h> 38103856Stjr#include <sys/systm.h> 39103856Stjr#include <sys/kernel.h> 40103856Stjr#include <sys/socket.h> 41103856Stjr#include <sys/malloc.h> 42103856Stjr#include <net/if.h> 43128844Sobrien#else 44103856Stjr#include <stdlib.h> 45103856Stjr#include <sys/mman.h> 46103856Stjr#include <sys/param.h> 47103856Stjr#endif 48103856Stjr 49149313Sstefanf#include <sys/types.h> 50103856Stjr 51103856Stjr#include <net/bpf.h> 52103856Stjr#include <net/bpf_jitter.h> 53103856Stjr 54103856Stjr#include <amd64/amd64/bpf_jit_machdep.h> 55103856Stjr 56103856Stjrbpf_filter_func bpf_jit_compile(struct bpf_insn *, u_int, size_t *, int *); 57103856Stjr 58103856Stjr/* 59103856Stjr * emit routine to update the jump table 60103856Stjr */ 61227753Stheravenstatic void 62103856Stjremit_length(bpf_bin_stream *stream, __unused u_int value, u_int len) 63103856Stjr{ 64103856Stjr 65103856Stjr (stream->refs)[stream->bpf_pc] += len; 66103856Stjr stream->cur_ip += len; 67103856Stjr} 68103856Stjr 69103856Stjr/* 70103856Stjr * emit routine to output the actual binary code 71103856Stjr */ 72103856Stjrstatic void 73103856Stjremit_code(bpf_bin_stream *stream, u_int value, u_int len) 74103856Stjr{ 75103856Stjr 76103856Stjr switch (len) { 77103856Stjr case 1: 78103856Stjr stream->ibuf[stream->cur_ip] = (u_char)value; 79103856Stjr stream->cur_ip++; 80103856Stjr break; 81103856Stjr 82117249Stjr case 2: 83117249Stjr *((u_short *)(stream->ibuf + stream->cur_ip)) = (u_short)value; 84103856Stjr stream->cur_ip += 2; 85103856Stjr break; 86103856Stjr 87103856Stjr case 4: 88103856Stjr *((u_int *)(stream->ibuf + stream->cur_ip)) = value; 89125283Sdas stream->cur_ip += 4; 90103856Stjr break; 91103856Stjr } 92103856Stjr 93103856Stjr return; 94103856Stjr} 95103856Stjr 96103856Stjr/* 97103856Stjr * Function that does the real stuff 98103856Stjr */ 99103856Stjrbpf_filter_func 100157381Sphkbpf_jit_compile(struct bpf_insn *prog, u_int nins, size_t *size, int *mem) 101227753Stheraven{ 102157381Sphk bpf_bin_stream stream; 103117249Stjr struct bpf_insn *ins; 104234585Sdas u_int i, pass; 105234585Sdas 106234585Sdas /* 107234585Sdas * NOTE: do not modify the name of this variable, as it's used by 108234585Sdas * the macros to emit code. 109103856Stjr */ 110234585Sdas emit_func emitm; 111234585Sdas 112234585Sdas /* Allocate the reference table for the jumps */ 113234585Sdas#ifdef _KERNEL 114234585Sdas stream.refs = (u_int *)malloc((nins + 1) * sizeof(u_int), 115234585Sdas M_BPFJIT, M_NOWAIT); 116234585Sdas#else 117234585Sdas stream.refs = (u_int *)malloc((nins + 1) * sizeof(u_int)); 118234585Sdas#endif 119234585Sdas if (stream.refs == NULL) 120234585Sdas return (NULL); 121234585Sdas 122234585Sdas /* Reset the reference table */ 123234585Sdas for (i = 0; i < nins + 1; i++) 124234585Sdas stream.refs[i] = 0; 125234585Sdas 126234585Sdas stream.cur_ip = 0; 127234585Sdas stream.bpf_pc = 0; 128234585Sdas 129234585Sdas /* 130234585Sdas * the first pass will emit the lengths of the instructions 131234585Sdas * to create the reference table 132187422Sdas */ 133187422Sdas emitm = emit_length; 134103856Stjr 135234585Sdas pass = 0; 136234585Sdas for (;;) { 137234585Sdas ins = prog; 138234585Sdas 139234585Sdas /* create the procedure header */ 140234585Sdas MOVrq2(RBX, R8); 141234836Sdumbbell MOVrq(RDI, RBX); 142234585Sdas MOVrd2(ESI, R9D); 143234585Sdas MOVrd(EDX, EDI); 144234585Sdas 145234585Sdas for (i = 0; i < nins; i++) { 146234585Sdas stream.bpf_pc++; 147234585Sdas 148234585Sdas switch (ins->code) { 149234585Sdas default: 150234825Sdas#ifdef _KERNEL 151234825Sdas return (NULL); 152234585Sdas#else 153234585Sdas abort(); 154234585Sdas#endif 155234825Sdas 156234585Sdas case BPF_RET|BPF_K: 157234585Sdas MOVid(ins->k, EAX); 158234585Sdas MOVrq3(R8, RBX); 159234585Sdas RET(); 160234585Sdas break; 161234585Sdas 162234585Sdas case BPF_RET|BPF_A: 163234585Sdas MOVrq3(R8, RBX); 164234585Sdas RET(); 165234585Sdas break; 166234585Sdas 167234585Sdas case BPF_LD|BPF_W|BPF_ABS: 168234585Sdas MOVid(ins->k, ESI); 169234585Sdas CMPrd(EDI, ESI); 170234585Sdas JAb(12); 171234585Sdas MOVrd(EDI, ECX); 172234585Sdas SUBrd(ESI, ECX); 173234585Sdas CMPid(sizeof(int32_t), ECX); 174234585Sdas JAEb(6); 175234585Sdas ZEROrd(EAX); 176234585Sdas MOVrq3(R8, RBX); 177234585Sdas RET(); 178234585Sdas MOVobd(RBX, RSI, EAX); 179234585Sdas BSWAP(EAX); 180234585Sdas break; 181234585Sdas 182234836Sdumbbell case BPF_LD|BPF_H|BPF_ABS: 183234585Sdas ZEROrd(EAX); 184234585Sdas MOVid(ins->k, ESI); 185234585Sdas CMPrd(EDI, ESI); 186234585Sdas JAb(12); 187234585Sdas MOVrd(EDI, ECX); 188234585Sdas SUBrd(ESI, ECX); 189234585Sdas CMPid(sizeof(int16_t), ECX); 190234585Sdas JAEb(4); 191234585Sdas MOVrq3(R8, RBX); 192234585Sdas RET(); 193234825Sdas MOVobw(RBX, RSI, AX); 194234825Sdas SWAP_AX(); 195234585Sdas break; 196234585Sdas 197234585Sdas case BPF_LD|BPF_B|BPF_ABS: 198234825Sdas ZEROrd(EAX); 199234585Sdas MOVid(ins->k, ESI); 200234585Sdas CMPrd(EDI, ESI); 201234585Sdas JBb(4); 202234585Sdas MOVrq3(R8, RBX); 203234585Sdas RET(); 204234585Sdas MOVobb(RBX, RSI, AL); 205234585Sdas break; 206234585Sdas 207234585Sdas case BPF_LD|BPF_W|BPF_LEN: 208234585Sdas MOVrd3(R9D, EAX); 209234585Sdas break; 210234585Sdas 211234585Sdas case BPF_LDX|BPF_W|BPF_LEN: 212234585Sdas MOVrd3(R9D, EDX); 213234585Sdas break; 214234585Sdas 215234585Sdas case BPF_LD|BPF_W|BPF_IND: 216234585Sdas CMPrd(EDI, EDX); 217234585Sdas JAb(27); 218234585Sdas MOVid(ins->k, ESI); 219234585Sdas MOVrd(EDI, ECX); 220234585Sdas SUBrd(EDX, ECX); 221234585Sdas CMPrd(ESI, ECX); 222234585Sdas JBb(14); 223234585Sdas ADDrd(EDX, ESI); 224234585Sdas MOVrd(EDI, ECX); 225234585Sdas SUBrd(ESI, ECX); 226234585Sdas CMPid(sizeof(int32_t), ECX); 227234585Sdas JAEb(6); 228234585Sdas ZEROrd(EAX); 229234585Sdas MOVrq3(R8, RBX); 230234585Sdas RET(); 231234585Sdas MOVobd(RBX, RSI, EAX); 232234585Sdas BSWAP(EAX); 233234585Sdas break; 234234585Sdas 235234585Sdas case BPF_LD|BPF_H|BPF_IND: 236234585Sdas ZEROrd(EAX); 237234585Sdas CMPrd(EDI, EDX); 238234585Sdas JAb(27); 239234585Sdas MOVid(ins->k, ESI); 240234836Sdumbbell MOVrd(EDI, ECX); 241234585Sdas SUBrd(EDX, ECX); 242234585Sdas CMPrd(ESI, ECX); 243234585Sdas JBb(14); 244234585Sdas ADDrd(EDX, ESI); 245234585Sdas MOVrd(EDI, ECX); 246234585Sdas SUBrd(ESI, ECX); 247234585Sdas CMPid(sizeof(int16_t), ECX); 248234585Sdas JAEb(4); 249234825Sdas MOVrq3(R8, RBX); 250234585Sdas RET(); 251234825Sdas MOVobw(RBX, RSI, AX); 252234585Sdas SWAP_AX(); 253234585Sdas break; 254234585Sdas 255234825Sdas case BPF_LD|BPF_B|BPF_IND: 256234585Sdas ZEROrd(EAX); 257234585Sdas CMPrd(EDI, EDX); 258234585Sdas JAEb(13); 259234585Sdas MOVid(ins->k, ESI); 260234585Sdas MOVrd(EDI, ECX); 261234585Sdas SUBrd(EDX, ECX); 262234585Sdas CMPrd(ESI, ECX); 263234585Sdas JAb(4); 264234585Sdas MOVrq3(R8, RBX); 265234585Sdas RET(); 266234585Sdas ADDrd(EDX, ESI); 267234585Sdas MOVobb(RBX, RSI, AL); 268234585Sdas break; 269234585Sdas 270234585Sdas case BPF_LDX|BPF_MSH|BPF_B: 271234585Sdas MOVid(ins->k, ESI); 272234585Sdas CMPrd(EDI, ESI); 273234585Sdas JBb(6); 274234585Sdas ZEROrd(EAX); 275234585Sdas MOVrq3(R8, RBX); 276234585Sdas RET(); 277234585Sdas ZEROrd(EDX); 278234585Sdas MOVobb(RBX, RSI, DL); 279234585Sdas ANDib(0x0f, DL); 280234585Sdas SHLib(2, EDX); 281234585Sdas break; 282234585Sdas 283234585Sdas case BPF_LD|BPF_IMM: 284234585Sdas MOVid(ins->k, EAX); 285234585Sdas break; 286234585Sdas 287234585Sdas case BPF_LDX|BPF_IMM: 288234585Sdas MOVid(ins->k, EDX); 289234585Sdas break; 290234585Sdas 291234585Sdas case BPF_LD|BPF_MEM: 292234585Sdas MOViq((uintptr_t)mem, RCX); 293234585Sdas MOVid(ins->k * 4, ESI); 294234585Sdas MOVobd(RCX, RSI, EAX); 295234585Sdas break; 296234585Sdas 297234585Sdas case BPF_LDX|BPF_MEM: 298234585Sdas MOViq((uintptr_t)mem, RCX); 299234585Sdas MOVid(ins->k * 4, ESI); 300234585Sdas MOVobd(RCX, RSI, EDX); 301234585Sdas break; 302234585Sdas 303234585Sdas case BPF_ST: 304234585Sdas /* 305234585Sdas * XXX this command and the following could 306234585Sdas * be optimized if the previous instruction 307234585Sdas * was already of this type 308234585Sdas */ 309234585Sdas MOViq((uintptr_t)mem, RCX); 310234585Sdas MOVid(ins->k * 4, ESI); 311234585Sdas MOVomd(EAX, RCX, RSI); 312234585Sdas break; 313234585Sdas 314234585Sdas case BPF_STX: 315234585Sdas MOViq((uintptr_t)mem, RCX); 316234585Sdas MOVid(ins->k * 4, ESI); 317234585Sdas MOVomd(EDX, RCX, RSI); 318234585Sdas break; 319234585Sdas 320234585Sdas case BPF_JMP|BPF_JA: 321234585Sdas JMP(stream.refs[stream.bpf_pc + ins->k] - 322234585Sdas stream.refs[stream.bpf_pc]); 323234585Sdas break; 324234585Sdas 325234585Sdas case BPF_JMP|BPF_JGT|BPF_K: 326234585Sdas if (ins->jt == 0 && ins->jf == 0) 327234585Sdas break; 328234585Sdas CMPid(ins->k, EAX); 329234585Sdas JCC(JA, JBE); 330234585Sdas break; 331234585Sdas 332234585Sdas case BPF_JMP|BPF_JGE|BPF_K: 333234585Sdas if (ins->jt == 0 && ins->jf == 0) 334234585Sdas break; 335234585Sdas CMPid(ins->k, EAX); 336234585Sdas JCC(JAE, JB); 337234585Sdas break; 338234585Sdas 339234585Sdas case BPF_JMP|BPF_JEQ|BPF_K: 340234585Sdas if (ins->jt == 0 && ins->jf == 0) 341234585Sdas break; 342234585Sdas CMPid(ins->k, EAX); 343234585Sdas JCC(JE, JNE); 344234585Sdas break; 345234585Sdas 346234585Sdas case BPF_JMP|BPF_JSET|BPF_K: 347234585Sdas if (ins->jt == 0 && ins->jf == 0) 348234585Sdas break; 349234585Sdas TESTid(ins->k, EAX); 350234585Sdas JCC(JNE, JE); 351234585Sdas break; 352234585Sdas 353234585Sdas case BPF_JMP|BPF_JGT|BPF_X: 354234585Sdas if (ins->jt == 0 && ins->jf == 0) 355234585Sdas break; 356234585Sdas CMPrd(EDX, EAX); 357234585Sdas JCC(JA, JBE); 358234585Sdas break; 359234585Sdas 360234585Sdas case BPF_JMP|BPF_JGE|BPF_X: 361234585Sdas if (ins->jt == 0 && ins->jf == 0) 362234585Sdas break; 363234585Sdas CMPrd(EDX, EAX); 364234585Sdas JCC(JAE, JB); 365234585Sdas break; 366234585Sdas 367234585Sdas case BPF_JMP|BPF_JEQ|BPF_X: 368234585Sdas if (ins->jt == 0 && ins->jf == 0) 369234585Sdas break; 370234585Sdas CMPrd(EDX, EAX); 371234585Sdas JCC(JE, JNE); 372234585Sdas break; 373234585Sdas 374234585Sdas case BPF_JMP|BPF_JSET|BPF_X: 375234836Sdumbbell if (ins->jt == 0 && ins->jf == 0) 376234585Sdas break; 377234585Sdas TESTrd(EDX, EAX); 378234585Sdas JCC(JNE, JE); 379234585Sdas break; 380234585Sdas 381234585Sdas case BPF_ALU|BPF_ADD|BPF_X: 382234585Sdas ADDrd(EDX, EAX); 383234585Sdas break; 384234585Sdas 385234585Sdas case BPF_ALU|BPF_SUB|BPF_X: 386234585Sdas SUBrd(EDX, EAX); 387234585Sdas break; 388234585Sdas 389234585Sdas case BPF_ALU|BPF_MUL|BPF_X: 390234585Sdas MOVrd(EDX, ECX); 391234585Sdas MULrd(EDX); 392234585Sdas MOVrd(ECX, EDX); 393234585Sdas break; 394234585Sdas 395234585Sdas case BPF_ALU|BPF_DIV|BPF_X: 396234585Sdas TESTrd(EDX, EDX); 397234585Sdas JNEb(6); 398234585Sdas ZEROrd(EAX); 399234585Sdas MOVrq3(R8, RBX); 400234585Sdas RET(); 401234585Sdas MOVrd(EDX, ECX); 402234585Sdas ZEROrd(EDX); 403234585Sdas DIVrd(ECX); 404234585Sdas MOVrd(ECX, EDX); 405234585Sdas break; 406234585Sdas 407234585Sdas case BPF_ALU|BPF_AND|BPF_X: 408234585Sdas ANDrd(EDX, EAX); 409234585Sdas break; 410234585Sdas 411234585Sdas case BPF_ALU|BPF_OR|BPF_X: 412234585Sdas ORrd(EDX, EAX); 413234585Sdas break; 414234585Sdas 415234585Sdas case BPF_ALU|BPF_LSH|BPF_X: 416234585Sdas MOVrd(EDX, ECX); 417234585Sdas SHL_CLrb(EAX); 418234585Sdas break; 419234585Sdas 420234585Sdas case BPF_ALU|BPF_RSH|BPF_X: 421234585Sdas MOVrd(EDX, ECX); 422103856Stjr SHR_CLrb(EAX); 423103856Stjr break; 424103856Stjr 425227753Stheraven case BPF_ALU|BPF_ADD|BPF_K: 426227753Stheraven ADD_EAXi(ins->k); 427103856Stjr break; 428103856Stjr 429227753Stheraven case BPF_ALU|BPF_SUB|BPF_K: 430103856Stjr SUB_EAXi(ins->k); 431103856Stjr break; 432103856Stjr 433227753Stheraven case BPF_ALU|BPF_MUL|BPF_K: 434103856Stjr MOVrd(EDX, ECX); 435103856Stjr MOVid(ins->k, EDX); 436103856Stjr MULrd(EDX); 437227753Stheraven MOVrd(ECX, EDX); 438227753Stheraven break; 439227753Stheraven 440227753Stheraven case BPF_ALU|BPF_DIV|BPF_K: 441227753Stheraven MOVrd(EDX, ECX); 442103856Stjr ZEROrd(EDX); 443103856Stjr MOVid(ins->k, ESI); 444103856Stjr DIVrd(ESI); 445103856Stjr MOVrd(ECX, EDX); 446103856Stjr break; 447227753Stheraven 448227753Stheraven case BPF_ALU|BPF_AND|BPF_K: 449103856Stjr ANDid(ins->k, EAX); 450234585Sdas break; 451103856Stjr 452103856Stjr case BPF_ALU|BPF_OR|BPF_K: 453103856Stjr ORid(ins->k, EAX); 454103856Stjr break; 455103856Stjr 456234585Sdas case BPF_ALU|BPF_LSH|BPF_K: 457103856Stjr SHLib((ins->k) & 0xff, EAX); 458103856Stjr break; 459234585Sdas 460103856Stjr case BPF_ALU|BPF_RSH|BPF_K: 461103856Stjr SHRib((ins->k) & 0xff, EAX); 462103856Stjr break; 463103856Stjr 464103856Stjr case BPF_ALU|BPF_NEG: 465103856Stjr NEGd(EAX); 466234585Sdas break; 467103856Stjr 468103856Stjr case BPF_MISC|BPF_TAX: 469103856Stjr MOVrd(EAX, EDX); 470103856Stjr break; 471103856Stjr 472227753Stheraven case BPF_MISC|BPF_TXA: 473227753Stheraven MOVrd(EDX, EAX); 474234588Sdas break; 475103856Stjr } 476227753Stheraven ins++; 477103856Stjr } 478103856Stjr 479103856Stjr pass++; 480103856Stjr if (pass >= 2) { 481103856Stjr#ifndef _KERNEL 482103856Stjr if (mprotect(stream.ibuf, stream.cur_ip, 483103856Stjr PROT_READ | PROT_EXEC) != 0) { 484103856Stjr munmap(stream.ibuf, stream.cur_ip); 485103856Stjr stream.ibuf = NULL; 486103856Stjr } 487103856Stjr#endif 488103856Stjr *size = stream.cur_ip; 489103856Stjr break; 490103856Stjr } 491227753Stheraven 492103856Stjr#ifdef _KERNEL 493103856Stjr stream.ibuf = (char *)contigmalloc(stream.cur_ip, M_BPFJIT, 494227753Stheraven M_NOWAIT, 0, ~0UL, 16, 0); 495103856Stjr if (stream.ibuf == NULL) 496103856Stjr break; 497103856Stjr#else 498103856Stjr stream.ibuf = (char *)mmap(NULL, stream.cur_ip, 499103856Stjr PROT_READ | PROT_WRITE, MAP_ANON, -1, 0); 500103856Stjr if (stream.ibuf == MAP_FAILED) { 501103856Stjr stream.ibuf = NULL; 502103856Stjr break; 503103856Stjr } 504103856Stjr#endif 505103856Stjr 506103856Stjr /* 507103856Stjr * modify the reference table to contain the offsets and 508103856Stjr * not the lengths of the instructions 509103856Stjr */ 510103856Stjr for (i = 1; i < nins + 1; i++) 511103856Stjr stream.refs[i] += stream.refs[i - 1]; 512103856Stjr 513103856Stjr /* Reset the counters */ 514103856Stjr stream.cur_ip = 0; 515103856Stjr stream.bpf_pc = 0; 516103856Stjr 517103856Stjr /* the second pass creates the actual code */ 518103856Stjr emitm = emit_code; 519103856Stjr } 520103856Stjr 521103856Stjr /* 522103856Stjr * the reference table is needed only during compilation, 523103856Stjr * now we can free it 524103856Stjr */ 525103856Stjr#ifdef _KERNEL 526103856Stjr free(stream.refs, M_BPFJIT); 527103856Stjr#else 528103856Stjr free(stream.refs); 529103856Stjr#endif 530103856Stjr 531103856Stjr return ((bpf_filter_func)stream.ibuf); 532103856Stjr} 533103856Stjr