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(®s)?;
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}