Skip to content

Commit e859ff9

Browse files
committed
MSR
Signed-off-by: Ludvig Liljenberg <[email protected]> Implement windows Signed-off-by: Ludvig Liljenberg <[email protected]> [clean this commit] add test Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 5bfe0bf commit e859ff9

File tree

14 files changed

+983
-130
lines changed

14 files changed

+983
-130
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 = { git = "https://github.com/rust-vmm/kvm", rev = "25f630aa384dad1306b288ec5eed3312f84e3393", features = ["fam-wrappers"], optional = true }
79+
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm", rev = "25f630aa384dad1306b288ec5eed3312f84e3393", 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: 223 additions & 4 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};
@@ -86,6 +86,9 @@ pub(crate) struct HyperlightVm {
8686
next_slot: u32, // Monotonically increasing slot number
8787
freed_slots: Vec<u32>, // Reusable slots from unmapped regions
8888

89+
// pml4 saved to be able to restore it if needed
90+
#[cfg(feature = "init-paging")]
91+
pml4_addr: u64,
8992
#[cfg(gdb)]
9093
gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
9194
#[cfg(gdb)]
@@ -94,6 +97,11 @@ pub(crate) struct HyperlightVm {
9497
trace_info: MemTraceInfo,
9598
#[cfg(crashdump)]
9699
rt_cfg: SandboxRuntimeConfig,
100+
// Windows does not support just "zeroing" out the xsave area - we need to preserve the XCOMP_BV.
101+
// Since it varies by hardware, we can't just hardcode a value, so we save it here.
102+
// Also note that windows uses compacted format for xsave.
103+
#[cfg(target_os = "windows")]
104+
xcomp_bv: u64,
97105
}
98106

99107
impl HyperlightVm {
@@ -128,6 +136,11 @@ impl HyperlightVm {
128136
None => return Err(NoHypervisorFound()),
129137
};
130138

139+
// MSR intercept can be unsafely disabled by the user in the config
140+
if !config.get_allow_msr() {
141+
vm.enable_msr_intercept()?;
142+
}
143+
131144
for (i, region) in mem_regions.iter().enumerate() {
132145
// Safety: slots are unique and region points to valid memory since we created the regions
133146
unsafe { vm.map_memory((i as u32, region))? };
@@ -172,6 +185,17 @@ impl HyperlightVm {
172185
dropped: AtomicBool::new(false),
173186
});
174187

188+
// get xsave header for debugging
189+
#[cfg(target_os = "windows")]
190+
let xsave = vm.xsave()?;
191+
192+
#[cfg(target_os = "windows")]
193+
let xcomp_bv = if xsave.len() >= 528 {
194+
u64::from_le_bytes(xsave[520..528].try_into().unwrap())
195+
} else {
196+
return Err(new_error!("XSAVE buffer too small to contain XCOMP_BV"));
197+
};
198+
175199
#[cfg_attr(not(gdb), allow(unused_mut))]
176200
let mut ret = Self {
177201
vm,
@@ -185,6 +209,8 @@ impl HyperlightVm {
185209
mmap_regions: Vec::new(),
186210
freed_slots: Vec::new(),
187211

212+
#[cfg(feature = "init-paging")]
213+
pml4_addr: _pml4_addr,
188214
#[cfg(gdb)]
189215
gdb_conn,
190216
#[cfg(gdb)]
@@ -193,6 +219,8 @@ impl HyperlightVm {
193219
trace_info,
194220
#[cfg(crashdump)]
195221
rt_cfg,
222+
#[cfg(target_os = "windows")]
223+
xcomp_bv,
196224
};
197225

198226
// Send the interrupt handle to the GDB thread if debugging is enabled
@@ -332,9 +360,6 @@ impl HyperlightVm {
332360
};
333361
self.vm.set_regs(&regs)?;
334362

335-
// reset fpu
336-
self.vm.set_fpu(&CommonFpu::default())?;
337-
338363
self.run(
339364
mem_mgr,
340365
host_funcs,
@@ -487,6 +512,12 @@ impl HyperlightVm {
487512
}
488513
}
489514
}
515+
Ok(HyperlightExit::MsrRead(msr_index)) => {
516+
break Err(HyperlightError::MsrReadViolation(msr_index));
517+
}
518+
Ok(HyperlightExit::MsrWrite { msr_index, value }) => {
519+
break Err(HyperlightError::MsrWriteViolation(msr_index, value));
520+
}
490521
Ok(HyperlightExit::Cancelled()) => {
491522
// If cancellation was not requested for this specific guest function call,
492523
// the vcpu was interrupted by a stale cancellation. This can occur when:
@@ -581,6 +612,42 @@ impl HyperlightVm {
581612
Ok(())
582613
}
583614

615+
pub(crate) fn reset_vcpu(&self) -> Result<()> {
616+
self.vm.set_regs(&CommonRegisters::default())?;
617+
self.vm.set_fpu(&CommonFpu::default())?;
618+
self.vm.set_debug_regs(&CommonDebugRegs::default())?;
619+
620+
#[cfg(feature = "init-paging")]
621+
self.vm
622+
.set_sregs(&CommonSpecialRegisters::standard_64bit_defaults(
623+
self.pml4_addr,
624+
))?;
625+
#[cfg(not(feature = "init-paging"))]
626+
self.vm
627+
.set_sregs(&CommonSpecialRegisters::standard_real_mode_defaults())?;
628+
629+
// Windows does not support just "zeroing" out the xsave area - we need to preserve the XCOMP_BV.
630+
#[cfg(target_os = "windows")]
631+
{
632+
// The XSAVE header starts at offset 512.
633+
// Bytes 7:0 of the header is XSTATE_BV.
634+
// Bytes 15:8 of the header is XCOMP_BV.
635+
// So XCOMP_BV starts at offset 512 + 8 = 520.
636+
// We are using a u32 array, so the index is 520 / 4 = 130.
637+
const XCOMP_BV_OFFSET_U32: usize = 520 / std::mem::size_of::<u32>();
638+
639+
let mut xsave = [0u32; 1024];
640+
xsave[XCOMP_BV_OFFSET_U32] = self.xcomp_bv as u32;
641+
xsave[XCOMP_BV_OFFSET_U32 + 1] = (self.xcomp_bv >> 32) as u32;
642+
self.vm.set_xsave(&xsave)?;
643+
}
644+
#[cfg(not(target_os = "windows"))]
645+
self.vm.set_xsave(&[0; 1024])?;
646+
647+
// MSRs don't need to be reset as they cannot be modified by guest (unless unsafely-allowed)
648+
Ok(())
649+
}
650+
584651
// Handle a debug exit
585652
#[cfg(gdb)]
586653
fn handle_debug(
@@ -818,6 +885,158 @@ fn get_memory_access_violation<'a>(
818885
None
819886
}
820887

888+
#[cfg(test)]
889+
mod tests {
890+
use super::*;
891+
use crate::is_hypervisor_present;
892+
use crate::mem::layout::SandboxMemoryLayout;
893+
use crate::mem::mgr::SandboxMemoryManager;
894+
use crate::mem::ptr::RawPtr;
895+
use crate::mem::ptr_offset::Offset;
896+
use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory};
897+
use crate::sandbox::SandboxConfiguration;
898+
use crate::sandbox::host_funcs::FunctionRegistry;
899+
#[cfg(feature = "mem_profile")]
900+
use crate::sandbox::trace::DummyUnwindInfo;
901+
#[cfg(feature = "mem_profile")]
902+
use crate::sandbox::trace::MemTraceInfo;
903+
#[cfg(crashdump)]
904+
use crate::sandbox::uninitialized::SandboxRuntimeConfig;
905+
use std::sync::{Arc, Mutex};
906+
907+
#[test]
908+
fn test_reset_vcpu() -> Result<()> {
909+
if !is_hypervisor_present() {
910+
return Ok(());
911+
}
912+
913+
#[cfg(target_os = "windows")]
914+
return Ok(());
915+
916+
#[cfg(not(target_os = "windows"))]
917+
{
918+
let mut code = Vec::new();
919+
// mov rax, 0x1111111111111111
920+
code.extend_from_slice(&[0x48, 0xb8, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11]);
921+
// mov rbx, 0x2222222222222222
922+
code.extend_from_slice(&[0x48, 0xbb, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22]);
923+
// mov rcx, 0x3333333333333333
924+
code.extend_from_slice(&[0x48, 0xb9, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33]);
925+
926+
// hlt
927+
code.extend_from_slice(&[0xf4]);
928+
929+
let config: SandboxConfiguration = Default::default();
930+
#[cfg(crashdump)]
931+
let rt_cfg: SandboxRuntimeConfig = Default::default();
932+
#[cfg(feature = "mem_profile")]
933+
let trace_info = MemTraceInfo::new(Arc::new(DummyUnwindInfo {})).unwrap();
934+
935+
let layout = SandboxMemoryLayout::new(
936+
config.clone(),
937+
code.len(), // code size
938+
0, // stack
939+
0, // heap
940+
0, // init data
941+
None,
942+
)?;
943+
944+
let mem_size = layout.get_memory_size()?;
945+
let eshm = ExclusiveSharedMemory::new(mem_size)?;
946+
947+
let stack_cookie = [0u8; 16];
948+
let mem_mgr = SandboxMemoryManager::new(
949+
layout.clone(),
950+
eshm,
951+
RawPtr::from(0),
952+
Offset::from(0),
953+
stack_cookie,
954+
);
955+
956+
let (mut mem_mgr_hshm, mut mem_mgr_gshm) = mem_mgr.build();
957+
958+
// Set up shared memory (page tables)
959+
#[cfg(feature = "init-paging")]
960+
let rsp = {
961+
let mut regions = layout.get_memory_regions(&mem_mgr_gshm.shared_mem)?;
962+
let mem_size = mem_mgr_gshm.shared_mem.mem_size() as u64;
963+
mem_mgr_gshm.set_up_shared_memory(mem_size, &mut regions)?
964+
};
965+
#[cfg(not(feature = "init-paging"))]
966+
let rsp = 0;
967+
968+
// Write code
969+
let code_offset = layout.get_guest_code_offset();
970+
mem_mgr_hshm
971+
.shared_mem
972+
.copy_from_slice(&code, code_offset)?;
973+
974+
// Get regions for VM
975+
let regions = layout
976+
.get_memory_regions(&mem_mgr_gshm.shared_mem)?
977+
.into_iter()
978+
.filter(|r| r.guest_region.len() > 0)
979+
.collect();
980+
981+
// Calculate pml4_addr
982+
#[cfg(feature = "init-paging")]
983+
let pml4_addr = SandboxMemoryLayout::PML4_OFFSET as u64;
984+
#[cfg(not(feature = "init-paging"))]
985+
let pml4_addr = 0;
986+
987+
// Entrypoint
988+
let entrypoint = layout.get_guest_code_address() as u64;
989+
990+
let mut vm = HyperlightVm::new(
991+
regions,
992+
pml4_addr,
993+
entrypoint,
994+
rsp,
995+
&config,
996+
#[cfg(gdb)]
997+
None,
998+
#[cfg(crashdump)]
999+
rt_cfg,
1000+
#[cfg(feature = "mem_profile")]
1001+
trace_info,
1002+
)?;
1003+
1004+
let host_funcs = Arc::new(Mutex::new(FunctionRegistry::default()));
1005+
#[cfg(gdb)]
1006+
let dbg_mem_access_fn = Arc::new(Mutex::new(mem_mgr_hshm.clone()));
1007+
1008+
// Run the VM
1009+
vm.initialise(
1010+
RawPtr::from(0), // peb_addr
1011+
0, // seed
1012+
4096, // page_size
1013+
&mut mem_mgr_hshm,
1014+
&host_funcs,
1015+
None,
1016+
#[cfg(gdb)]
1017+
dbg_mem_access_fn.clone(),
1018+
)?;
1019+
1020+
// After run, check registers
1021+
let regs = vm.vm.regs()?;
1022+
assert_eq!(regs.rax, 0x1111111111111111);
1023+
assert_eq!(regs.rbx, 0x2222222222222222);
1024+
assert_eq!(regs.rcx, 0x3333333333333333);
1025+
1026+
// Reset vcpu
1027+
vm.reset_vcpu()?;
1028+
1029+
// Check registers again
1030+
let regs = vm.vm.regs()?;
1031+
assert_eq!(regs.rax, 0);
1032+
assert_eq!(regs.rbx, 0);
1033+
assert_eq!(regs.rcx, 0);
1034+
}
1035+
1036+
Ok(())
1037+
}
1038+
}
1039+
8211040
#[cfg(gdb)]
8221041
mod debug {
8231042
use hyperlight_common::mem::PAGE_SIZE;

0 commit comments

Comments
 (0)