Skip to main content

hyperlight_guest/
exit.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025 The Hyperlight Authors.
3
4use core::ffi::{CStr, c_char};
5
6use hyperlight_common::outb::OutBAction;
7
8#[cfg_attr(target_arch = "x86_64", path = "arch/amd64/exit.rs")]
9#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/exit.rs")]
10mod arch;
11pub(crate) use arch::out32;
12
13/// Exits the VM with an Abort OUT action and code 0.
14#[unsafe(no_mangle)]
15pub extern "C" fn abort() -> ! {
16    abort_with_code(&[0, 0xFF])
17}
18
19/// Exits the VM with an Abort OUT action and a specific code.
20pub fn abort_with_code(code: &[u8]) -> ! {
21    // End any ongoing trace before aborting
22    #[cfg(all(feature = "trace_guest", target_arch = "x86_64"))]
23    hyperlight_guest_tracing::end_trace();
24    outb(OutBAction::Abort as u16, code);
25    outb(OutBAction::Abort as u16, &[0xFF]); // send abort terminator (if not included in code)
26    unreachable!()
27}
28
29/// Aborts the program with a code and a message.
30///
31/// # Safety
32/// This function is unsafe because it dereferences a raw pointer.
33pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_char) -> ! {
34    // End any ongoing trace before aborting
35    #[cfg(all(feature = "trace_guest", target_arch = "x86_64"))]
36    hyperlight_guest_tracing::end_trace();
37    unsafe {
38        // Step 1: Send abort code (typically 1 byte, but `code` allows flexibility)
39        outb(OutBAction::Abort as u16, code);
40
41        // Step 2: Convert the C string to bytes
42        let message_bytes = CStr::from_ptr(message_ptr).to_bytes(); // excludes null terminator
43
44        // Step 3: Send the message itself in chunks
45        outb(OutBAction::Abort as u16, message_bytes);
46
47        // Step 4: Send abort terminator to signal completion (e.g., 0xFF)
48        outb(OutBAction::Abort as u16, &[0xFF]);
49
50        // This function never returns
51        unreachable!()
52    }
53}
54
55/// This function exists to give the guest more manual control
56/// over the abort sequence. For example, in `hyperlight_guest_bin`'s panic handler,
57/// we have a message of unknown length that we want to stream
58/// to the host, which requires sending the message in chunks
59pub fn write_abort(code: &[u8]) {
60    outb(OutBAction::Abort as u16, code);
61}
62
63/// OUT bytes to the host through multiple exits.
64pub(crate) fn outb(port: u16, data: &[u8]) {
65    // Ensure all tracing data is flushed before sending OUT bytes
66    unsafe {
67        let mut i = 0;
68        while i < data.len() {
69            let remaining = data.len() - i;
70            let chunk_len = remaining.min(3);
71            let mut chunk = [0u8; 4];
72            chunk[0] = chunk_len as u8;
73            chunk[1..1 + chunk_len].copy_from_slice(&data[i..i + chunk_len]);
74            let val = u32::from_le_bytes(chunk);
75            out32(port, val);
76            i += chunk_len;
77        }
78    }
79}
80
81/// Prints a message using `OutBAction::DebugPrint`. It transmits bytes of a message
82/// through several VMExists and, with such, it is slower than
83/// `print_output_with_host_print`.
84///
85/// This function should be used in debug mode only. This function does not
86/// require memory to be setup to be used.
87pub fn debug_print(msg: &str) {
88    for byte in msg.bytes() {
89        unsafe {
90            out32(OutBAction::DebugPrint as u16, byte as u32);
91        }
92    }
93}