Skip to content

Commit efafa78

Browse files
committed
Implement crashdump/xsave
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent be89bc1 commit efafa78

File tree

5 files changed

+123
-180
lines changed

5 files changed

+123
-180
lines changed

src/hyperlight_host/src/hypervisor/hyperlight_vm.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,69 @@ impl HyperlightVm {
564564

565565
Ok(())
566566
}
567+
568+
#[cfg(crashdump)]
569+
pub(crate) fn crashdump_context(&self) -> Result<Option<super::crashdump::CrashDumpContext>> {
570+
if self.rt_cfg.guest_core_dump {
571+
let mut regs = [0; 27];
572+
573+
let vcpu_regs = self.vm.regs()?;
574+
let sregs = self.vm.sregs()?;
575+
let xsave = self.vm.xsave()?;
576+
577+
// Set up the registers for the crash dump
578+
regs[0] = vcpu_regs.r15; // r15
579+
regs[1] = vcpu_regs.r14; // r14
580+
regs[2] = vcpu_regs.r13; // r13
581+
regs[3] = vcpu_regs.r12; // r12
582+
regs[4] = vcpu_regs.rbp; // rbp
583+
regs[5] = vcpu_regs.rbx; // rbx
584+
regs[6] = vcpu_regs.r11; // r11
585+
regs[7] = vcpu_regs.r10; // r10
586+
regs[8] = vcpu_regs.r9; // r9
587+
regs[9] = vcpu_regs.r8; // r8
588+
regs[10] = vcpu_regs.rax; // rax
589+
regs[11] = vcpu_regs.rcx; // rcx
590+
regs[12] = vcpu_regs.rdx; // rdx
591+
regs[13] = vcpu_regs.rsi; // rsi
592+
regs[14] = vcpu_regs.rdi; // rdi
593+
regs[15] = 0; // orig rax
594+
regs[16] = vcpu_regs.rip; // rip
595+
regs[17] = sregs.cs.selector as u64; // cs
596+
regs[18] = vcpu_regs.rflags; // eflags
597+
regs[19] = vcpu_regs.rsp; // rsp
598+
regs[20] = sregs.ss.selector as u64; // ss
599+
regs[21] = sregs.fs.base; // fs_base
600+
regs[22] = sregs.gs.base; // gs_base
601+
regs[23] = sregs.ds.selector as u64; // ds
602+
regs[24] = sregs.es.selector as u64; // es
603+
regs[25] = sregs.fs.selector as u64; // fs
604+
regs[26] = sregs.gs.selector as u64; // gs
605+
606+
// Get the filename from the binary path
607+
let filename = self.rt_cfg.binary_path.clone().and_then(|path| {
608+
Path::new(&path)
609+
.file_name()
610+
.and_then(|name| name.to_os_string().into_string().ok())
611+
});
612+
613+
// Include both initial sandbox regions and dynamically mapped regions
614+
let mut regions: Vec<MemoryRegion> = self.sandbox_regions.clone();
615+
regions.extend(self.mmap_regions.iter().map(|(_, r)| r).cloned());
616+
Ok(Some(crashdump::CrashDumpContext::new(
617+
regions,
618+
regs,
619+
xsave.to_vec(),
620+
self.entrypoint,
621+
self.rt_cfg.binary_path.clone(),
622+
filename,
623+
)))
624+
} else {
625+
Ok(None)
626+
}
627+
}
628+
}
629+
}
567630
}
568631

569632
/// The vCPU tried to access the given addr

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 3 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -389,64 +389,9 @@ impl Hypervisor for HypervLinuxDriver {
389389
}
390390

