libxen 0.5.0

Safe bindings for Xen libraries
Documentation
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, RawFd};

use xen_sys::{
    xenevtchn_bind_interdomain, xenevtchn_notify, xenevtchn_pending, xenevtchn_unbind,
    xenevtchn_unmask,
};

use super::XenEventChannel;
use crate::{XenDomainId, XenError};

macro_rules! xc_check_error {
    ($self:ident, $rc:ident) => {
        if $rc < 0 {
            return Err(XenError::Io(std::io::Error::last_os_error()));
        }
    };
}

#[derive(Debug, Clone)]
pub struct XenEventChannelPort {
    evtchn: XenEventChannel,
    remote_port: u32,
    local_port: u32,
}

impl XenEventChannelPort {
    pub(crate) fn bind_interdomain(
        domain_id: XenDomainId,
        remote_port: u32,
    ) -> Result<Self, XenError> {
        let evtchn = XenEventChannel::new()?;
        let rc = unsafe { xenevtchn_bind_interdomain(*evtchn.0, domain_id.0, remote_port) };
        xc_check_error!(self, rc);

        let local_port = rc as u32;
        Ok(Self {
            evtchn,
            remote_port,
            local_port,
        })
    }

    pub fn local_port(&self) -> u32 {
        self.local_port
    }

    pub fn remote_port(&self) -> u32 {
        self.remote_port
    }

    pub fn wait(&self) -> Result<(), XenError> {
        let port = self.pending()?;
        assert_eq!(port, self.local_port);
        self.unmask()?;
        Ok(())
    }

    pub fn notify(&self) -> Result<(), XenError> {
        let rc = unsafe { xenevtchn_notify(*self.evtchn.0, self.local_port) };
        xc_check_error!(self, rc);
        Ok(())
    }

    fn pending(&self) -> Result<u32, XenError> {
        let rc = unsafe { xenevtchn_pending(*self.evtchn.0) };
        xc_check_error!(self, rc);

        let port = rc as u32;
        Ok(port)
    }

    fn unmask(&self) -> Result<(), XenError> {
        let rc = unsafe { xenevtchn_unmask(*self.evtchn.0, self.local_port) };
        xc_check_error!(self, rc);
        Ok(())
    }
}

impl Drop for XenEventChannelPort {
    fn drop(&mut self) {
        tracing::trace!(
            local_port = self.local_port,
            remote_port = self.remote_port,
            "unbinding Xen event channel port"
        );
        unsafe {
            xenevtchn_unbind(*self.evtchn.0, self.local_port);
        }
    }
}

impl AsFd for XenEventChannelPort {
    fn as_fd(&self) -> BorrowedFd<'_> {
        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
    }
}

impl AsRawFd for XenEventChannelPort {
    fn as_raw_fd(&self) -> RawFd {
        self.evtchn.as_raw_fd()
    }
}