1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
// Do not depend on entire libc just for this function
extern "C" {
fn ioctl(fd: i32, request: __u64, ...) -> i32;
fn munmap(addr: *mut std::os::raw::c_void, len: usize) -> i32;
}
use std::io::Result;
/// Structure assisting automatic memory map unmapping
///
/// It does not reference count on its own. Wrap it in Arc,
/// or Rc for it to take place
pub struct AutoMunmap {
memslots: Vec<vm_memslot>,
}
impl AutoMunmap {
/// Create automatic unmapping
///
/// # Safety
///
/// Drop implementation of this structure calls munmap on all mapped memory regions.
/// vm_memslots have to be correct for the runnings process, or causes undefined bahaviour.
pub unsafe fn new(memslots: Vec<vm_memslot>) -> Self {
Self { memslots }
}
}
impl Drop for AutoMunmap {
fn drop(&mut self) {
for slot in &self.memslots {
unsafe {
munmap(slot.host_base as _, slot.map_size as _);
}
}
}
}
/// Handle to memflow's kernel module
///
/// This is a handle to `/dev/memflow`. It functions as a file, closes automatically when dropped
pub struct ModuleHandle {
memflow: File,
}
impl ModuleHandle {
pub fn try_open() -> Result<Self> {
Ok(Self {
memflow: File::open("/dev/memflow")?,
})
}
}
impl AsRawFd for ModuleHandle {
fn as_raw_fd(&self) -> RawFd {
self.memflow.as_raw_fd()
}
}
/// Handle to a KVM VM
///
/// This is a handle to a single KVM instance. Allows access to basic memory layout information of the VM.
pub struct VMHandle {
vm: File,
}
impl VMHandle {
/// Open a KVM instance with a memflow handle
///
/// This may be useful when multiple VMs are accessed to save syscalls, but in regular scenarios,
/// use `try_open`.
///
/// # Arguments
///
/// * `memflow` - handle to the memflow driver
/// * `pid` - optional process identifier. If it is `None`, or `Some(0)`, handle to any of the KVM
/// VMs gets retrieved.
pub fn try_open_handle(memflow: &ModuleHandle, pid: Option<i32>) -> Result<Self> {
let vm_fd: RawFd = unsafe {
ioctl(
memflow.as_raw_fd(),
IO_MEMFLOW_OPEN_VM as u64,
pid.unwrap_or_default(),
)
};
if vm_fd < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(Self {
vm: unsafe { File::from_raw_fd(vm_fd) },
})
}
}
/// Open a KVM instance
///
/// # Arguments
///
/// * `pid` - optional process identifier. If it is `None`, or `Some(0)`, handle to any of the KVM
/// VMs gets retrieved. Otherwise, the specific PID gets targeted. And if the process does not exist,
/// this function will fail.
pub fn try_open(pid: Option<i32>) -> Result<Self> {
Self::try_open_handle(&ModuleHandle::try_open()?, pid)
}
/// Retrieve info about the KVM instance
///
/// Pulls info about the current VM handle. That means its PID, and the memory layout (in KVMs userspace).
pub fn info(&self, slot_count: usize) -> Result<(i32, Vec<vm_memslot>)> {
let mut vm_info = vm_info::default();
let mut memslots = vec![Default::default(); slot_count];
vm_info.slot_count = slot_count as u32;
vm_info.slots = memslots.as_mut_ptr();
let ret = unsafe { ioctl(self.vm.as_raw_fd(), IO_MEMFLOW_VM_INFO as u64, &mut vm_info) };
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
memslots.truncate(vm_info.slot_count as usize);
#[allow(clippy::unnecessary_cast)]
Ok((vm_info.userspace_pid as i32, memslots))
}
}
/// Memory map the KVM instance
///
/// Maps the memory of the KVM instance into local address space, and returns the mapped memory layout.
///
/// The memory map is permanent (unless manually unmapped using libc). KVM has the possibility of changing
/// the memory layout (in its process only), but this function can not account for it.
pub fn map_vm(&self, slot_count: usize) -> Result<Vec<vm_memslot>> {
let mut vm_info = vm_map_info::default();
let mut memslots = vec![Default::default(); slot_count];
vm_info.slot_count = slot_count as u32;
vm_info.slots = memslots.as_mut_ptr();
let ret = unsafe { ioctl(self.vm.as_raw_fd(), IO_MEMFLOW_MAP_VM as u64, &mut vm_info) };
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
memslots.truncate(vm_info.slot_count as usize);
Ok(memslots)
}
}
}