Skip to content

Commit 0c35d10

Browse files
committed
MSR
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 350b8e0 commit 0c35d10

File tree

10 files changed

+392
-13
lines changed

10 files changed

+392
-13
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 & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ use std::sync::LazyLock;
2222
use mshv_bindings::DebugRegisters;
2323
#[cfg(gdb)]
2424
use mshv_bindings::hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
25+
use mshv_bindings::hv_message_type_HVMSG_X64_MSR_INTERCEPT;
26+
use mshv_bindings::{XSave, mshv_install_intercept};
2527
use mshv_bindings::{
26-
hv_message_type, hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA,
28+
hv_intercept_parameters, hv_intercept_type_HV_INTERCEPT_TYPE_X64_MSR_INDEX, hv_message_type,
29+
hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA,
2730
hv_message_type_HVMSG_X64_HALT, hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT,
2831
hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
2932
hv_partition_synthetic_processor_features, hv_register_assoc,
@@ -32,7 +35,9 @@ use mshv_bindings::{
3235
use mshv_ioctls::{Mshv, VcpuFd, VmFd};
3336
use tracing::{Span, instrument};
3437

35-
use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
38+
use crate::hypervisor::regs::{
39+
CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters,
40+
};
3641
use crate::hypervisor::vm::{Vm, VmExit};
3742
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
3843
use crate::{Result, new_error};
@@ -83,8 +88,21 @@ impl MshvVm {
8388
vm_fd
8489
};
8590

86-
let vcpu_fd = vm_fd.create_vcpu(0)?;
91+
let intercept = mshv_install_intercept {
92+
access_type_mask: mshv_bindings::HV_INTERCEPT_ACCESS_MASK_WRITE, // | mshv_bindings::HV_INTERCEPT_ACCESS_MASK_READ to work
93+
intercept_type: mshv_bindings::hv_intercept_type_HV_INTERCEPT_TYPE_X64_MSR,
94+
intercept_parameter: Default::default(),
95+
};
96+
vm_fd.install_intercept(intercept)?;
97+
98+
// let intercept = mshv_install_intercept {
99+
// access_type_mask: mshv_bindings::HV_INTERCEPT_ACCESS_MASK_NONE,
100+
// intercept_type: mshv_bindings::hv_intercept_type_HV_INTERCEPT_TYPE_X64_MSR,
101+
// intercept_parameter: Default::default(),
102+
// };
103+
// vm_fd.install_intercept(intercept)?;
87104

105+
let vcpu_fd = vm_fd.create_vcpu(0)?;
88106
Ok(Self { vm_fd, vcpu_fd })
89107
}
90108
}
@@ -116,12 +134,32 @@ impl Vm for MshvVm {
116134
Ok(())
117135
}
118136

119-
#[cfg(crashdump)]
120137
fn xsave(&self) -> Result<Vec<u8>> {
121138
let xsave = self.vcpu_fd.get_xsave()?;
122139
Ok(xsave.buffer.to_vec())
123140
}
124141

142+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
143+
let mut buf = XSave::default();
144+
let (prefix, bytes, suffix) = unsafe { xsave.align_to() };
145+
assert!(prefix.is_empty());
146+
assert!(suffix.is_empty());
147+
buf.buffer.copy_from_slice(bytes);
148+
self.vcpu_fd.set_xsave(&buf)?;
149+
Ok(())
150+
}
151+
152+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
153+
let debug_regs = self.vcpu_fd.get_debug_regs()?;
154+
Ok(debug_regs.into())
155+
}
156+
157+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
158+
let mshv_debug_regs = drs.into();
159+
self.vcpu_fd.set_debug_regs(&mshv_debug_regs)?;
160+
Ok(())
161+
}
162+
125163
/// # Safety
126164
/// The caller must ensure that the memory region is valid and points to valid memory,
127165
/// and lives long enough for the VM to use it.
@@ -142,6 +180,7 @@ impl Vm for MshvVm {
142180
const IO_PORT: hv_message_type = hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
143181
const UNMAPPED_GPA: hv_message_type = hv_message_type_HVMSG_UNMAPPED_GPA;
144182
const INVALID_GPA: hv_message_type = hv_message_type_HVMSG_GPA_INTERCEPT;
183+
const MSR: hv_message_type = hv_message_type_HVMSG_X64_MSR_INTERCEPT;
145184
#[cfg(gdb)]
146185
const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
147186

@@ -183,6 +222,17 @@ impl Vm for MshvVm {
183222
_ => VmExit::Unknown("Unknown MMIO access".to_string()),
184223
}
185224
}
225+
MSR => {
226+
let msr_message = m.to_msr_info().map_err(mshv_ioctls::MshvError::from)?;
227+
let read = msr_message.header.intercept_access_type;
228+
let msr_number = msr_message.msr_number;
229+
let rdx = msr_message.rdx;
230+
let rax = msr_message.rax;
231+
panic!(
232+
"MSR access: msr_number=0x{:X}, read={}, rdx=0x{:X}, rax=0x{:X}",
233+
msr_number, read, rdx, rax
234+
);
235+
}
186236
#[cfg(gdb)]
187237
EXCEPTION_INTERCEPT => {
188238
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
@@ -346,6 +360,22 @@ impl Vm for WhpVm {
346360
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
347361
}
348362

363+
fn xsave(&self) -> Result<Vec<u8>> {
364+
todo!()
365+
}
366+
367+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
368+
todo!()
369+
}
370+
371+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
372+
todo!()
373+
}
374+
375+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
376+
todo!()
377+
}
378+
349379
#[expect(non_upper_case_globals, reason = "Windows API constant are lower case")]
350380
fn run_vcpu(&mut self) -> Result<VmExit> {
351381
let mut exit_context: WHV_RUN_VP_EXIT_CONTEXT = Default::default();
@@ -392,6 +422,23 @@ impl Vm for WhpVm {
392422
}
393423
// Execution was cancelled by the host.
394424
WHvRunVpExitReasonCanceled => VmExit::Cancelled(),
425+
// MSR access (read or write) - we configured all MSR writes to cause exits
426+
WHvRunVpExitReasonX64MsrAccess => {
427+
let msr_access = unsafe { exit_context.Anonymous.MsrAccess };
428+
let eax = msr_access.Rax;
429+
let edx = msr_access.Rdx;
430+
let written_value = (edx << 32) | eax;
431+
let access = unsafe { msr_access.AccessInfo.AsUINT32 } as u32;
432+
// Missing from rust bindings for some reason, see https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/265f685159dfd3b2fae8d1dcf1b7d206c31ee880/virtualization/api/hypervisor-platform/headers/WinHvPlatformDefs.h#L3020
433+
match access {
434+
0 => VmExit::MsrRead(msr_access.MsrNumber),
435+
1 => VmExit::MsrWrite {
436+
msr_index: msr_access.MsrNumber,
437+
value: written_value,
438+
},
439+
_ => VmExit::Unknown(format!("Unknown MSR access type={}", access)),
440+
}
441+
}
395442
#[cfg(gdb)]
396443
WHvRunVpExitReasonException => {
397444
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)