aboutsummaryrefslogtreecommitdiff
path: root/src/exception.rs
blob: ffe9f39db5c7ec98a387820455399ae868794428 (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
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! Exceptions

use ctxt::Token;
use Reserved;

/// 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 unsafe { (*::peripheral::SCB.get()).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: unsafe extern "C" fn(&NmiCtxt),
    /// All class of fault
    pub hard_fault: unsafe extern "C" fn(&HardFaultCtxt),
    /// Memory management
    pub mem_manage: unsafe extern "C" fn(&MemManageCtxt),
    /// Pre-fetch fault, memory access fault
    pub bus_fault: unsafe extern "C" fn(&BusFaultCtxt),
    /// Undefined instruction or illegal state
    pub usage_fault: unsafe extern "C" fn(&UsageFaultCtxt),
    /// Reserved spots in the vector table
    pub _reserved0: [Reserved; 4],
    /// System service call via SWI instruction
    pub svcall: unsafe extern "C" fn(&SvcallCtxt),
    /// Reserved spots in the vector table
    pub _reserved1: [Reserved; 2],
    /// Pendable request for system service
    pub pendsv: unsafe extern "C" fn(&PendsvCtxt),
    /// System tick timer
    pub sys_tick: unsafe extern "C" fn(&SysTickCtxt),
}

/// Identifies the Nmi exception
pub struct NmiCtxt {
    _0: (),
}

/// Identifies the HardFault exception
pub struct HardFaultCtxt {
    _0: (),
}

/// Identifies the MemManage exception
pub struct MemManageCtxt {
    _0: (),
}

/// Identifies the BusFault exception
pub struct BusFaultCtxt {
    _0: (),
}

/// Identifies the UsageFault exception
pub struct UsageFaultCtxt {
    _0: (),
}

/// Identifies the Svcall exception
pub struct SvcallCtxt {
    _0: (),
}

/// Identifies the Pendsv exception
pub struct PendsvCtxt {
    _0: (),
}

/// Identifies the Systick exception
pub struct SysTickCtxt {
    _0: (),
}

unsafe impl Token for NmiCtxt {}

unsafe impl Token for HardFaultCtxt {}

unsafe impl Token for MemManageCtxt {}

unsafe impl Token for BusFaultCtxt {}

unsafe impl Token for SvcallCtxt {}

unsafe impl Token for PendsvCtxt {}

unsafe impl Token for SysTickCtxt {}

/// 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 stacked registers
#[naked]
pub unsafe extern "C" fn default_handler<T>(_token: &T) {
    // This is the actual exception handler. `_sf` is a pointer to the previous
    // stack frame
    #[cfg(target_arch = "arm")]
    extern "C" fn handler(_sr: &StackedRegisters) -> ! {
        ::asm::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(&StackedRegisters) -> !)
                 :
                 : "volatile");

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

/// Registers stacked during an exception
#[repr(C)]
pub struct StackedRegisters {
    /// (General purpose) Register 0
    pub r0: u32,
    /// (General purpose) Register 1
    pub r1: u32,
    /// (General purpose) Register 2
    pub r2: u32,
    /// (General purpose) Register 3
    pub r3: u32,
    /// (General purpose) Register 12
    pub r12: u32,
    /// Linker Register
    pub lr: u32,
    /// Program Counter
    pub pc: u32,
    /// Program Status Register
    pub xpsr: u32,
}