1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Primitives for the eBPF instruction set. See [kernel docs](https://www.kernel.org/doc/html/latest/bpf/instruction-set.html)
//! for the canonical details
//!
//! The exports here should allow for the creation of simple eBPF programs that can be loaded into the kernel.
//! The functions provide a convenient away to create valid instructions.
//!
//! The exported functions currently return the underlying libbpf_sys's bpf_insn binding
//! so loading the program  through other libbpf_sys functions should work.
//! In the future, we should provide convenient functions to encapsulate this.
//!
//! # Instruction set (ISA) versions
//!
//! Not all of the instructions currently available were released at the same time. Instructions (mostly for jump ops)
//! have been added over time, resulting in different versions of the eBPF instruction set. We will denote
//! if an operation is part of the v2 or v3 instruction set.
//!
//! For more info, see [BPF Design Q&A](https://www.kernel.org/doc/html/latest/bpf/bpf_design_QA.html#q-why-bpf-jlt-and-bpf-jle-instructions-were-not-introduced-in-the-beginning)
//! and [Paul Chaignon's blog post](https://pchaigno.github.io/bpf/2021/10/20/ebpf-instruction-sets.html)
//!
use libbpf_sys as sys;
use num_enum::{IntoPrimitive, TryFromPrimitive};

/// Instruction classes
///
/// **Note**: 32-bit ALU ops are denoted with [`Class::ALU`] and 64-bit ALU ops are
/// [`Class::ALU64`] yet 32-bit jump ops are in [`Class::JMP32`] and 64-bit jump ops are in [`Class::JMP`].
#[repr(u8)]
#[derive(Debug, TryFromPrimitive, IntoPrimitive, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Class {
    /// Immediate loads
    LD = sys::BPF_LD as u8,
    /// Register loads
    LDX = sys::BPF_LDX as u8,
    /// Immediate stores
    ST = sys::BPF_ST as u8,
    /// Register stores
    STX = sys::BPF_STX as u8,
    /// Arithmetic operations (32-bit)
    ALU = sys::BPF_ALU as u8,
    /// Arithmetic operation (64-bit)
    ALU64 = sys::BPF_ALU64 as u8,
    /// Jump operations (64-bit)
    JMP = sys::BPF_JMP as u8,
    /// Jump operations (32-bit)
    JMP32 = sys::BPF_JMP32 as u8,
}

/// Register variants
///
/// Source: [kernel tree](https://github.com/torvalds/linux/blob/d569e86915b7f2f9795588591c8d5ea0b66481cb/tools/include/uapi/linux/bpf.h#L53)
#[repr(u8)]
#[derive(Debug, TryFromPrimitive, IntoPrimitive, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Register {
    /// Usually used as either the return value in function calls or as the exit value in programs
    R0 = sys::BPF_REG_0 as u8,
    R1 = sys::BPF_REG_1 as u8,
    R2 = sys::BPF_REG_2 as u8,
    R3 = sys::BPF_REG_3 as u8,
    R4 = sys::BPF_REG_4 as u8,
    R5 = sys::BPF_REG_5 as u8,
    R6 = sys::BPF_REG_6 as u8,
    R7 = sys::BPF_REG_7 as u8,
    R8 = sys::BPF_REG_8 as u8,
    R9 = sys::BPF_REG_9 as u8,
    /// Read-only frame pointer register
    R10 = sys::BPF_REG_10 as u8,
}

/// Arithmetic instructions
///
/// These are meant to be used with the BPF_ALU and BPF_ALU64 instruction classes.
///
/// In the pseudo-code described below, `dst` and `src` can refer to registers or immediate values
/// depending on other bits set within the opcode.
///
/// Source: [kernel tree](https://github.com/torvalds/linux/blob/d569e86915b7f2f9795588591c8d5ea0b66481cb/tools/include/uapi/linux/bpf_common.h#L31)
#[repr(u8)]
#[derive(Debug, TryFromPrimitive, IntoPrimitive, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AluOp {
    /// `dst += src`
    ADD = sys::BPF_ADD as u8,
    /// `dst -= src`
    SUB = sys::BPF_SUB as u8,
    /// `dst *= src`
    MUL = sys::BPF_MUL as u8,
    /// `dst /= src`
    DIV = sys::BPF_DIV as u8,
    /// `dst |= src`
    OR = sys::BPF_OR as u8,
    /// `dst &= src`
    AND = sys::BPF_AND as u8,
    /// `dst <<= src`
    LSH = sys::BPF_LSH as u8,
    /// `dst >>= src`
    RSH = sys::BPF_RSH as u8,
    /// `dst = ~src`
    NEG = sys::BPF_NEG as u8,
    /// `dst %= src`
    MOD = sys::BPF_MOD as u8,
    /// `dst ^= src`
    XOR = sys::BPF_XOR as u8,
    /// `dst = src`
    MOV = sys::BPF_MOV as u8,
    /// `dst >>= src` (with sign extension)
    ARSH = sys::BPF_ARSH as u8,
    /// Byte swap operations. See [kernel docs](https://www.kernel.org/doc/html/latest/bpf/instruction-set.html#byte-swap-instructions)
    END = sys::BPF_END as u8,
}

/// Jump operations
///
/// To be used with the BPF_JMP and BPF_JMP32 instruction classes
///
/// See [kernel docs](https://www.kernel.org/doc/html/latest/bpf/instruction-set.html#jump-instructions)
#[repr(u8)]
#[derive(Debug, TryFromPrimitive, IntoPrimitive, Clone, Copy, PartialEq, Eq, Hash)]
pub enum JmpOp {
    /// Only allowed with the BPF_JMP instruction class
    JA = sys::BPF_JA as u8,
    JEQ = sys::BPF_JEQ as u8,
    JGT = sys::BPF_JGT as u8,
    JGE = sys::BPF_JGE as u8,
    JSET = sys::BPF_JSET as u8,
    JNE = sys::BPF_JNE as u8,
    JSGT = sys::BPF_JSGT as u8,
    JSGE = sys::BPF_JSGE as u8,
    CALL = sys::BPF_CALL as u8,
    EXIT = sys::BPF_EXIT as u8,
    /// Part of [ISA v2](./#instruction-set-isa-versions)
    JLT = sys::BPF_JLT as u8,
    /// Part of [ISA v2](./#instruction-set-isa-versions)
    JLE = sys::BPF_JLE as u8,
    /// Part of [ISA v2](./#instruction-set-isa-versions)
    JSLT = sys::BPF_JSLT as u8,
    /// Part of [ISA v2](./#instruction-set-isa-versions)
    JSLE = sys::BPF_JSLE as u8,
}

pub fn mov64_imm(reg: Register, imm: i32) -> sys::bpf_insn {
    unsafe { sys::_BPF_MOV64_IMM(reg.into(), imm) }
}

pub fn alu64_imm(op: AluOp, reg: Register, imm: i32) -> sys::bpf_insn {
    unsafe { sys::_BPF_ALU64_IMM(op.into(), reg.into(), imm) }
}

pub fn jmp_imm(jmp: JmpOp, reg: Register, imm: i32, off: i16) -> sys::bpf_insn {
    unsafe { sys::_BPF_JMP_IMM(jmp.into(), reg.into(), imm, off) }
}

pub fn jmp32_imm(jmp: JmpOp, reg: Register, imm: i32, off: i16) -> sys::bpf_insn {
    unsafe { sys::_BPF_JMP32_IMM(jmp.into(), reg.into(), imm, off) }
}

pub fn exit() -> sys::bpf_insn {
    unsafe { sys::_BPF_EXIT_INSN() }
}