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