#![no_std]
#![cfg_attr(feature = "nightly", feature(debug_closure_helpers))]
pub mod ioctl;
pub mod raw;
pub use linux_io::result::Result;
use linux_io::{File, OpenOptions};
use linux_unsafe::int;
#[derive(Debug)]
pub struct Kvm {
f: File<ioctl::system::KvmSystem>,
}
impl Kvm {
pub fn open() -> Result<Self> {
let path = unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(b"/dev/kvm\0") };
let opts = OpenOptions::read_write().close_on_exec();
let f = File::open(path, opts)?;
let f = unsafe { f.to_device(ioctl::system::KvmSystem) };
Ok(Self::from_file(f))
}
#[inline(always)]
pub const fn from_file(f: File<ioctl::system::KvmSystem>) -> Self {
Self { f }
}
#[inline(always)]
pub fn get_api_version(&self) -> Result<int> {
self.f.ioctl(ioctl::system::KVM_GET_API_VERSION, ())
}
#[inline(always)]
pub fn check_extension(&self, ext: int) -> Result<int> {
self.f.ioctl(ioctl::system::KVM_CHECK_EXTENSION, &ext)
}
#[inline(always)]
pub fn create_vm(&self) -> Result<VirtualMachine<'_>> {
let f = self.f.ioctl(ioctl::system::KVM_CREATE_VM, ())?;
Ok(VirtualMachine::from_file(f, &self))
}
#[inline(always)]
pub fn get_vcpu_mmap_size(&self) -> Result<int> {
self.f.ioctl(ioctl::system::KVM_GET_VCPU_MMAP_SIZE, ())
}
}
#[derive(Debug)]
pub struct VirtualMachine<'a> {
f: File<ioctl::vm::KvmVm>,
kvm: &'a Kvm,
}
impl<'a> VirtualMachine<'a> {
#[inline(always)]
const fn from_file(f: File<ioctl::vm::KvmVm>, kvm: &'a Kvm) -> Self {
Self { f, kvm }
}
#[inline(always)]
pub fn check_extension(&self, ext: int) -> Result<int> {
self.f.ioctl(ioctl::vm::KVM_CHECK_EXTENSION, &ext)
}
#[inline(always)]
pub fn create_vcpu(&self, cpu_id: linux_unsafe::int) -> Result<VirtualCpu<'_>> {
self.f
.ioctl(ioctl::vm::KVM_CREATE_VCPU, cpu_id)
.map(|f| VirtualCpu::from_file(f, &self.kvm))
}
pub fn set_guest_memory_region<'r: 'a>(
&mut self,
slot: u32,
flags: u32,
guest_phys_addr: u64,
host_region: &'r mut MemoryRegion,
) -> Result<()> {
let desc = raw::kvm_userspace_memory_region {
slot,
flags,
guest_phys_addr,
memory_size: host_region.length as u64,
userspace_addr: host_region.addr as u64,
};
self.f
.ioctl(ioctl::vm::KVM_SET_USER_MEMORY_REGION, &desc)
.map(|_| ())
}
}
#[derive(Debug)]
pub struct VirtualCpu<'a> {
f: File<ioctl::vcpu::KvmVcpu>,
kvm: &'a Kvm,
}
impl<'a> VirtualCpu<'a> {
#[inline(always)]
const fn from_file(f: File<ioctl::vcpu::KvmVcpu>, kvm: &'a Kvm) -> Self {
Self { f, kvm }
}
#[inline(always)]
pub fn get_regs(&self) -> Result<raw::kvm_regs> {
self.f.ioctl(ioctl::vcpu::KVM_GET_REGS, ())
}
#[inline(always)]
pub fn set_regs(&self, new: &raw::kvm_regs) -> Result<()> {
self.f.ioctl(ioctl::vcpu::KVM_SET_REGS, new).map(|_| ())
}
pub fn to_runner(self) -> Result<VirtualCpuRunner<'a>> {
let mmap_size = self.kvm.get_vcpu_mmap_size()?;
VirtualCpuRunner::new(self, mmap_size as linux_unsafe::size_t)
}
}
#[derive(Debug)]
pub struct VirtualCpuRunner<'a> {
vcpu: VirtualCpu<'a>,
run: *mut raw::kvm_run,
run_len: linux_unsafe::size_t,
}
impl<'a> VirtualCpuRunner<'a> {
fn new(cpu: VirtualCpu<'a>, mmap_size: linux_unsafe::size_t) -> Result<Self> {
if core::mem::size_of::<raw::kvm_run>() > (mmap_size as usize) {
return Err(linux_io::result::Error::new(12 ));
}
let run_ptr = unsafe {
cpu.f.mmap_raw(
0,
mmap_size,
core::ptr::null_mut(),
0x1 | 0x2, 0x1, )
}? as *mut raw::kvm_run;
Ok(Self {
vcpu: cpu,
run: run_ptr,
run_len: mmap_size,
})
}
#[inline(always)]
pub fn get_regs(&self) -> Result<raw::kvm_regs> {
self.vcpu.get_regs()
}
#[inline(always)]
pub fn set_regs(&self, new: &raw::kvm_regs) -> Result<()> {
self.vcpu.set_regs(new)
}
#[inline]
pub fn modify_regs<R>(&self, f: impl FnOnce(&mut raw::kvm_regs) -> R) -> Result<R> {
let mut regs = self.get_regs()?;
let ret = f(&mut regs);
self.set_regs(®s)?;
Ok(ret)
}
#[inline]
pub fn with_raw_run_state<R>(&mut self, f: impl FnOnce(&mut raw::kvm_run) -> R) -> R {
f(unsafe { &mut *self.run })
}
#[inline(always)]
pub fn run_raw(&mut self) -> Result<()> {
self.vcpu.f.ioctl(ioctl::vcpu::KVM_RUN, ())?;
Ok(())
}
}
impl<'a> Drop for VirtualCpuRunner<'a> {
fn drop(&mut self) {
unsafe { linux_unsafe::munmap(self.run as *mut linux_unsafe::void, self.run_len) }.unwrap();
}
}
#[derive(Debug)]
pub struct MemoryRegion {
addr: *mut linux_unsafe::void,
length: linux_unsafe::size_t,
}
impl MemoryRegion {
#[inline]
pub fn new(length: linux_unsafe::size_t) -> Result<Self> {
let addr = unsafe {
linux_unsafe::mmap(
core::ptr::null_mut(),
length,
0x1 | 0x2, 0x1 | 0x20, -1, 0,
)
}?;
Ok(Self { addr, length })
}
pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.addr as *mut u8, self.length) }
}
}
impl<'a> Drop for MemoryRegion {
fn drop(&mut self) {
unsafe { linux_unsafe::munmap(self.addr, self.length) }.unwrap();
}
}