Skip to content

Commit aeca04d

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

File tree

10 files changed

+392
-14
lines changed

10 files changed

+392
-14
lines changed

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: 17 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;
@@ -532,6 +532,12 @@ impl HyperlightVm {
532532
}
533533
}
534534
}
535+
Ok(VmExit::MsrRead(msr_index)) => {
536+
break Err(HyperlightError::MsrReadViolation(msr_index));
537+
}
538+
Ok(VmExit::MsrWrite { msr_index, value }) => {
539+
break Err(HyperlightError::MsrWriteViolation(msr_index, value));
540+
}
535541
Ok(VmExit::Cancelled()) => {
536542
// If cancellation was not requested for this specific guest function call,
537543
// the vcpu was interrupted by a stale cancellation from a previous call
@@ -592,6 +598,16 @@ impl HyperlightVm {
592598
self.interrupt_handle.clone()
593599
}
594600

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

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 54 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};
@@ -81,8 +87,15 @@ impl MshvVm {
8187
vm_fd
8288
};
8389

84-
let vcpu_fd = vm_fd.create_vcpu(0)?;
90+
// Install MSR intercepts, will interrupt on all MSR accesses (READ & WRITE)
91+
let intercept = mshv_install_intercept {
92+
access_type_mask: HV_INTERCEPT_ACCESS_MASK_WRITE | HV_INTERCEPT_ACCESS_MASK_READ,
93+
intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_X64_MSR,
94+
intercept_parameter: Default::default(),
95+
};
96+
vm_fd.install_intercept(intercept)?;
8597

98+
let vcpu_fd = vm_fd.create_vcpu(0)?;
8699
Ok(Self { vm_fd, vcpu_fd })
87100
}
88101
}
@@ -114,12 +127,32 @@ impl Vm for MshvVm {
114127
Ok(())
115128
}
116129

117-
#[cfg(crashdump)]
118130
fn xsave(&self) -> Result<Vec<u8>> {
119131
let xsave = self.vcpu_fd.get_xsave()?;
120132
Ok(xsave.buffer.to_vec())
121133
}
122134

