Skip to content

Commit 0500ce8

Browse files
committed
[hyperlight_host/trace] Add HV interface for reading trace registers
This adds a new interface which tracing code can use to request the values of registers from the hypervisor supervising a sandbox. Signed-off-by: Lucy Menon <[email protected]>
1 parent f533641 commit 0500ce8

File tree

6 files changed

+112
-3
lines changed

6 files changed

+112
-3
lines changed

src/hyperlight_host/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ crashdump = ["dep:tempfile"] # Dumps the VM state to a file on unexpected errors
125125
# include any particularly expensive instrumentation unless other
126126
# features are enabled.
127127
trace_guest = []
128+
# This feature enables unwinding the guest stack from the host, in
129+
# order to produce stack traces for debugging or profiling.
130+
unwind_guest = [ "trace_guest" ]
128131
kvm = ["dep:kvm-bindings", "dep:kvm-ioctls"]
129132
mshv = ["dep:mshv-bindings", "dep:mshv-ioctls"]
130133
inprocess = []

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,18 @@ use mshv_bindings::{
2424
hv_register_name_HV_X64_REGISTER_RIP, hv_register_value, mshv_user_mem_region,
2525
FloatingPointUnit, SegmentRegister, SpecialRegisters, StandardRegisters,
2626
};
27+
#[cfg(feature = "unwind_guest")]
28+
use mshv_bindings::{
29+
hv_register_name, hv_register_name_HV_X64_REGISTER_RAX, hv_register_name_HV_X64_REGISTER_RBP,
30+
hv_register_name_HV_X64_REGISTER_RCX, hv_register_name_HV_X64_REGISTER_RSP,
31+
};
2732
use mshv_ioctls::{Mshv, VcpuFd, VmFd};
2833
use tracing::{instrument, Span};
2934

3035
use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
3136
use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
37+
#[cfg(feature = "unwind_guest")]
38+
use super::TraceRegister;
3239
use super::{
3340
Hypervisor, VirtualCPU, CR0_AM, CR0_ET, CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP, CR4_OSFXSR,
3441
CR4_OSXMMEXCPT, CR4_PAE, EFER_LMA, EFER_LME, EFER_NX, EFER_SCE,
@@ -165,6 +172,19 @@ impl Debug for HypervLinuxDriver {
165172
}
166173
}
167174

