Skip to content

Commit 23dcf47

Browse files
committed
[guest/{entrypoint,host_fxn_call,interrupts},host/outb] brought back panic info to abort
In #474, I removed the guest panic context region. With that, aborting was split into two steps: (1) printing panic info w/ debug_print, and (2) exiting w/ abort and codes. This PR addresses feedback I got in #474 to join the operations by buffering string output when aborting. This PR also cleans up our manual chunking logic w/ outb and leverages out32 w/ length prefixing to minimize VM exits. Abort detects it should finishing buffering by a terminator code (0xFF). Edited tests to check for panic message in aborts as they were prior to #474 merged. Signed-off-by: danbugs <[email protected]>
1 parent f6bdbee commit 23dcf47

File tree

13 files changed

+164
-100
lines changed

13 files changed

+164
-100
lines changed

src/hyperlight_guest/src/entrypoint.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use spin::Once;
2525
use crate::gdt::load_gdt;
2626
use crate::guest_function_call::dispatch_function;
2727
use crate::guest_logger::init_logger;
28-
use crate::host_function_call::{debug_print, outb};
28+
use crate::host_function_call::outb;
2929
use crate::idtr::load_idt;
3030
use crate::{
3131
__security_cookie, HEAP_ALLOCATOR, MIN_STACK_ADDRESS, OS_PAGE_SIZE, OUTB_PTR,
@@ -43,11 +43,12 @@ pub fn halt() {
4343

4444
#[no_mangle]
4545
pub extern "C" fn abort() -> ! {
46-
abort_with_code(&[0])
46+
abort_with_code(&[0, 0xFF])
4747
}
4848

4949
pub fn abort_with_code(code: &[u8]) -> ! {
5050
outb(OutBAction::Abort as u16, code);
51+
outb(OutBAction::Abort as u16, &[0xFF]); // send abort terminator (if not included in code)
5152
unreachable!()
5253
}
5354

@@ -56,13 +57,19 @@ pub fn abort_with_code(code: &[u8]) -> ! {
5657
/// # Safety
5758
/// This function is unsafe because it dereferences a raw pointer.
5859
pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_char) -> ! {
59-
debug_print(
60-
CStr::from_ptr(message_ptr)
61-
.to_str()
62-
.expect("Invalid UTF-8 string"),
63-
);
64-
60+
// Step 1: Send abort code (typically 1 byte, but `code` allows flexibility)
6561
outb(OutBAction::Abort as u16, code);
62+
63+
// Step 2: Convert the C string to bytes
64+
let message_bytes = CStr::from_ptr(message_ptr).to_bytes(); // excludes null terminator
65+
66+
// Step 3: Send the message itself in chunks
67+
outb(OutBAction::Abort as u16, message_bytes);
68+
69+
// Step 4: Send abort terminator to signal completion (e.g., 0xFF)
70+
outb(OutBAction::Abort as u16, &[0xFF]);
71+
72+
// This function never returns
6673
unreachable!()
6774
}
6875

src/hyperlight_guest/src/host_function_call.rs

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ limitations under the License.
1717
use alloc::format;
1818
use alloc::string::ToString;
1919
use alloc::vec::Vec;
20-
use core::arch::global_asm;
20+
use core::arch;
2121

2222
use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, FunctionCallType};
2323
use hyperlight_common::flatbuffer_wrappers::function_types::{
@@ -79,21 +79,16 @@ pub fn outb(port: u16, data: &[u8]) {
7979
unsafe {
8080
match RUNNING_MODE {
8181
RunMode::Hypervisor => {
82-
for chunk in data.chunks(4) {
83-
// Process the data in chunks of 4 bytes. If a chunk has fewer than 4 bytes,
84-
// pad it with 0x7F to ensure it can be converted into a 4-byte array.
85-
// The choice of 0x7F as the padding value is arbitrary and does not carry
86-
// any special meaning; it simply ensures consistent chunk size.
87-
let val = match chunk {
88-
[a, b, c, d] => u32::from_le_bytes([*a, *b, *c, *d]),
89-
[a, b, c] => u32::from_le_bytes([*a, *b, *c, 0x7F]),
90-
[a, b] => u32::from_le_bytes([*a, *b, 0x7F, 0x7F]),
91-
[a] => u32::from_le_bytes([*a, 0x7F, 0x7F, 0x7F]),
92-
[] => break,
93-
_ => unreachable!(),
94-
};
95-
96-
hloutd(val, port);
82+
let mut i = 0;
83+
while i < data.len() {
84+
let remaining = data.len() - i;
85+
let chunk_len = remaining.min(3);
86+
let mut chunk = [0u8; 4];
87+
chunk[0] = chunk_len as u8;
88+
chunk[1..1 + chunk_len].copy_from_slice(&data[i..i + chunk_len]);
89+
let val = u32::from_le_bytes(chunk);
90+
out32(port, val);
91+
i += chunk_len;
9792
}
9893
}
9994
RunMode::InProcessLinux | RunMode::InProcessWindows => {
@@ -119,11 +114,25 @@ pub fn outb(port: u16, data: &[u8]) {
119114
}
120115
}
121116

122-
extern "win64" {
123-
fn hloutd(value: u32, port: u16);
117+
pub(crate) unsafe fn out32(port: u16, val: u32) {
118+
arch::asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack));
124119
}
125120

126-
pub fn print_output_as_guest_function(function_call: &FunctionCall) -> Result<Vec<u8>> {
121+
/// Prints a message using `OutBAction::DebugPrint`. It transmits bytes of a message
122+
/// through several VMExists and, with such, it is slower than
123+
/// `print_output_with_host_print`.
124+
///
125+
/// This function should be used in debug mode only. This function does not
126+
/// require memory to be setup to be used.
127+
pub fn debug_print(msg: &str) {
128+
outb(OutBAction::DebugPrint as u16, msg.as_bytes());
129+
}
130+
131+
/// Print a message using the host's print function.
132+
///
133+
/// This function requires memory to be setup to be used. In particular, the
134+
/// existence of the input and output memory regions.
135+
pub fn print_output_with_host_print(function_call: &FunctionCall) -> Result<Vec<u8>> {
127136
if let ParameterValue::String(message) = function_call.parameters.clone().unwrap()[0].clone() {
128137
call_host_function(
129138
"HostPrint",
@@ -135,20 +144,7 @@ pub fn print_output_as_guest_function(function_call: &FunctionCall) -> Result<Ve
135144
} else {
136145
Err(HyperlightGuestError::new(
137146
ErrorCode::GuestError,
138-
"Wrong Parameters passed to print_output_as_guest_function".to_string(),
147+
"Wrong Parameters passed to print_output_with_host_print".to_string(),
139148
))
140149
}
141150
}
142-
143-
pub fn debug_print(msg: &str) {
144-
outb(OutBAction::DebugPrint as u16, msg.as_bytes());
145-
}
146-
147-
global_asm!(
148-
".global hloutd
149-
hloutd:
150-
mov eax, ecx
151-
mov dx, dx
152-
out dx, eax
153-
ret"
154-
);

src/hyperlight_guest/src/interrupt_handlers.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,9 @@ pub extern "sysv64" fn hl_exception_handler(
3131
) {
3232
let exception = Exception::try_from(exception_number as u8).expect("Invalid exception number");
3333
let msg = format!(
34-
"EXCEPTION: {:#?}\n\
35-
Page Fault Address: {:#x}\n\
34+
"Page Fault Address: {:#x}\n\
3635
Stack Pointer: {:#x}",
37-
exception, page_fault_address, stack_pointer
36+
page_fault_address, stack_pointer
3837
);
3938

4039
unsafe {

src/hyperlight_guest/src/lib.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@ limitations under the License.
1717
#![no_std]
1818
// Deps
1919
use alloc::string::ToString;
20-
use core::hint::unreachable_unchecked;
2120

2221
use buddy_system_allocator::LockedHeap;
2322
use guest_function_register::GuestFunctionRegister;
24-
use host_function_call::debug_print;
2523
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
2624
use hyperlight_common::mem::{HyperlightPEB, RunMode};
27-
use hyperlight_common::outb::OutBAction;
2825

29-
use crate::host_function_call::outb;
26+
use crate::entrypoint::abort_with_code_and_message;
3027
extern crate alloc;
3128

3229
// Modules
@@ -73,9 +70,11 @@ pub(crate) static _fltused: i32 = 0;
7370
// to satisfy the clippy when cfg == test
7471
#[allow(dead_code)]
7572
fn panic(info: &core::panic::PanicInfo) -> ! {
76-
debug_print(info.to_string().as_str());
77-
outb(OutBAction::Abort as u16, &[ErrorCode::UnknownError as u8]);
78-
unsafe { unreachable_unchecked() }
73+
let msg = info.to_string();
74+
let c_string = alloc::ffi::CString::new(msg)
75+
.unwrap_or_else(|_| alloc::ffi::CString::new("panic (invalid utf8)").unwrap());
76+
77+
unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], c_string.as_ptr()) }
7978
}
8079

8180
// Globals

src/hyperlight_host/src/hypervisor/handlers.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::{new_error, Result};
2525
/// has initiated an outb operation.
2626
pub trait OutBHandlerCaller: Sync + Send {
2727
/// Function that gets called when an outb operation has occurred.
28-
fn call(&mut self, port: u16, payload: Vec<u8>) -> Result<()>;
28+
fn call(&mut self, port: u16, payload: u32) -> Result<()>;
2929
}
3030

3131
/// A convenient type representing a common way `OutBHandler` implementations
@@ -36,7 +36,7 @@ pub trait OutBHandlerCaller: Sync + Send {
3636
/// a &mut self).
3737
pub type OutBHandlerWrapper = Arc<Mutex<dyn OutBHandlerCaller>>;
3838

39-
pub(crate) type OutBHandlerFunction = Box<dyn FnMut(u16, Vec<u8>) -> Result<()> + Send>;
39+
pub(crate) type OutBHandlerFunction = Box<dyn FnMut(u16, u32) -> Result<()> + Send>;
4040

4141
/// A `OutBHandler` implementation using a `OutBHandlerFunction`
4242
///
@@ -52,7 +52,7 @@ impl From<OutBHandlerFunction> for OutBHandler {
5252

5353
impl OutBHandlerCaller for OutBHandler {
5454
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
55-
fn call(&mut self, port: u16, payload: Vec<u8>) -> Result<()> {
55+
fn call(&mut self, port: u16, payload: u32) -> Result<()> {
5656
let mut func = self
5757
.0
5858
.try_lock()

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,10 +546,15 @@ impl Hypervisor for HypervLinuxDriver {
546546
instruction_length: u64,
547547
outb_handle_fn: OutBHandlerWrapper,
548548
) -> Result<()> {
549+
let mut padded = [0u8; 4];
550+
let copy_len = data.len().min(4);
551+
padded[..copy_len].copy_from_slice(&data[..copy_len]);
552+
let val = u32::from_le_bytes(padded);
553+
549554
outb_handle_fn
550555
.try_lock()
551556
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
552-
.call(port, data)?;
557+
.call(port, val)?;
553558

554559
// update rip
555560
self.vcpu_fd.set_reg(&[hv_register_assoc {

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,10 +390,15 @@ impl Hypervisor for HypervWindowsDriver {
390390
instruction_length: u64,
391391
outb_handle_fn: OutBHandlerWrapper,
392392
) -> Result<()> {
393+
let mut padded = [0u8; 4];
394+
let copy_len = data.len().min(4);
395+
padded[..copy_len].copy_from_slice(&data[..copy_len]);
396+
let val = u32::from_le_bytes(padded);
397+
393398
outb_handle_fn
394399
.try_lock()
395400
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
396-
.call(port, data)?;
401+
.call(port, val)?;
397402

398403
let mut regs = self.processor.get_regs()?;
399404
regs.rip = rip + instruction_length;
@@ -505,7 +510,7 @@ pub mod tests {
505510
#[serial]
506511
fn test_init() {
507512
let outb_handler = {
508-
let func: Box<dyn FnMut(u16, Vec<u8>) -> Result<()> + Send> =
513+
let func: Box<dyn FnMut(u16, u32) -> Result<()> + Send> =
509514
Box::new(|_, _| -> Result<()> { Ok(()) });
510515
Arc::new(Mutex::new(OutBHandler::from(func)))
511516
};

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,10 +497,15 @@ impl Hypervisor for KVMDriver {
497497
if data.is_empty() {
498498
log_then_return!("no data was given in IO interrupt");
499499
} else {
500+
let mut padded = [0u8; 4];
501+
let copy_len = data.len().min(4);
502+
padded[..copy_len].copy_from_slice(&data[..copy_len]);
503+
let value = u32::from_le_bytes(padded);
504+
500505
outb_handle_fn
501506
.try_lock()
502507
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
503-
.call(port, data)?;
508+
.call(port, value)?;
504509
}
505510

506511
Ok(())
@@ -664,7 +669,7 @@ mod tests {
664669
}
665670

666671
let outb_handler: Arc<Mutex<OutBHandler>> = {
667-
let func: Box<dyn FnMut(u16, Vec<u8>) -> Result<()> + Send> =
672+
let func: Box<dyn FnMut(u16, u32) -> Result<()> + Send> =
668673
Box::new(|_, _| -> Result<()> { Ok(()) });
669674
Arc::new(Mutex::new(OutBHandler::from(func)))
670675
};

src/hyperlight_host/src/sandbox/mem_mgr.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,39 @@ pub type StackCookie = [u8; STACK_COOKIE_LEN];
2929
/// A container with methods for accessing `SandboxMemoryManager` and other
3030
/// related objects
3131
#[derive(Clone)]
32-
pub(crate) struct MemMgrWrapper<S>(SandboxMemoryManager<S>, StackCookie);
32+
pub(crate) struct MemMgrWrapper<S> {
33+
mgr: SandboxMemoryManager<S>,
34+
stack_cookie: StackCookie,
35+
abort_buffer: Vec<u8>,
36+
}
3337

3438
impl<S: SharedMemory> MemMgrWrapper<S> {
3539
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
3640
pub(super) fn new(mgr: SandboxMemoryManager<S>, stack_cookie: StackCookie) -> Self {
37-
Self(mgr, stack_cookie)
41+
Self {
42+
mgr,
43+
stack_cookie,
44+
abort_buffer: Vec::new(),
45+
}
3846
}
3947

4048
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
4149
pub(crate) fn unwrap_mgr(&self) -> &SandboxMemoryManager<S> {
42-
&self.0
50+
&self.mgr
4351
}
4452

4553
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
4654
pub(crate) fn unwrap_mgr_mut(&mut self) -> &mut SandboxMemoryManager<S> {
47-
&mut self.0
55+
&mut self.mgr
4856
}
4957

5058
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
5159
pub(super) fn get_stack_cookie(&self) -> &StackCookie {
52-
&self.1
60+
&self.stack_cookie
61+
}
62+
63+
pub fn get_abort_buffer_mut(&mut self) -> &mut Vec<u8> {
64+
&mut self.abort_buffer
5365
}
5466
}
5567

@@ -74,8 +86,8 @@ impl MemMgrWrapper<ExclusiveSharedMemory> {
7486
MemMgrWrapper<HostSharedMemory>,
7587
SandboxMemoryManager<GuestSharedMemory>,
7688
) {
77-
let (hshm, gshm) = self.0.build();
78-
(MemMgrWrapper(hshm, self.1), gshm)
89+
let (hshm, gshm) = self.mgr.build();
90+
(MemMgrWrapper::new(hshm, self.stack_cookie), gshm)
7991
}
8092

8193
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]

0 commit comments

Comments
 (0)