use std::mem::size_of;
use std::os::unix::io::AsRawFd;
use std::ptr::{NonNull, null_mut};
use kvm_bindings::{
KVM_COALESCED_MMIO_PAGE_OFFSET, kvm_coalesced_mmio, kvm_coalesced_mmio_ring, kvm_run,
};
use vmm_sys_util::errno;
pub mod device;
pub mod system;
pub mod vcpu;
pub mod vm;
pub type Result<T> = std::result::Result<T, errno::Error>;
#[derive(Debug)]
pub(crate) struct KvmCoalescedIoRing {
addr: NonNull<kvm_coalesced_mmio_ring>,
page_size: usize,
}
impl KvmCoalescedIoRing {
pub(crate) fn mmap_from_fd<F: AsRawFd>(fd: &F) -> Result<Self> {
let page_size = match unsafe { libc::sysconf(libc::_SC_PAGESIZE) } {
-1 => return Err(errno::Error::last()),
ps => ps as usize,
};
let offset = KVM_COALESCED_MMIO_PAGE_OFFSET * page_size as u32;
let addr = unsafe {
libc::mmap(
null_mut(),
page_size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd.as_raw_fd(),
offset.into(),
)
};
let addr = NonNull::new(addr)
.filter(|addr| addr.as_ptr() != libc::MAP_FAILED)
.ok_or_else(errno::Error::last)?;
Ok(Self {
addr: addr.cast(),
page_size,
})
}
const fn ring_max(&self) -> usize {
(self.page_size - size_of::<kvm_coalesced_mmio_ring>()) / size_of::<kvm_coalesced_mmio>()
}
fn ring_mut(&mut self) -> &mut kvm_coalesced_mmio_ring {
unsafe { self.addr.as_mut() }
}
pub(crate) fn read_entry(&mut self) -> Option<kvm_coalesced_mmio> {
let ring_max = self.ring_max();
let ring = self.ring_mut();
if ring.first == ring.last {
return None;
}
let entries = ring.coalesced_mmio.as_ptr();
let elem = unsafe { entries.add(ring.first as usize).read() };
ring.first = (ring.first + 1) % ring_max as u32;
Some(elem)
}
}
impl Drop for KvmCoalescedIoRing {
fn drop(&mut self) {
unsafe {
libc::munmap(self.addr.as_ptr().cast(), self.page_size);
}
}
}
unsafe impl Send for KvmCoalescedIoRing {}
unsafe impl Sync for KvmCoalescedIoRing {}
#[derive(Debug)]
pub struct KvmRunWrapper {
kvm_run_ptr: NonNull<kvm_run>,
mmap_size: usize,
}
unsafe impl Send for KvmRunWrapper {}
unsafe impl Sync for KvmRunWrapper {}
impl KvmRunWrapper {
pub fn mmap_from_fd<F: AsRawFd>(fd: &F, size: usize) -> Result<KvmRunWrapper> {
let addr = unsafe {
libc::mmap(
null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd.as_raw_fd(),
0,
)
};
let addr = NonNull::new(addr)
.filter(|addr| addr.as_ptr() != libc::MAP_FAILED)
.ok_or_else(errno::Error::last)?;
Ok(KvmRunWrapper {
kvm_run_ptr: addr.cast(),
mmap_size: size,
})
}
pub fn as_mut_ref(&mut self) -> &mut kvm_run {
unsafe { self.kvm_run_ptr.as_mut() }
}
}
impl AsRef<kvm_run> for KvmRunWrapper {
fn as_ref(&self) -> &kvm_run {
unsafe { self.kvm_run_ptr.as_ref() }
}
}
impl Drop for KvmRunWrapper {
fn drop(&mut self) {
unsafe {
libc::munmap(self.kvm_run_ptr.as_ptr().cast(), self.mmap_size);
}
}
}