[][src]Macro sunrise_kernel::generate_trap_gate_handler

macro_rules! generate_trap_gate_handler {
    (__gen kernel_fault; name: $exception_name:literal, $hwcontext:ident, errcode: true, strategy: panic) => { ... };
    (__gen kernel_fault; name: $exception_name:literal, $hwcontext:ident, errcode: false, strategy: panic) => { ... };
    (__gen user_fault; name: $exception_name:literal, $hwcontext:ident, errcode: true, strategy: panic) => { ... };
    (__gen user_fault; name: $exception_name:literal, $hwcontext:ident, errcode: false, strategy: panic) => { ... };
    (__gen handler; name: $exception_name:literal, $hwcontext:ident, errcode: true, strategy: panic) => { ... };
    (__gen handler; name: $exception_name:literal, $hwcontext:ident, errcode: false, strategy: panic) => { ... };
    (__gen handler; name: $exception_name:literal, $hwcontext:ident, errcode: true, strategy: kill) => { ... };
    (__gen handler; name: $exception_name:literal, $hwcontext:ident, errcode: false, strategy: kill) => { ... };
    (__gen $_all:ident; name: $_exception_name:literal, $_hwcontext:ident, errcode: $_errcode:ident, strategy: ignore) => { ... };
    (__gen $_all:ident; name: $exception_name:literal, $hwcontext:ident, errcode: $errcode:ident, strategy: $fnname:ident) => { ... };
    (__gen asm_wrapper; $wrapper_asm_fnname:ident, $wrapper_rust_fnname:ident, $errcode:ident) => { ... };
    (
    name: $exception_name:literal,
    has_errcode: $has_errcode:ident,
    wrapper_asm_fnname: $wrapper_asm_fnname:ident,
    wrapper_rust_fnname: $wrapper_rust_fnname:ident,
    kernel_fault_strategy: $kernel_fault_strategy:ident,
    user_fault_strategy: $user_fault_strategy:ident,
    handler_strategy: $handler_strategy:ident
    ) => { ... };
    (
    name: $exception_name:literal,
    has_errcode: $has_errcode:ident,
    wrapper_asm_fnname: $wrapper_asm_fnname:ident,
    wrapper_rust_fnname: $wrapper_rust_fnname:ident,
    kernel_fault_strategy: $kernel_fault_strategy:ident,
    user_fault_strategy: $user_fault_strategy:ident,
    handler_strategy: $handler_strategy:ident,
    interrupt_context: $interrupt_context:literal
    ) => { ... };
}

Generates a trap/interrupt gate isr.

Goal

This macro generates a handler for a trap/interrupt gate that will:

  1. save userspace hardware context in the ThreadStruct
  2. check boilerplate conditions like if the kernel generated the instruction, or if "panic-on-exception" is on.
  3. call a function to handle the interrupt
  4. check if the current process was killed, in which case unschedule instead ourselves of returning
  5. restore the userspace context
  6. 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.
);

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();
    }
}