[−][src]Macro sunrise_kernel::generate_trap_gate_handler
Generates a trap/interrupt gate isr.
Goal
This macro generates a handler for a trap/interrupt gate that will:
- save userspace hardware context in the ThreadStruct
- check boilerplate conditions like if the kernel generated the instruction, or if "panic-on-exception" is on.
- call a function to handle the interrupt
- check if the current process was killed, in which case unschedule instead ourselves of returning
- restore the userspace context
iret
This macro is designed to be modular, the idea being that every exception does pretty much the same thing, but in a slightly different way. Because of this we want the step 2 and 3 to be parameterizable.
The way we do this is defining a few standard strategies for step 2 and 3, letting the user choose which one it wants, and also letting the user override those strategies if they do not fit its use case.
The macro uses trap_gate_asm
as a the low-level asm handler.
Usage
You are expected to use this macro in the following way:
generate_trap_gate_handler!(name: "BOUND Range Exceeded Exception", // name of this interrupt, used for logging and when panicking. has_errcode: false, // whether the cpu pushes an error code on the stack for this interrupt. wrapper_asm_fnname: bound_range_exceeded_exception_asm_wrapper, // name for the raw asm function this macro will generate. You can then put this function's address in the IDT. wrapper_rust_fnname: bound_range_exceeded_exception_rust_wrapper, // name for the high-level rust handler this macro will generate. kernel_fault_strategy: panic, // what to do if we were in kernelspace when this interruption happened. user_fault_strategy: panic, // what to do if we were in userspace when this interruption happened, and feature "panic-on-exception" is enabled. handler_strategy: kill, // what to for this interrupt otherwise interrupt_context: false // OPTIONAL: basically: are IRQs disabled. False by default, true used for IRQs. );
- The possible values for
kernel_fault_strategy
anduser_fault_strategy
are:panic
: causes a kernel panic.ignore
: don't do anything for this condition.my_handler_func
: callsmy_handler_func
to handle this condition. Useful if you want to override a standard strategy.
- The possible values for
handler_strategy
are:panic
: causes a kernel panic.ignore
: don't do anything for this interrupt.kill
: kills the process in which this interrupt originated.my_handler_func
: callsmy_handler_func
to handle this interrupt. Useful if you want to override a standard strategy.
When providing a custom function as strategy, the function must be of signature:
fn my_handler_func(exception_name: &'static str, hwcontext: &mut UserspaceHardwareContext, has_errcode: bool)
The UserspaceHardwareContext saved by the wrapper is passed by mut reference so the handler can modify it.
Those modifications will be effective as soon as we iret
.
Generates
This will generate some code along the lines of:
#[naked] extern "C" fn $wrapper_asm_fnname() { unsafe { llvm_asm!(interrupt_gate_llvm_asm!(has_errorcode: $has_errcode) :: "s"($wrapper_rust_fnname as extern "C" fn (&mut UserspaceHardwareContext) : "memory" : "volatile", "intel"); } } extern "C" fn $wrapper_rust_fnname(userspace_context: &mut UserspaceHardwareContext) { if coming from Ring == 0 { kernel_panic(&PanicOrigin::KernelFault { // exception_message: format_args!("{}, exception errcode: {:?}", // $exception_name, // kernel_fault_strategy userspace_context.errcode), // (here: panic) kernel_hardware_context: userspace_context.clone() // }); // } else { // we come from userspace, backup the hardware context in the thread struct { *get_current_thread().userspace_hwcontext.lock() = *userspace_context } if cfg!(feature = "panic-on-exception") { kernel_panic(&PanicOrigin::UserspaceFault { // exception_message: format_args ! ("{}, exception errcode: {:?}", // $exception_name, // user_fault_strategy userspace_context.errcode), // (here: panic) userspace_hardware_context: userspace_context.clone() // }); // } } // do the handler { let thread = get_current_thread(); // error!("{}, errorcode: {}, in {:#?}", // handler_strategy $exception_name, $hwcontext.errcode, thread); // (here: kill) ProcessStruct::kill_current_process(); // } // if we're returning to userspace, check we haven't been killed if comming from Ring == 3 { check_thread_killed(); } }