Skip to content

Commit 76cd801

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 6395c96 commit 76cd801

File tree

5 files changed

+102
-1
lines changed

5 files changed

+102
-1
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: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ 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::{
2628
WHvX64RegisterCr0, WHvX64RegisterCr3, WHvX64RegisterCr4, WHvX64RegisterCs, WHvX64RegisterEfer,
2729
WHV_MEMORY_ACCESS_TYPE, WHV_PARTITION_HANDLE, WHV_REGISTER_VALUE, WHV_RUN_VP_EXIT_CONTEXT,
@@ -34,6 +36,8 @@ use super::surrogate_process::SurrogateProcess;
3436
use super::surrogate_process_manager::*;
3537
use super::windows_hypervisor_platform::{VMPartition, VMProcessor};
3638
use super::wrappers::WHvFPURegisters;
39+
#[cfg(feature = "unwind_guest")]
40+
use super::TraceRegister;
3741
use super::{
3842
windows_hypervisor_platform as whp, HyperlightExit, Hypervisor, VirtualCPU, CR0_AM, CR0_ET,
3943
CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP, CR4_OSFXSR, CR4_OSXMMEXCPT, CR4_PAE, EFER_LMA,
@@ -44,9 +48,9 @@ use crate::hypervisor::hypervisor_handler::HypervisorHandler;
4448
use crate::hypervisor::wrappers::WHvGeneralRegisters;
4549
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
4650
use crate::mem::ptr::{GuestPtr, RawPtr};
47-
use crate::HyperlightError::{NoHypervisorFound, WindowsAPIError};
4851
#[cfg(feature = "trace_guest")]
4952
use crate::sandbox::TraceInfo;
53+
use crate::HyperlightError::{NoHypervisorFound, WindowsAPIError};
5054
use crate::{debug, log_then_return, new_error, Result};
5155

5256
/// A Hypervisor driver for HyperV-on-Windows.
@@ -303,6 +307,19 @@ impl Debug for HypervWindowsDriver {
303307
}
304308
}
305309

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

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

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)