Skip to content

Commit 209f415

Browse files
00xcJonathanWoollett-Light
authored andcommitted
Add support for userspace MSR handling
Add the appropriate types to allow for a VMM to intercept RDMSR and WRMSR instructions if access to an MSR is denied. By default, KVM injects #GP on denied accesses. To do this we need to add a new Cap variant which corresponds to KVM_CAP_X86_USER_SPACE_MSR, and two new VcpuExit variants that correspond to KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR respectively. We also need helper types to pass the relevant information about the exit to the VMM. Signed-off-by: Carlos López <[email protected]>
1 parent b07d906 commit 209f415

File tree

5 files changed

+87
-2
lines changed

5 files changed

+87
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ reg_size as a public method.
99
trait for `IoEventAddress` and `NoDatamatch`.
1010
- [[#242](https://github.com/rust-vmm/kvm-ioctls/pull/242)] x86: add support
1111
for SMI injection via `Vcpu::smi()` (`KVM_SMI` ioctl).
12+
- [[#241](https://github.com/rust-vmm/kvm-ioctls/pull/241)] Add support for
13+
userspace MSR handling.
1214

1315
# v0.15.0
1416

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ edition = "2021"
1313
libc = "0.2.39"
1414
kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] }
1515
vmm-sys-util = "0.11.0"
16+
bitflags = "2.4.1"
1617

1718
[dev-dependencies]
1819
byteorder = "1.2.1"

src/cap.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,6 @@ pub enum Cap {
164164
ArmPtrAuthAddress = KVM_CAP_ARM_PTRAUTH_ADDRESS,
165165
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
166166
ArmPtrAuthGeneric = KVM_CAP_ARM_PTRAUTH_GENERIC,
167+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
168+
X86UserSpaceMsr = KVM_CAP_X86_USER_SPACE_MSR,
167169
}

src/ioctls/vcpu.rs

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ use std::os::unix::io::{AsRawFd, RawFd};
1313
use crate::ioctls::{KvmRunWrapper, Result};
1414
use crate::kvm_ioctls::*;
1515
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
16-
use kvm_bindings::{CpuId, Msrs, KVM_MAX_CPUID_ENTRIES};
16+
use kvm_bindings::{
17+
CpuId, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MSR_EXIT_REASON_FILTER, KVM_MSR_EXIT_REASON_INVAL,
18+
KVM_MSR_EXIT_REASON_UNKNOWN,
19+
};
1720
use vmm_sys_util::errno;
1821
use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref};
1922
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
@@ -25,6 +28,55 @@ pub fn reg_size(reg_id: u64) -> usize {
2528
2_usize.pow(((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT) as u32)
2629
}
2730

31+
/// Information about a [`VcpuExit`] triggered by an MSR read (`KVM_EXIT_X86_RDMSR`).
32+
#[derive(Debug)]
33+
pub struct ReadMsrExit<'a> {
34+
/// Must be set to 1 by the the user if the read access should fail. This
35+
/// will inject a #GP fault into the guest when the VCPU is executed
36+
/// again.
37+
pub error: &'a mut u8,
38+
/// The reason for this exit.
39+
pub reason: MsrExitReason,
40+
/// The MSR the guest wants to read.
41+
pub index: u32,
42+
/// The data to be supplied by the user as the MSR Contents to the guest.
43+
pub data: &'a mut u64,
44+
}
45+
46+
/// Information about a [`VcpuExit`] triggered by an MSR write (`KVM_EXIT_X86_WRMSR`).
47+
#[derive(Debug)]
48+
pub struct WriteMsrExit<'a> {
49+
/// Must be set to 1 by the the user if the write access should fail. This
50+
/// will inject a #GP fault into the guest when the VCPU is executed
51+
/// again.
52+
pub error: &'a mut u8,
53+
/// The reason for this exit.
54+
pub reason: MsrExitReason,
55+
/// The MSR the guest wants to write.
56+
pub index: u32,
57+
/// The data the guest wants to write into the MSR.
58+
pub data: u64,
59+
}
60+
61+
bitflags::bitflags! {
62+
/// The reason for a [`VcpuExit::X86Rdmsr`] or[`VcpuExit::X86Wrmsr`]. This
63+
/// is also used when enabling
64+
/// [`Cap::X86UserSpaceMsr`](crate::Cap::X86UserSpaceMsr) to specify which
65+
/// reasons should be forwarded to the user via those exits.
66+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67+
pub struct MsrExitReason: u32 {
68+
/// Corresponds to [`KVM_MSR_EXIT_REASON_UNKNOWN`]. The exit was
69+
/// triggered by an access to an MSR that is unknown to KVM.
70+
const Unknown = KVM_MSR_EXIT_REASON_UNKNOWN;
71+
/// Corresponds to [`KVM_MSR_EXIT_REASON_INVAL`]. The exit was
72+
/// triggered by an access to an invalid MSR or to reserved bits.
73+
const Inval = KVM_MSR_EXIT_REASON_INVAL;
74+
/// Corresponds to [`KVM_MSR_EXIT_REASON_FILTER`]. The exit was
75+
/// triggered by an access to a filtered MSR.
76+
const Filter = KVM_MSR_EXIT_REASON_FILTER;
77+
}
78+
}
79+
2880
/// Reasons for vCPU exits.
2981
///
3082
/// The exit reasons are mapped to the `KVM_EXIT_*` defines in the
@@ -102,6 +154,10 @@ pub enum VcpuExit<'a> {
102154
IoapicEoi(u8 /* vector */),
103155
/// Corresponds to KVM_EXIT_HYPERV.
104156
Hyperv,
157+
/// Corresponds to KVM_EXIT_X86_RDMSR.
158+
X86Rdmsr(ReadMsrExit<'a>),
159+
/// Corresponds to KVM_EXIT_X86_WRMSR.
160+
X86Wrmsr(WriteMsrExit<'a>),
105161
/// Corresponds to an exit reason that is unknown from the current version
106162
/// of the kvm-ioctls crate. Let the consumer decide about what to do with
107163
/// it.
@@ -1422,6 +1478,30 @@ impl VcpuFd {
14221478
Ok(VcpuExit::MmioRead(addr, data_slice))
14231479
}
14241480
}
1481+
KVM_EXIT_X86_RDMSR => {
1482+
// SAFETY: Safe because the exit_reason (which comes from the kernel) told us
1483+
// which union field to use.
1484+
let msr = unsafe { &mut run.__bindgen_anon_1.msr };
1485+
let exit = ReadMsrExit {
1486+
error: &mut msr.error,
1487+
reason: MsrExitReason::from_bits_truncate(msr.reason),
1488+
index: msr.index,
1489+
data: &mut msr.data,
1490+
};
1491+
Ok(VcpuExit::X86Rdmsr(exit))
1492+
}
1493+
KVM_EXIT_X86_WRMSR => {
1494+
// SAFETY: Safe because the exit_reason (which comes from the kernel) told us
1495+
// which union field to use.
1496+
let msr = unsafe { &mut run.__bindgen_anon_1.msr };
1497+
let exit = WriteMsrExit {
1498+
error: &mut msr.error,
1499+
reason: MsrExitReason::from_bits_truncate(msr.reason),
1500+
index: msr.index,
1501+
data: msr.data,
1502+
};
1503+
Ok(VcpuExit::X86Wrmsr(exit))
1504+
}
14251505
KVM_EXIT_IRQ_WINDOW_OPEN => Ok(VcpuExit::IrqWindowOpen),
14261506
KVM_EXIT_SHUTDOWN => Ok(VcpuExit::Shutdown),
14271507
KVM_EXIT_FAIL_ENTRY => {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ pub use ioctls::vcpu::reg_size;
224224
pub use ioctls::vcpu::{VcpuExit, VcpuFd};
225225

226226
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
227-
pub use ioctls::vcpu::SyncReg;
227+
pub use ioctls::vcpu::{MsrExitReason, ReadMsrExit, SyncReg, WriteMsrExit};
228228

229229
pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd};
230230
// The following example is used to verify that our public

0 commit comments

Comments
 (0)