s2n_quic_platform/bpf/
cbpf.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use core::fmt;
5
6#[cfg(all(test, not(miri)))]
7mod tests;
8
9pub use super::common::*;
10pub type Instruction = super::Instruction<Cbpf>;
11pub type Program<'a> = super::Program<'a, Cbpf>;
12
13#[derive(Clone, Copy, Debug, Default)]
14pub struct Cbpf;
15
16impl super::instruction::Dialect for Cbpf {
17    // https://github.com/torvalds/linux/blob/b947cc5bf6d793101135265352e205aeb30b54f0/include/uapi/linux/bpf_common.h#L54
18    const MAX_INSTRUCTIONS: usize = 4096;
19    const SOCKOPT: libc::c_int = libc::SO_ATTACH_REUSEPORT_CBPF as _;
20
21    fn debug(i: &Instruction, f: &mut fmt::Formatter) -> fmt::Result {
22        let code = i.code;
23        let k = i.k;
24        let jt = i.jt;
25        let jf = i.jf;
26        let alt = f.alternate();
27
28        let mut f = f.debug_struct("Instruction");
29        let class = Class::decode(code);
30
31        if alt {
32            f.field("code", &code).field("jt", &jt).field("jf", &jf);
33        }
34
35        f.field("class", &class);
36
37        match class {
38            Class::ALU => {
39                f.field("op", &Alu::decode(code));
40            }
41            Class::JMP => {
42                f.field("op", &Jump::decode(code));
43            }
44            Class::LD | Class::LDX | Class::RET => {
45                f.field("size", &Size::decode(code))
46                    .field("mode", &Mode::decode(code));
47            }
48            _ => {}
49        }
50
51        if jt > 0 {
52            f.field("jt", &jt);
53        }
54
55        if jf > 0 {
56            f.field("jf", &jf);
57        }
58
59        let prefix = if k == 0 { "" } else { "0x" };
60        f.field("k", &format_args!("{prefix}{k:x}")).finish()
61    }
62
63    fn display(i: &Instruction, f: &mut fmt::Formatter, line: Option<usize>) -> fmt::Result {
64        let code = i.code;
65        let k = i.k;
66        let jt = i.jt;
67        let jf = i.jf;
68
69        if let Some(line) = line {
70            write!(f, "l{line:<4}: ")?;
71        }
72
73        let class = Class::decode(code);
74
75        match class {
76            Class::LD | Class::LDX => {
77                let size = Size::decode(code).suffix();
78                let mode = Mode::decode(code);
79
80                match mode {
81                    Mode::IMM => return write!(f, "{class}{size} #{k}"),
82                    Mode::ABS => {
83                        let prefix = if k == 0 { "" } else { "0x" };
84                        return if let Some(info) = super::ancillary::lookup(k) {
85                            write!(
86                                f,
87                                "{class}{size} {} ; [{prefix}{k:x}] // {}",
88                                info.extension, info.capi
89                            )
90                        } else {
91                            write!(f, "{class}{size} [{prefix}{k:x}]")
92                        };
93                    }
94                    Mode::LEN => return write!(f, "{class}{size} len"),
95                    Mode::IND => return write!(f, "{class}{size} [x + {k}]"),
96                    _ => {}
97                }
98            }
99            Class::ALU => {
100                let op = Alu::decode(code);
101                let source = Source::decode(code);
102
103                return match source {
104                    Source::K => write!(f, "{op} #{k}"),
105                    Source::X => write!(f, "{op} x"),
106                };
107            }
108            Class::JMP => {
109                let op = Jump::decode(code);
110                let source = Source::decode(code);
111
112                match source {
113                    Source::K => write!(f, "{op} #{k}")?,
114                    Source::X => write!(f, "{op} x")?,
115                }
116
117                if let Some(line) = line {
118                    let line = line + 1;
119                    let jt = line + jt as usize;
120                    let jf = line + jf as usize;
121                    write!(f, ",l{jt},l{jf}")?
122                } else {
123                    write!(f, ",{jt},{jf}")?
124                }
125
126                return Ok(());
127            }
128            Class::RET => {
129                let source = Source::decode(code);
130                let size = Size::decode(code);
131
132                return match (source, size) {
133                    (Source::K, Size::B) if k == 0 => write!(f, "{class} %a"),
134                    (Source::K, _) => write!(f, "{class} #{k}"),
135                    (Source::X, _) => write!(f, "{class} %x"),
136                };
137            }
138            Class::MISC => {
139                let misc = Misc::decode(code);
140                return match misc {
141                    Misc::TAX => write!(f, "tax"),
142                    Misc::TXA => write!(f, "txa"),
143                };
144            }
145            _ => {}
146        }
147
148        write!(f, "<unknown instruction {i:?}>")
149    }
150}
151
152// https://github.com/torvalds/linux/blob/b947cc5bf6d793101135265352e205aeb30b54f0/include/uapi/linux/bpf_common.h#L6
153define!(
154    #[mask(0x07)]
155    pub enum Class {
156        LD = 0x00,
157        LDX = 0x01,
158        ST = 0x02,
159        STX = 0x03,
160        ALU = 0x04,
161        JMP = 0x05,
162        RET = 0x06,
163        MISC = 0x07,
164    }
165);
166
167// https://github.com/torvalds/linux/blob/b947cc5bf6d793101135265352e205aeb30b54f0/include/uapi/linux/bpf_common.h#L17
168define!(
169    #[mask(0x18)]
170    pub enum Size {
171        // word (4 bytes)
172        W = 0x00,
173        // half word (2 bytes)
174        H = 0x08,
175        // byte
176        B = 0x10,
177    }
178);
179
180impl Size {
181    #[inline]
182    pub const fn suffix(self) -> &'static str {
183        match self {
184            Self::W => "",
185            Self::H => "H",
186            Self::B => "B",
187        }
188    }
189}
190
191// https://github.com/torvalds/linux/blob/b947cc5bf6d793101135265352e205aeb30b54f0/include/uapi/linux/bpf_common.h#L22
192define!(
193    #[mask(0xe0)]
194    pub enum Mode {
195        IMM = 0x00,
196        ABS = 0x20,
197        IND = 0x40,
198        MEM = 0x60,
199        LEN = 0x80,
200        MSH = 0xa0,
201    }
202);
203
204// https://github.com/torvalds/linux/blob/b947cc5bf6d793101135265352e205aeb30b54f0/include/uapi/linux/bpf_common.h#L31
205define!(
206    #[mask(0xf0)]
207    pub enum Alu {
208        ADD = 0x00,
209        SUB = 0x10,
210        MUL = 0x20,
211        DIV = 0x30,
212        OR = 0x40,
213        AND = 0x50,
214        LSH = 0x60,
215        RSH = 0x70,
216        NEG = 0x80,
217        MOD = 0x90,
218        XOR = 0xa0,
219    }
220);
221
222// https://github.com/torvalds/linux/blob/b947cc5bf6d793101135265352e205aeb30b54f0/include/uapi/linux/bpf_common.h#L44
223define!(
224    #[mask(0xf0)]
225    pub enum Jump {
226        JA = 0x00,
227        JEQ = 0x10,
228        JGT = 0x20,
229        JSET = 0x40,
230    }
231);
232
233define!(
234    #[mask(0xf0)]
235    pub enum Misc {
236        TAX = 0x00,
237        TXA = 0x80,
238    }
239);
240
241impl_ops!();
242impl_ret!();
243
244pub const fn len() -> K {
245    K {
246        mode: Mode::LEN,
247        value: 0,
248    }
249}
250
251pub const fn tax() -> Instruction {
252    Instruction::raw(Class::MISC as u16 | Misc::TAX as u16, 0, 0, 0)
253}
254
255pub const fn txa() -> Instruction {
256    Instruction::raw(Class::MISC as u16 | Misc::TXA as u16, 0, 0, 0)
257}