1
//! Interrupt handling
2
//!
3
//! Peripheral interrupts go through the interrupt matrix, which routes them to the appropriate CPU
4
//! interrupt line. The interrupt matrix is largely the same across devices, but CPU interrupts are
5
//! device-specific before CLIC.
6
//!
7
//! Peripheral interrupts can be bound directly to CPU interrupts for better performance, but
8
//! due to the limited number of CPU interrupts, the preferred mechanism is to use the vectored
9
//! interrupts. The vectored interrupt handlers will call the appropriate interrupt handlers.
10
//! The configuration of vectored interrupt handlers cannot be changed in runtime.
11

            
12
#[cfg(feature = "rt")]
13
#[instability::unstable]
14
pub use esp_riscv_rt::TrapFrame;
15

            
16
#[cfg_attr(interrupt_controller = "riscv_basic", path = "riscv/basic.rs")]
17
#[cfg_attr(interrupt_controller = "plic", path = "riscv/plic.rs")]
18
#[cfg_attr(interrupt_controller = "clic", path = "riscv/clic.rs")]
19
mod cpu_int;
20

            
21
// riscv::interrupt::free is not safe on C6/H2
22
#[inline]
23
pub(crate) fn free<F, R>(f: F) -> R
24
where
25
    F: FnOnce() -> R,
