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
//! Provides the default entry points and lang items for panics and unwinding. 
//! 
//! These lang items are required by the Rust compiler. 
//! They should never be directly invoked by developers, only by the compiler. 
//! 

#![no_std]
#![feature(alloc_error_handler)]
#![allow(internal_features)]
#![feature(lang_items)]
#![feature(panic_info_message)]

extern crate alloc;

use core::panic::PanicInfo;
use log::error;

#[cfg(target_arch = "x86_64")]
use early_printer::println;
#[cfg(target_arch = "aarch64")]
use log::info as println;

/// The singular entry point for a language-level panic.
/// 
/// The Rust compiler will rename this to "rust_begin_unwind".
#[cfg(not(test))]
#[panic_handler] // same as:  #[lang = "panic_impl"]
fn panic_entry_point(info: &PanicInfo) -> ! {
    // Since a panic could occur before the memory subsystem is initialized,
    // we must check before using alloc types or other functions that depend on the memory system (the heap).
    // We can check that by seeing if the kernel mmi has been initialized.
    let kernel_mmi_ref = memory::get_kernel_mmi_ref();  
    let res = if kernel_mmi_ref.is_some() {
        // proceed with calling the panic_wrapper, but don't shutdown with try_exit() if errors occur here
        #[cfg(not(loadable))] {
            panic_wrapper::panic_wrapper(info)
        }
        #[cfg(loadable)] {
            // An internal function for calling the panic_wrapper, but returning errors along the way.
            // We must make sure to not hold any locks when invoking the panic_wrapper function.
            fn invoke_panic_wrapper(info: &PanicInfo) -> Result<(), &'static str> {
                type PanicWrapperFunc = fn(&PanicInfo) -> Result<(), &'static str>;
                const PANIC_WRAPPER_SYMBOL: &'static str = "panic_wrapper::panic_wrapper::";
                let section = {
                    mod_mgmt::get_initial_kernel_namespace()
                        .and_then(|namespace| namespace.get_symbol_starting_with(PANIC_WRAPPER_SYMBOL).upgrade())
                        .ok_or("Couldn't get single symbol matching \"panic_wrapper::panic_wrapper::\"")?
                };
                let func: &PanicWrapperFunc = unsafe { section.as_func() }?;
                func(info)
            }

            // call the above internal function
            invoke_panic_wrapper(info)
        }
    }
    else {
        Err("memory subsystem not yet initialized, cannot call panic_wrapper because it requires alloc types")
    };

    if let Err(_e) = res {
        error!("Halting due to early panic: {}", info);
        // basic early panic printing with no dependencies
        println!("\nHalting due to early panic: {}", info);
    }

    // If we failed to handle the panic, there's not really much we can do about it,
    // other than just let the thread spin endlessly (which doesn't hurt correctness but is inefficient). 
    // But in general, this task should be killed by the panic_wrapper, so it shouldn't reach this point.
    // Only panics early on in the initialization process will get here, meaning that the OS will basically stop.
    loop { core::hint::spin_loop() }
}



/// Typically this would be an entry point in the unwinding procedure, in which a stack frame is unwound. 
/// However, in Theseus we use our own unwinding flow which is simpler.
/// 
/// This function will always be renamed to "rust_eh_personality" no matter what function name we give it here.
#[cfg(not(test))]
#[lang = "eh_personality"]
#[no_mangle]
#[doc(hidden)]
extern "C" fn rust_eh_personality() -> ! {
    panic!("BUG: Theseus does not use rust_eh_personality. Why has it been invoked?")
}

/// This function is automatically jumped to after each unwinding cleanup routine finishes executing,
/// so it's basically the return address of every cleanup routine.
///
/// Just like the panic_entry_point() above, this is effectively just an entry point
/// that invokes the real `unwind_resume()` function in the `unwind` crate, 
/// but does so dynamically in loadable mode.
#[no_mangle]
#[cfg(target_arch = "x86_64")]
extern "C" fn _Unwind_Resume(arg: usize) -> ! {
    #[cfg(not(loadable))] {
        unwind::unwind_resume(arg)
    }
    #[cfg(loadable)] {
        // An internal function for calling the real unwind_resume function, but returning errors along the way.
        // We must make sure to not hold any locks when invoking the function.
        fn invoke_unwind_resume(arg: usize) -> Result<(), &'static str> {
            type UnwindResumeFunc = fn(usize) -> !;
            const UNWIND_RESUME_SYMBOL: &'static str = "unwind::unwind_resume::";
            let section = {
                mod_mgmt::get_initial_kernel_namespace()
                    .and_then(|namespace| namespace.get_symbol_starting_with(UNWIND_RESUME_SYMBOL).upgrade())
                    .ok_or("Couldn't get single symbol matching \"unwind::unwind_resume::\"")?
            };
            let func: &UnwindResumeFunc = unsafe { section.as_func() }?;
            func(arg)
        }
        match invoke_unwind_resume(arg) {
            Ok(()) => error!("BUG: _Unwind_Resume: unexpectedly returned Ok(()) from unwind::unwind_resume()"),
            Err(e) => error!("_Unwind_Resume: failed to dynamically invoke unwind::unwind_resume! Error: {}", e),
        }
        loop { core::hint::spin_loop() }
    }
}

#[no_mangle]
#[cfg(target_arch = "aarch64")]
extern "C" fn _Unwind_Resume(_arg: usize) -> ! {
    todo!("_Unwind_Resume is not yet implemented on aarch64")
}

/// This is the callback entry point that gets invoked when the heap allocator runs out of memory.
#[alloc_error_handler]
#[cfg(not(test))]
fn oom(_layout: core::alloc::Layout) -> ! {
    error!("\n(oom) Out of Heap Memory! requested allocation: {:?}", _layout);
    panic!("\n(oom) Out of Heap Memory! requested allocation: {:?}", _layout);
}