hyperlight_guest_bin/
lib.rs

1/*
2Copyright 2025  The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16#![no_std]
17
18// === Dependencies ===
19extern crate alloc;
20
21use core::fmt::Write;
22
23use buddy_system_allocator::LockedHeap;
24#[cfg(target_arch = "x86_64")]
25use exceptions::{gdt::load_gdt, idtr::load_idt};
26use guest_function::call::dispatch_function;
27use guest_function::register::GuestFunctionRegister;
28use guest_logger::init_logger;
29use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
30use hyperlight_common::mem::HyperlightPEB;
31#[cfg(feature = "mem_profile")]
32use hyperlight_common::outb::OutBAction;
33use hyperlight_guest::exit::{halt, write_abort};
34use hyperlight_guest::guest_handle::handle::GuestHandle;
35use log::LevelFilter;
36use spin::Once;
37
38// === Modules ===
39#[cfg(target_arch = "x86_64")]
40pub mod exceptions {
41    pub(super) mod gdt;
42    pub mod handler;
43    mod idt;
44    pub(super) mod idtr;
45    mod interrupt_entry;
46}
47pub mod guest_function {
48    pub(super) mod call;
49    pub mod definition;
50    pub mod register;
51}
52
53pub mod guest_logger;
54pub mod host_comm;
55pub mod memory;
56pub mod paging;
57
58// Globals
59#[cfg(feature = "mem_profile")]
60struct ProfiledLockedHeap<const ORDER: usize>(LockedHeap<ORDER>);
61#[cfg(feature = "mem_profile")]
62unsafe impl<const ORDER: usize> alloc::alloc::GlobalAlloc for ProfiledLockedHeap<ORDER> {
63    unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
64        let addr = unsafe { self.0.alloc(layout) };
65        unsafe {
66            core::arch::asm!("out dx, al",
67                in("dx") OutBAction::TraceMemoryAlloc as u16,
68                in("rax") layout.size() as u64,
69                in("rcx") addr as u64);
70        }
71        addr
72    }
73    unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
74        unsafe {
75            core::arch::asm!("out dx, al",
76                in("dx") OutBAction::TraceMemoryFree as u16,
77                in("rax") layout.size() as u64,
78                in("rcx") ptr as u64);
79            self.0.dealloc(ptr, layout)
80        }
81    }
82    unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 {
83        let addr = unsafe { self.0.alloc_zeroed(layout) };
84        unsafe {
85            core::arch::asm!("out dx, al",
86                in("dx") OutBAction::TraceMemoryAlloc as u16,
87                in("rax") layout.size() as u64,
88                in("rcx") addr as u64);
89        }
90        addr
91    }
92    unsafe fn realloc(
93        &self,
94        ptr: *mut u8,
95        layout: core::alloc::Layout,
96        new_size: usize,
97    ) -> *mut u8 {
98        let new_ptr = unsafe { self.0.realloc(ptr, layout, new_size) };
99        unsafe {
100            core::arch::asm!("out dx, al",
101                in("dx") OutBAction::TraceMemoryFree as u16,
102                in("rax") layout.size() as u64,
103                in("rcx") ptr);
104            core::arch::asm!("out dx, al",
105                in("dx") OutBAction::TraceMemoryAlloc as u16,
106                in("rax") new_size as u64,
107                in("rcx") new_ptr);
108        }
109        new_ptr
110    }
111}
112
113// === Globals ===
114#[cfg(not(feature = "mem_profile"))]
115#[global_allocator]
116pub(crate) static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty();
117#[cfg(feature = "mem_profile")]
118#[global_allocator]
119pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap<32> =
120    ProfiledLockedHeap(LockedHeap::<32>::empty());
121
122pub static mut GUEST_HANDLE: GuestHandle = GuestHandle::new();
123pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister =
124    GuestFunctionRegister::new();
125
126pub static mut MIN_STACK_ADDRESS: u64 = 0;
127
128pub static mut OS_PAGE_SIZE: u32 = 0;
129
130// === Panic Handler ===
131// It looks like rust-analyzer doesn't correctly manage no_std crates,
132// and so it displays an error about a duplicate panic_handler.
133// See more here: https://github.com/rust-lang/rust-analyzer/issues/4490
134// The cfg_attr attribute is used to avoid clippy failures as test pulls in std which pulls in a panic handler
135#[cfg_attr(not(test), panic_handler)]
136#[allow(clippy::panic)]
137// to satisfy the clippy when cfg == test
138#[allow(dead_code)]
139fn panic(info: &core::panic::PanicInfo) -> ! {
140    _panic_handler(info)
141}
142
143/// A writer that sends all output to the hyperlight host
144/// using output ports. This allows us to not impose a
145/// buffering limit on error message size on the guest end,
146/// though one exists for the host.
147struct HyperlightAbortWriter;
148impl core::fmt::Write for HyperlightAbortWriter {
149    fn write_str(&mut self, s: &str) -> core::fmt::Result {
150        write_abort(s.as_bytes());
151        Ok(())
152    }
153}
154
155#[inline(always)]
156fn _panic_handler(info: &core::panic::PanicInfo) -> ! {
157    let mut w = HyperlightAbortWriter;
158
159    // begin abort sequence by writing the error code
160    write_abort(&[ErrorCode::UnknownError as u8]);
161
162    let write_res = write!(w, "{}", info);
163    if write_res.is_err() {
164        write_abort("panic: message format failed".as_bytes());
165    }
166
167    // write abort terminator to finish the abort
168    // and signal to the host that the message can now be read
169    write_abort(&[0xFF]);
170    unreachable!();
171}
172
173// === Entrypoint ===
174
175unsafe extern "C" {
176    fn hyperlight_main();
177    fn srand(seed: u32);
178}
179
180static INIT: Once = Once::new();
181
182#[unsafe(no_mangle)]
183pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_level: u64) {
184    // Save the guest start TSC for tracing
185    #[cfg(feature = "trace_guest")]
186    let guest_start_tsc = hyperlight_guest_tracing::invariant_tsc::read_tsc();
187
188    if peb_address == 0 {
189        panic!("PEB address is null");
190    }
191
192    INIT.call_once(|| {
193        unsafe {
194            GUEST_HANDLE = GuestHandle::init(peb_address as *mut HyperlightPEB);
195            #[allow(static_mut_refs)]
196            let peb_ptr = GUEST_HANDLE.peb().unwrap();
197
198            let srand_seed = (((peb_address << 8) ^ (seed >> 4)) >> 32) as u32;
199
200            // Set the seed for the random number generator for C code using rand;
201            srand(srand_seed);
202
203            // This static is to make it easier to implement the __chkstk function in assembly.
204            // It also means that should we change the layout of the struct in the future, we
205            // don't have to change the assembly code.
206            MIN_STACK_ADDRESS = (*peb_ptr).guest_stack.min_user_stack_address;
207
208            #[cfg(target_arch = "x86_64")]
209            {
210                // Setup GDT and IDT
211                load_gdt();
212                load_idt();
213            }
214
215            let heap_start = (*peb_ptr).guest_heap.ptr as usize;
216            let heap_size = (*peb_ptr).guest_heap.size as usize;
217            #[cfg(not(feature = "mem_profile"))]
218            let heap_allocator = &HEAP_ALLOCATOR;
219            #[cfg(feature = "mem_profile")]
220            let heap_allocator = &HEAP_ALLOCATOR.0;
221            heap_allocator
222                .try_lock()
223                .expect("Failed to access HEAP_ALLOCATOR")
224                .init(heap_start, heap_size);
225
226            OS_PAGE_SIZE = ops as u32;
227
228            (*peb_ptr).guest_function_dispatch_ptr = dispatch_function as usize as u64;
229
230            // set up the logger
231            let max_log_level = LevelFilter::iter()
232                .nth(max_log_level as usize)
233                .expect("Invalid log level");
234            init_logger(max_log_level);
235
236            // It is important that all the tracing events are produced after the tracing is initialized.
237            #[cfg(feature = "trace_guest")]
238            if max_log_level != LevelFilter::Off {
239                hyperlight_guest_tracing::init_guest_tracing(guest_start_tsc);
240            }
241
242            #[cfg(feature = "macros")]
243            for registration in __private::GUEST_FUNCTION_INIT {
244                registration();
245            }
246
247            hyperlight_main();
248        }
249    });
250
251    halt();
252}
253
254#[cfg(feature = "macros")]
255#[doc(hidden)]
256pub mod __private {
257    pub use hyperlight_common::func::ResultType;
258    pub use hyperlight_guest::error::HyperlightGuestError;
259    pub use linkme;
260
261    #[linkme::distributed_slice]
262    pub static GUEST_FUNCTION_INIT: [fn()];
263
264    pub trait FromResult {
265        type Output;
266        fn from_result(res: Result<Self::Output, HyperlightGuestError>) -> Self;
267    }
268
269    use alloc::string::String;
270    use alloc::vec::Vec;
271
272    use hyperlight_common::for_each_return_type;
273
274    macro_rules! impl_maybe_unwrap {
275        ($ty:ty, $enum:ident) => {
276            impl FromResult for $ty {
277                type Output = Self;
278                fn from_result(res: Result<Self::Output, HyperlightGuestError>) -> Self {
279                    // Unwrapping here is fine as this would only run in a guest
280                    // and not in the host.
281                    res.unwrap()
282                }
283            }
284
285            impl FromResult for Result<$ty, HyperlightGuestError> {
286                type Output = $ty;
287                fn from_result(res: Result<Self::Output, HyperlightGuestError>) -> Self {
288                    res
289                }
290            }
291        };
292    }
293
294    for_each_return_type!(impl_maybe_unwrap);
295}
296
297#[cfg(feature = "macros")]
298pub use hyperlight_guest_macro::{guest_function, host_function};