26
{
27
    use esp_sync::raw::{RawLock, SingleCoreInterruptLock};
28
    let irq_token = unsafe { SingleCoreInterruptLock.enter() };
29

            
30
    let r = f();
31

            
32
    unsafe { SingleCoreInterruptLock.exit(irq_token) };
33

            
34
    r
35
}
36

            
37
use crate::{
38
    interrupt::{PriorityError, RunLevel},
39
    peripherals::Interrupt,
40
    system::Cpu,
41
};
42

            
43
/// Interrupt kind
44
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45
#[instability::unstable]
46
pub enum InterruptKind {
47
    /// Level interrupt
48
    Level,
49
    /// Edge interrupt
50
    Edge,
51
}
52

            
53
for_each_interrupt!(
54
    (all $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
55
        paste::paste! {
56
            /// Enumeration of available CPU interrupts.
57
            #[repr(u32)]
58
            #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
59
            #[cfg_attr(feature = "defmt", derive(defmt::Format))]
60
            #[instability::unstable]
61
            pub enum CpuInterrupt {
62
                $(
63
2081
                    #[doc = concat!(" Interrupt number ", stringify!($n), ".")]
64
                    [<Interrupt $n>] = $n,
65
                )*
66
            }
67

            
68
            impl CpuInterrupt {
69
                #[inline]
70
                pub(crate) fn from_u32(n: u32) -> Option<Self> {
71
                    match n {
72
                        $(n if n == $n && n != DISABLED_CPU_INTERRUPT => Some(Self:: [<Interrupt $n>]),)*
73
                        _ => None
74
                    }
75
                }
76
            }
77
        }
78
    };
79
);
80

            
81
1
for_each_classified_interrupt!(
82
    (context_switch ([$class:ident $idx_in_class:literal] $n:literal)) => {
83
        paste::paste! {
84
            pub(crate) const IPC_INTERRUPT: CpuInterrupt = CpuInterrupt::[<Interrupt $n>];
85
        }
86
    };
87

            
88
    (direct_bindable $( ([$class:ident $idx_in_class:literal] $n:literal) ),* ) => {
89
        paste::paste! {
90
            /// Enumeration of CPU interrupts available for direct binding.
91
            #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
92
            #[cfg_attr(feature = "defmt", derive(defmt::Format))]
93
            #[instability::unstable]
94
            pub enum DirectBindableCpuInterrupt {
95
                $(
96
                    #[doc = concat!(" Direct bindable CPU interrupt number ", stringify!($idx_in_class), ".")]
97
                    #[doc = " "]
98
                    #[doc = concat!(" Corresponds to CPU interrupt ", stringify!($n), ".")]
99
                    [<Interrupt $idx_in_class>] = $n,
100
                )*
101
            }
102

            
103
            impl From<DirectBindableCpuInterrupt> for CpuInterrupt {
104
                fn from(bindable: DirectBindableCpuInterrupt) -> CpuInterrupt {
105
                    match bindable {
106
                        $(
107
                            DirectBindableCpuInterrupt::[<Interrupt $idx_in_class>] => CpuInterrupt::[<Interrupt $n>],
108
                        )*
109
                    }
110
                }
111
            }
112
        }
113
    };
114
);
115

            
116
impl CpuInterrupt {
117
    #[inline]
118
    #[cfg(feature = "rt")]
119
    pub(crate) fn is_vectored(self) -> bool {
120
        // Assumes contiguous interrupt allocation.
121
        const VECTORED_CPU_INTERRUPT_RANGE: core::ops::RangeInclusive<u32> = PRIORITY_TO_INTERRUPT
122
            [0] as u32
123
            ..=PRIORITY_TO_INTERRUPT[PRIORITY_TO_INTERRUPT.len() - 1] as u32;
124
        VECTORED_CPU_INTERRUPT_RANGE.contains(&(self as u32))
125
    }
126

            
127
    /// Enables the CPU interrupt.
128
    #[inline]
129
    #[instability::unstable]
130
    pub fn enable(self) {
131
        cpu_int::enable_cpu_interrupt_raw(self as u32);
132
    }
133

            
134
    /// Clears the CPU interrupt status bit.
135
    #[inline]
136
    #[instability::unstable]
137
    pub fn clear(self) {
138
        cpu_int::clear_raw(self as u32);
139
    }
140

            
141
    /// Sets the interrupt kind (i.e. level or edge) of an CPU interrupt.
142
    ///
143
    /// This is safe to call when the `vectored` feature is enabled. The
144
    /// vectored interrupt handler will take care of clearing edge interrupt
145
    /// bits.
146
    #[inline]
147
    #[instability::unstable]
148
    pub fn set_kind(self, kind: InterruptKind) {
149
        cpu_int::set_kind_raw(self as u32, kind);
150
    }
151

            
152
    /// Sets the priority level of a CPU interrupt.
153
    #[inline]
154
    #[instability::unstable]
155
    pub fn set_priority(self, priority: Priority) {
156
        cpu_int::set_priority_raw(self as u32, priority);
157
    }
158

            
159
    /// Returns the interrupt priority for the CPU.
160
    #[inline]
161
    #[instability::unstable]
162
    pub fn priority(self) -> Priority {
163
        unwrap!(Priority::try_from_u32(self.level()))
164
    }
165

            
166
    #[inline]
167
    pub(crate) fn level(self) -> u32 {
168
        cpu_int::cpu_interrupt_priority_raw(self as u32) as u32
169
    }
170
}
171

            
172
2078
for_each_interrupt_priority!(
173
    (all $( ($idx:literal, $n:literal, $ident:ident, $level:ident) ),*) => {
174
        /// Interrupt priority levels.
175
        ///
176
        /// A higher numeric value means higher priority. Interrupt requests at higher priority
177
        /// levels will be able to preempt code running at a lower [`RunLevel`][super::RunLevel].
178
        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
179
        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
180
        #[repr(u8)]
181
        pub enum Priority {
182
            $(
183
                #[doc = concat!(" Priority level ", stringify!($n), ".")]
184
                $ident = $n,
185
            )*
186
        }
187

            
188
        impl Priority {
189
            fn iter() -> impl Iterator<Item = Priority> {
190
                [$(Priority::$ident,)*].into_iter()
191
            }
192
        }
193

            
194
        /// Interrupt run levels.
195
        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
196
        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
197
        #[repr(u8)]
198
        pub enum ElevatedRunLevel {
199
            $(
200
                #[doc = concat!("Run level ", stringify!($n), ".")]
201
                $level = $n,
202
            )*
203
        }
204

            
205
        impl ElevatedRunLevel {
206
            /// Converts a [`Priority`] into an [`ElevatedRunLevel`].
207
            pub const fn from_priority(priority: Priority) -> Self {
208
                match priority {
209
                    $(Priority::$ident => Self::$level,)*
210
                }
211
            }
212
        }
213
    };
214
);
215

            
216
impl Priority {
217
    /// Maximum interrupt priority
218
    #[allow(unused_assignments)]
219
    #[instability::unstable]
220
    pub const fn max() -> Priority {
221
        const {
222
            let mut last = Self::min();
223
            for_each_interrupt_priority!(
224
                ($_idx:literal, $_n:literal, $ident:ident, $_level:ident) => {
225
                    last = Self::$ident;
226
                };
227
            );
228
            last
229
        }
230
    }
231

            
232
    /// Minimum interrupt priority
233
    pub const fn min() -> Priority {
234
        Priority::Priority1
235
    }
236

            
237
    pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
238
        let result;
239
        for_each_interrupt_priority!(
240
            (all $( ($idx:literal, $n:literal, $ident:ident, $_level:ident) ),*) => {
241
                result = match priority {
242
                    $($n => Ok(Priority::$ident),)*
243
                    _ => Err(PriorityError::InvalidInterruptPriority),
244
                }
245
            };
246
        );
247
        result
248
    }
