use alloc::sync::Arc;
use core::{any::Any, fmt::Debug};
use ax_errno::{AxError, AxResult};
use ax_memory_addr::PhysAddr;
use axpoll::{IoEvents, PollSet, Pollable};
use kbpf_basic::{
linux_bpf::perf_event_sample_format,
perf::{PerfProbeArgs, bpf::BpfPerfEvent},
};
use kprobe::PtRegs;
use rbpf::EbpfVmRaw;
use super::PerfEventOps;
use crate::{
ebpf::{BPF_HELPER_FUN_SET, error::BpfResultExt, prog::BpfProg},
file::FileLike,
};
pub struct BpfPerfEventWrapper {
inner: BpfPerfEvent,
poll_ready: PollSet,
phys_addr: Option<(PhysAddr, usize)>,
}
impl BpfPerfEventWrapper {
pub fn new(inner: BpfPerfEvent) -> Self {
Self {
inner,
poll_ready: PollSet::new(),
phys_addr: None,
}
}
pub fn write_event(&mut self, data: &[u8]) -> AxResult<()> {
if self.phys_addr.is_none() {
return Ok(());
}
self.inner.write_event(data).into_ax_result()?;
if self.inner.enabled() {
self.poll_ready.wake();
}
Ok(())
}
}
impl Debug for BpfPerfEventWrapper {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "BpfPerfEventWrapper")
}
}
impl PerfEventOps for BpfPerfEventWrapper {
fn enable(&mut self) -> AxResult<()> {
self.inner.enable().into_ax_result()?;
Ok(())
}
fn disable(&mut self) -> AxResult<()> {
self.inner.disable().into_ax_result()?;
Ok(())
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl Drop for BpfPerfEventWrapper {
fn drop(&mut self) {
let _ = self.phys_addr.take();
}
}
impl Pollable for BpfPerfEventWrapper {
fn poll(&self) -> axpoll::IoEvents {
if self.inner.readable() {
IoEvents::IN
} else {
IoEvents::empty()
}
}
fn register(&self, context: &mut core::task::Context<'_>, events: axpoll::IoEvents) {
if events.contains(IoEvents::IN) {
self.poll_ready.register(context.waker());
}
}
}
pub fn perf_event_open_bpf(args: PerfProbeArgs) -> BpfPerfEventWrapper {
debug_assert_eq!(
args.sample_type,
Some(perf_event_sample_format::PERF_SAMPLE_RAW)
);
BpfPerfEventWrapper::new(BpfPerfEvent::new(args))
}
pub struct OwnedEbpfVm {
vm: EbpfVmRaw<'static>,
_prog: Arc<BpfProg>,
}
impl OwnedEbpfVm {
pub fn new(bpf_prog: Arc<dyn FileLike>) -> AxResult<Self> {
let prog = bpf_prog
.into_any_arc()
.downcast::<BpfProg>()
.map_err(|_| AxError::InvalidInput)?;
let prog_slice = prog.insns();
let prog_slice =
unsafe { core::slice::from_raw_parts(prog_slice.as_ptr(), prog_slice.len()) };
let mut vm = EbpfVmRaw::new(Some(prog_slice)).map_err(|e| {
error!("rbpf::EbpfVmRaw::new failed: {e:?}");
AxError::InvalidInput
})?;
if let Some(table) = BPF_HELPER_FUN_SET.get() {
for (key, value) in table.iter() {
let _ = vm.register_helper(*key, *value);
}
}
vm.register_allowed_memory(0..u64::MAX);
Ok(Self { vm, _prog: prog })
}
pub fn execute_program(&self, ctx: &mut [u8]) -> Result<u64, rbpf::lib::Error> {
self.vm.execute_program(ctx)
}
pub fn execute_with_ptregs(&self, pt_regs: &mut PtRegs) -> Result<u64, rbpf::lib::Error> {
let probe_context = unsafe {
core::slice::from_raw_parts_mut(
pt_regs as *mut PtRegs as *mut u8,
core::mem::size_of::<PtRegs>(),
)
};
self.vm.execute_program(probe_context)
}
}
impl Debug for OwnedEbpfVm {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "OwnedEbpfVm")
}
}
unsafe impl Send for OwnedEbpfVm {}
unsafe impl Sync for OwnedEbpfVm {}