@@ -21,46 +21,171 @@ use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
2121use hyperlight_common:: outb:: Exception ;
2222use hyperlight_guest:: exit:: abort_with_code_and_message;
2323
24+ /// Error type for handler installation failures
25+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
26+ pub enum InstallError {
27+ /// Exception vector must be in range 0-30 (architecture-defined exceptions only)
28+ InvalidVector ,
29+ /// A handler is already installed for this vector
30+ HandlerAlreadyInstalled ,
31+ }
32+
33+ /// Exception information pushed onto the stack by the Hyperlight exception handler.
34+ ///
2435/// See AMD64 Architecture Programmer's Manual, Volume 2
2536/// §8.9.3 Interrupt Stack Frame, pp. 283--284
2637/// Figure 8-14: Long-Mode Stack After Interrupt---Same Privilege,
2738/// 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
39+ /// Note: For exceptions that don't provide an error code, we push a dummy value of 0.
3040#[ repr( C ) ]
3141pub struct ExceptionInfo {
42+ /// Error code provided by the processor (or 0 if not applicable).
3243 pub error_code : u64 ,
44+ /// Instruction pointer at the time of the exception.
3345 pub rip : u64 ,
46+ /// Code segment selector.
3447 pub cs : u64 ,
48+ /// CPU flags register.
3549 pub rflags : u64 ,
50+ /// Stack pointer at the time of the exception.
3651 pub rsp : u64 ,
52+ /// Stack segment selector.
3753 pub ss : u64 ,
3854}
3955const _: ( ) = assert ! ( core:: mem:: offset_of!( ExceptionInfo , rip) == 8 ) ;
4056const _: ( ) = assert ! ( core:: mem:: offset_of!( ExceptionInfo , rsp) == 32 ) ;
4157
58+ /// Saved CPU context pushed onto the stack by exception entry code.
59+ ///
60+ /// This structure contains all the saved CPU state needed to resume execution
61+ /// after handling an exception. It includes segment registers, floating-point state,
62+ /// and general-purpose registers.
4263#[ repr( C ) ]
43- /// Saved context, pushed onto the stack by exception entry code
4464pub struct Context {
45- /// in order: gs, fs, es
46- pub segments : [ u64 ; 3 ] ,
65+ /// Segment registers in order: GS, FS, ES, DS.
66+ pub segments : [ u64 ; 4 ] ,
67+ /// FPU/SSE state saved via FXSAVE instruction (512 bytes).
4768 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
69+ /// General-purpose registers (RAX through R15, excluding RSP).
70+ ///
71+ /// The stack pointer (RSP) is not included here since it's saved
72+ /// by the processor in the `ExceptionInfo` structure.
73+ /// RAX is at index 0, R15 is at index 14.
5174 pub gprs : [ u64 ; 15 ] ,
75+ /// Padding to ensure 16-byte alignment when combined with ExceptionInfo.
76+ padding : [ u64 ; 1 ] ,
5277}
53- const _: ( ) = assert ! ( size_of:: <Context >( ) == 152 + 512 ) ;
78+ const _: ( ) = assert ! ( size_of:: <Context >( ) == 32 + 512 + 120 + 8 ) ;
79+ const _: ( ) = assert ! ( ( size_of:: <Context >( ) + size_of:: <ExceptionInfo >( ) ) % 16 == 0 ) ;
5480
55- // TODO: This will eventually need to end up in a per-thread context,
56- // when there are threads.
57- pub static HANDLERS : [ core:: sync:: atomic:: AtomicU64 ; 31 ] =
81+ /// Array of installed exception handlers for vectors 0-30.
82+ ///
83+ /// TODO: This will eventually need to be part of a per-thread context when threading is implemented.
84+ static HANDLERS : [ core:: sync:: atomic:: AtomicU64 ; 31 ] =
5885 [ const { core:: sync:: atomic:: AtomicU64 :: new ( 0 ) } ; 31 ] ;
59- pub type HandlerT = fn ( n : u64 , info : * mut ExceptionInfo , ctx : * mut Context , pf_addr : u64 ) -> bool ;
6086
61- /// Exception handler
87+ /// Exception handler function type.
88+ ///
89+ /// Handlers receive mutable pointers to the exception information and CPU context,
90+ /// allowing direct access and modification of exception state.
91+ ///
92+ /// # Parameters
93+ /// * `exception_number` - Exception vector number (0-30)
94+ /// * `exception_info` - Mutable pointer to exception information (instruction pointer, error code, etc.)
95+ /// * `context` - Mutable pointer to saved CPU context (registers, FPU state, etc.)
96+ /// * `page_fault_address` - Page fault address (only valid for page fault exceptions)
97+ ///
98+ /// # Returns
99+ /// * `true` - Suppress the default abort behavior and continue execution
100+ /// * `false` - Allow the default abort to occur
101+ ///
102+ /// # Safety
103+ /// This function type uses raw mutable pointers. Handlers must ensure:
104+ /// - Pointers are valid for the duration of the handler
105+ /// - Any modifications to exception state maintain system integrity
106+ /// - Modified values are valid for CPU state (e.g., valid instruction pointers, aligned stack pointers)
107+ pub type ExceptionHandler = fn (
108+ exception_number : u64 ,
109+ exception_info : * mut ExceptionInfo ,
110+ context : * mut Context ,
111+ page_fault_address : u64 ,
112+ ) -> bool ;
113+
114+ /// Install a custom exception handler for a specific vector.
115+ ///
116+ /// # Arguments
117+ /// * `vector` - Exception vector (0-30). Must be an architecture-defined exception.
118+ /// * `handler` - The handler function to invoke when this exception occurs.
119+ ///
120+ /// # Returns
121+ /// * `Ok(())` if the handler was successfully installed.
122+ /// * `Err(InstallError::InvalidVector)` if `vector >= 31`.
123+ /// * `Err(InstallError::HandlerAlreadyInstalled)` if a handler is already registered for this vector.
124+ ///
125+ /// # Example
126+ /// ```ignore
127+ /// fn my_exception_handler(
128+ /// exception_number: u64,
129+ /// exception_info: *mut ExceptionInfo,
130+ /// context: *mut Context,
131+ /// page_fault_address: u64,
132+ /// ) -> bool {
133+ /// unsafe {
134+ /// // Read the faulting instruction pointer
135+ /// let faulting_rip = core::ptr::read_volatile(&(*exception_info).rip);
136+ ///
137+ /// // Save the original RIP to R9 register
138+ /// core::ptr::write_volatile(&mut (*context).gprs[8], faulting_rip);
139+ ///
140+ /// // Skip past the faulting instruction
141+ /// core::ptr::write_volatile(&mut (*exception_info).rip, faulting_rip + 2);
142+ /// }
143+ ///
144+ /// true // Return true to suppress abort and continue execution
145+ /// }
146+ ///
147+ /// install_handler(3, my_exception_handler)?; // Install for INT3 (breakpoint)
148+ /// ```
149+ pub fn install_handler ( vector : u8 , handler : ExceptionHandler ) -> Result < ( ) , InstallError > {
150+ if vector >= 31 {
151+ return Err ( InstallError :: InvalidVector ) ;
152+ }
153+
154+ // Use compare_exchange to atomically check and set, preventing races
155+ match HANDLERS [ vector as usize ] . compare_exchange (
156+ 0 ,
157+ handler as usize as u64 ,
158+ core:: sync:: atomic:: Ordering :: AcqRel ,
159+ core:: sync:: atomic:: Ordering :: Acquire ,
160+ ) {
161+ Ok ( _) => Ok ( ( ) ) ,
162+ Err ( _) => Err ( InstallError :: HandlerAlreadyInstalled ) ,
163+ }
164+ }
165+
166+ /// Remove a custom exception handler for a specific vector.
167+ ///
168+ /// # Arguments
169+ /// * `vector` - Exception vector (0-30).
170+ ///
171+ /// # Returns
172+ /// * `Ok(())` if the handler was successfully removed (or was already uninstalled).
173+ /// * `Err(InstallError::InvalidVector)` if `vector >= 31`.
174+ pub fn uninstall_handler ( vector : u8 ) -> Result < ( ) , InstallError > {
175+ if vector >= 31 {
176+ return Err ( InstallError :: InvalidVector ) ;
177+ }
178+
179+ HANDLERS [ vector as usize ] . store ( 0 , core:: sync:: atomic:: Ordering :: Release ) ;
180+ Ok ( ( ) )
181+ }
182+
183+ /// Internal exception handler invoked by the low-level exception entry code.
184+ ///
185+ /// This function is called from assembly when an exception occurs. It checks for
186+ /// registered user handlers and either invokes them or aborts with an error message.
62187#[ unsafe( no_mangle) ]
63- pub extern "C" fn hl_exception_handler (
188+ pub ( crate ) extern "C" fn hl_exception_handler (
64189 stack_pointer : u64 ,
65190 exception_number : u64 ,
66191 page_fault_address : u64 ,
@@ -82,20 +207,18 @@ pub extern "C" fn hl_exception_handler(
82207 exception_number, saved_rip, page_fault_address, error_code, stack_pointer
83208 ) ;
84209
85- // We don't presently have any need for user-defined interrupts,
86- // so we only support handlers for the architecture-defined
87- // vectors (0-31)
210+ // Check for registered user handlers (only for architecture-defined vectors 0-30)
88211 if exception_number < 31 {
89212 let handler =
90213 HANDLERS [ exception_number as usize ] . load ( core:: sync:: atomic:: Ordering :: Acquire ) ;
91- if handler != 0
92- && unsafe {
93- core:: mem:: transmute :: < u64 , fn ( u64 , * mut ExceptionInfo , * mut Context , u64 ) -> bool > (
94- handler ,
95- ) ( exception_number , exn_info , ctx , page_fault_address )
96- }
97- {
98- return ;
214+ if handler != 0 {
215+ unsafe {
216+ let handler = core:: mem:: transmute :: < u64 , ExceptionHandler > ( handler ) ;
217+ if handler ( exception_number , exn_info , ctx , page_fault_address ) {
218+ return ;
219+ }
220+ // Handler returned false, fall through to abort
221+ } ;
99222 }
100223 }
101224
0 commit comments