249
}
250

            
251
impl ElevatedRunLevel {
252
    /// Returns the highest run level.
253
    #[instability::unstable]
254
    pub const fn max() -> ElevatedRunLevel {
255
        Self::from_priority(Priority::max())
256
    }
257

            
258
    /// Minimum elevated run level
259
    pub const fn min() -> ElevatedRunLevel {
260
        Self::from_priority(Priority::min())
261
    }
262

            
263
    pub(crate) fn try_from_u32(level: u32) -> Result<Self, PriorityError> {
264
        Priority::try_from_u32(level).map(Self::from_priority)
265
    }
266
}
267

            
268
#[instability::unstable]
269
impl TryFrom<u32> for ElevatedRunLevel {
270
    type Error = PriorityError;
271

            
272
    fn try_from(value: u32) -> Result<Self, Self::Error> {
273
        Self::try_from_u32(value)
274
    }
275
}
276

            
277
impl From<Priority> for ElevatedRunLevel {
278
    fn from(priority: Priority) -> Self {
279
        Self::from_priority(priority)
280
    }
281
}
282

            
283
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
284
pub(super) static DISABLED_CPU_INTERRUPT: u32 = property!("interrupts.disabled_interrupt");
285

            
286
/// The number of vectored interrupts / The number of priority levels.
287
const VECTOR_COUNT: usize = const {
288
    let mut count = 0;
289
    for_each_interrupt!(([vector $n:tt] $_:literal) => { count += 1; };);
290

            
291
    core::assert!(count == Priority::max() as usize);
292

            
293
    count
294
};
295

            
296
/// Maps priority levels to their corresponding interrupt vectors.
297
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
298
pub(super) static PRIORITY_TO_INTERRUPT: [CpuInterrupt; VECTOR_COUNT] = const {
299
    let mut counter = 0;
300
    let mut vector = [CpuInterrupt::Interrupt0; VECTOR_COUNT];
301

            
302
    for_each_interrupt!(
303
        ([vector $_n:tt] $interrupt:literal) => {
304
            vector[counter] = paste::paste! { CpuInterrupt::[<Interrupt $interrupt>] };
305
            counter += 1;
306
        };
307
    );
308
    vector
309
};
310

            
311
/// Enables an interrupt by directly binding it to an available CPU interrupt.
312
///
313
/// ⚠️ This installs a *raw trap handler*: the provided `handler` is written
314
/// directly into the CPU interrupt vector table. That means:
315
///
316
/// - The provided handler is used as an actual trap handler.
317
/// - The caller must:
318
///   - Save and restore all used registers.
319
///   - Clear the interrupt source if necessary.
320
///   - Return using the `mret` instruction.
321
/// - The handler must be declared as a naked function. The compiler does not insert a function
322
///   prologue or epilogue; a normal Rust `fn` results in an error
323
///
324
/// Unless low-level control is required for the lowest possible latency,
325
/// [`enable`][crate::interrupt::enable] is usually preferable.
326
#[instability::unstable]
327
1
pub fn enable_direct(
328
    interrupt: Interrupt,
329
    level: Priority,
330
    cpu_interrupt: DirectBindableCpuInterrupt,
331
    handler: unsafe extern "C" fn(),
332
) {
333
1
    enable_direct_inner(interrupt, level, cpu_interrupt.into(), handler)
334
}
335

            
336
65
pub(crate) fn enable_direct_inner(
337
    interrupt: Interrupt,
338
    level: Priority,
339
    cpu_interrupt: CpuInterrupt,
340
    handler: unsafe extern "C" fn(),
341
) {
342
    bind_cpu_interrupt(cpu_interrupt, handler);
343
    super::map_raw(Cpu::current(), interrupt, cpu_interrupt as u32);
344
    enable_cpu_interrupt(cpu_interrupt, level);
345
65
}
346

            
347
/// Writes `handler` into the vector table entry of `cpu_interrupt`.
348
pub(crate) fn bind_cpu_interrupt(cpu_interrupt: CpuInterrupt, handler: unsafe extern "C" fn()) {
349
    cfg_select! {
350
        interrupt_controller = "clic" => {
351
            let clic = unsafe { crate::soc::pac::CLIC::steal() };
352

            
353
            // Enable hardware vectoring
354
            clic.int_attr(cpu_interrupt as usize).modify(|_, w| {
355
                w.shv().hardware();
356
                w.trig().positive_level()
357
            });
358

            
359
            let mtvt_table: *mut [u32; 48];
360
            unsafe { core::arch::asm!("csrr {0}, 0x307", out(reg) mtvt_table) };
361

            
362
            let int_slot = mtvt_table
363
                .cast::<u32>()
364
                .wrapping_add(cpu_interrupt as usize);
365

            
366
            let instr = handler as usize as u32;
367
        }
368
        _ => {
369
            use riscv::register::mtvec;
370
            let mt = mtvec::read();
371

            
372
65
            assert_eq!(
373
                mt.trap_mode().into_usize(),
374
                mtvec::TrapMode::Vectored.into_usize()
375
            );
376

            
377
            let base_addr = mt.address() as usize;
378

            
379
65
            let int_slot = base_addr.wrapping_add((cpu_interrupt as usize) * 4) as *mut u32;
380

            
381
            let instr = encode_jal_x0(handler as usize, int_slot as usize);
382
        }
383
    }
384

            
385
65
    if crate::debugger::debugger_connected() {
386
        unsafe { core::ptr::write_volatile(int_slot, instr) };
387
    } else {
388
        crate::debugger::DEBUGGER_LOCK.lock(|| unsafe {
389
            let wp = crate::debugger::clear_watchpoint(1);
390
            core::ptr::write_volatile(int_slot, instr);
391
            crate::debugger::restore_watchpoint(1, wp);
392
        });
393
    }
394
    unsafe {
395
65
        core::arch::asm!("fence.i");
396
    }
397

            
398
    #[cfg(esp32p4)]
399
    unsafe {
400
        // Write back the cache to make sure the new interrupt handler is visible to the CPU.
401
        crate::soc::cache_writeback_addr(mtvt_table as u32, 48 * 4);
402
        // Invalidate the cache to make sure the CPU does not read from a stale instruction cache.
403
        crate::soc::cache_invalidate_icache_addr(mtvt_table as u32, 48 * 4);
404
    }
405
}
406

            
407
/// Enables `cpu_interrupt` as a level-triggered interrupt at `level`.
408
pub(crate) fn enable_cpu_interrupt(cpu_interrupt: CpuInterrupt, level: Priority) {
409
    cpu_int::set_priority_raw(cpu_interrupt as u32, level);
410
    cpu_int::set_kind_raw(cpu_interrupt as u32, InterruptKind::Level);
411
    cpu_int::enable_cpu_interrupt_raw(cpu_interrupt as u32);
412
}
413

            
414
// helper: returns correctly encoded RISC-V `jal` instruction
415
#[cfg(not(interrupt_controller = "clic"))]
416
fn encode_jal_x0(target: usize, pc: usize) -> u32 {
417
65
    let offset = (target as isize) - (pc as isize);
418

            
419
    const MIN: isize = -(1isize << 20);
420
    const MAX: isize = (1isize << 20) - 1;
421

            
422
65
    assert!(offset % 2 == 0 && (MIN..=MAX).contains(&offset));
423

            
424
    let imm = offset as u32;
425
    let imm20 = (imm >> 20) & 0x1;
426
    let imm10_1 = (imm >> 1) & 0x3ff;
427
    let imm11 = (imm >> 11) & 0x1;
428
65
    let imm19_12 = (imm >> 12) & 0xff;
429

            
430
65
    (imm20 << 31)
431
        | (imm19_12 << 12)
432
65
        | (imm11 << 20)
433
65
        | (imm10_1 << 21)
434
        // https://lhtin.github.io/01world/app/riscv-isa/?xlen=32&insn_name=jal
435
        | 0b1101111u32
436
}
437

            
438
// Runlevel APIs
439

            
440
/// Returns the current run level (the level below which interrupts are masked).
441
71
pub(crate) fn current_raw_runlevel() -> u32 {
442
73
    cpu_int::current_runlevel() as u32
443
71
}
444

            
445
/// Changes the current run level (the level below which interrupts are
446
/// masked), and returns the previous run level.
447
///
448
/// # Safety
449
///
450
/// Must only be used to raise the runlevel and to restore it to a previous
451
/// value. Must not be used to arbitrarily lower the runlevel.
452
pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
453
    let previous = cpu_int::change_current_runlevel(level);
