1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! Signal handlers for CPU exceptions/errors, like POSIX-style signals, but safer and Rusty.
//! 
//! Instead of directly supporting POSIX signals like SIGSEGV, SIGILL, etc,
//! this crate only supports a few categories of signals that represent a CPU/machine exception,
//! e.g., divide errors, page faults, illegal instructions, etc.
//! 
//! Each task can [register] up to one signal handler per signal kind.
//! If/when that exception occurs, the full [context] of that exception is provided as an argument
//! to the registered signal handler.
//! 
//! Signal handlers can only be invoked once. If an exception occurs, it is up to the task logic
//! to re-register that signal handler again. 
//! 
//! [register]: register_signal_handler
//! [context]: SignalContext

#![no_std]
#![feature(trait_alias, variant_count)]

extern crate alloc;


use alloc::boxed::Box;
use core::cell::RefCell;
use memory::VirtualAddress;
use x86_64::structures::idt::PageFaultErrorCode;
use thread_local_macro::thread_local;


thread_local!{
    /// The signal handlers registered for the current task.
    static SIGNAL_HANDLERS: [RefCell<Option<Box<dyn SignalHandler>>>; NUM_SIGNALS] = Default::default();
}


/// Register a [`SignalHandler`] callback function for the current task.
/// 
/// If an exception/error occurs during the execution of the current task,
/// the given `handler` will be invoked with details of that exception.
/// 
/// # Return
/// * `Ok` if the signal handler was registered successfully.
/// * `Err` if a handler was already registered for the given `signal`.
pub fn register_signal_handler(
    signal: Signal,
    handler: Box<dyn SignalHandler>,
) -> Result<(), AlreadyRegistered> {
    SIGNAL_HANDLERS.with(|sig_handlers| {
        let handler_slot = &sig_handlers[signal as usize];
        if handler_slot.borrow().is_some() {
            return Err(AlreadyRegistered);
        }
        *handler_slot.borrow_mut() = Some(handler);
        Ok(())
    })
}

/// An error type indicating a handler had already been registered
/// for a particular [`Signal`].
#[derive(Debug)]
pub struct AlreadyRegistered;


/// Take the [`SignalHandler`] registered for the given `signal` for the current task.
/// 
/// This **removes** the signal handler registered for this `signal` for the current task.
/// Thus, if another exception occurs that triggers this `signal`, 
/// the returned handler will no longer exist to be invoked.
/// You'd need to re-register another handler for it using [`register_signal_handler`].
pub fn take_signal_handler(signal: Signal) -> Option<Box<dyn SignalHandler>> {
    SIGNAL_HANDLERS.with(|sig_handlers| {
        sig_handlers[signal as usize].borrow_mut().take()
    })
}


/// A signal handler is a callback function that will be invoked
/// when a task's execution causes an illegal error or exception.
/// 
/// Returning `Ok` indicates the signal was handled and that the task may continue exection.
/// Returning `Err` indicates it was not handled and that the system should proceed
/// to its default procedure of cleaning up that task.
pub trait SignalHandler = FnOnce(&SignalContext) -> Result<(), ()>;


/// The possible signals that may occur due to CPU exceptions.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Signal {
    /// Bad virtual address, unexpected page fault.
    /// Analogous to SIGSEGV.
    InvalidAddress                  = 0,
    /// Invalid opcode, malformed instruction, etc.
    /// Analogous to SIGILL.
    IllegalInstruction              = 1,
    /// Bad memory alignment, non-existent physical address.
    /// Analogous to SIGBUS.
    BusError                        = 2,
    /// Bad arithmetic operation, e.g., divide by zero.
    /// Analogous to SIGFPE.
    ArithmeticError                 = 3,
}
const NUM_SIGNALS: usize = core::mem::variant_count::<Signal>();


/// Information that is passed to a registered [`SignalHandler`]
/// about an exception that occurred during execution.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SignalContext {
    pub instruction_pointer: VirtualAddress,
    pub stack_pointer: VirtualAddress,
    pub signal: Signal,
    pub error_code: Option<ErrorCode>,
}

/// Possible error codes that may be provided by the CPU during an exception.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorCode {
    PageFaultError {
        accessed_address: usize,
        pf_error: PageFaultErrorCode,
    },
    Other(u64),
}
impl From<u64> for ErrorCode {
    fn from(other_error: u64) -> Self {
        Self::Other(other_error)
    }
}


/*
 * Note: this is currently unused, but I've left it in here in case
 *       we wish to allow tasks to register exception handlers in the future.
 *  
bitflags::bitflags! {
    /// The subset of machine exceptions that may occur on x86
    /// that Theseus allows tasks to register callback handlers for.
    pub struct Exceptions: u32 {
        const DIVIDE_ERROR                  = 1 <<  0;
        const OVERFLOW                      = 1 <<  1;
        const BOUND_RANGE_EXCEEDED          = 1 <<  2;
        const INVALID_OPCODE                = 1 <<  3;
        const DEVICE_NOT_AVAILABLE          = 1 <<  4;
        const DOUBLE_FAULT                  = 1 <<  5;
        const INVALID_TSS                   = 1 <<  6;
        const SEGMENT_NOT_PRESENT           = 1 <<  7;
        const STACK_SEGMENT_FAULT           = 1 <<  8;
        const GENERAL_PROTECTION_FAULT      = 1 <<  9;
        const PAGE_FAULT                    = 1 << 10;
        const X87_FLOATING_POINT            = 1 << 11;
        const ALIGNMENT_CHECK               = 1 << 12;
        const SIMD_FLOATING_POINT           = 1 << 13;
        //
        // Note: items below here are either reserved exceptions, or exceptions
        //       that shouldn't ever be forwarded to other tasks for handling.
        //
        // const DEBUG                         = 1 << 0;
        // const NON_MASKABLE_INTERRUPT        = 1 << 0;
        // const BREAKPOINT                    = 1 << 0;
        // reserved: 0x09
        // reserved: 0x0F
        // const MACHINE_CHECK                 = 1 << 0;
        // const VIRTUALIZATION                = 1 << 0;
        // reserved: 0x15 - 0x1C
        // const VMM_COMMUNICATION_EXCEPTION   = 1 << 0;
        // const SECURITY_EXCEPTION            = 1 << 0;
        // reserved: 0x1F
    }
}
*/