Skip to content

Commit 968f2f4

Browse files
committed
MSR
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 7285b11 commit 968f2f4

File tree

12 files changed

+491
-20
lines changed

12 files changed

+491
-20
lines changed

Cargo.lock

Lines changed: 0 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/hyperlight_host/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ windows-version = "0.1"
7575
lazy_static = "1.4.0"
7676

7777
[target.'cfg(unix)'.dependencies]
78-
kvm-bindings = { version = "0.14", features = ["fam-wrappers"], optional = true }
79-
kvm-ioctls = { version = "0.24", optional = true }
78+
kvm-bindings = { path = "../../../kvm/kvm-bindings", features = ["fam-wrappers"], optional = true }
79+
kvm-ioctls = { path = "../../../kvm/kvm-ioctls", optional = true }
8080
mshv-bindings = { version = "0.6", optional = true }
8181
mshv-ioctls = { version = "0.6", optional = true}
8282

src/hyperlight_host/src/error.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ pub enum HyperlightError {
144144
#[error("Memory Allocation Failed with OS Error {0:?}.")]
145145
MemoryAllocationFailed(Option<i32>),
146146

147+
/// MSR Read Violation - Guest attempted to read from a Model-Specific Register
148+
#[error("Guest attempted to read from MSR {0:#x} which is not allowed")]
149+
MsrReadViolation(u32),
150+
151+
/// MSR Write Violation - Guest attempted to write to a Model-Specific Register
152+
#[error("Guest attempted to write {1:#x} to MSR {0:#x} which is not allowed")]
153+
MsrWriteViolation(u32, u64),
154+
147155
/// Memory Protection Failed
148156
#[error("Memory Protection Failed with OS Error {0:?}.")]
149157
MemoryProtectionFailed(Option<i32>),
@@ -322,7 +330,9 @@ impl HyperlightError {
322330
| HyperlightError::PoisonedSandbox
323331
| HyperlightError::ExecutionAccessViolation(_)
324332
| HyperlightError::StackOverflow()
325-
| HyperlightError::MemoryAccessViolation(_, _, _) => true,
333+
| HyperlightError::MemoryAccessViolation(_, _, _)
334+
| HyperlightError::MsrReadViolation(_)
335+
| HyperlightError::MsrWriteViolation(_, _) => true,
326336

327337
// All other errors do not poison the sandbox.
328338
HyperlightError::AnyhowError(_)

src/hyperlight_host/src/hypervisor/hyperlight_vm.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use crate::hypervisor::hyperv_linux::MshvVm;
4646
use crate::hypervisor::hyperv_windows::WhpVm;
4747
#[cfg(kvm)]
4848
use crate::hypervisor::kvm::KvmVm;
49-
use crate::hypervisor::regs::CommonSpecialRegisters;
49+
use crate::hypervisor::regs::{CommonDebugRegs, CommonSpecialRegisters};
5050
use crate::hypervisor::vm::{Vm, VmExit};
5151
#[cfg(target_os = "windows")]
5252
use crate::hypervisor::wrappers::HandleWrapper;
@@ -116,6 +116,8 @@ impl HyperlightVm {
116116
None => return Err(NoHypervisorFound()),
117117
};
118118

119+
vm.enable_msr_intercept()?;
120+
119121
for (i, region) in mem_regions.iter().enumerate() {
120122
// Safety: slots are unique and region points to valid memory since we created the regions
121123
unsafe { vm.map_memory((i as u32, region))? };
@@ -532,6 +534,12 @@ impl HyperlightVm {
532534
}
533535
}
534536
}
537+
Ok(VmExit::MsrRead(msr_index)) => {
538+
break Err(HyperlightError::MsrReadViolation(msr_index));
539+
}
540+
Ok(VmExit::MsrWrite { msr_index, value }) => {
541+
break Err(HyperlightError::MsrWriteViolation(msr_index, value));
542+
}
535543
Ok(VmExit::Cancelled()) => {
536544
// If cancellation was not requested for this specific guest function call,
537545
// the vcpu was interrupted by a stale cancellation from a previous call
@@ -592,6 +600,16 @@ impl HyperlightVm {
592600
self.interrupt_handle.clone()
593601
}
594602

603+
pub(crate) fn reset_vcpu(&self) -> Result<()> {
604+
self.vm.set_regs(&CommonRegisters::default())?;
605+
self.vm.set_sregs(&CommonSpecialRegisters::default())?;
606+
self.vm.set_fpu(&CommonFpu::default())?;
607+
self.vm.set_xsave(&[0; 1024])?;
608+
self.vm.set_debug_regs(&CommonDebugRegs::default())?;
609+
// TODO reset MSRs
610+
Ok(())
611+
}
612+
595613
#[cfg(gdb)]
596614
fn handle_debug(
597615
&mut self,

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,22 @@ use std::sync::LazyLock;
2121
#[cfg(gdb)]
2222
use mshv_bindings::{DebugRegisters, hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT};
2323
use mshv_bindings::{
24-
hv_message_type, hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA,
24+
HV_INTERCEPT_ACCESS_MASK_READ, HV_INTERCEPT_ACCESS_MASK_WRITE, HV_INTERCEPT_ACCESS_READ,
25+
HV_INTERCEPT_ACCESS_WRITE, XSave, hv_intercept_type_HV_INTERCEPT_TYPE_X64_MSR, hv_message_type,
26+
hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA,
2527
hv_message_type_HVMSG_X64_HALT, hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT,
28+
hv_message_type_HVMSG_X64_MSR_INTERCEPT,
2629
hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
2730
hv_partition_synthetic_processor_features, hv_register_assoc,
28-
hv_register_name_HV_X64_REGISTER_RIP, hv_register_value, mshv_user_mem_region,
31+
hv_register_name_HV_X64_REGISTER_RIP, hv_register_value, mshv_install_intercept,
32+
mshv_user_mem_region,
2933
};
3034
use mshv_ioctls::{Mshv, VcpuFd, VmFd};
3135
use tracing::{Span, instrument};
3236

33-
use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
37+
use crate::hypervisor::regs::{
38+
CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters,
39+
};
3440
use crate::hypervisor::vm::{Vm, VmExit};
3541
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
3642
use crate::{Result, new_error};
@@ -82,7 +88,6 @@ impl MshvVm {
8288
};
8389

8490
let vcpu_fd = vm_fd.create_vcpu(0)?;
85-
8691
Ok(Self { vm_fd, vcpu_fd })
8792
}
8893
}
@@ -114,12 +119,32 @@ impl Vm for MshvVm {
114119
Ok(())
115120
}
116121

117-
#[cfg(crashdump)]
118122
fn xsave(&self) -> Result<Vec<u8>> {
119123
let xsave = self.vcpu_fd.get_xsave()?;
120124
Ok(xsave.buffer.to_vec())
121125
}
122126

127+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
128+
let mut buf = XSave::default();
129+
let (prefix, bytes, suffix) = unsafe { xsave.align_to() };
130+
assert!(prefix.is_empty());
131+
assert!(suffix.is_empty());
132+
buf.buffer.copy_from_slice(bytes);
133+
self.vcpu_fd.set_xsave(&buf)?;
134+
Ok(())
135+
}
136+
137+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
138+
let debug_regs = self.vcpu_fd.get_debug_regs()?;
139+
Ok(debug_regs.into())
140+
}
141+
142+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
143+
let mshv_debug_regs = drs.into();
144+
self.vcpu_fd.set_debug_regs(&mshv_debug_regs)?;
145+
Ok(())
146+
}
147+
123148
/// # Safety
124149
/// The caller must ensure that the memory region is valid and points to valid memory,
125150
/// and lives long enough for the VM to use it.
@@ -140,6 +165,7 @@ impl Vm for MshvVm {
140165
const IO_PORT: hv_message_type = hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
141166
const UNMAPPED_GPA: hv_message_type = hv_message_type_HVMSG_UNMAPPED_GPA;
142167
const INVALID_GPA: hv_message_type = hv_message_type_HVMSG_GPA_INTERCEPT;
168+
const MSR: hv_message_type = hv_message_type_HVMSG_X64_MSR_INTERCEPT;
143169
#[cfg(gdb)]
144170
const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
145171

@@ -181,6 +207,21 @@ impl Vm for MshvVm {
181207
_ => VmExit::Unknown("Unknown MMIO access".to_string()),
182208
}
183209
}
210+
MSR => {
211+
let msr_message = m.to_msr_info().map_err(mshv_ioctls::MshvError::from)?;
212+
let edx = msr_message.rdx;
213+
let eax = msr_message.rax;
214+
let written_value = (edx << 32) | eax;
215+
let access = msr_message.header.intercept_access_type as u32;
216+
match access {
217+
HV_INTERCEPT_ACCESS_READ => VmExit::MsrRead(msr_message.msr_number),
218+
HV_INTERCEPT_ACCESS_WRITE => VmExit::MsrWrite {
219+
msr_index: msr_message.msr_number,
220+
value: written_value,
221+
},
222+
_ => VmExit::Unknown(format!("Unknown MSR access type={}", access)),
223+
}
224+
}
184225
#[cfg(gdb)]
185226
EXCEPTION_INTERCEPT => {
186227
let exception_message = m
@@ -203,6 +244,17 @@ impl Vm for MshvVm {
203244
Ok(result)
204245
}
205246

247+
fn enable_msr_intercept(&mut self) -> Result<()> {
248+
// Install MSR intercepts, will interrupt on all MSR accesses (READ & WRITE)
249+
let intercept = mshv_install_intercept {
250+
access_type_mask: HV_INTERCEPT_ACCESS_MASK_WRITE | HV_INTERCEPT_ACCESS_MASK_READ,
251+
intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_X64_MSR,
252+
intercept_parameter: Default::default(),
253+
};
254+
self.vm_fd.install_intercept(intercept)?;
255+
Ok(())
256+
}
257+
206258
// -- DEBUGGING RELATED BELOW ---
207259

208260
#[cfg(gdb)]

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ use super::vm::{Vm, VmExit};
3434
#[cfg(not(gdb))]
3535
use super::vm::{Vm, VmExit};
3636
use super::wrappers::HandleWrapper;
37-
use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
37+
use crate::hypervisor::regs::{
38+
CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters,
39+
};
3840
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
3941
use crate::{Result, log_then_return, new_error};
4042

@@ -104,6 +106,7 @@ impl WhpVm {
104106
&NUM_CPU as *const _ as *const _,
105107
std::mem::size_of_val(&NUM_CPU) as _,
106108
)?;
109+
107110
WHvSetupPartition(partition)?;
108111
WHvCreateVirtualProcessor(partition, 0, 0)?;
109112
partition
@@ -392,6 +395,22 @@ impl Vm for WhpVm {
392395
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
393396
}
394397

398+
fn xsave(&self) -> Result<Vec<u8>> {
399+
todo!()
400+
}
401+
402+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
403+
todo!()
404+
}
405+
406+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
407+
todo!()
408+
}
409+
410+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
411+
todo!()
412+
}
413+
395414
#[expect(non_upper_case_globals, reason = "Windows API constant are lower case")]
396415
fn run_vcpu(&mut self) -> Result<VmExit> {
397416
let mut exit_context: WHV_RUN_VP_EXIT_CONTEXT = Default::default();
@@ -438,6 +457,23 @@ impl Vm for WhpVm {
438457
}
439458
// Execution was cancelled by the host.
440459
WHvRunVpExitReasonCanceled => VmExit::Cancelled(),
460+
// MSR access (read or write) - we configured all MSR writes to cause exits
461+
WHvRunVpExitReasonX64MsrAccess => {
462+
let msr_access = unsafe { exit_context.Anonymous.MsrAccess };
463+
let eax = msr_access.Rax;
464+
let edx = msr_access.Rdx;
465+
let written_value = (edx << 32) | eax;
466+
let access = unsafe { msr_access.AccessInfo.AsUINT32 } as u32;
467+
// Missing from rust bindings for some reason, see https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/265f685159dfd3b2fae8d1dcf1b7d206c31ee880/virtualization/api/hypervisor-platform/headers/WinHvPlatformDefs.h#L3020
468+
match access {
469+
0 => VmExit::MsrRead(msr_access.MsrNumber),
470+
1 => VmExit::MsrWrite {
471+
msr_index: msr_access.MsrNumber,
472+
value: written_value,
473+
},
474+
_ => VmExit::Unknown(format!("Unknown MSR access type={}", access)),
475+
}
476+
}
441477
#[cfg(gdb)]
442478
WHvRunVpExitReasonException => {
443479
let exception = unsafe { exit_context.Anonymous.VpException };
@@ -471,6 +507,26 @@ impl Vm for WhpVm {
471507
Ok(result)
472508
}
473509

510+
fn enable_msr_intercept(&mut self) -> Result<()> {
511+
// Enable MSR exits through Extended VM Exits
512+
// Note: This must be set BEFORE WHvSetupPartition for WHP, so this implementation
513+
// is a no-op since the setup is already done in the constructor.
514+
// For WHP, MSR intercepts must be enabled during partition creation.
515+
// X64MsrExit bit position, missing from rust bindings for some reason.
516+
// See https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/265f685159dfd3b2fae8d1dcf1b7d206c31ee880/virtualization/api/hypervisor-platform/headers/WinHvPlatformDefs.h#L1495
517+
let mut extended_exits_property = WHV_PARTITION_PROPERTY::default();
518+
extended_exits_property.ExtendedVmExits.AsUINT64 = 1 << 1;
519+
unsafe {
520+
WHvSetPartitionProperty(
521+
self.partition,
522+
WHvPartitionPropertyCodeExtendedVmExits,
523+
&extended_exits_property as *const _ as *const _,
524+
std::mem::size_of::<WHV_PARTITION_PROPERTY>() as _,
525+
)?;
526+
}
527+
Ok(())
528+
}
529+
474530
#[cfg(gdb)]
475531
fn translate_gva(&self, gva: u64) -> Result<u64> {
476532
let mut gpa = 0;

0 commit comments

Comments
 (0)