454
2075
    unwrap!(RunLevel::try_from_u32(previous as u32))
455
}
456

            
457
fn cpu_wait_mode_on() -> bool {
458
    cfg_select! {
459
        // Each core gates its own WAITI clock, cannot unify with soc_has_pcr case.
460
        esp32h4 => {
461
            let conf = crate::peripherals::PCR::regs().cpu_waiti_conf().read();
462
            match Cpu::current() {
463
                Cpu::ProCpu => conf.cpu0_wait_mode_force_on().bit_is_set(),
464
                Cpu::AppCpu => conf.cpu1_wait_mode_force_on().bit_is_set(),
465
            }
466
        }
467
        soc_has_pcr => crate::peripherals::PCR::regs()
468
            .cpu_waiti_conf()
469
            .read()
470
            .cpu_wait_mode_force_on()
471
            .bit_is_set(),
472
        soc_has_hp_sys => crate::peripherals::HP_SYS::regs()
473
            .cpu_waiti_conf()
474
            .read()
475
            .cpu_wait_mode_force_on()
476
            .bit_is_set(),
477
        _ => crate::peripherals::SYSTEM::regs()
478
            .cpu_per_conf()
479
            .read()
480
            .cpu_wait_mode_force_on()
481
            .bit_is_set(),
482
    }
483
}
484

            
485
/// Waits for an interrupt to occur.
486
///
487
/// Causes the current CPU core to execute its Wait For Interrupt (WFI or
488
/// equivalent) instruction. After this call, the CPU core stops execution until
489
/// an interrupt occurs.
490
///
491
/// Returns immediately when a debugger is attached; intended to be called in a
492
/// loop.
493
#[inline(always)]
494
#[instability::unstable]
495
pub fn wait_for_interrupt() {
496
362
    if crate::debugger::debugger_connected() && !cpu_wait_mode_on() {
497
        // when SYSTEM_CPU_WAIT_MODE_FORCE_ON is disabled in WFI mode SBA access to memory does not
498
        // work for debugger, so do not enter that mode when debugger is connected.
499
        // https://github.com/espressif/esp-idf/blob/b9a308a47ca4128d018495662b009a7c461b6780/components/esp_hw_support/cpu.c#L57-L60
500
        return;
501
    }
502
362
    unsafe { core::arch::asm!("wfi") };
503
}
504

            
505
pub(crate) fn priority_to_cpu_interrupt(_interrupt: Interrupt, level: Priority) -> CpuInterrupt {
506
80
    PRIORITY_TO_INTERRUPT[(level as usize) - 1]
507
}
508

            
509
/// Sets up interrupts ready for vectoring.
510
///
511
/// # Safety
512
///
513
/// Must be called only during core startup.
514
#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
515
pub(crate) unsafe fn init_vectoring() {
516
    use riscv::register::mtvec;
517

            
518
    unsafe extern "C" {
519
        static _vector_table: u32;
520
    }
521

            
522
    // A machine software interrupt survives a CPU reset, so a request that the previous program
523
    // raised would fire as soon as this core takes interrupts again.
524
    #[cfg(soc_has_clint)]
525
    crate::peripherals::CLINT::regs()
526
        .msip()
527
        .write(|w| w.msip().clear_bit());
528

            
529
    unsafe {
530
        let vec_table = (&raw const _vector_table).addr();
531

            
532
        #[cfg(not(interrupt_controller = "clic"))]
533
        {
534
            mtvec::write({
535
                let mut mtvec = mtvec::Mtvec::from_bits(0);
536
                mtvec.set_trap_mode(mtvec::TrapMode::Vectored);
537
                mtvec.set_address(vec_table);
538
                mtvec
539
            });
540
        }
541

            
542
        #[cfg(interrupt_controller = "clic")]
543
        {
544
            mtvec::write({
545
                let mut mtvec = mtvec::Mtvec::from_bits(0x03); // MODE = CLIC
546
                mtvec.set_address(vec_table);
547
                mtvec
548
            });
549

            
550
            // set mtvt (hardware vector base)
551
            let mtvt_table = match Cpu::current() {
552
                Cpu::ProCpu => {
553
                    unsafe extern "C" {
554
                        static _mtvt_table: u32;
555
                    }
556
                    (&raw const _mtvt_table).addr()
557
                }
558
                #[cfg(multi_core)]
559
                Cpu::AppCpu => {
560
                    unsafe extern "C" {
561
                        static _mtvt_table2: u32;
562
                    }
563
                    (&raw const _mtvt_table2).addr()
564
                }
565
            };
566
            core::arch::asm!("csrw 0x307, {0}", in(reg) mtvt_table);
567
        }
568
    };
569

            
570
    // Configure CLIC for hardware-vectored mode (shv=1) and set nlbits.
571
    // Must run on each core since CLIC control registers are per-core.
572
    #[cfg(feature = "rt")]
573
    cpu_int::init();
574

            
575
    // Configure and enable vectored interrupts
576
    for (int, prio) in PRIORITY_TO_INTERRUPT.iter().copied().zip(Priority::iter()) {
577
        let num = int as u32;
578
        cpu_int::set_kind_raw(num, InterruptKind::Level);
579
        cpu_int::set_priority_raw(num, prio);
580
        cpu_int::enable_cpu_interrupt_raw(num);
581
    }
582
}
583

            
584
#[cfg(feature = "rt")]
585
pub(crate) mod rt {
586
    use esp_riscv_rt::TrapFrame;
587
    use riscv::register::mcause;
588

            
589
    use super::*;
590
    use crate::interrupt::InterruptStatus;
591

            
592
    /// The total number of interrupts.
593
    #[cfg(not(interrupt_controller = "clic"))]
594
    const INTERRUPT_COUNT: usize = const {
595
        let mut count = 0;
596
        for_each_interrupt!(([$_class:tt $n:tt] $_:literal) => { count += 1; };);
597
        count
598
    };
599

            
600
    /// Maps interrupt numbers to their vector priority levels.
601
    #[cfg(not(interrupt_controller = "clic"))]
602
    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
603
    pub(super) static INTERRUPT_TO_PRIORITY: [Option<Priority>; INTERRUPT_COUNT] = const {
604
        let mut priorities = [None; INTERRUPT_COUNT];
605

            
606
        for_each_interrupt!(
607
            ([vector $n:tt] $int:literal) => {
608
                for_each_interrupt_priority!(($n, $__:tt, $ident:ident, $_level:ident) => { priorities[$int] = Some(Priority::$ident); };);
609
            };
610
        );
611

            
612
        priorities
613
    };
614

            
615
    /// # Safety
616
    ///
617
    /// Called from an assembly trap handler.
618
    #[doc(hidden)]
619
    #[unsafe(link_section = ".trap.rust")]
620
    #[unsafe(export_name = "_start_trap_rust_hal")]
621
    unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
622
        assert!(
623
            mcause::read().is_exception(),
624
            "Arrived into _start_trap_rust_hal but mcause is not an exception!"
625
        );
626
        unsafe extern "C" {
627
            fn ExceptionHandler(tf: *mut TrapFrame);
628
        }
629
        unsafe {
630
            ExceptionHandler(trap_frame);
631
        }
632
    }
