use alloc::sync::{Arc, Weak};
use core::{any::Any, fmt::Debug};
use ax_alloc::GlobalPage;
use ax_errno::{AxError, AxResult};
use ax_hal::mem::virt_to_phys;
use ax_memory_addr::{PAGE_SIZE_4K, 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,
pages: Option<Weak<GlobalPage>>,
}
impl BpfPerfEventWrapper {
pub fn new(inner: BpfPerfEvent) -> Self {
Self {
inner,
poll_ready: PollSet::new(),
pages: None,
}
}
fn is_mapped(&self) -> bool {
self.pages.as_ref().is_some_and(|w| w.strong_count() > 0)
}
pub fn write_event(&mut self, data: &[u8]) -> AxResult<()> {
if !self.is_mapped() {
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
}
fn device_mmap(&mut self, len: usize) -> AxResult<(PhysAddr, Arc<dyn Any + Send + Sync>)> {
if self.is_mapped() {
return Err(AxError::ResourceBusy);
}
if len == 0 || !len.is_multiple_of(PAGE_SIZE_4K) {
return Err(AxError::InvalidInput);
}
let num_pages = len / PAGE_SIZE_4K;
if num_pages < 2 || !(num_pages - 1).is_power_of_two() {
return Err(AxError::InvalidInput);
}
let mut pages = GlobalPage::alloc_contiguous(num_pages, PAGE_SIZE_4K)?;
pages.zero();
let kvirt = pages.start_vaddr();
let paddr = virt_to_phys(kvirt);
self.inner
.do_mmap(kvirt.as_usize(), len, 0)
.map_err(|_| AxError::InvalidInput)?;
let pages = Arc::new(pages);
self.pages = Some(Arc::downgrade(&pages));
let anchor: Arc<dyn Any + Send + Sync> = pages;
Ok((paddr, anchor))
}
}
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 {}