aboutsummaryrefslogtreecommitdiff
path: root/src/exception.rs
blob: cd38366381fbe5cd460dad4637f1c9988b9d2b9e (plain) (blame)
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
//! Exceptions

use {Handler, Reserved};
#[cfg(target_arch = "arm")]
use StackFrame;

/// Kind of exception
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Exception {
    /// i.e. currently not servicing an exception
    ThreadMode,
    /// Non-maskable interrupt.
    Nmi,
    /// All class of fault.
    HardFault,
    /// Memory management.
    MemoryManagementFault,
    /// Pre-fetch fault, memory access fault.
    BusFault,
    /// Undefined instruction or illegal state.
    UsageFault,
    /// System service call via SWI instruction
    SVCall,
    /// Pendable request for system service
    PendSV,
    /// System tick timer
    Systick,
    /// An interrupt
    Interrupt(u8),
    // Unreachable variant
    #[doc(hidden)]
    Reserved,
}

impl Exception {
    /// Returns the kind of exception that's currently being serviced
    pub fn current() -> Exception {
        match ::peripheral::scb().icsr.read() as u8 {
            0 => Exception::ThreadMode,
            2 => Exception::Nmi,
            3 => Exception::HardFault,
            4 => Exception::MemoryManagementFault,
            5 => Exception::BusFault,
            6 => Exception::UsageFault,
            11 => Exception::SVCall,
            14 => Exception::PendSV,
            15 => Exception::Systick,
            n if n >= 16 => Exception::Interrupt(n - 16),
            _ => Exception::Reserved,
        }
    }
}

/// Exception handlers
#[repr(C)]
pub struct Handlers {
    /// Non-maskable interrupt
    pub nmi: Handler,
    /// All class of fault
    pub hard_fault: Handler,
    /// Memory management
    pub mem_manage: Handler,
    /// Pre-fetch fault, memory access fault
    pub bus_fault: Handler,
    /// Undefined instruction or illegal state
    pub usage_fault: Handler,
    /// Reserved spots in the vector table
    pub _reserved0: [Reserved; 4],
    /// System service call via SWI instruction
    pub svcall: Handler,
    /// Reserved spots in the vector table
    pub _reserved1: [Reserved; 2],
    /// Pendable request for system service
    pub pendsv: Handler,
    /// System tick timer
    pub sys_tick: Handler,
}

/// Default exception handlers
pub const DEFAULT_HANDLERS: Handlers = Handlers {
    _reserved0: [Reserved::Vector; 4],
    _reserved1: [Reserved::Vector; 2],
    bus_fault: default_handler,
    hard_fault: default_handler,
    mem_manage: default_handler,
    nmi: default_handler,
    pendsv: default_handler,
    svcall: default_handler,
    sys_tick: default_handler,
    usage_fault: default_handler,
};

/// The default exception handler
///
/// This handler triggers a breakpoint (`bkpt`) and gives you access, within a
/// GDB session, to the stack frame (`_sf`) where the exception occurred.
// This needs asm!, #[naked] and unreachable() to avoid modifying the stack
// pointer (MSP), that way it points to the previous stack frame
#[naked]
pub unsafe extern "C" fn default_handler() {
    // This is the actual exception handler. `_sf` is a pointer to the previous
    // stack frame
    #[cfg(target_arch = "arm")]
    extern "C" fn handler(_sf: &StackFrame) -> ! {
        #[cfg(feature = "semihosting")]
        hprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), _sf.pc);

        unsafe {
            bkpt!();
        }

        loop {}
    }

    match () {
        #[cfg(target_arch = "arm")]
        () => {
            // "trampoline" to get to the real exception handler.
            asm!("mrs r0, MSP
                  ldr r1, [r0, #20]
                  b $0"
                 :
                 : "i"(handler as extern "C" fn(&StackFrame) -> !)
                 :
                 : "volatile");

            ::core::intrinsics::unreachable()
        }
        #[cfg(not(target_arch = "arm"))]
        () => {}
    }
}