| ... | @@ -5,6 +5,61 @@ | ... | @@ -5,6 +5,61 @@ |
| 5 | // and substantial portions of the software. | 5 | // and substantial portions of the software. |
| 6 | usingnamespace std.os; | 6 | usingnamespace std.os; |
| 7 | const std = @import("../../../std.zig"); | 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 | // instruction classes | 64 | // instruction classes |
| 10 | /// jmp mode in word width | 65 | /// jmp mode in word width |
| ... | @@ -13,8 +68,6 @@ pub const JMP32 = 0x06; | ... | @@ -13,8 +68,6 @@ pub const JMP32 = 0x06; |
| 13 | pub const ALU64 = 0x07; | 68 | pub const ALU64 = 0x07; |
| 14 | | 69 | |
| 15 | // ld/ldx fields | 70 | // ld/ldx fields |
| 16 | /// double word (64-bit) | | |
| 17 | pub const DW = 0x18; | | |
| 18 | /// exclusive add | 71 | /// exclusive add |
| 19 | pub const XADD = 0xc0; | 72 | pub const XADD = 0xc0; |
| 20 | | 73 | |
| ... | @@ -153,6 +206,130 @@ pub const BPF_F_CLONE = 0x200; | ... | @@ -153,6 +206,130 @@ pub const BPF_F_CLONE = 0x200; |
| 153 | /// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map | 206 | /// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map |
| 154 | pub const BPF_F_MMAPABLE = 0x400; | 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 | /// a single BPF instruction | 333 | /// a single BPF instruction |
| 157 | pub const Insn = packed struct { | 334 | pub const Insn = packed struct { |
| 158 | code: u8, | 335 | code: u8, |
| ... | @@ -163,32 +340,163 @@ pub const Insn = packed struct { | ... | @@ -163,32 +340,163 @@ pub const Insn = packed struct { |
| 163 | | 340 | |
| 164 | /// r0 - r9 are general purpose 64-bit registers, r10 points to the stack | 341 | /// r0 - r9 are general purpose 64-bit registers, r10 points to the stack |
| 165 | /// frame | 342 | /// frame |
| 166 | pub const Reg = enum(u4) { | 343 | pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; |
| 167 | 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, |
| | 358 | }; |
| | 359 | |
| | 360 | pub const Size = packed enum(u8) { |
| | 361 | byte = B, |
| | 362 | half_word = H, |
| | 363 | word = W, |
| | 364 | double_word = DW, |
| 168 | }; | 365 | }; |
| 169 | | 366 | |
| 170 | const alu = 0x04; | 367 | const JmpOp = packed enum(u8) { |
| 171 | const jmp = 0x05; | 368 | ja = JA, |
| 172 | const mov = 0xb0; | 369 | jeq = JEQ, |
| 173 | const k = 0; | 370 | jgt = JGT, |
| 174 | const exit_code = 0x90; | 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 | } |
| | 415 | |
| | 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 | } |
| 175 | | 431 | |
| 176 | // TODO: implement more factory functions for the other instructions | 432 | pub fn stx_mem(size: Size, dst: Reg, src: Reg, off: i16) Insn { |
| 177 | /// load immediate value into a register | | |
| 178 | pub fn load_imm(dst: Reg, imm: i32) Insn { | | |
| 179 | return Insn{ | 433 | return Insn{ |
| 180 | .code = alu | mov | k, | 434 | .code = STX | @enumToInt(size) | MEM, |
| 181 | .dst = @enumToInt(dst), | 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 | .src = 0, | 457 | .src = 0, |
| 183 | .off = 0, | 458 | .off = 0, |
| 184 | .imm = imm, | 459 | .imm = imm, |
| 185 | }; | 460 | }; |
| 186 | } | 461 | } |
| 187 | | 462 | |
| | 463 | fn ld_imm_impl(dst: Reg, src: Reg, imm: u64) [2]Insn { |
| | 464 | return [2]Insn{ |
| | 465 | Insn{ |
| | 466 | .code = LD | DW | IMM, |
| | 467 | .dst = @enumToInt(dst), |
| | 468 | .src = @enumToInt(src), |
| | 469 | .off = 0, |
| | 470 | .imm = @intCast(i32, @truncate(u32, imm)), |
| | 471 | }, |
| | 472 | Insn{ |
| | 473 | .code = 0, |
| | 474 | .dst = 0, |
| | 475 | .src = 0, |
| | 476 | .off = 0, |
| | 477 | .imm = @intCast(i32, @truncate(u32, imm >> 32)), |
| | 478 | }, |
| | 479 | }; |
| | 480 | } |
| | 481 | |
| | 482 | pub fn ld_map_fd(dst: Reg, map_fd: fd_t) [2]Insn { |
| | 483 | return ld_imm_impl(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd)); |
| | 484 | } |
| | 485 | |
| | 486 | pub fn call(helper: Helper) Insn { |
| | 487 | return Insn{ |
| | 488 | .code = JMP | CALL, |
| | 489 | .dst = 0, |
| | 490 | .src = 0, |
| | 491 | .off = 0, |
| | 492 | .imm = @enumToInt(helper), |
| | 493 | }; |
| | 494 | } |
| | 495 | |
| 188 | /// exit BPF program | 496 | /// exit BPF program |
| 189 | pub fn exit() Insn { | 497 | pub fn exit() Insn { |
| 190 | return Insn{ | 498 | return Insn{ |
| 191 | .code = jmp | exit_code, | 499 | .code = JMP | EXIT, |
| 192 | .dst = 0, | 500 | .dst = 0, |
| 193 | .src = 0, | 501 | .src = 0, |
| 194 | .off = 0, | 502 | .off = 0, |
| ... | @@ -197,6 +505,62 @@ pub const Insn = packed struct { | ... | @@ -197,6 +505,62 @@ pub const Insn = packed struct { |
| 197 | } | 505 | } |
| 198 | }; | 506 | }; |
| 199 | | 507 | |
| | 508 | fn expect_insn(insn: Insn, val: u64) void { |
| | 509 | expectEqual(@bitCast(u64, insn), val); |
| | 510 | } |
| | 511 | |
| | 512 | test "insn bitsize" { |
| | 513 | expectEqual(@bitSizeOf(Insn), 64); |
| | 514 | } |
| | 515 | |
| | 516 | // mov instructions |
| | 517 | test "mov imm" { |
| | 518 | expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7); |
| | 519 | } |
| | 520 | |
| | 521 | test "mov reg" { |
| | 522 | expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf); |
| | 523 | } |
| | 524 | |
| | 525 | // alu instructions |
| | 526 | test "add imm" { |
| | 527 | expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207); |
| | 528 | } |
| | 529 | |
| | 530 | // ld instructions |
| | 531 | test "ld_abs" { |
| | 532 | expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030); |
| | 533 | } |
| | 534 | |
| | 535 | test "ld_map_fd" { |
| | 536 | const insns = Insn.ld_map_fd(.r1, 42); |
| | 537 | expect_insn(insns[0], 0x0000002a00001118); |
| | 538 | expect_insn(insns[1], 0x0000000000000000); |
| | 539 | } |
| | 540 | |
| | 541 | // st instructions |
| | 542 | test "stx_mem" { |
| | 543 | expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63); |
| | 544 | } |
| | 545 | |
| | 546 | test "xadd" { |
| | 547 | expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db); |
| | 548 | } |
| | 549 | |
| | 550 | // jmp instructions |
| | 551 | test "jeq imm" { |
| | 552 | expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015); |
| | 553 | } |
| | 554 | |
| | 555 | // other instructions |
| | 556 | test "call" { |
| | 557 | expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085); |
| | 558 | } |
| | 559 | |
| | 560 | test "exit" { |
| | 561 | expect_insn(Insn.exit(), 0x0000000000000095); |
| | 562 | } |
| | 563 | |
| 200 | pub const Cmd = extern enum(usize) { | 564 | pub const Cmd = extern enum(usize) { |
| 201 | map_create, | 565 | map_create, |
| 202 | map_lookup_elem, | 566 | map_lookup_elem, |
| ... | @@ -605,7 +969,3 @@ pub const Attr = extern union { | ... | @@ -605,7 +969,3 @@ pub const Attr = extern union { |
| 605 | enable_stats: EnableStatsAttr, | 969 | enable_stats: EnableStatsAttr, |
| 606 | iter_create: IterCreateAttr, | 970 | iter_create: IterCreateAttr, |
| 607 | }; | 971 | }; |
| 608 | | | |
| 609 | pub fn bpf(cmd: Cmd, attr: *Attr, size: u32) usize { | | |
| 610 | return syscall3(.bpf, @enumToInt(cmd), @ptrToInt(attr), size); | | |
| 611 | } | | |