| ... | ... | @@ -5,6 +5,61 @@ |
| 5 | 5 | // and substantial portions of the software. |
| 6 | 6 | usingnamespace std.os; |
| 7 | 7 | const std = @import("../../../std.zig"); |
| 8 | const expectEqual = std.testing.expectEqual; |
| 9 | const fd_t = std.os.fd_t; |
| 10 | const pid_t = std.os.pid_t; |
| 11 | |
| 12 | // instruction classes |
| 13 | pub const LD = 0x00; |
| 14 | pub const LDX = 0x01; |
| 15 | pub const ST = 0x02; |
| 16 | pub const STX = 0x03; |
| 17 | pub const ALU = 0x04; |
| 18 | pub const JMP = 0x05; |
| 19 | pub const RET = 0x06; |
| 20 | pub const MISC = 0x07; |
| 21 | |
| 22 | /// 32-bit |
| 23 | pub const W = 0x00; |
| 24 | /// 16-bit |
| 25 | pub const H = 0x08; |
| 26 | /// 8-bit |
| 27 | pub const B = 0x10; |
| 28 | /// 64-bit |
| 29 | pub const DW = 0x18; |
| 30 | |
| 31 | pub const IMM = 0x00; |
| 32 | pub const ABS = 0x20; |
| 33 | pub const IND = 0x40; |
| 34 | pub const MEM = 0x60; |
| 35 | pub const LEN = 0x80; |
| 36 | pub const MSH = 0xa0; |
| 37 | |
| 38 | // alu fields |
| 39 | pub const ADD = 0x00; |
| 40 | pub const SUB = 0x10; |
| 41 | pub const MUL = 0x20; |
| 42 | pub const DIV = 0x30; |
| 43 | pub const OR = 0x40; |
| 44 | pub const AND = 0x50; |
| 45 | pub const LSH = 0x60; |
| 46 | pub const RSH = 0x70; |
| 47 | pub const NEG = 0x80; |
| 48 | pub const MOD = 0x90; |
| 49 | pub const XOR = 0xa0; |
| 50 | |
| 51 | // jmp fields |
| 52 | pub const JA = 0x00; |
| 53 | pub const JEQ = 0x10; |
| 54 | pub const JGT = 0x20; |
| 55 | pub const JGE = 0x30; |
| 56 | pub const JSET = 0x40; |
| 57 | |
| 58 | //#define BPF_SRC(code) ((code) & 0x08) |
| 59 | pub const K = 0x00; |
| 60 | pub const X = 0x08; |
| 61 | |
| 62 | pub const MAXINSNS = 4096; |
| 8 | 63 | |
| 9 | 64 | // instruction classes |
| 10 | 65 | /// jmp mode in word width |
| ... | ... | @@ -13,8 +68,6 @@ pub const JMP32 = 0x06; |
| 13 | 68 | pub const ALU64 = 0x07; |
| 14 | 69 | |
| 15 | 70 | // ld/ldx fields |
| 16 | | /// double word (64-bit) |
| 17 | | pub const DW = 0x18; |
| 18 | 71 | /// exclusive add |
| 19 | 72 | pub const XADD = 0xc0; |
| 20 | 73 | |
| ... | ... | @@ -153,6 +206,130 @@ pub const BPF_F_CLONE = 0x200; |
| 153 | 206 | /// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map |
| 154 | 207 | pub const BPF_F_MMAPABLE = 0x400; |
| 155 | 208 | |
| 209 | /// These values correspond to "syscalls" within the BPF program's environment |
| 210 | pub const Helper = enum(i32) { |
| 211 | unspec, |
| 212 | map_lookup_elem, |
| 213 | map_update_elem, |
| 214 | map_delete_elem, |
| 215 | probe_read, |
| 216 | ktime_get_ns, |
| 217 | trace_printk, |
| 218 | get_prandom_u32, |
| 219 | get_smp_processor_id, |
| 220 | skb_store_bytes, |
| 221 | l3_csum_replace, |
| 222 | l4_csum_replace, |
| 223 | tail_call, |
| 224 | clone_redirect, |
| 225 | get_current_pid_tgid, |
| 226 | get_current_uid_gid, |
| 227 | get_current_comm, |
| 228 | get_cgroup_classid, |
| 229 | skb_vlan_push, |
| 230 | skb_vlan_pop, |
| 231 | skb_get_tunnel_key, |
| 232 | skb_set_tunnel_key, |
| 233 | perf_event_read, |
| 234 | redirect, |
| 235 | get_route_realm, |
| 236 | perf_event_output, |
| 237 | skb_load_bytes, |
| 238 | get_stackid, |
| 239 | csum_diff, |
| 240 | skb_get_tunnel_opt, |
| 241 | skb_set_tunnel_opt, |
| 242 | skb_change_proto, |
| 243 | skb_change_type, |
| 244 | skb_under_cgroup, |
| 245 | get_hash_recalc, |
| 246 | get_current_task, |
| 247 | probe_write_user, |
| 248 | current_task_under_cgroup, |
| 249 | skb_change_tail, |
| 250 | skb_pull_data, |
| 251 | csum_update, |
| 252 | set_hash_invalid, |
| 253 | get_numa_node_id, |
| 254 | skb_change_head, |
| 255 | xdp_adjust_head, |
| 256 | probe_read_str, |
| 257 | get_socket_cookie, |
| 258 | get_socket_uid, |
| 259 | set_hash, |
| 260 | setsockopt, |
| 261 | skb_adjust_room, |
| 262 | redirect_map, |
| 263 | sk_redirect_map, |
| 264 | sock_map_update, |
| 265 | xdp_adjust_meta, |
| 266 | perf_event_read_value, |
| 267 | perf_prog_read_value, |
| 268 | getsockopt, |
| 269 | override_return, |
| 270 | sock_ops_cb_flags_set, |
| 271 | msg_redirect_map, |
| 272 | msg_apply_bytes, |
| 273 | msg_cork_bytes, |
| 274 | msg_pull_data, |
| 275 | bind, |
| 276 | xdp_adjust_tail, |
| 277 | skb_get_xfrm_state, |
| 278 | get_stack, |
| 279 | skb_load_bytes_relative, |
| 280 | fib_lookup, |
| 281 | sock_hash_update, |
| 282 | msg_redirect_hash, |
| 283 | sk_redirect_hash, |
| 284 | lwt_push_encap, |
| 285 | lwt_seg6_store_bytes, |
| 286 | lwt_seg6_adjust_srh, |
| 287 | lwt_seg6_action, |
| 288 | rc_repeat, |
| 289 | rc_keydown, |
| 290 | skb_cgroup_id, |
| 291 | get_current_cgroup_id, |
| 292 | get_local_storage, |
| 293 | sk_select_reuseport, |
| 294 | skb_ancestor_cgroup_id, |
| 295 | sk_lookup_tcp, |
| 296 | sk_lookup_udp, |
| 297 | sk_release, |
| 298 | map_push_elem, |
| 299 | map_pop_elem, |
| 300 | map_peek_elem, |
| 301 | msg_push_data, |
| 302 | msg_pop_data, |
| 303 | rc_pointer_rel, |
| 304 | spin_lock, |
| 305 | spin_unlock, |
| 306 | sk_fullsock, |
| 307 | tcp_sock, |
| 308 | skb_ecn_set_ce, |
| 309 | get_listener_sock, |
| 310 | skc_lookup_tcp, |
| 311 | tcp_check_syncookie, |
| 312 | sysctl_get_name, |
| 313 | sysctl_get_current_value, |
| 314 | sysctl_get_new_value, |
| 315 | sysctl_set_new_value, |
| 316 | strtol, |
| 317 | strtoul, |
| 318 | sk_storage_get, |
| 319 | sk_storage_delete, |
| 320 | send_signal, |
| 321 | tcp_gen_syncookie, |
| 322 | skb_output, |
| 323 | probe_read_user, |
| 324 | probe_read_kernel, |
| 325 | probe_read_user_str, |
| 326 | probe_read_kernel_str, |
| 327 | tcp_send_ack, |
| 328 | send_signal_thread, |
| 329 | jiffies64, |
| 330 | _, |
| 331 | }; |
| 332 | |
| 156 | 333 | /// a single BPF instruction |
| 157 | 334 | pub const Insn = packed struct { |
| 158 | 335 | code: u8, |
| ... | ... | @@ -163,32 +340,168 @@ pub const Insn = packed struct { |
| 163 | 340 | |
| 164 | 341 | /// r0 - r9 are general purpose 64-bit registers, r10 points to the stack |
| 165 | 342 | /// frame |
| 166 | | pub const Reg = enum(u4) { |
| 167 | | r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 |
| 343 | pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; |
| 344 | const Source = packed enum(u1) { reg, imm }; |
| 345 | const AluOp = packed enum(u8) { |
| 346 | add = ADD, |
| 347 | sub = SUB, |
| 348 | mul = MUL, |
| 349 | div = DIV, |
| 350 | op_or = OR, |
| 351 | op_and = AND, |
| 352 | lsh = LSH, |
| 353 | rsh = RSH, |
| 354 | neg = NEG, |
| 355 | mod = MOD, |
| 356 | xor = XOR, |
| 357 | mov = MOV, |
| 168 | 358 | }; |
| 169 | 359 | |
| 170 | | const alu = 0x04; |
| 171 | | const jmp = 0x05; |
| 172 | | const mov = 0xb0; |
| 173 | | const k = 0; |
| 174 | | const exit_code = 0x90; |
| 360 | pub const Size = packed enum(u8) { |
| 361 | byte = B, |
| 362 | half_word = H, |
| 363 | word = W, |
| 364 | double_word = DW, |
| 365 | }; |
| 366 | |
| 367 | const JmpOp = packed enum(u8) { |
| 368 | ja = JA, |
| 369 | jeq = JEQ, |
| 370 | jgt = JGT, |
| 371 | jge = JGE, |
| 372 | jset = JSET, |
| 373 | }; |
| 374 | |
| 375 | const ImmOrReg = union(Source) { |
| 376 | imm: i32, |
| 377 | reg: Reg, |
| 378 | }; |
| 379 | |
| 380 | fn imm_reg(code: u8, dst: Reg, src: anytype, off: i16) Insn { |
| 381 | const imm_or_reg = if (@typeInfo(@TypeOf(src)) == .EnumLiteral) |
| 382 | ImmOrReg{ .reg = @as(Reg, src) } |
| 383 | else |
| 384 | ImmOrReg{ .imm = src }; |
| 385 | |
| 386 | const src_type = switch (imm_or_reg) { |
| 387 | .imm => K, |
| 388 | .reg => X, |
| 389 | }; |
| 390 | |
| 391 | return Insn{ |
| 392 | .code = code | src_type, |
| 393 | .dst = @enumToInt(dst), |
| 394 | .src = switch (imm_or_reg) { |
| 395 | .imm => 0, |
| 396 | .reg => |r| @enumToInt(r), |
| 397 | }, |
| 398 | .off = off, |
| 399 | .imm = switch (imm_or_reg) { |
| 400 | .imm => |i| i, |
| 401 | .reg => 0, |
| 402 | }, |
| 403 | }; |
| 404 | } |
| 405 | |
| 406 | fn alu(comptime width: comptime_int, op: AluOp, dst: Reg, src: anytype) Insn { |
| 407 | const width_bitfield = switch (width) { |
| 408 | 32 => ALU, |
| 409 | 64 => ALU64, |
| 410 | else => @compileError("width must be 32 or 64"), |
| 411 | }; |
| 412 | |
| 413 | return imm_reg(width_bitfield | @enumToInt(op), dst, src, 0); |
| 414 | } |
| 175 | 415 | |
| 176 | | // TODO: implement more factory functions for the other instructions |
| 177 | | /// load immediate value into a register |
| 178 | | pub fn load_imm(dst: Reg, imm: i32) Insn { |
| 416 | pub fn mov(dst: Reg, src: anytype) Insn { |
| 417 | return alu(64, .mov, dst, src); |
| 418 | } |
| 419 | |
| 420 | pub fn add(dst: Reg, src: anytype) Insn { |
| 421 | return alu(64, .add, dst, src); |
| 422 | } |
| 423 | |
| 424 | fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn { |
| 425 | return imm_reg(JMP | @enumToInt(op), dst, src, off); |
| 426 | } |
| 427 | |
| 428 | pub fn jeq(dst: Reg, src: anytype, off: i16) Insn { |
| 429 | return jmp(.jeq, dst, src, off); |
| 430 | } |
| 431 | |
| 432 | pub fn stx_mem(size: Size, dst: Reg, src: Reg, off: i16) Insn { |
| 179 | 433 | return Insn{ |
| 180 | | .code = alu | mov | k, |
| 434 | .code = STX | @enumToInt(size) | MEM, |
| 181 | 435 | .dst = @enumToInt(dst), |
| 436 | .src = @enumToInt(src), |
| 437 | .off = off, |
| 438 | .imm = 0, |
| 439 | }; |
| 440 | } |
| 441 | |
| 442 | pub fn xadd(dst: Reg, src: Reg) Insn { |
| 443 | return Insn{ |
| 444 | .code = STX | XADD | DW, |
| 445 | .dst = @enumToInt(dst), |
| 446 | .src = @enumToInt(src), |
| 447 | .off = 0, |
| 448 | .imm = 0, |
| 449 | }; |
| 450 | } |
| 451 | |
| 452 | /// direct packet access, R0 = *(uint *)(skb->data + imm32) |
| 453 | pub fn ld_abs(size: Size, imm: i32) Insn { |
| 454 | return Insn{ |
| 455 | .code = LD | @enumToInt(size) | ABS, |
| 456 | .dst = 0, |
| 182 | 457 | .src = 0, |
| 183 | 458 | .off = 0, |
| 184 | 459 | .imm = imm, |
| 185 | 460 | }; |
| 186 | 461 | } |
| 187 | 462 | |
| 463 | fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn { |
| 464 | return Insn{ |
| 465 | .code = LD | DW | IMM, |
| 466 | .dst = @enumToInt(dst), |
| 467 | .src = @enumToInt(src), |
| 468 | .off = 0, |
| 469 | .imm = @intCast(i32, @truncate(u32, imm)), |
| 470 | }; |
| 471 | } |
| 472 | |
| 473 | fn ld_imm_impl2(imm: u64) Insn { |
| 474 | return Insn{ |
| 475 | .code = 0, |
| 476 | .dst = 0, |
| 477 | .src = 0, |
| 478 | .off = 0, |
| 479 | .imm = @intCast(i32, @truncate(u32, imm >> 32)), |
| 480 | }; |
| 481 | } |
| 482 | |
| 483 | pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn { |
| 484 | return ld_imm_impl1(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd)); |
| 485 | } |
| 486 | |
| 487 | pub fn ld_map_fd2(map_fd: fd_t) Insn { |
| 488 | return ld_imm_impl2(@intCast(u64, map_fd)); |
| 489 | } |
| 490 | |
| 491 | pub fn call(helper: Helper) Insn { |
| 492 | return Insn{ |
| 493 | .code = JMP | CALL, |
| 494 | .dst = 0, |
| 495 | .src = 0, |
| 496 | .off = 0, |
| 497 | .imm = @enumToInt(helper), |
| 498 | }; |
| 499 | } |
| 500 | |
| 188 | 501 | /// exit BPF program |
| 189 | 502 | pub fn exit() Insn { |
| 190 | 503 | return Insn{ |
| 191 | | .code = jmp | exit_code, |
| 504 | .code = JMP | EXIT, |
| 192 | 505 | .dst = 0, |
| 193 | 506 | .src = 0, |
| 194 | 507 | .off = 0, |
| ... | ... | @@ -197,6 +510,61 @@ pub const Insn = packed struct { |
| 197 | 510 | } |
| 198 | 511 | }; |
| 199 | 512 | |
| 513 | fn expect_insn(insn: Insn, val: u64) void { |
| 514 | expectEqual(@bitCast(u64, insn), val); |
| 515 | } |
| 516 | |
| 517 | test "insn bitsize" { |
| 518 | expectEqual(@bitSizeOf(Insn), 64); |
| 519 | } |
| 520 | |
| 521 | // mov instructions |
| 522 | test "mov imm" { |
| 523 | expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7); |
| 524 | } |
| 525 | |
| 526 | test "mov reg" { |
| 527 | expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf); |
| 528 | } |
| 529 | |
| 530 | // alu instructions |
| 531 | test "add imm" { |
| 532 | expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207); |
| 533 | } |
| 534 | |
| 535 | // ld instructions |
| 536 | test "ld_abs" { |
| 537 | expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030); |
| 538 | } |
| 539 | |
| 540 | test "ld_map_fd" { |
| 541 | expect_insn(Insn.ld_map_fd1(.r1, 42), 0x0000002a00001118); |
| 542 | expect_insn(Insn.ld_map_fd2(42), 0x0000000000000000); |
| 543 | } |
| 544 | |
| 545 | // st instructions |
| 546 | test "stx_mem" { |
| 547 | expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63); |
| 548 | } |
| 549 | |
| 550 | test "xadd" { |
| 551 | expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db); |
| 552 | } |
| 553 | |
| 554 | // jmp instructions |
| 555 | test "jeq imm" { |
| 556 | expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015); |
| 557 | } |
| 558 | |
| 559 | // other instructions |
| 560 | test "call" { |
| 561 | expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085); |
| 562 | } |
| 563 | |
| 564 | test "exit" { |
| 565 | expect_insn(Insn.exit(), 0x0000000000000095); |
| 566 | } |
| 567 | |
| 200 | 568 | pub const Cmd = extern enum(usize) { |
| 201 | 569 | map_create, |
| 202 | 570 | map_lookup_elem, |
| ... | ... | @@ -605,7 +973,3 @@ pub const Attr = extern union { |
| 605 | 973 | enable_stats: EnableStatsAttr, |
| 606 | 974 | iter_create: IterCreateAttr, |
| 607 | 975 | }; |
| 608 | | |
| 609 | | pub fn bpf(cmd: Cmd, attr: *Attr, size: u32) usize { |
| 610 | | return syscall3(.bpf, @enumToInt(cmd), @ptrToInt(attr), size); |
| 611 | | } |