hyperlight_guest_bin/exceptions/
handler.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
17use alloc::format;
18use core::ffi::c_char;
19
20use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
21use hyperlight_common::outb::Exception;
22use hyperlight_guest::exit::abort_with_code_and_message;
23
24/// See AMD64 Architecture Programmer's Manual, Volume 2
25///     ยง8.9.3 Interrupt Stack Frame, pp. 283--284
26///       Figure 8-14: Long-Mode Stack After Interrupt---Same Privilege,
27///       Figure 8-15: Long-Mode Stack After Interrupt---Higher Privilege
28/// Subject to the proviso that we push a dummy error code of 0 for exceptions
29/// for which the processor does not provide one
30#[repr(C)]
31pub struct ExceptionInfo {
32    pub error_code: u64,
33    pub rip: u64,
34    pub cs: u64,
35    pub rflags: u64,
36    pub rsp: u64,
37    pub ss: u64,
38}
39const _: () = assert!(core::mem::offset_of!(ExceptionInfo, rip) == 8);
40const _: () = assert!(core::mem::offset_of!(ExceptionInfo, rsp) == 32);
41
42#[repr(C)]
43/// Saved context, pushed onto the stack by exception entry code
44pub struct Context {
45    /// in order: gs, fs, es
46    pub segments: [u64; 3],
47    pub fxsave: [u8; 512],
48    pub ds: u64,
49    /// no `rsp`, since the processor saved it
50    /// `rax` is at the top, `r15` the bottom
51    pub gprs: [u64; 15],
52}
53const _: () = assert!(size_of::<Context>() == 152 + 512);
54
55// TODO: This will eventually need to end up in a per-thread context,
56// when there are threads.
57pub static HANDLERS: [core::sync::atomic::AtomicU64; 31] =
58    [const { core::sync::atomic::AtomicU64::new(0) }; 31];
59pub type HandlerT = fn(n: u64, info: *mut ExceptionInfo, ctx: *mut Context, pf_addr: u64) -> bool;
60
61/// Exception handler
62#[unsafe(no_mangle)]
63pub extern "C" fn hl_exception_handler(
64    stack_pointer: u64,
65    exception_number: u64,
66    page_fault_address: u64,
67) {
68    // When using the `trace_function` macro, it wraps the function body with create_trace_record
69    // call, which generates a warning because of the `abort_with_code_and_message` call which does
70    // not return.
71    // This is manually added to avoid the warning.
72    hyperlight_guest_tracing::trace!("> hl_exception_handler");
73
74    let ctx = stack_pointer as *mut Context;
75    let exn_info = (stack_pointer + size_of::<Context>() as u64) as *mut ExceptionInfo;
76
77    let exception = Exception::try_from(exception_number as u8).expect("Invalid exception number");
78
79    let saved_rip = unsafe { (&raw const (*exn_info).rip).read_volatile() };
80    let error_code = unsafe { (&raw const (*exn_info).error_code).read_volatile() };
81
82    let msg = format!(
83        "Exception vector: {:#}\n\
84         Faulting Instruction: {:#x}\n\
85         Page Fault Address: {:#x}\n\
86         Error code: {:#x}\n\
87         Stack Pointer: {:#x}",
88        exception_number, saved_rip, page_fault_address, error_code, stack_pointer
89    );
90
91    // We don't presently have any need for user-defined interrupts,
92    // so we only support handlers for the architecture-defined
93    // vectors (0-31)
94    if exception_number < 31 {
95        let handler =
96            HANDLERS[exception_number as usize].load(core::sync::atomic::Ordering::Acquire);
97        if handler != 0
98            && unsafe {
99                core::mem::transmute::<u64, fn(u64, *mut ExceptionInfo, *mut Context, u64) -> bool>(
100                    handler,
101                )(exception_number, exn_info, ctx, page_fault_address)
102            }
103        {
104            hyperlight_guest_tracing::trace!("< hl_exception_handler");
105            return;
106        }
107    }
108
109    unsafe {
110        abort_with_code_and_message(
111            &[ErrorCode::GuestError as u8, exception as u8],
112            msg.as_ptr() as *const c_char,
113        );
114    }
115}