|
| 1 | +/* |
| 2 | +Copyright 2024 The Hyperlight Authors. |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +use core::arch::asm; |
| 18 | +use core::ffi::{c_char, CStr}; |
| 19 | + |
| 20 | +use hyperlight_common::outb::OutBAction; |
| 21 | + |
| 22 | +/// Halt the execution of the guest and returns control to the host. |
| 23 | +#[inline(never)] |
| 24 | +pub fn halt() { |
| 25 | + unsafe { asm!("hlt", options(nostack)) } |
| 26 | +} |
| 27 | + |
| 28 | +/// Exits the VM with an Abort OUT action and code 0. |
| 29 | +#[unsafe(no_mangle)] |
| 30 | +pub extern "C" fn abort() -> ! { |
| 31 | + abort_with_code(&[0, 0xFF]) |
| 32 | +} |
| 33 | + |
| 34 | +/// Exits the VM with an Abort OUT action and a specific code. |
| 35 | +pub fn abort_with_code(code: &[u8]) -> ! { |
| 36 | + outb(OutBAction::Abort as u16, code); |
| 37 | + outb(OutBAction::Abort as u16, &[0xFF]); // send abort terminator (if not included in code) |
| 38 | + unreachable!() |
| 39 | +} |
| 40 | + |
| 41 | +/// Aborts the program with a code and a message. |
| 42 | +/// |
| 43 | +/// # Safety |
| 44 | +/// This function is unsafe because it dereferences a raw pointer. |
| 45 | +pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_char) -> ! { |
| 46 | + unsafe { |
| 47 | + // Step 1: Send abort code (typically 1 byte, but `code` allows flexibility) |
| 48 | + outb(OutBAction::Abort as u16, code); |
| 49 | + |
| 50 | + // Step 2: Convert the C string to bytes |
| 51 | + let message_bytes = CStr::from_ptr(message_ptr).to_bytes(); // excludes null terminator |
| 52 | + |
| 53 | + // Step 3: Send the message itself in chunks |
| 54 | + outb(OutBAction::Abort as u16, message_bytes); |
| 55 | + |
| 56 | + // Step 4: Send abort terminator to signal completion (e.g., 0xFF) |
| 57 | + outb(OutBAction::Abort as u16, &[0xFF]); |
| 58 | + |
| 59 | + // This function never returns |
| 60 | + unreachable!() |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +/// OUT bytes to the host through multiple exits. |
| 65 | +pub(crate) fn outb(port: u16, data: &[u8]) { |
| 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 | +/// OUT function for sending a 32-bit value to the host. |
| 82 | +pub(crate) unsafe fn out32(port: u16, val: u32) { |
| 83 | + unsafe { |
| 84 | + asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack)); |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +/// Prints a message using `OutBAction::DebugPrint`. It transmits bytes of a message |
| 89 | +/// through several VMExists and, with such, it is slower than |
| 90 | +/// `print_output_with_host_print`. |
| 91 | +/// |
| 92 | +/// This function should be used in debug mode only. This function does not |
| 93 | +/// require memory to be setup to be used. |
| 94 | +pub fn debug_print(msg: &str) { |
| 95 | + for byte in msg.bytes() { |
| 96 | + unsafe { |
| 97 | + out32(OutBAction::DebugPrint as u16, byte as u32); |
| 98 | + } |
| 99 | + } |
| 100 | +} |
0 commit comments