bpf_jit_machdep.c revision 179967
1226031Sstas/*- 2226031Sstas * Copyright (c) 2002 - 2003 NetGroup, Politecnico di Torino (Italy) 3226031Sstas * Copyright (c) 2005 Jung-uk Kim <jkim@FreeBSD.org> 4226031Sstas * All rights reserved. 5226031Sstas * 6226031Sstas * Redistribution and use in source and binary forms, with or without 7226031Sstas * modification, are permitted provided that the following conditions 8226031Sstas * are met: 9226031Sstas * 10226031Sstas * 1. Redistributions of source code must retain the above copyright 11226031Sstas * notice, this list of conditions and the following disclaimer. 12226031Sstas * 2. Redistributions in binary form must reproduce the above copyright 13226031Sstas * notice, this list of conditions and the following disclaimer in the 14226031Sstas * documentation and/or other materials provided with the distribution. 15226031Sstas * 3. Neither the name of the Politecnico di Torino nor the names of its 16226031Sstas * contributors may be used to endorse or promote products derived from 17226031Sstas * this software without specific prior written permission. 18226031Sstas * 19226031Sstas * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20226031Sstas * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21226031Sstas * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22226031Sstas * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23226031Sstas * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24226031Sstas * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25226031Sstas * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26226031Sstas * DATA, OR PROFITS; OR BUSINESS intERRUPTION) HOWEVER CAUSED AND ON ANY 27226031Sstas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28226031Sstas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29226031Sstas * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30226031Sstas */ 31226031Sstas 32226031Sstas#include <sys/cdefs.h> 33226031Sstas__FBSDID("$FreeBSD: head/sys/amd64/amd64/bpf_jit_machdep.c 179967 2008-06-23 23:09:52Z jkim $"); 34226031Sstas 35226031Sstas#include "opt_bpf.h" 36226031Sstas 37226031Sstas#include <sys/param.h> 38226031Sstas#include <sys/systm.h> 39226031Sstas#include <sys/kernel.h> 40226031Sstas#include <sys/types.h> 41226031Sstas#include <sys/socket.h> 42226031Sstas#include <sys/malloc.h> 43226031Sstas 44226031Sstas#include <net/if.h> 45226031Sstas#include <net/bpf.h> 46226031Sstas#include <net/bpf_jitter.h> 47226031Sstas 48226031Sstas#include <amd64/amd64/bpf_jit_machdep.h> 49226031Sstas 50226031Sstasbpf_filter_func bpf_jit_compile(struct bpf_insn *, u_int, int *); 51226031Sstas 52226031Sstas/* 53226031Sstas * emit routine to update the jump table 54226031Sstas */ 55226031Sstasstatic void 56226031Sstasemit_length(bpf_bin_stream *stream, u_int value, u_int len) 57226031Sstas{ 58226031Sstas 59226031Sstas (stream->refs)[stream->bpf_pc] += len; 60226031Sstas stream->cur_ip += len; 61226031Sstas} 62226031Sstas 63226031Sstas/* 64226031Sstas * emit routine to output the actual binary code 65226031Sstas */ 66226031Sstasstatic void 67226031Sstasemit_code(bpf_bin_stream *stream, u_int value, u_int len) 68226031Sstas{ 69226031Sstas 70226031Sstas switch (len) { 71226031Sstas case 1: 72226031Sstas stream->ibuf[stream->cur_ip] = (u_char)value; 73226031Sstas stream->cur_ip++; 74226031Sstas break; 75226031Sstas 76226031Sstas case 2: 77226031Sstas *((u_short *)(stream->ibuf + stream->cur_ip)) = (u_short)value; 78226031Sstas stream->cur_ip += 2; 79226031Sstas break; 80226031Sstas 81226031Sstas case 4: 82226031Sstas *((u_int *)(stream->ibuf + stream->cur_ip)) = value; 83226031Sstas stream->cur_ip += 4; 84226031Sstas break; 85226031Sstas } 86226031Sstas 87226031Sstas return; 88226031Sstas} 89226031Sstas 90226031Sstas/* 91226031Sstas * Function that does the real stuff 92226031Sstas */ 93226031Sstasbpf_filter_func 94226031Sstasbpf_jit_compile(struct bpf_insn *prog, u_int nins, int *mem) 95226031Sstas{ 96226031Sstas struct bpf_insn *ins; 97226031Sstas u_int i, pass; 98226031Sstas bpf_bin_stream stream; 99226031Sstas 100226031Sstas /* 101226031Sstas * NOTE: do not modify the name of this variable, as it's used by 102226031Sstas * the macros to emit code. 103226031Sstas */ 104226031Sstas emit_func emitm; 105226031Sstas 106226031Sstas /* Do not compile an empty filter. */ 107226031Sstas if (nins == 0) 108226031Sstas return NULL; 109226031Sstas 110226031Sstas /* Allocate the reference table for the jumps */ 111226031Sstas stream.refs = (u_int *)malloc((nins + 1) * sizeof(u_int), 112226031Sstas M_BPFJIT, M_NOWAIT); 113226031Sstas if (stream.refs == NULL) 114226031Sstas return NULL; 115226031Sstas 116226031Sstas /* Reset the reference table */ 117226031Sstas for (i = 0; i < nins + 1; i++) 118226031Sstas stream.refs[i] = 0; 119226031Sstas 120226031Sstas stream.cur_ip = 0; 121226031Sstas stream.bpf_pc = 0; 122226031Sstas 123226031Sstas /* 124226031Sstas * the first pass will emit the lengths of the instructions 125226031Sstas * to create the reference table 126226031Sstas */ 127226031Sstas emitm = emit_length; 128226031Sstas 129226031Sstas pass = 0; 130226031Sstas for (;;) { 131226031Sstas ins = prog; 132226031Sstas 133226031Sstas /* create the procedure header */ 134226031Sstas PUSH(RBP); 135226031Sstas MOVrq(RSP, RBP); 136226031Sstas MOVdoq(ESI, -8, RBP); 137226031Sstas MOVdoq(EDX, -12, RBP); 138226031Sstas PUSH(RBX); 139226031Sstas MOVrq(RDI, RBX); 140226031Sstas 141226031Sstas for (i = 0; i < nins; i++) { 142226031Sstas stream.bpf_pc++; 143226031Sstas 144226031Sstas switch (ins->code) { 145226031Sstas default: 146226031Sstas return NULL; 147226031Sstas 148226031Sstas case BPF_RET|BPF_K: 149226031Sstas MOVid(ins->k, EAX); 150226031Sstas POP(RBX); 151226031Sstas LEAVE_RET(); 152226031Sstas break; 153226031Sstas 154226031Sstas case BPF_RET|BPF_A: 155226031Sstas POP(RBX); 156226031Sstas LEAVE_RET(); 157226031Sstas break; 158226031Sstas 159226031Sstas case BPF_LD|BPF_W|BPF_ABS: 160226031Sstas MOVid(ins->k, ECX); 161226031Sstas MOVrd(ECX, ESI); 162226031Sstas ADDib(sizeof(int), ECX); 163226031Sstas CMPoqd(-12, RBP, ECX); 164226031Sstas JLEb(5); 165226031Sstas ZERO_EAX(); 166226031Sstas POP(RBX); 167226031Sstas LEAVE_RET(); 168226031Sstas MOVobd(RBX, RSI, EAX); 169226031Sstas BSWAP(EAX); 170226031Sstas break; 171226031Sstas 172226031Sstas case BPF_LD|BPF_H|BPF_ABS: 173226031Sstas ZERO_EAX(); 174226031Sstas MOVid(ins->k, ECX); 175226031Sstas MOVrd(ECX, ESI); 176226031Sstas ADDib(sizeof(short), ECX); 177 CMPoqd(-12, RBP, ECX); 178 JLEb(3); 179 POP(RBX); 180 LEAVE_RET(); 181 MOVobw(RBX, RSI, AX); 182 SWAP_AX(); 183 break; 184 185 case BPF_LD|BPF_B|BPF_ABS: 186 ZERO_EAX(); 187 MOVid(ins->k, ECX); 188 CMPoqd(-12, RBP, ECX); 189 JLEb(3); 190 POP(RBX); 191 LEAVE_RET(); 192 MOVobb(RBX, RCX, AL); 193 break; 194 195 case BPF_LD|BPF_W|BPF_LEN: 196 MOVoqd(-8, RBP, EAX); 197 break; 198 199 case BPF_LDX|BPF_W|BPF_LEN: 200 MOVoqd(-8, RBP, EDX); 201 break; 202 203 case BPF_LD|BPF_W|BPF_IND: 204 MOVid(ins->k, ECX); 205 ADDrd(EDX, ECX); 206 MOVrd(ECX, ESI); 207 ADDib(sizeof(int), ECX); 208 CMPoqd(-12, RBP, ECX); 209 JLEb(5); 210 ZERO_EAX(); 211 POP(RBX); 212 LEAVE_RET(); 213 MOVobd(RBX, RSI, EAX); 214 BSWAP(EAX); 215 break; 216 217 case BPF_LD|BPF_H|BPF_IND: 218 ZERO_EAX(); 219 MOVid(ins->k, ECX); 220 ADDrd(EDX, ECX); 221 MOVrd(ECX, ESI); 222 ADDib(sizeof(short), ECX); 223 CMPoqd(-12, RBP, ECX); 224 JLEb(3); 225 POP(RBX); 226 LEAVE_RET(); 227 MOVobw(RBX, RSI, AX); 228 SWAP_AX(); 229 break; 230 231 case BPF_LD|BPF_B|BPF_IND: 232 ZERO_EAX(); 233 MOVid(ins->k, ECX); 234 ADDrd(EDX, ECX); 235 CMPoqd(-12, RBP, ECX); 236 JLEb(3); 237 POP(RBX); 238 LEAVE_RET(); 239 MOVobb(RBX, RCX, AL); 240 break; 241 242 case BPF_LDX|BPF_MSH|BPF_B: 243 MOVid(ins->k, ECX); 244 CMPoqd(-12, RBP, ECX); 245 JLEb(5); 246 ZERO_EAX(); 247 POP(RBX); 248 LEAVE_RET(); 249 ZERO_EDX(); 250 MOVobb(RBX, RCX, DL); 251 ANDib(0xf, DL); 252 SHLib(2, EDX); 253 break; 254 255 case BPF_LD|BPF_IMM: 256 MOVid(ins->k, EAX); 257 break; 258 259 case BPF_LDX|BPF_IMM: 260 MOVid(ins->k, EDX); 261 break; 262 263 case BPF_LD|BPF_MEM: 264 MOViq((uintptr_t)mem, RCX); 265 MOVid(ins->k * 4, ESI); 266 MOVobd(RCX, RSI, EAX); 267 break; 268 269 case BPF_LDX|BPF_MEM: 270 MOViq((uintptr_t)mem, RCX); 271 MOVid(ins->k * 4, ESI); 272 MOVobd(RCX, RSI, EDX); 273 break; 274 275 case BPF_ST: 276 /* 277 * XXX this command and the following could 278 * be optimized if the previous instruction 279 * was already of this type 280 */ 281 MOViq((uintptr_t)mem, RCX); 282 MOVid(ins->k * 4, ESI); 283 MOVomd(EAX, RCX, RSI); 284 break; 285 286 case BPF_STX: 287 MOViq((uintptr_t)mem, RCX); 288 MOVid(ins->k * 4, ESI); 289 MOVomd(EDX, RCX, RSI); 290 break; 291 292 case BPF_JMP|BPF_JA: 293 JMP(stream.refs[stream.bpf_pc + ins->k] - 294 stream.refs[stream.bpf_pc]); 295 break; 296 297 case BPF_JMP|BPF_JGT|BPF_K: 298 CMPid(ins->k, EAX); 299 /* 5 is the size of the following JMP */ 300 JG(stream.refs[stream.bpf_pc + ins->jt] - 301 stream.refs[stream.bpf_pc] + 5 ); 302 JMP(stream.refs[stream.bpf_pc + ins->jf] - 303 stream.refs[stream.bpf_pc]); 304 break; 305 306 case BPF_JMP|BPF_JGE|BPF_K: 307 CMPid(ins->k, EAX); 308 JGE(stream.refs[stream.bpf_pc + ins->jt] - 309 stream.refs[stream.bpf_pc] + 5); 310 JMP(stream.refs[stream.bpf_pc + ins->jf] - 311 stream.refs[stream.bpf_pc]); 312 break; 313 314 case BPF_JMP|BPF_JEQ|BPF_K: 315 CMPid(ins->k, EAX); 316 JE(stream.refs[stream.bpf_pc + ins->jt] - 317 stream.refs[stream.bpf_pc] + 5); 318 JMP(stream.refs[stream.bpf_pc + ins->jf] - 319 stream.refs[stream.bpf_pc]); 320 break; 321 322 case BPF_JMP|BPF_JSET|BPF_K: 323 MOVrd(EAX, ECX); 324 ANDid(ins->k, ECX); 325 JE(stream.refs[stream.bpf_pc + ins->jf] - 326 stream.refs[stream.bpf_pc] + 5); 327 JMP(stream.refs[stream.bpf_pc + ins->jt] - 328 stream.refs[stream.bpf_pc]); 329 break; 330 331 case BPF_JMP|BPF_JGT|BPF_X: 332 CMPrd(EDX, EAX); 333 JA(stream.refs[stream.bpf_pc + ins->jt] - 334 stream.refs[stream.bpf_pc] + 5); 335 JMP(stream.refs[stream.bpf_pc + ins->jf] - 336 stream.refs[stream.bpf_pc]); 337 break; 338 339 case BPF_JMP|BPF_JGE|BPF_X: 340 CMPrd(EDX, EAX); 341 JAE(stream.refs[stream.bpf_pc + ins->jt] - 342 stream.refs[stream.bpf_pc] + 5); 343 JMP(stream.refs[stream.bpf_pc + ins->jf] - 344 stream.refs[stream.bpf_pc]); 345 break; 346 347 case BPF_JMP|BPF_JEQ|BPF_X: 348 CMPrd(EDX, EAX); 349 JE(stream.refs[stream.bpf_pc + ins->jt] - 350 stream.refs[stream.bpf_pc] + 5); 351 JMP(stream.refs[stream.bpf_pc + ins->jf] - 352 stream.refs[stream.bpf_pc]); 353 break; 354 355 case BPF_JMP|BPF_JSET|BPF_X: 356 MOVrd(EAX, ECX); 357 ANDrd(EDX, ECX); 358 JE(stream.refs[stream.bpf_pc + ins->jf] - 359 stream.refs[stream.bpf_pc] + 5); 360 JMP(stream.refs[stream.bpf_pc + ins->jt] - 361 stream.refs[stream.bpf_pc]); 362 break; 363 364 case BPF_ALU|BPF_ADD|BPF_X: 365 ADDrd(EDX, EAX); 366 break; 367 368 case BPF_ALU|BPF_SUB|BPF_X: 369 SUBrd(EDX, EAX); 370 break; 371 372 case BPF_ALU|BPF_MUL|BPF_X: 373 MOVrd(EDX, ECX); 374 MULrd(EDX); 375 MOVrd(ECX, EDX); 376 break; 377 378 case BPF_ALU|BPF_DIV|BPF_X: 379 CMPid(0, EDX); 380 JNEb(5); 381 ZERO_EAX(); 382 POP(RBX); 383 LEAVE_RET(); 384 MOVrd(EDX, ECX); 385 ZERO_EDX(); 386 DIVrd(ECX); 387 MOVrd(ECX, EDX); 388 break; 389 390 case BPF_ALU|BPF_AND|BPF_X: 391 ANDrd(EDX, EAX); 392 break; 393 394 case BPF_ALU|BPF_OR|BPF_X: 395 ORrd(EDX, EAX); 396 break; 397 398 case BPF_ALU|BPF_LSH|BPF_X: 399 MOVrd(EDX, ECX); 400 SHL_CLrb(EAX); 401 break; 402 403 case BPF_ALU|BPF_RSH|BPF_X: 404 MOVrd(EDX, ECX); 405 SHR_CLrb(EAX); 406 break; 407 408 case BPF_ALU|BPF_ADD|BPF_K: 409 ADD_EAXi(ins->k); 410 break; 411 412 case BPF_ALU|BPF_SUB|BPF_K: 413 SUB_EAXi(ins->k); 414 break; 415 416 case BPF_ALU|BPF_MUL|BPF_K: 417 MOVrd(EDX, ECX); 418 MOVid(ins->k, EDX); 419 MULrd(EDX); 420 MOVrd(ECX, EDX); 421 break; 422 423 case BPF_ALU|BPF_DIV|BPF_K: 424 MOVrd(EDX, ECX); 425 ZERO_EDX(); 426 MOVid(ins->k, ESI); 427 DIVrd(ESI); 428 MOVrd(ECX, EDX); 429 break; 430 431 case BPF_ALU|BPF_AND|BPF_K: 432 ANDid(ins->k, EAX); 433 break; 434 435 case BPF_ALU|BPF_OR|BPF_K: 436 ORid(ins->k, EAX); 437 break; 438 439 case BPF_ALU|BPF_LSH|BPF_K: 440 SHLib((ins->k) & 0xff, EAX); 441 break; 442 443 case BPF_ALU|BPF_RSH|BPF_K: 444 SHRib((ins->k) & 0xff, EAX); 445 break; 446 447 case BPF_ALU|BPF_NEG: 448 NEGd(EAX); 449 break; 450 451 case BPF_MISC|BPF_TAX: 452 MOVrd(EAX, EDX); 453 break; 454 455 case BPF_MISC|BPF_TXA: 456 MOVrd(EDX, EAX); 457 break; 458 } 459 ins++; 460 } 461 462 pass++; 463 if (pass == 2) 464 break; 465 466 stream.ibuf = (char *)malloc(stream.cur_ip, M_BPFJIT, M_NOWAIT); 467 if (stream.ibuf == NULL) { 468 free(stream.refs, M_BPFJIT); 469 return NULL; 470 } 471 472 /* 473 * modify the reference table to contain the offsets and 474 * not the lengths of the instructions 475 */ 476 for (i = 1; i < nins + 1; i++) 477 stream.refs[i] += stream.refs[i - 1]; 478 479 /* Reset the counters */ 480 stream.cur_ip = 0; 481 stream.bpf_pc = 0; 482 483 /* the second pass creates the actual code */ 484 emitm = emit_code; 485 } 486 487 /* 488 * the reference table is needed only during compilation, 489 * now we can free it 490 */ 491 free(stream.refs, M_BPFJIT); 492 493 return (bpf_filter_func)stream.ibuf; 494} 495