kvm_rs/
vcpu.rs

1// SPDX-License-Identifier: MIT
2//
3// Copyright (c) 2021, Johannes Stoelp <dev@memzero.de>
4
5//! VCPU system ioctls.
6
7use std::fs;
8use std::io;
9
10use crate::{ioctl, kvm_sys, KvmRun};
11
12/// Exit reasons for the [`Vcpu::run`][crate::vcpu::Vcpu::run] function.
13///
14/// Details for the different exit reasons can be found in the [`kvm_run`
15/// structure][kvm-run-struct] description.
16///
17/// [kvm-run]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-run
18/// [kvm-run-struct]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#the-kvm-run-structure
19pub enum KvmExit<'cpu> {
20    Halt,
21    IoIn(u16, &'cpu mut [u8]),
22    IoOut(u16, &'cpu [u8]),
23    MmioRead(u64, &'cpu mut [u8]),
24    MmioWrite(u64, &'cpu [u8]),
25    Debug(u64),
26}
27
28/// Wrapper for VCPU ioctls.
29///
30/// Representation of the file descriptor obtained by the [`KVM_CREATE_VCPU`][kvm-create-vcpu] ioctl.
31/// This wrapper provides access to the `VCPU ioctls` as described in [KVM API][kvm].
32///
33/// [kvm]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#general-description
34/// [kvm-create-vcpu]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-create-vcpu
35pub struct Vcpu {
36    vcpu: fs::File,
37    kvm_run: KvmRun,
38}
39
40impl Vcpu {
41    pub(crate) fn new(vcpu: fs::File, kvm_run: KvmRun) -> Vcpu {
42        Vcpu { vcpu, kvm_run }
43    }
44
45    /// Get the general purpose registers with the [`KVM_GET_REGS`][kvm-get-regs] ioctl in form of
46    /// [`kvm_regs`](crate::kvm_sys::kvm_regs).
47    ///
48    /// [kvm-get-regs]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-get-regs
49    pub fn get_regs(&self) -> io::Result<kvm_sys::kvm_regs> {
50        let mut regs = kvm_sys::kvm_regs::default();
51        ioctl(
52            &self.vcpu,
53            kvm_sys::KVM_GET_REGS,
54            &mut regs as *mut _ as u64,
55        )?;
56        Ok(regs)
57    }
58
59    /// Set the general purpose registers with the [`KVM_SET_REGS`][kvm-set-regs] ioctl in form of
60    /// [`kvm_regs`](crate::kvm_sys::kvm_regs).
61    ///
62    /// [kvm-set-regs]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-set-regs
63    pub fn set_regs(&self, regs: kvm_sys::kvm_regs) -> io::Result<()> {
64        ioctl(&self.vcpu, kvm_sys::KVM_SET_REGS, &regs as *const _ as u64).map(|_| ())
65    }
66
67    /// Get the special registers with the [`KVM_GET_SREGS`][kvm-get-sregs] ioctl in form of
68    /// [`kvm_sregs`](crate::kvm_sys::kvm_sregs).
69    ///
70    /// [kvm-get-sregs]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-get-sregs
71    pub fn get_sregs(&self) -> io::Result<kvm_sys::kvm_sregs> {
72        let mut sregs = kvm_sys::kvm_sregs::default();
73        ioctl(
74            &self.vcpu,
75            kvm_sys::KVM_GET_SREGS,
76            &mut sregs as *mut _ as u64,
77        )?;
78        Ok(sregs)
79    }
80
81    /// Set the special registers with the [`KVM_SET_SREGS`][kvm-set-sregs] ioctl in form of
82    /// [`kvm_sregs`](crate::kvm_sys::kvm_sregs).
83    ///
84    /// [kvm-set-sregs]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-set-sregs
85    pub fn set_sregs(&self, sregs: kvm_sys::kvm_sregs) -> io::Result<()> {
86        ioctl(
87            &self.vcpu,
88            kvm_sys::KVM_SET_SREGS,
89            &sregs as *const _ as u64,
90        )
91        .map(|_| ())
92    }
93
94    /// Get the debug registers with the [`KVM_GET_DEBUGREGS`][kvm-get-debugregs] ioctl in form of
95    /// [`kvm_debugregs`](crate::kvm_sys::kvm_debugregs).
96    ///
97    /// [kvm-get-debugregs]:
98    /// https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-get-debugregs
99    #[cfg(target_arch = "x86_64")]
100    pub fn get_debugregs(&self) -> io::Result<kvm_sys::kvm_debugregs> {
101        let mut dregs = kvm_sys::kvm_debugregs::default();
102        ioctl(
103            &self.vcpu,
104            kvm_sys::KVM_GET_DEBUGREGS,
105            &mut dregs as *mut _ as u64,
106        )?;
107        Ok(dregs)
108    }
109
110    /// Set the debug registers with the [`KVM_SET_DEBUGREGS`][kvm-set-debugregs] ioctl in form of
111    /// [`kvm_debugregs`](crate::kvm_sys::kvm_debugregs).
112    ///
113    /// [kvm-set-debugregs]:
114    /// https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-set-debugregs
115    #[cfg(target_arch = "x86_64")]
116    pub fn set_debugregs(&self, dregs: kvm_sys::kvm_debugregs) -> io::Result<()> {
117        ioctl(
118            &self.vcpu,
119            kvm_sys::KVM_SET_DEBUGREGS,
120            &dregs as *const _ as u64,
121        )
122        .map(|_| ())
123    }
124
125    /// Enable or disable guest single steppig (debug) with the
126    /// [`KVM_GUESTDBG_ENABLE`][kvm-guest-debug] ioctl.
127    ///
128    /// [kvm-guest-debug]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-set-guest-debug
129    #[cfg(target_arch = "x86_64")]
130    pub fn set_single_step(&self, enable: bool) -> io::Result<()> {
131        let mut dbg = kvm_sys::kvm_guest_debug::default();
132
133        if enable {
134            // Enable guest debugging and single stepping.
135            dbg.control = kvm_sys::KVM_GUESTDBG_ENABLE | kvm_sys::KVM_GUESTDBG_SINGLESTEP;
136        }
137
138        // Initialize debug registers based on current VCPUs debug register values.
139        let dregs = self.get_debugregs()?;
140        dbg.arch.debugreg[0..4].copy_from_slice(&dregs.db);
141        // DR4-DR5 are reserved.
142        dbg.arch.debugreg[6] = dregs.dr6;
143        dbg.arch.debugreg[7] = dregs.dr7;
144
145        ioctl(
146            &self.vcpu,
147            kvm_sys::KVM_SET_GUEST_DEBUG,
148            &dbg as *const _ as u64,
149        )
150        .map(|_| ())
151    }
152
153    /// Run the guest VCPU with the [`KVM_RUN`][kvm-run] ioctl until it exits with one of the exit
154    /// reasons described in [`KvmExit`](crate::vcpu::KvmExit).
155    ///
156    /// [kvm-run]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-run
157    pub fn run(&mut self) -> io::Result<KvmExit<'_>> {
158        ioctl(&self.vcpu, kvm_sys::KVM_RUN, 0)?;
159
160        let kvm_run = self.kvm_run.as_mut();
161
162        match kvm_run.exit_reason as u64 {
163            kvm_sys::KVM_EXIT_HLT => Ok(KvmExit::Halt),
164            kvm_sys::KVM_EXIT_IO => {
165                // Safe to use union `io` field, as Kernel instructed us to.
166                let io = unsafe { kvm_run.inner.io };
167
168                let kvm_run_ptr = kvm_run as *mut kvm_sys::kvm_run as *mut u8;
169
170                // Create IO buffer located at `kvm_run + io.offset`.
171                let data = unsafe {
172                    std::slice::from_raw_parts_mut(
173                        kvm_run_ptr.offset(io.data_offset as isize),
174                        io.count /* num blocks */ as usize * io.size /* bytes per block */ as usize,
175                    )
176                };
177
178                match io.direction as u64 {
179                    kvm_sys::KVM_EXIT_IO_IN => Ok(KvmExit::IoIn(io.port, data)),
180                    kvm_sys::KVM_EXIT_IO_OUT => Ok(KvmExit::IoOut(io.port, data)),
181                    _ => unreachable!(),
182                }
183            }
184            kvm_sys::KVM_EXIT_MMIO => {
185                // Safe to use union `mmio` filed, as Kernel instructed us to.
186                let mmio = unsafe { &mut kvm_run.inner.mmio };
187                let len = mmio.len as usize;
188
189                match mmio.is_write {
190                    0 => Ok(KvmExit::MmioRead(mmio.phys_addr, &mut mmio.data[..len])),
191                    1 => Ok(KvmExit::MmioWrite(mmio.phys_addr, &mmio.data[..len])),
192                    _ => unreachable!(),
193                }
194            }
195            kvm_sys::KVM_EXIT_DEBUG => {
196                // Safe to use union `debug` field, as Kernel instructed us to.
197                let debug = unsafe { kvm_run.inner.debug };
198
199                Ok(KvmExit::Debug(debug.pc))
200            }
201            r @ _ => {
202                todo!("KVM_EXIT_... (exit_reason={}) not implemented!", r)
203            }
204        }
205    }
206}