use alloc::{collections::btree_map::BTreeMap, sync::Arc, vec};
use ax_errno::{AxError, AxResult};
use ax_io::Read;
use ax_lazyinit::LazyInit;
use kbpf_basic::{
helper::RawBPFHelperFn,
linux_bpf::{bpf_attr, bpf_cmd},
map::{
BpfMapGetNextKeyArg, BpfMapMeta, BpfMapUpdateArg, bpf_lookup_elem, bpf_map_delete_elem,
bpf_map_freeze, bpf_map_get_next_key, bpf_map_lookup_and_delete_elem, bpf_map_update_elem,
},
prog::BpfProgMeta,
raw_tracepoint::BpfRawTracePointArg,
};
pub(crate) mod error;
pub mod map;
pub mod prog;
pub mod transform;
pub use transform::EbpfKernelAuxiliary;
use crate::{
ebpf::{error::BpfResultExt, map::create_map, prog::load_prog},
file::add_file_like,
mm::VmBytes,
perf::raw_tracepoint::bpf_raw_tracepoint_open,
};
pub static BPF_HELPER_FUN_SET: LazyInit<BTreeMap<u32, RawBPFHelperFn>> = LazyInit::new();
pub fn init_ebpf() {
let set = kbpf_basic::helper::init_helper_functions::<EbpfKernelAuxiliary>();
BPF_HELPER_FUN_SET.init_once(set);
}
fn read_bpf_attr(uattr: usize, size: u32) -> AxResult<bpf_attr> {
let mut buf = vec![0u8; core::mem::size_of::<bpf_attr>()];
let copy_len = (size as usize).min(buf.len());
let mut reader = VmBytes::new(uattr as *mut u8, copy_len);
reader.read(&mut buf[..copy_len])?;
let attr = unsafe { core::ptr::read(buf.as_ptr() as *const bpf_attr) };
Ok(attr)
}
fn handle_map_create(attr: &bpf_attr) -> AxResult<isize> {
let meta = BpfMapMeta::try_from(attr).into_ax_result()?;
let map = create_map(meta).into_ax_result()?;
let fd = add_file_like(Arc::new(map), true)?;
Ok(fd as isize)
}
fn handle_prog_load(attr: &bpf_attr) -> AxResult<isize> {
let mut meta = BpfProgMeta::try_from_bpf_attr::<EbpfKernelAuxiliary>(attr).into_ax_result()?;
let prog = load_prog(&mut meta).into_ax_result()?;
let fd = add_file_like(Arc::new(prog), true)?;
Ok(fd as isize)
}
fn handle_map_update(attr: &bpf_attr) -> AxResult<isize> {
let arg = BpfMapUpdateArg::from(attr);
bpf_map_update_elem::<EbpfKernelAuxiliary>(arg).into_ax_result()?;
Ok(0)
}
fn handle_map_lookup(attr: &bpf_attr) -> AxResult<isize> {
let arg = BpfMapUpdateArg::from(attr);
bpf_lookup_elem::<EbpfKernelAuxiliary>(arg).into_ax_result()?;
Ok(0)
}
fn handle_map_delete(attr: &bpf_attr) -> AxResult<isize> {
let arg = BpfMapUpdateArg::from(attr);
bpf_map_delete_elem::<EbpfKernelAuxiliary>(arg).into_ax_result()?;
Ok(0)
}
fn handle_map_get_next_key(attr: &bpf_attr) -> AxResult<isize> {
let arg = BpfMapGetNextKeyArg::from(attr);
bpf_map_get_next_key::<EbpfKernelAuxiliary>(arg).into_ax_result()?;
Ok(0)
}
fn handle_map_freeze(attr: &bpf_attr) -> AxResult<isize> {
let map_fd = unsafe { attr.__bindgen_anon_2.map_fd };
bpf_map_freeze::<EbpfKernelAuxiliary>(map_fd).into_ax_result()?;
Ok(0)
}
fn handle_map_lookup_and_delete(attr: &bpf_attr) -> AxResult<isize> {
let arg = BpfMapUpdateArg::from(attr);
bpf_map_lookup_and_delete_elem::<EbpfKernelAuxiliary>(arg).into_ax_result()?;
Ok(0)
}
fn handle_raw_tracepoint_open(attr: &bpf_attr) -> AxResult<isize> {
let arg =
BpfRawTracePointArg::try_from_bpf_attr::<EbpfKernelAuxiliary>(attr).into_ax_result()?;
bpf_raw_tracepoint_open(arg)
}
pub fn sys_bpf(cmd: u64, uattr: usize, size: u32) -> AxResult<isize> {
let cmd = bpf_cmd::try_from(cmd as u32).map_err(|_| {
warn!("bpf: unrecognized command {cmd}");
AxError::InvalidInput
})?;
let attr = read_bpf_attr(uattr, size)?;
match cmd {
bpf_cmd::BPF_MAP_CREATE => handle_map_create(&attr),
bpf_cmd::BPF_PROG_LOAD => handle_prog_load(&attr),
bpf_cmd::BPF_RAW_TRACEPOINT_OPEN => handle_raw_tracepoint_open(&attr),
bpf_cmd::BPF_MAP_UPDATE_ELEM => handle_map_update(&attr),
bpf_cmd::BPF_MAP_LOOKUP_ELEM => handle_map_lookup(&attr),
bpf_cmd::BPF_MAP_DELETE_ELEM => handle_map_delete(&attr),
bpf_cmd::BPF_MAP_GET_NEXT_KEY => handle_map_get_next_key(&attr),
bpf_cmd::BPF_MAP_FREEZE => handle_map_freeze(&attr),
bpf_cmd::BPF_MAP_LOOKUP_AND_DELETE_ELEM => handle_map_lookup_and_delete(&attr),
other => {
warn!("bpf: unsupported command {other:?}");
Err(AxError::InvalidInput)
}
}
}