linux_kvm/
lib.rs

1//! This package wraps the lower-level crate [`linux_io`] to provide more
2//! convenient access to the linux KVM API, which allows you to create and
3//! run kernel-managed virtual machines on architectures that support that.
4//!
5//! For now this crate is largely just serving as a prototype case for building
6//! low-cost safe abstractions on top of [`linux_io`], so it doesn't support
7//! the full KVM API. Hopefully over time it'll gain enough to be useful.
8#![no_std]
9#![cfg_attr(feature = "nightly", feature(debug_closure_helpers))]
10
11pub mod ioctl;
12pub mod raw;
13
14pub use linux_io::result::Result;
15use linux_io::{File, OpenOptions};
16use linux_unsafe::int;
17
18/// Represents the kernel's whole KVM subsystem.
19///
20/// This is the entry point for obtaining all other KVM objects, whether
21/// directly or indirectly.
22#[derive(Debug)]
23pub struct Kvm {
24    f: File<ioctl::system::KvmSystem>,
25}
26
27impl Kvm {
28    /// Opens the KVM device `/dev/kvm` and returns a [`Kvm`] instance wrapping
29    /// it.
30    ///
31    /// Fails with an error on a system where `/dev/kvm` doesn't exist for some
32    /// reason, such as if KVM is not enabled in the kernel.
33    ///
34    /// **Warning:** The safety of this function relies on there being a
35    /// reasonable device node at `/dev/kvm`. If the target system has some
36    /// other unrelated device node or a non-device entry at that location
37    /// then the returned object will allow issuing ioctl requests to that
38    /// file that may cause memory corruption depending on how the opened
39    /// device reacts to the KVM ioctl numbers.
40    ///
41    /// This function is not marked as `unsafe` because a system configured in
42    /// that way is considered unreasonable, and this crate is optimized for
43    /// reasonable Linux configurations that follow the filesystem layout given
44    /// in the kernel documentation.
45    pub fn open() -> Result<Self> {
46        let path = unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(b"/dev/kvm\0") };
47        let opts = OpenOptions::read_write().close_on_exec();
48        let f = File::open(path, opts)?;
49
50        // Safety: On any reasonable Linux system /dev/kvm should either not
51        // exist (and we would've returned an error by now) or refer to the
52        // main KVM system device, and should therefore be suitable to
53        // accept the KvmSystem ioctls.
54        let f = unsafe { f.to_device(ioctl::system::KvmSystem) };
55        Ok(Self::from_file(f))
56    }
57
58    /// Wraps the given already-opened file in a `Kvm` object.
59    #[inline(always)]
60    pub const fn from_file(f: File<ioctl::system::KvmSystem>) -> Self {
61        Self { f }
62    }
63
64    /// Identifies the version of the KVM API used by the current kernel.
65    ///
66    /// The stable API always returns version 12. The kernel documentation suggests
67    /// that applications should always call this and refuse to run if it returns
68    /// any value other than that; the version number is not expected to change
69    /// in the future because future API additions will use [`Self::check_extension`]
70    /// instead.
71    #[inline(always)]
72    pub fn get_api_version(&self) -> Result<int> {
73        self.f.ioctl(ioctl::system::KVM_GET_API_VERSION, ())
74    }
75
76    /// Query whether the KVM subsystem in the current kernel supports a particular
77    /// extension.
78    ///
79    /// A result of zero indicates a lack of support while nonzero indicates
80    /// support. The nonzero value may carry additional meanings for some
81    /// extensions.
82    #[inline(always)]
83    pub fn check_extension(&self, ext: int) -> Result<int> {
84        self.f.ioctl(ioctl::system::KVM_CHECK_EXTENSION, &ext)
85    }
86
87    /// Create a new virtual machine.
88    #[inline(always)]
89    pub fn create_vm(&self) -> Result<VirtualMachine<'_>> {
90        let f = self.f.ioctl(ioctl::system::KVM_CREATE_VM, ())?;
91        Ok(VirtualMachine::from_file(f, &self))
92    }
93
94    /// Determine the size of the shared memory regions that will be used
95    /// between kernel and userspace for each VCPU.
96    #[inline(always)]
97    pub fn get_vcpu_mmap_size(&self) -> Result<int> {
98        self.f.ioctl(ioctl::system::KVM_GET_VCPU_MMAP_SIZE, ())
99    }
100}
101
102/// An individual virtual machine created through a [`Kvm`] object.
103#[derive(Debug)]
104pub struct VirtualMachine<'a> {
105    f: File<ioctl::vm::KvmVm>,
106    kvm: &'a Kvm,
107}
108
109impl<'a> VirtualMachine<'a> {
110    /// Wraps the given already-opened file in a `VirtualMachine` object.
111    #[inline(always)]
112    const fn from_file(f: File<ioctl::vm::KvmVm>, kvm: &'a Kvm) -> Self {
113        Self { f, kvm }
114    }
115
116    /// Query whether the KVM subsystem in the current kernel supports a particular
117    /// extension for a specific VM.
118    ///
119    /// A result of zero indicates a lack of support while nonzero indicates
120    /// support. The nonzero value may carry additional meanings for some
121    /// extensions.
122    #[inline(always)]
123    pub fn check_extension(&self, ext: int) -> Result<int> {
124        self.f.ioctl(ioctl::vm::KVM_CHECK_EXTENSION, &ext)
125    }
126
127    /// Create a new VCPU for this VM.
128    ///
129    /// If creating multiple VCPUs in the same VM, start with `cpu_id` zero
130    /// and then increment for each new VM. The kernel enforces a
131    /// platform-specific limit on VCPUs per VM, which you can determine by
132    /// querying extensions using [`Self::check_extension`].
133    #[inline(always)]
134    pub fn create_vcpu(&self, cpu_id: linux_unsafe::int) -> Result<VirtualCpu<'_>> {
135        self.f
136            .ioctl(ioctl::vm::KVM_CREATE_VCPU, cpu_id)
137            .map(|f| VirtualCpu::from_file(f, &self.kvm))
138    }
139
140    /// Sets one of the VM's memory region slots to refer to the given
141    /// memory region, which must outlive this VCPU.
142    pub fn set_guest_memory_region<'r: 'a>(
143        &mut self,
144        slot: u32,
145        flags: u32,
146        guest_phys_addr: u64,
147        host_region: &'r mut MemoryRegion,
148    ) -> Result<()> {
149        let desc = raw::kvm_userspace_memory_region {
150            slot,
151            flags,
152            guest_phys_addr,
153            memory_size: host_region.length as u64,
154            userspace_addr: host_region.addr as u64,
155        };
156        self.f
157            .ioctl(ioctl::vm::KVM_SET_USER_MEMORY_REGION, &desc)
158            .map(|_| ())
159    }
160}
161
162/// A virtual CPU belonging to a [`VirtualMachine`].
163#[derive(Debug)]
164pub struct VirtualCpu<'a> {
165    f: File<ioctl::vcpu::KvmVcpu>,
166    kvm: &'a Kvm,
167}
168
169impl<'a> VirtualCpu<'a> {
170    /// Wraps the given already-opened file in a `VirtualCpu` object.
171    #[inline(always)]
172    const fn from_file(f: File<ioctl::vcpu::KvmVcpu>, kvm: &'a Kvm) -> Self {
173        Self { f, kvm }
174    }
175
176    /// Get the architecture-specific representation of the current register
177    /// values of this vCPU.
178    #[inline(always)]
179    pub fn get_regs(&self) -> Result<raw::kvm_regs> {
180        self.f.ioctl(ioctl::vcpu::KVM_GET_REGS, ())
181    }
182
183    /// Set the architecture-specific representation of the current register
184    /// values of this vCPU.
185    #[inline(always)]
186    pub fn set_regs(&self, new: &raw::kvm_regs) -> Result<()> {
187        self.f.ioctl(ioctl::vcpu::KVM_SET_REGS, new).map(|_| ())
188    }
189
190    /// Wrap this CPU into an object that has the necessary extra state to
191    /// run it.
192    ///
193    /// This encapsulates the step of using `mmap` on the VCPU file descriptor
194    /// to establish a shared memory space with the KVM subsystem, so failure
195    /// here represents failure of either that `mmap` operation or the
196    /// `ioctl` call to discover its parameters.
197    pub fn to_runner(self) -> Result<VirtualCpuRunner<'a>> {
198        let mmap_size = self.kvm.get_vcpu_mmap_size()?;
199        VirtualCpuRunner::new(self, mmap_size as linux_unsafe::size_t)
200    }
201}
202
203/// Wraps a [`VirtualCpu`] with some extra state required to run it.
204#[derive(Debug)]
205pub struct VirtualCpuRunner<'a> {
206    vcpu: VirtualCpu<'a>,
207    run: *mut raw::kvm_run,
208    run_len: linux_unsafe::size_t,
209}
210
211impl<'a> VirtualCpuRunner<'a> {
212    fn new(cpu: VirtualCpu<'a>, mmap_size: linux_unsafe::size_t) -> Result<Self> {
213        if core::mem::size_of::<raw::kvm_run>() > (mmap_size as usize) {
214            // We can't safely use our struct type over the mmap region if
215            // the region isn't long enough. This shouldn't happen because
216            // we're using the documented structure.
217            return Err(linux_io::result::Error::new(12 /* ENOMEM */));
218        }
219
220        let run_ptr = unsafe {
221            cpu.f.mmap_raw(
222                0,
223                mmap_size,
224                core::ptr::null_mut(),
225                0x1 | 0x2, // PROT_READ | PROT_WRITE
226                0x1,       // MAP_SHARED
227            )
228        }? as *mut raw::kvm_run;
229
230        // Safety: We assume that the kernel has placed valid initial values for
231        // all of the fields of kvm_run in this shared memory area before
232        // returning it, so we don't need to initialize it further here.
233
234        Ok(Self {
235            vcpu: cpu,
236            run: run_ptr,
237            run_len: mmap_size,
238        })
239    }
240
241    /// Get the architecture-specific representation of the current register
242    /// values of this vCPU.
243    #[inline(always)]
244    pub fn get_regs(&self) -> Result<raw::kvm_regs> {
245        self.vcpu.get_regs()
246    }
247
248    /// Set the architecture-specific representation of the current register
249    /// values of this vCPU.
250    #[inline(always)]
251    pub fn set_regs(&self, new: &raw::kvm_regs) -> Result<()> {
252        self.vcpu.set_regs(new)
253    }
254
255    /// Modify in place the architecturte-specific register values of this vCPU.
256    #[inline]
257    pub fn modify_regs<R>(&self, f: impl FnOnce(&mut raw::kvm_regs) -> R) -> Result<R> {
258        let mut regs = self.get_regs()?;
259        let ret = f(&mut regs);
260        self.set_regs(&regs)?;
261        Ok(ret)
262    }
263
264    #[inline]
265    pub fn with_raw_run_state<R>(&mut self, f: impl FnOnce(&mut raw::kvm_run) -> R) -> R {
266        f(unsafe { &mut *self.run })
267    }
268
269    /// Run the VCPU until it exits.
270    #[inline(always)]
271    pub fn run_raw(&mut self) -> Result<()> {
272        self.vcpu.f.ioctl(ioctl::vcpu::KVM_RUN, ())?;
273        Ok(())
274    }
275}
276
277impl<'a> Drop for VirtualCpuRunner<'a> {
278    /// [`VirtualCpuRunner`] automatically releases its kernel shared memory
279    /// mapping when dropped, and will panic if that fails.
280    fn drop(&mut self) {
281        unsafe { linux_unsafe::munmap(self.run as *mut linux_unsafe::void, self.run_len) }.unwrap();
282    }
283}
284
285/// A page-aligned host memory region that can be mapped into the guest memory
286/// space of a [`VirtualMachine`].
287#[derive(Debug)]
288pub struct MemoryRegion {
289    addr: *mut linux_unsafe::void,
290    length: linux_unsafe::size_t,
291}
292
293impl MemoryRegion {
294    /// Attempts to allocate a new memory region of a given size.
295    #[inline]
296    pub fn new(length: linux_unsafe::size_t) -> Result<Self> {
297        let addr = unsafe {
298            linux_unsafe::mmap(
299                core::ptr::null_mut(),
300                length,
301                0x1 | 0x2,  // PROT_READ | PROT_WRITE
302                0x1 | 0x20, // MAP_SHARED | MAP_ANONYMOUS
303                -1,         // no fd, because MAP_ANONYMOUS
304                0,
305            )
306        }?;
307        Ok(Self { addr, length })
308    }
309
310    /// Returns a view of the memory region as a mutable slice, which
311    /// the caller can then modify to populate the memory area.
312    pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [u8] {
313        // Safety: Caller can't interact with the memory region in any other
314        // way while still holding the mutable borrow we return here, so
315        // nothing else should access it.
316        unsafe { core::slice::from_raw_parts_mut(self.addr as *mut u8, self.length) }
317    }
318}
319
320impl<'a> Drop for MemoryRegion {
321    /// [`MemoryRegion`] automatically releases its memory mapping when dropped,
322    /// and will panic if that fails.
323    fn drop(&mut self) {
324        unsafe { linux_unsafe::munmap(self.addr, self.length) }.unwrap();
325    }
326}