Skip to content

Commit 91ba9e0

Browse files
committed
MSR
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 09fdc1c commit 91ba9e0

File tree

13 files changed

+514
-17
lines changed

13 files changed

+514
-17
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}")]
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}")]
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: 35 additions & 5 deletions
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
#[cfg(target_os = "windows")]
5151
use crate::hypervisor::wrappers::HandleWrapper;
5252
use crate::hypervisor::{HyperlightExit, InterruptHandle, InterruptHandleImpl, get_max_log_level};
@@ -80,6 +80,9 @@ pub(crate) struct HyperlightVm {
8080
next_slot: u32, // Monotonically increasing slot number
8181
freed_slots: Vec<u32>, // Reusable slots from unmapped regions
8282

83+
// pml4 saved to be able to restore it if needed
84+
#[cfg(feature = "init-paging")]
85+
pml4_addr: u64,
8386
#[cfg(gdb)]
8487
gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
8588
#[cfg(gdb)]
@@ -99,7 +102,7 @@ impl HyperlightVm {
99102
_pml4_addr: u64,
100103
entrypoint: u64,
101104
rsp: u64,
102-
#[cfg_attr(not(any(kvm, mshv3)), allow(unused_variables))] config: &SandboxConfiguration,
105+
config: &SandboxConfiguration,
103106
#[cfg(target_os = "windows")] handle: HandleWrapper,
104107
#[cfg(target_os = "windows")] raw_size: usize,
105108
#[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
@@ -122,6 +125,11 @@ impl HyperlightVm {
122125
None => return Err(NoHypervisorFound()),
123126
};
124127

128+
// MSR intercept can be unsafely disabled by the user in the config
129+
if !config.get_allow_msr() {
130+
vm.enable_msr_intercept()?;
131+
}
132+
125133
for (i, region) in mem_regions.iter().enumerate() {
126134
// Safety: slots are unique and region points to valid memory since we created the regions
127135
unsafe { vm.map_memory((i as u32, region))? };
@@ -191,6 +199,8 @@ impl HyperlightVm {
191199
mmap_regions: Vec::new(),
192200
freed_slots: Vec::new(),
193201

202+
#[cfg(feature = "init-paging")]
203+
pml4_addr: _pml4_addr,
194204
#[cfg(gdb)]
195205
gdb_conn,
196206
#[cfg(gdb)]
@@ -273,9 +283,6 @@ impl HyperlightVm {
273283
};
274284
self.vm.set_regs(&regs)?;
275285

276-
// reset fpu
277-
self.vm.set_fpu(&CommonFpu::default())?;
278-
279286
self.run(
280287
mem_mgr,
281288
host_funcs,
@@ -497,6 +504,12 @@ impl HyperlightVm {
497504
}
498505
}
499506
}
507+
Ok(HyperlightExit::MsrRead(msr_index)) => {
508+
break Err(HyperlightError::MsrReadViolation(msr_index));
509+
}
510+
Ok(HyperlightExit::MsrWrite { msr_index, value }) => {
511+
break Err(HyperlightError::MsrWriteViolation(msr_index, value));
512+
}
500513
Ok(HyperlightExit::Cancelled()) => {
501514
// If cancellation was not requested for this specific guest function call,
502515
// the vcpu was interrupted by a stale cancellation. This can occur when:
@@ -565,6 +578,23 @@ impl HyperlightVm {
565578
self.interrupt_handle.clear_cancel();
566579
}
567580

581+
pub(crate) fn reset_vcpu(&self) -> Result<()> {
582+
self.vm.set_regs(&CommonRegisters::default())?;
583+
#[cfg(feature = "init-paging")]
584+
self.vm
585+
.set_sregs(&CommonSpecialRegisters::standard_64bit_defaults(
586+
self.pml4_addr,
587+
))?;
588+
#[cfg(not(feature = "init-paging"))]
589+
self.vm
590+
.set_sregs(&CommonSpecialRegisters::standard_real_mode_defaults())?;
591+
self.vm.set_fpu(&CommonFpu::default())?;
592+
self.vm.set_xsave(&[0; 1024])?;
593+
self.vm.set_debug_regs(&CommonDebugRegs::default())?;
594+
// MSRs don't need to be reset as they cannot be modified by guest (unless unsafely-allowed)
595+
Ok(())
596+
}
597+
568598
#[cfg(gdb)]
569599
fn handle_debug(
570600
&mut self,

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,23 @@ use std::sync::LazyLock;
2121
#[cfg(gdb)]
2222
use mshv_bindings::{DebugRegisters, hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT};
2323
use mshv_bindings::{
24-
FloatingPointUnit, SpecialRegisters, StandardRegisters, hv_message_type,
24+
FloatingPointUnit, HV_INTERCEPT_ACCESS_MASK_READ, HV_INTERCEPT_ACCESS_MASK_WRITE,
25+
HV_INTERCEPT_ACCESS_READ, HV_INTERCEPT_ACCESS_WRITE, SpecialRegisters, StandardRegisters,
26+
XSave, hv_intercept_type_HV_INTERCEPT_TYPE_X64_MSR, hv_message_type,
2527
hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA,
2628
hv_message_type_HVMSG_X64_HALT, hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT,
29+
hv_message_type_HVMSG_X64_MSR_INTERCEPT,
2730
hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
2831
hv_partition_synthetic_processor_features, hv_register_assoc,
29-
hv_register_name_HV_X64_REGISTER_RIP, hv_register_value, mshv_user_mem_region,
32+
hv_register_name_HV_X64_REGISTER_RIP, hv_register_value, mshv_install_intercept,
33+
mshv_user_mem_region,
3034
};
3135
use mshv_ioctls::{Mshv, VcpuFd, VmFd};
3236
use tracing::{Span, instrument};
3337

3438
#[cfg(gdb)]
3539
use crate::hypervisor::gdb::DebuggableVm;
40+
use crate::hypervisor::regs::CommonDebugRegs;
3641
use crate::hypervisor::{HyperlightExit, Hypervisor};
3742
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
3843
use crate::{Result, new_error};
@@ -108,6 +113,7 @@ impl Hypervisor for MshvVm {
108113
hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
109114
const UNMAPPED_GPA_MESSAGE: hv_message_type = hv_message_type_HVMSG_UNMAPPED_GPA;
110115
const INVALID_GPA_ACCESS_MESSAGE: hv_message_type = hv_message_type_HVMSG_GPA_INTERCEPT;
116+
const MSR_MESSAGE: hv_message_type = hv_message_type_HVMSG_X64_MSR_INTERCEPT;
111117
#[cfg(gdb)]
112118
const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
113119

@@ -152,6 +158,21 @@ impl Hypervisor for MshvVm {
152158
_ => HyperlightExit::Unknown("Unknown MMIO access".to_string()),
153159
}
154160
}
161+
MSR_MESSAGE => {
162+
let msr_message = m.to_msr_info().map_err(mshv_ioctls::MshvError::from)?;
163+
let edx = msr_message.rdx;
164+
let eax = msr_message.rax;
165+
let written_value = (edx << 32) | eax;
166+
let access = msr_message.header.intercept_access_type as u32;
167+
match access {
168+
HV_INTERCEPT_ACCESS_READ => HyperlightExit::MsrRead(msr_message.msr_number),
169+
HV_INTERCEPT_ACCESS_WRITE => HyperlightExit::MsrWrite {
170+
msr_index: msr_message.msr_number,
171+
value: written_value,
172+
},
173+
_ => HyperlightExit::Unknown(format!("Unknown MSR access type={}", access)),
174+
}
175+
}
155176
#[cfg(gdb)]
156177
EXCEPTION_INTERCEPT => {
157178
let ex_info = m
@@ -213,6 +234,38 @@ impl Hypervisor for MshvVm {
213234
let xsave = self.vcpu_fd.get_xsave()?;
214235
Ok(xsave.buffer.to_vec())
215236
}
237+
238+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
239+
let mut buf = XSave::default();
240+
let (prefix, bytes, suffix) = unsafe { xsave.align_to() };
241+
assert!(prefix.is_empty());
242+
assert!(suffix.is_empty());
243+
buf.buffer.copy_from_slice(bytes);
244+
self.vcpu_fd.set_xsave(&buf)?;
245+
Ok(())
246+
}
247+
248+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
249+
let debug_regs = self.vcpu_fd.get_debug_regs()?;
250+
Ok(debug_regs.into())
251+
}
252+
253+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
254+
let mshv_debug_regs = drs.into();
255+
self.vcpu_fd.set_debug_regs(&mshv_debug_regs)?;
256+
Ok(())
257+
}
258+
259+
fn enable_msr_intercept(&mut self) -> Result<()> {
260+
// Install MSR intercepts, will interrupt on all MSR accesses (READ & WRITE)
261+
let intercept = mshv_install_intercept {
262+
access_type_mask: HV_INTERCEPT_ACCESS_MASK_WRITE | HV_INTERCEPT_ACCESS_MASK_READ,
263+
intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_X64_MSR,
264+
intercept_parameter: Default::default(),
265+
};
266+
self.vm_fd.install_intercept(intercept)?;
267+
Ok(())
268+
}
216269
}
217270

218271
#[cfg(gdb)]

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use super::wrappers::HandleWrapper;
3333
#[cfg(gdb)]
3434
use crate::hypervisor::gdb::DebuggableVm;
3535
use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
36-
use crate::hypervisor::{HyperlightExit, Hypervisor};
36+
use crate::hypervisor::{CommonDebugRegs, HyperlightExit, Hypervisor};
3737
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
3838
use crate::{Result, log_then_return, new_error};
3939

@@ -266,6 +266,23 @@ impl Hypervisor for WhpVm {
266266
}
267267
// Execution was cancelled by the host.
268268
WHvRunVpExitReasonCanceled => HyperlightExit::Cancelled(),
269+
// MSR access (read or write) - we configured all MSR writes to cause exits
270+
WHvRunVpExitReasonX64MsrAccess => {
271+
let msr_access = unsafe { exit_context.Anonymous.MsrAccess };
272+
let eax = msr_access.Rax;
273+
let edx = msr_access.Rdx;
274+
let written_value = (edx << 32) | eax;
275+
let access = unsafe { msr_access.AccessInfo.AsUINT32 };
276+
// Missing from rust bindings for some reason, see https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/265f685159dfd3b2fae8d1dcf1b7d206c31ee880/virtualization/api/hypervisor-platform/headers/WinHvPlatformDefs.h#L3020
277+
match access {
278+
0 => HyperlightExit::MsrRead(msr_access.MsrNumber),
279+
1 => HyperlightExit::MsrWrite {
280+
msr_index: msr_access.MsrNumber,
281+
value: written_value,
282+
},
283+
_ => HyperlightExit::Unknown(format!("Unknown MSR access type={}", access)),
284+
}
285+
}
269286
#[cfg(gdb)]
270287
WHvRunVpExitReasonException => {
271288
let exception = unsafe { exit_context.Anonymous.VpException };
@@ -452,6 +469,38 @@ impl Hypervisor for WhpVm {
452469
Ok(xsave_buffer)
453470
}
454471

472+
fn set_xsave(&self, _xsave: &[u32; 1024]) -> Result<()> {
473+
todo!()
474+
}
475+
476+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
477+
todo!()
478+
}
479+
480+
fn set_debug_regs(&self, _drs: &CommonDebugRegs) -> Result<()> {
481+
todo!()
482+
}
483+
484+
fn enable_msr_intercept(&mut self) -> Result<()> {
485+
// Enable MSR exits through Extended VM Exits
486+
// Note: This must be set BEFORE WHvSetupPartition for WHP, so this implementation
487+
// is a no-op since the setup is already done in the constructor.
488+
// For WHP, MSR intercepts must be enabled during partition creation.
489+
// X64MsrExit bit position, missing from rust bindings for some reason.
490+
// See https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/265f685159dfd3b2fae8d1dcf1b7d206c31ee880/virtualization/api/hypervisor-platform/headers/WinHvPlatformDefs.h#L1495
491+
let mut extended_exits_property = WHV_PARTITION_PROPERTY::default();
492+
extended_exits_property.ExtendedVmExits.AsUINT64 = 1 << 1;
493+
unsafe {
494+
WHvSetPartitionProperty(
495+
self.partition,
496+
WHvPartitionPropertyCodeExtendedVmExits,
497+
&extended_exits_property as *const _ as *const _,
498+
std::mem::size_of::<WHV_PARTITION_PROPERTY>() as _,
499+
)?;
500+
}
501+
Ok(())
502+
}
503+
455504
/// Mark that initial memory setup is complete. After this, map_memory will fail.
456505
fn complete_initial_memory_setup(&mut self) {
457506
self.initial_memory_setup_done = true;

0 commit comments

Comments
 (0)