diff --git a/Cargo.toml b/Cargo.toml index edf461b..1c95a22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/xen/xencall", "crates/xen/xenclient", "crates/xen/xenevtchn", + "crates/xen/xengnt", "crates/xen/xenstore", ] resolver = "2" diff --git a/crates/xen/xengnt/Cargo.toml b/crates/xen/xengnt/Cargo.toml new file mode 100644 index 0000000..5d14706 --- /dev/null +++ b/crates/xen/xengnt/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "xengnt" +version.workspace = true +edition = "2021" +resolver = "2" + +[dependencies] +log = { workspace = true } +thiserror = { workspace = true } +nix = { workspace = true, features = ["ioctl"] } +tokio = { workspace = true } + +[lib] +name = "xengnt" diff --git a/crates/xen/xengnt/src/error.rs b/crates/xen/xengnt/src/error.rs new file mode 100644 index 0000000..51926e5 --- /dev/null +++ b/crates/xen/xengnt/src/error.rs @@ -0,0 +1,13 @@ +use std::io; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("kernel error")] + Kernel(#[from] nix::errno::Errno), + #[error("io issue encountered")] + Io(#[from] io::Error), + #[error("failed to read structure")] + StructureReadFailed, +} + +pub type Result = std::result::Result; diff --git a/crates/xen/xengnt/src/lib.rs b/crates/xen/xengnt/src/lib.rs new file mode 100644 index 0000000..e858e8c --- /dev/null +++ b/crates/xen/xengnt/src/lib.rs @@ -0,0 +1,142 @@ +pub mod error; +pub mod sys; + +use error::{Error, Result}; +use std::{ + fs::{File, OpenOptions}, + os::fd::AsRawFd, +}; +use sys::{ + AllocGref, DeallocGref, GetOffsetForVaddr, GrantRef, MapGrantRef, SetMaxGrants, UnmapGrantRef, + UnmapNotify, UNMAP_NOTIFY_CLEAR_BYTE, UNMAP_NOTIFY_SEND_EVENT, +}; + +pub struct GrantDevice { + handle: File, +} + +impl GrantDevice { + pub fn open() -> Result { + let handle = OpenOptions::new() + .read(true) + .write(true) + .open("/dev/xen/gntdev")?; + Ok(GrantDevice { handle }) + } + + pub fn map_grant_ref(&self, count: u32) -> Result<(u64, Vec)> { + let refs: Vec = vec![ + GrantRef { + domid: 0, + reference: 0 + }; + count as usize + ]; + let mut request = MapGrantRef::write(refs.as_slice()); + unsafe { + sys::map_grant_ref(self.handle.as_raw_fd(), request.as_mut_ptr())?; + }; + let result = + MapGrantRef::read(refs.len() as u32, request).ok_or(Error::StructureReadFailed)?; + Ok((result.index, result.refs)) + } + + pub fn unmap_grant_ref(&self, index: u64, count: u32) -> Result<()> { + let mut request = UnmapGrantRef { + index, + count, + pad: 0, + }; + unsafe { + sys::unmap_grant_ref(self.handle.as_raw_fd(), &mut request)?; + } + Ok(()) + } + + pub fn get_offset_for_vaddr(&self, vaddr: u64) -> Result<(u64, u32)> { + let mut request = GetOffsetForVaddr { + vaddr, + pad: 0, + offset: 0, + count: 0, + }; + unsafe { + sys::get_offset_for_vaddr(self.handle.as_raw_fd(), &mut request)?; + } + Ok((request.offset, request.count)) + } + + pub fn set_max_grants(&self, count: u32) -> Result<()> { + let mut request = SetMaxGrants { count }; + unsafe { + sys::set_max_grants(self.handle.as_raw_fd(), &mut request)?; + } + Ok(()) + } + + pub fn unmap_notify(&self, index: u64, send: bool, port: u32) -> Result<()> { + let mut request = UnmapNotify { + index, + action: if send { + UNMAP_NOTIFY_SEND_EVENT + } else { + UNMAP_NOTIFY_CLEAR_BYTE + }, + port, + }; + unsafe { + sys::unmap_notify(self.handle.as_raw_fd(), &mut request)?; + } + Ok(()) + } +} + +pub struct GrantAlloc { + handle: File, +} + +impl GrantAlloc { + pub fn open() -> Result { + let handle = OpenOptions::new() + .read(true) + .write(true) + .open("/dev/xen/gntalloc")?; + Ok(GrantAlloc { handle }) + } + + pub fn alloc_gref(&self, domid: u16, flags: u16, count: u32) -> Result<(u64, Vec)> { + let mut request = AllocGref::write(AllocGref { + domid, + flags, + count, + }); + unsafe { + sys::alloc_gref(self.handle.as_raw_fd(), request.as_mut_ptr())?; + }; + AllocGref::read(count, request).ok_or(Error::StructureReadFailed) + } + + pub fn dealloc_gref(&self, index: u64, count: u32) -> Result<()> { + let mut request = DeallocGref { index, count }; + unsafe { + sys::dealloc_gref(self.handle.as_raw_fd(), &mut request)?; + }; + Ok(()) + } + + pub fn unmap_notify(&self, index: u64, send: bool, port: u32) -> Result<()> { + let mut request = UnmapNotify { + index, + action: if send { + UNMAP_NOTIFY_SEND_EVENT + } else { + UNMAP_NOTIFY_CLEAR_BYTE + }, + port, + }; + unsafe { + sys::unmap_notify(self.handle.as_raw_fd(), &mut request)?; + } + Ok(()) + } +} diff --git a/crates/xen/xengnt/src/sys.rs b/crates/xen/xengnt/src/sys.rs new file mode 100644 index 0000000..398b648 --- /dev/null +++ b/crates/xen/xengnt/src/sys.rs @@ -0,0 +1,177 @@ +use std::mem::size_of; + +use nix::{ioc, ioctl_readwrite_bad}; + +#[repr(C)] +#[derive(Clone)] +pub struct GrantRef { + pub domid: u32, + pub reference: u32, +} + +pub struct MapGrantRef { + pub count: u32, + pub pad: u32, + pub index: u64, + pub refs: Vec, +} + +impl MapGrantRef { + pub fn write(slice: &[GrantRef]) -> Vec { + let mut values = vec![slice.len() as u32, 0, 0, 0]; + for r in slice { + values.push(r.domid); + values.push(r.reference); + } + values + } + + pub fn read(count: u32, data: Vec) -> Option { + let mut refs = Vec::new(); + + if data.len() < (4 + (count as u64 * 2)) as usize { + return None; + } + + let index = (*data.get(2)? as u64) << 32 | *data.get(3)? as u64; + for i in (4..data.len()).step_by(2) { + let Some(domid) = data.get(i) else { + break; + }; + let Some(reference) = data.get(i + 1) else { + break; + }; + refs.push(GrantRef { + domid: *domid, + reference: *reference, + }); + } + + Some(MapGrantRef { + count, + pad: 0, + index, + refs, + }) + } +} + +#[repr(C)] +pub struct UnmapGrantRef { + pub index: u64, + pub count: u32, + pub pad: u32, +} + +#[repr(C)] +pub struct GetOffsetForVaddr { + pub vaddr: u64, + pub offset: u64, + pub count: u32, + pub pad: u32, +} + +#[repr(C)] +pub struct SetMaxGrants { + pub count: u32, +} + +#[repr(C)] +pub struct UnmapNotify { + pub index: u64, + pub action: u32, + pub port: u32, +} + +pub const UNMAP_NOTIFY_CLEAR_BYTE: u32 = 0x1; +pub const UNMAP_NOTIFY_SEND_EVENT: u32 = 0x2; + +ioctl_readwrite_bad!(map_grant_ref, ioc!(nix::sys::ioctl::NONE, 'G', 0, 24), u32); +ioctl_readwrite_bad!( + unmap_grant_ref, + ioc!(nix::sys::ioctl::NONE, 'G', 1, size_of::()), + UnmapGrantRef +); +ioctl_readwrite_bad!( + get_offset_for_vaddr, + ioc!( + nix::sys::ioctl::NONE, + 'G', + 2, + size_of::() + ), + GetOffsetForVaddr +); +ioctl_readwrite_bad!( + set_max_grants, + ioc!(nix::sys::ioctl::NONE, 'G', 3, size_of::()), + SetMaxGrants +); +ioctl_readwrite_bad!( + unmap_notify, + ioc!(nix::sys::ioctl::NONE, 'G', 7, size_of::()), + UnmapNotify +); + +#[repr(C)] +pub struct AllocGref { + pub domid: u16, + pub flags: u16, + pub count: u32, +} + +impl AllocGref { + pub fn write(gref: AllocGref) -> Vec { + let mut values = vec![ + gref.domid, + gref.flags, + (gref.count << 16) as u16, + gref.count as u16, + 0, + 0, + 0, + 0, + ]; + for _ in 0..gref.count { + values.push(0); + values.push(0); + } + values + } + + pub fn read(count: u32, data: Vec) -> Option<(u64, Vec)> { + let mut refs = Vec::new(); + + if data.len() < (8 + (count as u64 * 2)) as usize { + return None; + } + + let index = (*data.get(4)? as u64) << 48 + | (*data.get(5)? as u64) << 32 + | (*data.get(6)? as u64) << 16 + | *data.get(7)? as u64; + for i in (8..data.len()).step_by(2) { + let Some(bits_low) = data.get(i) else { + break; + }; + let Some(bits_high) = data.get(i + 1) else { + break; + }; + refs.push((*bits_low as u32) << 16 | *bits_high as u32); + } + Some((index, refs)) + } +} + +#[repr(C)] +pub struct DeallocGref { + pub index: u64, + pub count: u32, +} + +ioctl_readwrite_bad!(alloc_gref, ioc!(nix::sys::ioctl::NONE, 'G', 5, 20), u16); +ioctl_readwrite_bad!( + dealloc_gref, + ioc!(nix::sys::ioctl::NONE, 'G', 6, size_of::()), + DeallocGref +);