391391
#[cfg(crashdump)]
392-
fn crashdump_context(&self) -> Result<Option<super::crashdump::CrashDumpContext>> {
393-
if self.rt_cfg.guest_core_dump {
394-
let mut regs = [0; 27];
395-
396-
let vcpu_regs = self.vcpu_fd.get_regs()?;
397-
let sregs = self.vcpu_fd.get_sregs()?;
398-
let xsave = self.vcpu_fd.get_xsave()?;
399-
400-
// Set up the registers for the crash dump
401-
regs[0] = vcpu_regs.r15; // r15
402-
regs[1] = vcpu_regs.r14; // r14
403-
regs[2] = vcpu_regs.r13; // r13
404-
regs[3] = vcpu_regs.r12; // r12
405-
regs[4] = vcpu_regs.rbp; // rbp
406-
regs[5] = vcpu_regs.rbx; // rbx
407-
regs[6] = vcpu_regs.r11; // r11
408-
regs[7] = vcpu_regs.r10; // r10
409-
regs[8] = vcpu_regs.r9; // r9
410-
regs[9] = vcpu_regs.r8; // r8
411-
regs[10] = vcpu_regs.rax; // rax
412-
regs[11] = vcpu_regs.rcx; // rcx
413-
regs[12] = vcpu_regs.rdx; // rdx
414-
regs[13] = vcpu_regs.rsi; // rsi
415-
regs[14] = vcpu_regs.rdi; // rdi
416-
regs[15] = 0; // orig rax
417-
regs[16] = vcpu_regs.rip; // rip
418-
regs[17] = sregs.cs.selector as u64; // cs
419-
regs[18] = vcpu_regs.rflags; // eflags
420-
regs[19] = vcpu_regs.rsp; // rsp
421-
regs[20] = sregs.ss.selector as u64; // ss
422-
regs[21] = sregs.fs.base; // fs_base
423-
regs[22] = sregs.gs.base; // gs_base
424-
regs[23] = sregs.ds.selector as u64; // ds
425-
regs[24] = sregs.es.selector as u64; // es
426-
regs[25] = sregs.fs.selector as u64; // fs
427-
regs[26] = sregs.gs.selector as u64; // gs
428-
429-
// Get the filename from the binary path
430-
let filename = self.rt_cfg.binary_path.clone().and_then(|path| {
431-
Path::new(&path)
432-
.file_name()
433-
.and_then(|name| name.to_os_string().into_string().ok())
434-
});
435-
436-
// Include both initial sandbox regions and dynamically mapped regions
437-
let mut regions: Vec<MemoryRegion> = self.sandbox_regions.clone();
438-
regions.extend(self.mmap_regions.iter().cloned());
439-
Ok(Some(crashdump::CrashDumpContext::new(
440-
regions,
441-
regs,
442-
xsave.buffer.to_vec(),
443-
self.entrypoint,
444-
self.rt_cfg.binary_path.clone(),
445-
filename,
446-
)))
447-
} else {
448-
Ok(None)
449-
}
392+
fn xsave(&self) -> Result<Vec<u8>> {
393+
let xsave = self.vcpu_fd.get_xsave()?;
394+
Ok(xsave.buffer.to_vec())
450395
}
451396

452397
#[cfg(feature = "mem_profile")]

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 49 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -395,64 +395,56 @@ impl Hypervisor for HypervWindowsDriver {
395395
}
396396

397397
#[cfg(crashdump)]
398-
fn crashdump_context(&self) -> Result<Option<crashdump::CrashDumpContext>> {
399-
if self.rt_cfg.guest_core_dump {
400-
let mut regs = [0; 27];
401-
402-
let vcpu_regs = self.processor.regs()?;
403-
let sregs = self.processor.sregs()?;
404-
let xsave = self.processor.get_xsave()?;
405-
406-
// Set the registers in the order expected by the crashdump context
407-
regs[0] = vcpu_regs.r15; // r15
408-
regs[1] = vcpu_regs.r14; // r14
409-
regs[2] = vcpu_regs.r13; // r13
410-
regs[3] = vcpu_regs.r12; // r12
411-
regs[4] = vcpu_regs.rbp; // rbp
412-
regs[5] = vcpu_regs.rbx; // rbx
413-
regs[6] = vcpu_regs.r11; // r11
414-
regs[7] = vcpu_regs.r10; // r10
415-
regs[8] = vcpu_regs.r9; // r9
416-
regs[9] = vcpu_regs.r8; // r8
417-
regs[10] = vcpu_regs.rax; // rax
418-
regs[11] = vcpu_regs.rcx; // rcx
419-
regs[12] = vcpu_regs.rdx; // rdx
420-
regs[13] = vcpu_regs.rsi; // rsi
421-
regs[14] = vcpu_regs.rdi; // rdi
422-
regs[15] = 0; // orig rax
423-
regs[16] = vcpu_regs.rip; // rip
424-
regs[17] = sregs.cs.selector as u64; // cs
425-
regs[18] = vcpu_regs.rflags; // eflags
426-
regs[19] = vcpu_regs.rsp; // rsp
427-
regs[20] = sregs.ss.selector as u64; // ss
428-
regs[21] = sregs.fs.base; // fs_base
429-
regs[22] = sregs.gs.base; // gs_base
430-
regs[23] = sregs.ds.selector as u64; // ds
431-
regs[24] = sregs.es.selector as u64; // es
432-
regs[25] = sregs.fs.selector as u64; // fs
433-
regs[26] = sregs.gs.selector as u64; // gs
434-
435-
// Get the filename from the config
436-
let filename = self.rt_cfg.binary_path.clone().and_then(|path| {
437-
Path::new(&path)
438-
.file_name()
439-
.and_then(|name| name.to_os_string().into_string().ok())
440-
});
441-
442-
// Include both initial sandbox regions and dynamically mapped regions
443-
let mut regions: Vec<MemoryRegion> = self.sandbox_regions.clone();
444-
regions.extend(self.mmap_regions.iter().cloned());
445-
Ok(Some(crashdump::CrashDumpContext::new(
446-
regions,
447-
regs,
448-
xsave,
449-
self.entrypoint,
450-
self.rt_cfg.binary_path.clone(),
451-
filename,
452-
)))
453-
} else {
454-
Ok(None)
398+
fn xsave(&self) -> Result<Vec<u8>> {
399+
use crate::HyperlightError;
400+
401+
// Get the required buffer size by calling with NULL buffer.
402+
// If the buffer is not large enough (0 won't be), WHvGetVirtualProcessorXsaveState returns
403+
// WHV_E_INSUFFICIENT_BUFFER and sets buffer_size_needed to the required size.
404+
let mut buffer_size_needed: u32 = 0;
405+
406+
let result = unsafe {
407+
WHvGetVirtualProcessorXsaveState(
408+
self.partition,
409+
0,
410+
std::ptr::null_mut(),
411+
0,
412+
&mut buffer_size_needed,
413+
)
414+
};
415+
416+
// Expect insufficient buffer error; any other error is unexpected
417+
if let Err(e) = result
418+
&& e.code() != windows::Win32::Foundation::WHV_E_INSUFFICIENT_BUFFER
419+
{
420+
return Err(HyperlightError::WindowsAPIError(e));
455421
}
422+
423+
// Allocate buffer with the required size
424+
let mut xsave_buffer = vec![0u8; buffer_size_needed as usize];
425+
let mut written_bytes = 0;
426+
427+
// Get the actual Xsave state
428+
unsafe {
429+
WHvGetVirtualProcessorXsaveState(
430+
self.partition,
431+
0,
432+
xsave_buffer.as_mut_ptr() as *mut std::ffi::c_void,
433+
buffer_size_needed,
434+
&mut written_bytes,
435+
)
436+
}?;
437+
438+
// Verify the number of written bytes matches the expected size
439+
if written_bytes != buffer_size_needed {
440+
return Err(new_error!(
441+
"Failed to get Xsave state: expected {} bytes, got {}",
442+
buffer_size_needed,
443+
written_bytes
444+
));
445+
}
446+
447+
Ok(xsave_buffer)
456448
}
457449

458450

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 7 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -306,70 +306,13 @@ impl Hypervisor for KVMDriver {
306306
}
307307

308308
#[cfg(crashdump)]
309-
fn crashdump_context(&self) -> Result<Option<crashdump::CrashDumpContext>> {
310-
if self.rt_cfg.guest_core_dump {
311-
let mut regs = [0; 27];
312-
313-
let vcpu_regs = self.vcpu_fd.get_regs()?;
314-
let sregs = self.vcpu_fd.get_sregs()?;
315-
let xsave = self.vcpu_fd.get_xsave()?;
316-
317-
// Set the registers in the order expected by the crashdump context
318-
regs[0] = vcpu_regs.r15; // r15
319-
regs[1] = vcpu_regs.r14; // r14
320-
regs[2] = vcpu_regs.r13; // r13
321-
regs[3] = vcpu_regs.r12; // r12
322-
regs[4] = vcpu_regs.rbp; // rbp
323-
regs[5] = vcpu_regs.rbx; // rbx
324-
regs[6] = vcpu_regs.r11; // r11
325-
regs[7] = vcpu_regs.r10; // r10
326-
regs[8] = vcpu_regs.r9; // r9
327-
regs[9] = vcpu_regs.r8; // r8
328-
regs[10] = vcpu_regs.rax; // rax
329-
regs[11] = vcpu_regs.rcx; // rcx
330-
regs[12] = vcpu_regs.rdx; // rdx
331-
regs[13] = vcpu_regs.rsi; // rsi
332-
regs[14] = vcpu_regs.rdi; // rdi
333-
regs[15] = 0; // orig rax
334-
regs[16] = vcpu_regs.rip; // rip
335-
regs[17] = sregs.cs.selector as u64; // cs
336-
regs[18] = vcpu_regs.rflags; // eflags
337-
regs[19] = vcpu_regs.rsp; // rsp
338-
regs[20] = sregs.ss.selector as u64; // ss
339-
regs[21] = sregs.fs.base; // fs_base
340-
regs[22] = sregs.gs.base; // gs_base
341-
regs[23] = sregs.ds.selector as u64; // ds
342-
regs[24] = sregs.es.selector as u64; // es
343-
regs[25] = sregs.fs.selector as u64; // fs
344-
regs[26] = sregs.gs.selector as u64; // gs
345-
346-
// Get the filename from the runtime config
347-
let filename = self.rt_cfg.binary_path.clone().and_then(|path| {
348-
Path::new(&path)
349-
.file_name()
350-
.and_then(|name| name.to_os_string().into_string().ok())
351-
});
352-
353-
// The [`CrashDumpContext`] accepts xsave as a vector of u8, so we need to convert the
354-
// xsave region to a vector of u8
355-
// Also include mapped regions in addition to the initial sandbox regions
356-
let mut regions: Vec<MemoryRegion> = self.sandbox_regions.clone();
357-
regions.extend(self.mmap_regions.iter().map(|(r, _)| r.clone()));
358-
Ok(Some(crashdump::CrashDumpContext::new(
359-
regions,
360-
regs,
361-
xsave
362-
.region
363-
.iter()
364-
.flat_map(|item| item.to_le_bytes())
365-
.collect::<Vec<u8>>(),
366-
self.entrypoint,
367-
self.rt_cfg.binary_path.clone(),
368-
filename,
369-
)))
370-
} else {
371-
Ok(None)
372-
}
309+
fn xsave(&self) -> Result<Vec<u8>> {
310+
let xsave = self.vcpu_fd.get_xsave()?;
311+
Ok(xsave
312+
.region
313+
.into_iter()
314+
.flat_map(u32::to_le_bytes)
315+
.collect())
373316
}
374317

375318
#[cfg(feature = "mem_profile")]

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ pub(crate) trait Hypervisor: Debug + Send {
190190
fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor;
191191

192192
#[cfg(crashdump)]
193-
fn crashdump_context(&self) -> Result<Option<crashdump::CrashDumpContext>>;
193+
fn xsave(&self) -> Result<Vec<u8>>;
194194

195195
/// Get a mutable reference of the trace info for the guest
196196
#[cfg(feature = "mem_profile")]

0 commit comments

Comments
 (0)