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 hyperlight_guest_tracing::{trace, trace_function};
36use log::LevelFilter;
37use spin::Once;
38
39// === Modules ===
40#[cfg(target_arch = "x86_64")]
41pub mod exceptions {
42    pub(super) mod gdt;
43    pub mod handler;
44    mod idt;
45    pub(super) mod idtr;
46    mod interrupt_entry;
47}
48pub mod guest_err;
49pub mod guest_function {
50    pub(super) mod call;
51    pub mod definition;
52    pub mod register;
53}
54
55pub mod guest_logger;
56pub mod host_comm;
57pub mod memory;
58pub mod paging;
59
60// Globals
61#[cfg(feature = "mem_profile")]
62struct ProfiledLockedHeap<const ORDER: usize>(LockedHeap<ORDER>);
63#[cfg(feature = "mem_profile")]
64unsafe impl<const ORDER: usize> alloc::alloc::GlobalAlloc for ProfiledLockedHeap<ORDER> {
65    unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
66        let addr = unsafe { self.0.alloc(layout) };
67        unsafe {
68            core::arch::asm!("out dx, al",
69                in("dx") OutBAction::TraceMemoryAlloc as u16,
70                in("rax") layout.size() as u64,
71                in("rcx") addr as u64);
72        }
73        addr
74    }
75    unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
76        unsafe {
77            core::arch::asm!("out dx, al",
78                in("dx") OutBAction::TraceMemoryFree as u16,
79                in("rax") layout.size() as u64,
80                in("rcx") ptr as u64);
81            self.0.dealloc(ptr, layout)
82        }
83    }
84    unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 {
85        let addr = unsafe { self.0.alloc_zeroed(layout) };
86        unsafe {
87            core::arch::asm!("out dx, al",
88                in("dx") OutBAction::TraceMemoryAlloc as u16,
89                in("rax") layout.size() as u64,
90                in("rcx") addr as u64);
91        }
92        addr
93    }
94    unsafe fn realloc(
95        &self,
96        ptr: *mut u8,
97        layout: core::alloc::Layout,
98        new_size: usize,
99    ) -> *mut u8 {
100        let new_ptr = unsafe { self.0.realloc(ptr, layout, new_size) };
101        unsafe {
102            core::arch::asm!("out dx, al",
103                in("dx") OutBAction::TraceMemoryFree as u16,
104                in("rax") layout.size() as u64,
105                in("rcx") ptr);
106            core::arch::asm!("out dx, al",
107                in("dx") OutBAction::TraceMemoryAlloc as u16,
108                in("rax") new_size as u64,
109                in("rcx") new_ptr);
110        }
111        new_ptr
112    }
113}
114
115// === Globals ===
116#[cfg(not(feature = "mem_profile"))]
117#[global_allocator]
118pub(crate) static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty();
119#[cfg(feature = "mem_profile")]
120#[global_allocator]
121pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap<32> =
122    ProfiledLockedHeap(LockedHeap::<32>::empty());
123
124pub(crate) static mut GUEST_HANDLE: GuestHandle = GuestHandle::new();
125pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister =
126    GuestFunctionRegister::new();
127
128pub static mut MIN_STACK_ADDRESS: u64 = 0;
129
130pub static mut OS_PAGE_SIZE: u32 = 0;
131
132// === Panic Handler ===
133// It looks like rust-analyzer doesn't correctly manage no_std crates,
134// and so it displays an error about a duplicate panic_handler.
135// See more here: https://github.com/rust-lang/rust-analyzer/issues/4490
136// The cfg_attr attribute is used to avoid clippy failures as test pulls in std which pulls in a panic handler
137#[cfg_attr(not(test), panic_handler)]
138#[allow(clippy::panic)]
139// to satisfy the clippy when cfg == test
140#[allow(dead_code)]
141fn panic(info: &core::panic::PanicInfo) -> ! {
142    _panic_handler(info)
143}
144
145/// A writer that sends all output to the hyperlight host
146/// using output ports. This allows us to not impose a
147/// buffering limit on error message size on the guest end,
148/// though one exists for the host.
149struct HyperlightAbortWriter;
150impl core::fmt::Write for HyperlightAbortWriter {
151    fn write_str(&mut self, s: &str) -> core::fmt::Result {
152        write_abort(s.as_bytes());
153        Ok(())
154    }
155}
156
157#[inline(always)]
158fn _panic_handler(info: &core::panic::PanicInfo) -> ! {
159    let mut w = HyperlightAbortWriter;
160
161    // begin abort sequence by writing the error code
162    write_abort(&[ErrorCode::UnknownError as u8]);
163
164    let write_res = write!(w, "{}", info);
165    if write_res.is_err() {
166        write_abort("panic: message format failed".as_bytes());
167    }
168
169    // write abort terminator to finish the abort
170    // and signal to the host that the message can now be read
171    write_abort(&[0xFF]);
172    unreachable!();
173}
174
175// === Entrypoint ===
176
177unsafe extern "C" {
178    fn hyperlight_main();
179    fn srand(seed: u32);
180}
181
182static INIT: Once = Once::new();
183
184#[unsafe(no_mangle)]
185#[trace_function]
186pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_level: u64) {
187    if peb_address == 0 {
188        panic!("PEB address is null");
189    }
190
191    INIT.call_once(|| {
192        unsafe {
193            GUEST_HANDLE = GuestHandle::init(peb_address as *mut HyperlightPEB);
194            #[allow(static_mut_refs)]
195            let peb_ptr = GUEST_HANDLE.peb().unwrap();
196
197            let srand_seed = (((peb_address << 8) ^ (seed >> 4)) >> 32) as u32;
198
199            // Set the seed for the random number generator for C code using rand;
200            srand(srand_seed);
201
202            // This static is to make it easier to implement the __chkstk function in assembly.
203            // It also means that should we change the layout of the struct in the future, we
204            // don't have to change the assembly code.
205            MIN_STACK_ADDRESS = (*peb_ptr).guest_stack.min_user_stack_address;
206
207            #[cfg(target_arch = "x86_64")]
208            {
209                // Setup GDT and IDT
210                load_gdt();
211                load_idt();
212            }
213
214            let heap_start = (*peb_ptr).guest_heap.ptr as usize;
215            let heap_size = (*peb_ptr).guest_heap.size as usize;
216            #[cfg(not(feature = "mem_profile"))]
217            let heap_allocator = &HEAP_ALLOCATOR;
218            #[cfg(feature = "mem_profile")]
219            let heap_allocator = &HEAP_ALLOCATOR.0;
220            heap_allocator
221                .try_lock()
222                .expect("Failed to access HEAP_ALLOCATOR")
223                .init(heap_start, heap_size);
224
225            OS_PAGE_SIZE = ops as u32;
226
227            (*peb_ptr).guest_function_dispatch_ptr = dispatch_function as usize as u64;
228
229            // set up the logger
230            let max_log_level = LevelFilter::iter()
231                .nth(max_log_level as usize)
232                .expect("Invalid log level");
233            init_logger(max_log_level);
234
235            trace!("hyperlight_main",
236                hyperlight_main();
237            );
238        }
239    });
240
241    halt();
242}