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