135+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
136+
let mut buf = XSave::default();
137+
let (prefix, bytes, suffix) = unsafe { xsave.align_to() };
138+
assert!(prefix.is_empty());
139+
assert!(suffix.is_empty());
140+
buf.buffer.copy_from_slice(bytes);
141+
self.vcpu_fd.set_xsave(&buf)?;
142+
Ok(())
143+
}
144+
145+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
146+
let debug_regs = self.vcpu_fd.get_debug_regs()?;
147+
Ok(debug_regs.into())
148+
}
149+
150+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
151+
let mshv_debug_regs = drs.into();
152+
self.vcpu_fd.set_debug_regs(&mshv_debug_regs)?;
153+
Ok(())
154+
}
155+
123156
/// # Safety
124157
/// The caller must ensure that the memory region is valid and points to valid memory,
125158
/// and lives long enough for the VM to use it.
@@ -140,6 +173,7 @@ impl Vm for MshvVm {
140173
const IO_PORT: hv_message_type = hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
141174
const UNMAPPED_GPA: hv_message_type = hv_message_type_HVMSG_UNMAPPED_GPA;
142175
const INVALID_GPA: hv_message_type = hv_message_type_HVMSG_GPA_INTERCEPT;
176+
const MSR: hv_message_type = hv_message_type_HVMSG_X64_MSR_INTERCEPT;
143177
#[cfg(gdb)]
144178
const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
145179

@@ -181,6 +215,21 @@ impl Vm for MshvVm {
181215
_ => VmExit::Unknown("Unknown MMIO access".to_string()),
182216
}
183217
}
218+
MSR => {
219+
let msr_message = m.to_msr_info().map_err(mshv_ioctls::MshvError::from)?;
220+
let edx = msr_message.rdx;
221+
let eax = msr_message.rax;
222+
let written_value = (edx << 32) | eax;
223+
let access = msr_message.header.intercept_access_type as u32;
224+
match access {
225+
HV_INTERCEPT_ACCESS_READ => VmExit::MsrRead(msr_message.msr_number),
226+
HV_INTERCEPT_ACCESS_WRITE => VmExit::MsrWrite {
227+
msr_index: msr_message.msr_number,
228+
value: written_value,
229+
},
230+
_ => VmExit::Unknown(format!("Unknown MSR access type={}", access)),
231+
}
232+
}
184233
#[cfg(gdb)]
185234
EXCEPTION_INTERCEPT => {
186235
let exception_message = m

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 48 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,18 @@ impl WhpVm {
104106
&NUM_CPU as *const _ as *const _,
105107
std::mem::size_of_val(&NUM_CPU) as _,
106108
)?;
109+
110+
// Enable MSR exits through Extended VM Exits - must be set BEFORE WHvSetupPartition
111+
let mut extended_exits_property = WHV_PARTITION_PROPERTY::default();
112+
// X64MsrExit bit position, missing from rust bindings for some reason. See https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/265f685159dfd3b2fae8d1dcf1b7d206c31ee880/virtualization/api/hypervisor-platform/headers/WinHvPlatformDefs.h#L1495
113+
extended_exits_property.ExtendedVmExits.AsUINT64 = 1 << 1;
114+
WHvSetPartitionProperty(
115+
partition,
116+
WHvPartitionPropertyCodeExtendedVmExits,
117+
&extended_exits_property as *const _ as *const _,
118+
std::mem::size_of::<WHV_PARTITION_PROPERTY>() as _,
119+
)?;
120+
107121
WHvSetupPartition(partition)?;
108122
WHvCreateVirtualProcessor(partition, 0, 0)?;
109123
partition
@@ -392,6 +406,22 @@ impl Vm for WhpVm {
392406
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
393407
}
394408

409+
fn xsave(&self) -> Result<Vec<u8>> {
410+
todo!()
411+
}
412+
413+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
414+
todo!()
415+
}
416+
417+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
418+
todo!()
419+
}
420+
421+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
422+
todo!()
423+
}
424+
395425
#[expect(non_upper_case_globals, reason = "Windows API constant are lower case")]
396426
fn run_vcpu(&mut self) -> Result<VmExit> {
397427
let mut exit_context: WHV_RUN_VP_EXIT_CONTEXT = Default::default();
@@ -438,6 +468,23 @@ impl Vm for WhpVm {
438468
}
439469
// Execution was cancelled by the host.
440470
WHvRunVpExitReasonCanceled => VmExit::Cancelled(),
471+
// MSR access (read or write) - we configured all MSR writes to cause exits
472+
WHvRunVpExitReasonX64MsrAccess => {
473+
let msr_access = unsafe { exit_context.Anonymous.MsrAccess };
474+
let eax = msr_access.Rax;
475+
let edx = msr_access.Rdx;
476+
let written_value = (edx << 32) | eax;
477+
let access = unsafe { msr_access.AccessInfo.AsUINT32 } as u32;
478+
// Missing from rust bindings for some reason, see https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/265f685159dfd3b2fae8d1dcf1b7d206c31ee880/virtualization/api/hypervisor-platform/headers/WinHvPlatformDefs.h#L3020
479+
match access {
480+
0 => VmExit::MsrRead(msr_access.MsrNumber),
481+
1 => VmExit::MsrWrite {
482+
msr_index: msr_access.MsrNumber,
483+
value: written_value,
484+
},
485+
_ => VmExit::Unknown(format!("Unknown MSR access type={}", access)),
486+
}
487+
}
441488
#[cfg(gdb)]
442489
WHvRunVpExitReasonException => {
443490
let exception = unsafe { exit_context.Anonymous.VpException };

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ use std::sync::LazyLock;
1818

1919
#[cfg(gdb)]
2020
use kvm_bindings::kvm_guest_debug;
21-
use kvm_bindings::kvm_userspace_memory_region;
22-
use kvm_ioctls::Cap::UserMemory;
21+
use kvm_bindings::{kvm_debugregs, kvm_userspace_memory_region, kvm_xsave};
22+
use kvm_ioctls::Cap::{self, UserMemory};
2323
use kvm_ioctls::{Kvm, VcpuExit, VcpuFd, VmFd};
2424
use tracing::{Span, instrument};
2525

26-
use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
26+
use crate::hypervisor::regs::{
27+
CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters,
28+
};
2729
use crate::hypervisor::vm::{Vm, VmExit};
2830
use crate::mem::memory_region::MemoryRegion;
2931
use crate::{Result, new_error};
@@ -107,7 +109,6 @@ impl Vm for KvmVm {
107109
Ok(self.vcpu_fd.set_fpu(&fpu.into())?)
108110
}
109111

110-
#[cfg(crashdump)]
111112
fn xsave(&self) -> Result<Vec<u8>> {
112113
let xsave = self.vcpu_fd.get_xsave()?;
113114
Ok(xsave
@@ -117,6 +118,34 @@ impl Vm for KvmVm {
117118
.collect())
118119
}
119120

121+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
122+
if self.vm_fd.check_extension_int(Cap::Xsave2) as usize != xsave.len() * size_of::<u32>() {
123+
return Err(new_error!(
124+
"KVM_CAP_XSAVE2 not supported: {}",
125+
self.vm_fd.check_extension_int(Cap::Xsave2)
126+
));
127+
}
128+
let xsave = kvm_xsave {
129+
region: *xsave,
130+
..Default::default()
131+
};
132+
// we made sure above that SET_XSAVE only copies 4096 bytes
133+
unsafe { self.vcpu_fd.set_xsave(&xsave)? };
134+
135+
Ok(())
136+
}
137+
138+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
139+
let kvm_debug_regs = self.vcpu_fd.get_debug_regs()?;
140+
Ok(kvm_debug_regs.into())
141+
}
142+
143+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
144+
let kvm_debug_regs: kvm_debugregs = drs.into();
145+
self.vcpu_fd.set_debug_regs(&kvm_debug_regs)?;
146+
Ok(())
147+
}
148+
120149
unsafe fn map_memory(&mut self, (slot, region): (u32, &MemoryRegion)) -> Result<()> {
121150
let mut kvm_region: kvm_userspace_memory_region = region.into();
122151
kvm_region.slot = slot;
@@ -258,3 +287,16 @@ impl Vm for KvmVm {
258287
Ok(())
259288
}
260289
}
290+
291+
#[test]
292+
fn test() {
293+
let msr_feature_index_list = KVM.as_ref().unwrap().get_msr_index_list().unwrap();
294+
msr_feature_index_list.as_slice().iter().for_each(|msr| {
295+
println!("Writable MSR: 0x{:08X}", msr);
296+
});
297+
println!(
298+
"Total Writable MSRs: {}",
299+
msr_feature_index_list.as_slice().len()
300+
);
301+
// println!("msr_feature_index_list: {:?}", msr_feature_index_list);
302+
}

src/hyperlight_host/src/hypervisor/regs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
mod debug_regs;
1718
mod fpu;
1819
mod special_regs;
1920
mod standard_regs;
2021

2122
#[cfg(target_os = "windows")]
2223
use std::collections::HashSet;
2324

25+
pub(crate) use debug_regs::*;
2426
pub(crate) use fpu::*;
2527
pub(crate) use special_regs::*;
2628
pub(crate) use standard_regs::*;

0 commit comments

Comments
 (0)