633

            
634
    #[doc(hidden)]
635
    #[unsafe(no_mangle)]
636
    #[unsafe(link_section = ".init")]
637
328
    unsafe fn _setup_interrupts() {
638
        crate::soc::riscv_preinit();
639
        crate::interrupt::setup_interrupts();
640

            
641
        #[cfg(interrupt_controller = "plic")]
642
        unsafe {
643
            core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
644
        }
645
328
    }
646

            
647
    #[unsafe(no_mangle)]
648
    #[crate::ram]
649
2078
    unsafe fn handle_interrupts(cpu_intr: CpuInterrupt) {
650
        let status = InterruptStatus::current();
651

            
652
        // this has no effect on level interrupts, but the interrupt may be an edge one
653
        // so we clear it anyway
654
        cpu_intr.clear();
655

            
656
        cfg_select! {
657
            interrupt_controller = "clic" => {
658
                let prio = cpu_int::current_runlevel();
659
                let mcause = riscv::register::mcause::read();
660
            }
661
            _ => {
662
                // Change the current runlevel so that interrupt handlers can access the correct
663
                // runlevel.
664
                let prio = unwrap!(INTERRUPT_TO_PRIORITY[cpu_intr as usize]);
665
                let level = unsafe {
666
                    change_current_runlevel(RunLevel::Interrupt(ElevatedRunLevel::from(prio)))
667
                };
668
                let prio = prio as u8;
669
            }
670
        }
671

            
672
        let handle_interrupts = || unsafe {
673
2070
            for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
674
                crate::interrupt::should_handle(Cpu::current(), interrupt_nr as u32, prio as u32)
675
            }) {
676
                let handler =
677
2070
                    crate::soc::pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler;
678

            
679
2070
                handler();
680
            }
681
        };
682

            
683
        // Do not enable nesting on the highest priority level. Older interrupt controllers couldn't
684
        // properly mask the highest priority interrupt, and for CLIC we don't want to waste
685
        // the cycles it takes to enable nesting unnecessarily.
686
        if prio != Priority::max() as u8 {
687
            unsafe {
688
                riscv::interrupt::nested(handle_interrupts);
689
            }
690
        } else {
691
            handle_interrupts();
692
        }
693

            
694
        cfg_select! {
695
            interrupt_controller = "clic" => {
696
                // In case the target uses the CLIC, it is mandatory to restore `mcause` register
697
                // since it contains the former CPU priority. When executing `mret`,
698
                // the hardware will restore the former threshold, from `mcause` to
699
                // `mintstatus` CSR
700
                unsafe { core::arch::asm!("csrw 0x342, {}", in(reg) mcause.bits()) }
701
            }
702
            _ => {
703
                unsafe { change_current_runlevel(level) };
704
            }
705
        }
706
2069
    }
707
}