175+
#[cfg(feature = "unwind_guest")]
176+
impl From<TraceRegister> for hv_register_name {
177+
fn from(r: TraceRegister) -> Self {
178+
match r {
179+
TraceRegister::RAX => hv_register_name_HV_X64_REGISTER_RAX,
180+
TraceRegister::RCX => hv_register_name_HV_X64_REGISTER_RCX,
181+
TraceRegister::RIP => hv_register_name_HV_X64_REGISTER_RIP,
182+
TraceRegister::RSP => hv_register_name_HV_X64_REGISTER_RSP,
183+
TraceRegister::RBP => hv_register_name_HV_X64_REGISTER_RBP,
184+
}
185+
}
186+
}
187+
168188
impl Hypervisor for HypervLinuxDriver {
169189
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
170190
fn initialise(
@@ -366,6 +386,17 @@ impl Hypervisor for HypervLinuxDriver {
366386
Ok(result)
367387
}
368388

389+
#[cfg(feature = "unwind_guest")]
390+
fn read_trace_reg(&self, reg: TraceRegister) -> Result<u64> {
391+
let mut assoc = [hv_register_assoc {
392+
name: reg.into(),
393+
..Default::default()
394+
}];
395+
self.vcpu_fd.get_reg(&mut assoc)?;
396+
// safety: all registers that we currently support are 64-bit
397+
unsafe { Ok(assoc[0].value.reg64) }
398+
}
399+
369400
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
370401
fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
371402
self as &mut dyn Hypervisor

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ use std::string::String;
2222
use hyperlight_common::mem::PAGE_SIZE_USIZE;
2323
use tracing::{instrument, Span};
2424
use windows::Win32::Foundation::HANDLE;
25+
#[cfg(feature = "trace_guest")]
26+
use windows::Win32::System::Hypervisor::WHV_REGISTER_NAME;
2527
use windows::Win32::System::Hypervisor::{
26-
WHvX64RegisterCr0, WHvX64RegisterCr3, WHvX64RegisterCr4, WHvX64RegisterCs, WHvX64RegisterEfer,
27-
WHV_MEMORY_ACCESS_TYPE, WHV_PARTITION_HANDLE, WHV_REGISTER_VALUE, WHV_RUN_VP_EXIT_CONTEXT,
28-
WHV_RUN_VP_EXIT_REASON, WHV_X64_SEGMENT_REGISTER, WHV_X64_SEGMENT_REGISTER_0,
28+
WHvGetVirtualProcessorRegisters, WHvX64RegisterCr0, WHvX64RegisterCr3, WHvX64RegisterCr4,
29+
WHvX64RegisterCs, WHvX64RegisterEfer, WHV_MEMORY_ACCESS_TYPE, WHV_PARTITION_HANDLE,
30+
WHV_REGISTER_VALUE, WHV_RUN_VP_EXIT_CONTEXT, WHV_RUN_VP_EXIT_REASON, WHV_X64_SEGMENT_REGISTER,
31+
WHV_X64_SEGMENT_REGISTER_0,
2932
};
3033

3134
use super::fpu::{FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
@@ -34,6 +37,8 @@ use super::surrogate_process::SurrogateProcess;
3437
use super::surrogate_process_manager::*;
3538
use super::windows_hypervisor_platform::{VMPartition, VMProcessor};
3639
use super::wrappers::WHvFPURegisters;
40+
#[cfg(feature = "unwind_guest")]
41+
use super::TraceRegister;
3742
use super::{
3843
windows_hypervisor_platform as whp, HyperlightExit, Hypervisor, VirtualCPU, CR0_AM, CR0_ET,
3944
CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP, CR4_OSFXSR, CR4_OSXMMEXCPT, CR4_PAE, EFER_LMA,
@@ -303,6 +308,19 @@ impl Debug for HypervWindowsDriver {
303308
}
304309
}
305310

311+
#[cfg(feature = "trace_guest")]
312+
impl From<TraceRegister> for WHV_REGISTER_NAME {
313+
fn from(r: TraceRegister) -> Self {
314+
match r {
315+
TraceRegister::RAX => windows::Win32::System::Hypervisor::WHvX64RegisterRax,
316+
TraceRegister::RCX => windows::Win32::System::Hypervisor::WHvX64RegisterRcx,
317+
TraceRegister::RIP => windows::Win32::System::Hypervisor::WHvX64RegisterRip,
318+
TraceRegister::RSP => windows::Win32::System::Hypervisor::WHvX64RegisterRsp,
319+
TraceRegister::RBP => windows::Win32::System::Hypervisor::WHvX64RegisterRbp,
320+
}
321+
}
322+
}
323+
306324
impl Hypervisor for HypervWindowsDriver {
307325
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
308326
fn initialise(
@@ -547,6 +565,23 @@ impl Hypervisor for HypervWindowsDriver {
547565
self.processor.get_partition_hdl()
548566
}
549567

568+
#[cfg(feature = "unwind_guest")]
569+
fn read_trace_reg(&self, reg: TraceRegister) -> Result<u64> {
570+
let register_names = [WHV_REGISTER_NAME::from(reg)];
571+
let mut register_values: [WHV_REGISTER_VALUE; 1] = Default::default();
572+
unsafe {
573+
WHvGetVirtualProcessorRegisters(
574+
self.get_partition_hdl(),
575+
0,
576+
register_names.as_ptr(),
577+
register_names.len() as u32,
578+
register_values.as_mut_ptr(),
579+
)?;
580+
// safety: all registers that we currently support are 64-bit
581+
Ok(register_values[0].Reg64)
582+
}
583+
}
584+
550585
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
551586
fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
552587
self as &mut dyn Hypervisor

src/hyperlight_host/src/hypervisor/inprocess.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
use std::fmt::Debug;
1818
use std::os::raw::c_void;
1919

20+
#[cfg(feature = "unwind_guest")]
21+
use super::TraceRegister;
2022
use super::{HyperlightExit, Hypervisor};
2123
#[cfg(crashdump)]
2224
use crate::mem::memory_region::MemoryRegion;
@@ -122,6 +124,11 @@ impl<'a> Hypervisor for InprocessDriver<'a> {
122124
unimplemented!("run should not be needed since we are in in-process mode")
123125
}
124126

127+
#[cfg(feature = "unwind_guest")]
128+
fn read_trace_reg(&self, _reg: TraceRegister) -> Result<u64> {
129+
unimplemented!("read_trace_reg is not implemented in in-process mode")
130+
}
131+
125132
fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
126133
self
127134
}

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ use tracing::{instrument, Span};
2424

2525
use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
2626
use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
27+
#[cfg(feature = "unwind_guest")]
28+
use super::TraceRegister;
2729
use super::{
2830
HyperlightExit, Hypervisor, VirtualCPU, CR0_AM, CR0_ET, CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP,
2931
CR4_OSFXSR, CR4_OSXMMEXCPT, CR4_PAE, EFER_LMA, EFER_LME, EFER_NX, EFER_SCE,
@@ -337,6 +339,18 @@ impl Hypervisor for KVMDriver {
337339
Ok(result)
338340
}
339341

342+
#[cfg(feature = "unwind_guest")]
343+
fn read_trace_reg(&self, reg: TraceRegister) -> Result<u64> {
344+
let regs = self.vcpu_fd.get_regs()?;
345+
Ok(match reg {
346+
TraceRegister::RAX => regs.rax,
347+
TraceRegister::RCX => regs.rcx,
348+
TraceRegister::RIP => regs.rip,
349+
TraceRegister::RSP => regs.rsp,
350+
TraceRegister::RBP => regs.rbp,
351+
})
352+
}
353+
340354
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
341355
fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
342356
self as &mut dyn Hypervisor

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,21 @@ pub enum HyperlightExit {
103103
Retry(),
104104
}
105105

106+
/// Registers which may be useful for tracing/stack unwinding
107+
#[cfg(feature = "trace_guest")]
108+
pub enum TraceRegister {
109+
/// RAX
110+
RAX,
111+
/// RCX
112+
RCX,
113+
/// RIP
114+
RIP,
115+
/// RSP
116+
RSP,
117+
/// RBP
118+
RBP,
119+
}
120+
106121
/// A common set of hypervisor functionality
107122
///
108123
/// Note: a lot of these structures take in an `Option<HypervisorHandler>`.
@@ -194,6 +209,10 @@ pub(crate) trait Hypervisor: Debug + Sync + Send {
194209

195210
#[cfg(crashdump)]
196211
fn get_memory_regions(&self) -> &[MemoryRegion];
212+
213+
/// Read a register for trace/unwind purposes
214+
#[cfg(feature = "unwind_guest")]
215+
fn read_trace_reg(&self, reg: TraceRegister) -> Result<u64>;
197216
}
198217

199218
/// A virtual CPU that can be run until an exit occurs

0 commit comments

Comments
 (0)