struct Insn [src]

a single BPF instruction

Fields

code: u8
dst: u4
src: u4
off: i16
imm: i32

Members

Source

pub const Insn = packed struct { code: u8, dst: u4, src: u4, off: i16, imm: i32, /// r0 - r9 are general purpose 64-bit registers, r10 points to the stack /// frame pub const Reg = enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; const Source = enum(u1) { reg, imm }; const Mode = enum(u8) { imm = IMM, abs = ABS, ind = IND, mem = MEM, len = LEN, msh = MSH, }; pub const AluOp = enum(u8) { add = ADD, sub = SUB, mul = MUL, div = DIV, alu_or = OR, alu_and = AND, lsh = LSH, rsh = RSH, neg = NEG, mod = MOD, xor = XOR, mov = MOV, arsh = ARSH, }; pub const Size = enum(u8) { byte = B, half_word = H, word = W, double_word = DW, }; pub const JmpOp = enum(u8) { ja = JA, jeq = JEQ, jgt = JGT, jge = JGE, jset = JSET, jlt = JLT, jle = JLE, jne = JNE, jsgt = JSGT, jsge = JSGE, jslt = JSLT, jsle = JSLE, }; const ImmOrReg = union(Source) { reg: Reg, imm: i32, }; fn imm_reg(code: u8, dst: Reg, src: anytype, off: i16) Insn { const imm_or_reg = if (@TypeOf(src) == Reg or @typeInfo(@TypeOf(src)) == .enum_literal) ImmOrReg{ .reg = @as(Reg, src) } else ImmOrReg{ .imm = src }; const src_type: u8 = switch (imm_or_reg) { .imm => K, .reg => X, }; return Insn{ .code = code | src_type, .dst = @intFromEnum(dst), .src = switch (imm_or_reg) { .imm => 0, .reg => |r| @intFromEnum(r), }, .off = off, .imm = switch (imm_or_reg) { .imm => |i| i, .reg => 0, }, }; } pub fn alu(comptime width: comptime_int, op: AluOp, dst: Reg, src: anytype) Insn { const width_bitfield = switch (width) { 32 => ALU, 64 => ALU64, else => @compileError("width must be 32 or 64"), }; return imm_reg(width_bitfield | @intFromEnum(op), dst, src, 0); } pub fn mov(dst: Reg, src: anytype) Insn { return alu(64, .mov, dst, src); } pub fn add(dst: Reg, src: anytype) Insn { return alu(64, .add, dst, src); } pub fn sub(dst: Reg, src: anytype) Insn { return alu(64, .sub, dst, src); } pub fn mul(dst: Reg, src: anytype) Insn { return alu(64, .mul, dst, src); } pub fn div(dst: Reg, src: anytype) Insn { return alu(64, .div, dst, src); } pub fn alu_or(dst: Reg, src: anytype) Insn { return alu(64, .alu_or, dst, src); } pub fn alu_and(dst: Reg, src: anytype) Insn { return alu(64, .alu_and, dst, src); } pub fn lsh(dst: Reg, src: anytype) Insn { return alu(64, .lsh, dst, src); } pub fn rsh(dst: Reg, src: anytype) Insn { return alu(64, .rsh, dst, src); } pub fn neg(dst: Reg) Insn { return alu(64, .neg, dst, 0); } pub fn mod(dst: Reg, src: anytype) Insn { return alu(64, .mod, dst, src); } pub fn xor(dst: Reg, src: anytype) Insn { return alu(64, .xor, dst, src); } pub fn arsh(dst: Reg, src: anytype) Insn { return alu(64, .arsh, dst, src); } pub fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn { return imm_reg(JMP | @intFromEnum(op), dst, src, off); } pub fn ja(off: i16) Insn { return jmp(.ja, .r0, 0, off); } pub fn jeq(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jeq, dst, src, off); } pub fn jgt(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jgt, dst, src, off); } pub fn jge(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jge, dst, src, off); } pub fn jlt(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jlt, dst, src, off); } pub fn jle(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jle, dst, src, off); } pub fn jset(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jset, dst, src, off); } pub fn jne(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jne, dst, src, off); } pub fn jsgt(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jsgt, dst, src, off); } pub fn jsge(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jsge, dst, src, off); } pub fn jslt(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jslt, dst, src, off); } pub fn jsle(dst: Reg, src: anytype, off: i16) Insn { return jmp(.jsle, dst, src, off); } pub fn xadd(dst: Reg, src: Reg) Insn { return Insn{ .code = STX | XADD | DW, .dst = @intFromEnum(dst), .src = @intFromEnum(src), .off = 0, .imm = 0, }; } fn ld(mode: Mode, size: Size, dst: Reg, src: Reg, imm: i32) Insn { return Insn{ .code = @intFromEnum(mode) | @intFromEnum(size) | LD, .dst = @intFromEnum(dst), .src = @intFromEnum(src), .off = 0, .imm = imm, }; } pub fn ld_abs(size: Size, dst: Reg, src: Reg, imm: i32) Insn { return ld(.abs, size, dst, src, imm); } pub fn ld_ind(size: Size, dst: Reg, src: Reg, imm: i32) Insn { return ld(.ind, size, dst, src, imm); } pub fn ldx(size: Size, dst: Reg, src: Reg, off: i16) Insn { return Insn{ .code = MEM | @intFromEnum(size) | LDX, .dst = @intFromEnum(dst), .src = @intFromEnum(src), .off = off, .imm = 0, }; } fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn { return Insn{ .code = LD | DW | IMM, .dst = @intFromEnum(dst), .src = @intFromEnum(src), .off = 0, .imm = @as(i32, @intCast(@as(u32, @truncate(imm)))), }; } fn ld_imm_impl2(imm: u64) Insn { return Insn{ .code = 0, .dst = 0, .src = 0, .off = 0, .imm = @as(i32, @intCast(@as(u32, @truncate(imm >> 32)))), }; } pub fn ld_dw1(dst: Reg, imm: u64) Insn { return ld_imm_impl1(dst, .r0, imm); } pub fn ld_dw2(imm: u64) Insn { return ld_imm_impl2(imm); } pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn { return ld_imm_impl1(dst, @as(Reg, @enumFromInt(PSEUDO_MAP_FD)), @as(u64, @intCast(map_fd))); } pub fn ld_map_fd2(map_fd: fd_t) Insn { return ld_imm_impl2(@as(u64, @intCast(map_fd))); } pub fn st(size: Size, dst: Reg, off: i16, imm: i32) Insn { return Insn{ .code = MEM | @intFromEnum(size) | ST, .dst = @intFromEnum(dst), .src = 0, .off = off, .imm = imm, }; } pub fn stx(size: Size, dst: Reg, off: i16, src: Reg) Insn { return Insn{ .code = MEM | @intFromEnum(size) | STX, .dst = @intFromEnum(dst), .src = @intFromEnum(src), .off = off, .imm = 0, }; } fn endian_swap(endian: std.builtin.Endian, comptime size: Size, dst: Reg) Insn { return Insn{ .code = switch (endian) { .big => 0xdc, .little => 0xd4, }, .dst = @intFromEnum(dst), .src = 0, .off = 0, .imm = switch (size) { .byte => @compileError("can't swap a single byte"), .half_word => 16, .word => 32, .double_word => 64, }, }; } pub fn le(comptime size: Size, dst: Reg) Insn { return endian_swap(.little, size, dst); } pub fn be(comptime size: Size, dst: Reg) Insn { return endian_swap(.big, size, dst); } pub fn call(helper: Helper) Insn { return Insn{ .code = JMP | CALL, .dst = 0, .src = 0, .off = 0, .imm = @intFromEnum(helper), }; } /// exit BPF program pub fn exit() Insn { return Insn{ .code = JMP | EXIT, .dst = 0, .src = 0, .off = 0, .imm = 0, }; } }