earlgrey/
chip.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! High-level setup and interrupt mapping for the chip.
6
7use core::fmt::{Display, Write};
8use core::marker::PhantomData;
9use core::ptr::addr_of;
10use kernel::platform::chip::{Chip, InterruptService};
11use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
12use rv32i::csr::{mcause, mie::mie, mtvec::mtvec, CSR};
13use rv32i::pmp::{PMPUserMPU, TORUserPMP};
14use rv32i::syscall::SysCall;
15
16use crate::chip_config::EarlGreyConfig;
17use crate::interrupts;
18use crate::pinmux_config::EarlGreyPinmuxConfig;
19use crate::plic::Plic;
20use crate::plic::PLIC;
21
22pub struct EarlGrey<
23    'a,
24    const MPU_REGIONS: usize,
25    I: InterruptService + 'a,
26    CFG: EarlGreyConfig + 'static,
27    PINMUX: EarlGreyPinmuxConfig,
28    PMP: TORUserPMP<{ MPU_REGIONS }> + Display + 'static,
29> {
30    userspace_kernel_boundary: SysCall,
31    pub mpu: PMPUserMPU<MPU_REGIONS, PMP>,
32    plic: &'a Plic,
33    timer: &'static crate::timer::RvTimer<'static, CFG>,
34    pwrmgr: lowrisc::pwrmgr::PwrMgr,
35    plic_interrupt_service: &'a I,
36    _cfg: PhantomData<CFG>,
37    _pinmux: PhantomData<PINMUX>,
38}
39
40pub struct EarlGreyDefaultPeripherals<'a, CFG: EarlGreyConfig, PINMUX: EarlGreyPinmuxConfig> {
41    pub aes: crate::aes::Aes<'a>,
42    pub hmac: lowrisc::hmac::Hmac<'a>,
43    pub usb: lowrisc::usbdev::Usb<'a>,
44    pub uart0: lowrisc::uart::Uart<'a>,
45    pub otbn: lowrisc::otbn::Otbn<'a>,
46    pub gpio_port: crate::gpio::Port<'a>,
47    pub i2c0: lowrisc::i2c::I2c<'a>,
48    pub spi_host0: lowrisc::spi_host::SpiHost<'a>,
49    pub spi_host1: lowrisc::spi_host::SpiHost<'a>,
50    pub flash_ctrl: lowrisc::flash_ctrl::FlashCtrl<'a>,
51    pub rng: lowrisc::csrng::CsRng<'a>,
52    pub watchdog: lowrisc::aon_timer::AonTimer,
53    _cfg: PhantomData<CFG>,
54    _pinmux: PhantomData<PINMUX>,
55}
56
57impl<CFG: EarlGreyConfig, PINMUX: EarlGreyPinmuxConfig>
58    EarlGreyDefaultPeripherals<'_, CFG, PINMUX>
59{
60    pub fn new() -> Self {
61        Self {
62            aes: crate::aes::Aes::new(),
63            hmac: lowrisc::hmac::Hmac::new(crate::hmac::HMAC0_BASE),
64            usb: lowrisc::usbdev::Usb::new(crate::usbdev::USB0_BASE),
65            uart0: lowrisc::uart::Uart::new(crate::uart::UART0_BASE, CFG::PERIPHERAL_FREQ),
66            otbn: lowrisc::otbn::Otbn::new(crate::otbn::OTBN_BASE),
67            gpio_port: crate::gpio::Port::new::<PINMUX>(),
68            i2c0: lowrisc::i2c::I2c::new(crate::i2c::I2C0_BASE, (1 / CFG::CPU_FREQ) * 1000 * 1000),
69            spi_host0: lowrisc::spi_host::SpiHost::new(
70                crate::spi_host::SPIHOST0_BASE,
71                CFG::CPU_FREQ,
72            ),
73            spi_host1: lowrisc::spi_host::SpiHost::new(
74                crate::spi_host::SPIHOST1_BASE,
75                CFG::CPU_FREQ,
76            ),
77            flash_ctrl: lowrisc::flash_ctrl::FlashCtrl::new(
78                crate::flash_ctrl::FLASH_CTRL_BASE,
79                lowrisc::flash_ctrl::FlashRegion::REGION0,
80            ),
81
82            rng: lowrisc::csrng::CsRng::new(crate::csrng::CSRNG_BASE),
83            watchdog: lowrisc::aon_timer::AonTimer::new(
84                crate::aon_timer::AON_TIMER_BASE,
85                CFG::CPU_FREQ,
86            ),
87            _cfg: PhantomData,
88            _pinmux: PhantomData,
89        }
90    }
91
92    pub fn init(&'static self) {
93        kernel::deferred_call::DeferredCallClient::register(&self.aes);
94        kernel::deferred_call::DeferredCallClient::register(&self.uart0);
95    }
96}
97
98impl<CFG: EarlGreyConfig, PINMUX: EarlGreyPinmuxConfig> InterruptService
99    for EarlGreyDefaultPeripherals<'_, CFG, PINMUX>
100{
101    unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
102        match interrupt {
103            interrupts::UART0_TX_WATERMARK..=interrupts::UART0_RX_PARITYERR => {
104                self.uart0.handle_interrupt();
105            }
106            int_pin @ interrupts::GPIO_PIN0..=interrupts::GPIO_PIN31 => {
107                let pin = &self.gpio_port[(int_pin - interrupts::GPIO_PIN0) as usize];
108                pin.handle_interrupt();
109            }
110            interrupts::HMAC_HMACDONE..=interrupts::HMAC_HMACERR => {
111                self.hmac.handle_interrupt();
112            }
113            interrupts::USBDEV_PKTRECEIVED..=interrupts::USBDEV_LINKOUTERR => {
114                self.usb.handle_interrupt();
115            }
116            interrupts::FLASHCTRL_PROGEMPTY..=interrupts::FLASHCTRL_OPDONE => {
117                self.flash_ctrl.handle_interrupt()
118            }
119            interrupts::I2C0_FMTWATERMARK..=interrupts::I2C0_HOSTTIMEOUT => {
120                self.i2c0.handle_interrupt()
121            }
122            interrupts::OTBN_DONE => self.otbn.handle_interrupt(),
123            interrupts::CSRNG_CSCMDREQDONE..=interrupts::CSRNG_CSFATALERR => {
124                self.rng.handle_interrupt()
125            }
126            interrupts::SPIHOST0_ERROR..=interrupts::SPIHOST0_SPIEVENT => {
127                self.spi_host0.handle_interrupt()
128            }
129            interrupts::SPIHOST1_ERROR..=interrupts::SPIHOST1_SPIEVENT => {
130                self.spi_host1.handle_interrupt()
131            }
132            interrupts::AON_TIMER_AON_WKUP_TIMER_EXPIRED
133                ..=interrupts::AON_TIMER_AON_WDOG_TIMER_BARK => self.watchdog.handle_interrupt(),
134            _ => return false,
135        }
136        true
137    }
138}
139
140impl<
141        'a,
142        const MPU_REGIONS: usize,
143        I: InterruptService + 'a,
144        CFG: EarlGreyConfig,
145        PINMUX: EarlGreyPinmuxConfig,
146        PMP: TORUserPMP<{ MPU_REGIONS }> + Display + 'static,
147    > EarlGrey<'a, MPU_REGIONS, I, CFG, PINMUX, PMP>
148{
149    pub unsafe fn new(
150        plic_interrupt_service: &'a I,
151        timer: &'static crate::timer::RvTimer<CFG>,
152        pmp: PMP,
153    ) -> Self {
154        Self {
155            userspace_kernel_boundary: SysCall::new(),
156            mpu: PMPUserMPU::new(pmp),
157            plic: &*addr_of!(PLIC),
158            pwrmgr: lowrisc::pwrmgr::PwrMgr::new(crate::pwrmgr::PWRMGR_BASE),
159            timer,
160            plic_interrupt_service,
161            _cfg: PhantomData,
162            _pinmux: PhantomData,
163        }
164    }
165
166    pub unsafe fn enable_plic_interrupts(&self) {
167        self.plic.disable_all();
168        self.plic.enable_all();
169    }
170
171    unsafe fn handle_plic_interrupts(&self) {
172        while let Some(interrupt) = self.plic.get_saved_interrupts() {
173            match interrupt {
174                interrupts::PWRMGRAONWAKEUP => {
175                    self.pwrmgr.handle_interrupt();
176                    self.check_until_true_or_interrupt(
177                        || self.pwrmgr.check_clock_propagation(),
178                        None,
179                    );
180                }
181                interrupts::RVTIMERTIMEREXPIRED0_0 => self.timer.service_interrupt(),
182                _ => {
183                    if interrupt >= interrupts::HMAC_HMACDONE
184                        && interrupt <= interrupts::HMAC_HMACERR
185                    {
186                        // Claim the interrupt before we handle it.
187                        // Currently the interrupt has been claimed but not completed.
188                        // This means that if the interrupt re-asserts we will loose the
189                        // re-assertion. Generally this isn't a problem, but some of the
190                        // interrupt handlers expect that interrupts could occur.
191                        // For example the HMAC interrupt handler will write data to the
192                        // HMAC buffer. We then rely on an interrupt triggering when that
193                        // buffer becomes empty. This can happen while we are still in the
194                        // interrupt handler. To ensure we don't loose the interrupt we
195                        // claim it here.
196                        // In order to stop an interrupt loop, we first disable the
197                        // interrupt. `service_pending_interrupts()` will re-enable
198                        // interrupts once they are all handled.
199                        self.with_interrupts_disabled(|| {
200                            // Safe as interrupts are disabled
201                            self.plic.disable(interrupt);
202                            self.plic.complete(interrupt);
203                        });
204                    }
205                    if !self.plic_interrupt_service.service_interrupt(interrupt) {
206                        panic!("Unknown interrupt: {}", interrupt);
207                    }
208                }
209            }
210
211            match interrupt {
212                interrupts::HMAC_HMACDONE..=interrupts::HMAC_HMACERR => {}
213                _ => {
214                    self.with_interrupts_disabled(|| {
215                        self.plic.complete(interrupt);
216                    });
217                }
218            }
219        }
220    }
221
222    /// Run a function in an interruptable loop.
223    ///
224    /// The function will run until it returns true, an interrupt occurs or if
225    /// `max_tries` is not `None` and that limit is reached.
226    /// If the function returns true this call will also return true. If an
227    /// interrupt occurs or `max_tries` is reached this call will return false.
228    fn check_until_true_or_interrupt<F>(&self, f: F, max_tries: Option<usize>) -> bool
229    where
230        F: Fn() -> bool,
231    {
232        match max_tries {
233            Some(t) => {
234                for _i in 0..t {
235                    if self.has_pending_interrupts() {
236                        return false;
237                    }
238                    if f() {
239                        return true;
240                    }
241                }
242            }
243            None => {
244                while !self.has_pending_interrupts() {
245                    if f() {
246                        return true;
247                    }
248                }
249            }
250        }
251
252        false
253    }
254}
255
256impl<
257        'a,
258        const MPU_REGIONS: usize,
259        I: InterruptService + 'a,
260        CFG: EarlGreyConfig,
261        PINMUX: EarlGreyPinmuxConfig,
262        PMP: TORUserPMP<{ MPU_REGIONS }> + Display + 'static,
263    > kernel::platform::chip::Chip for EarlGrey<'a, MPU_REGIONS, I, CFG, PINMUX, PMP>
264{
265    type MPU = PMPUserMPU<MPU_REGIONS, PMP>;
266    type UserspaceKernelBoundary = SysCall;
267    type ThreadIdProvider = rv32i::thread_id::RiscvThreadIdProvider;
268
269    fn mpu(&self) -> &Self::MPU {
270        &self.mpu
271    }
272
273    fn userspace_kernel_boundary(&self) -> &SysCall {
274        &self.userspace_kernel_boundary
275    }
276
277    fn service_pending_interrupts(&self) {
278        loop {
279            if self.plic.get_saved_interrupts().is_some() {
280                unsafe {
281                    self.handle_plic_interrupts();
282                }
283            }
284
285            if self.plic.get_saved_interrupts().is_none() {
286                break;
287            }
288        }
289
290        // Re-enable all MIE interrupts that we care about. Since we looped
291        // until we handled them all, we can re-enable all of them.
292        CSR.mie.modify(mie::mext::SET + mie::mtimer::CLEAR);
293        self.plic.enable_all();
294    }
295
296    fn has_pending_interrupts(&self) -> bool {
297        self.plic.get_saved_interrupts().is_some()
298    }
299
300    fn sleep(&self) {
301        unsafe {
302            self.pwrmgr.enable_low_power();
303            self.check_until_true_or_interrupt(|| self.pwrmgr.check_clock_propagation(), None);
304            rv32i::support::wfi();
305        }
306    }
307
308    unsafe fn with_interrupts_disabled<F, R>(&self, f: F) -> R
309    where
310        F: FnOnce() -> R,
311    {
312        rv32i::support::with_interrupts_disabled(f)
313    }
314
315    unsafe fn print_state(&self, writer: &mut dyn Write) {
316        let _ = writer.write_fmt(format_args!(
317            "\r\n---| OpenTitan Earlgrey configuration for {} |---",
318            CFG::NAME
319        ));
320        rv32i::print_riscv_state(writer);
321        let _ = writer.write_fmt(format_args!("{}", self.mpu.pmp));
322    }
323}
324
325fn handle_exception(exception: mcause::Exception) {
326    match exception {
327        mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
328
329        // Breakpoints occur from the tests running on hardware
330        mcause::Exception::Breakpoint => loop {
331            unsafe { rv32i::support::wfi() }
332        },
333
334        mcause::Exception::InstructionMisaligned
335        | mcause::Exception::InstructionFault
336        | mcause::Exception::IllegalInstruction
337        | mcause::Exception::LoadMisaligned
338        | mcause::Exception::LoadFault
339        | mcause::Exception::StoreMisaligned
340        | mcause::Exception::StoreFault
341        | mcause::Exception::MachineEnvCall
342        | mcause::Exception::InstructionPageFault
343        | mcause::Exception::LoadPageFault
344        | mcause::Exception::StorePageFault
345        | mcause::Exception::Unknown => {
346            panic!("fatal exception: {:?}: {:#x}", exception, CSR.mtval.get());
347        }
348    }
349}
350
351unsafe fn handle_interrupt(intr: mcause::Interrupt) {
352    match intr {
353        mcause::Interrupt::UserSoft
354        | mcause::Interrupt::UserTimer
355        | mcause::Interrupt::UserExternal => {
356            panic!("unexpected user-mode interrupt");
357        }
358        mcause::Interrupt::SupervisorExternal
359        | mcause::Interrupt::SupervisorTimer
360        | mcause::Interrupt::SupervisorSoft => {
361            panic!("unexpected supervisor-mode interrupt");
362        }
363
364        mcause::Interrupt::MachineSoft => {
365            CSR.mie.modify(mie::msoft::CLEAR);
366        }
367        mcause::Interrupt::MachineTimer => {
368            CSR.mie.modify(mie::mtimer::CLEAR);
369        }
370        mcause::Interrupt::MachineExternal => {
371            // We received an interrupt, disable interrupts while we handle them
372            CSR.mie.modify(mie::mext::CLEAR);
373
374            // Claim the interrupt, unwrap() as we know an interrupt exists
375            // Once claimed this interrupt won't fire until it's completed
376            // NOTE: The interrupt is no longer pending in the PLIC
377            loop {
378                let interrupt = (*addr_of!(PLIC)).next_pending();
379
380                match interrupt {
381                    Some(irq) => {
382                        // Safe as interrupts are disabled
383                        (*addr_of!(PLIC)).save_interrupt(irq);
384                    }
385                    None => {
386                        // Enable generic interrupts
387                        CSR.mie.modify(mie::mext::SET);
388                        break;
389                    }
390                }
391            }
392        }
393
394        mcause::Interrupt::Unknown(_) => {
395            panic!("interrupt of unknown cause");
396        }
397    }
398}
399
400/// Trap handler for board/chip specific code.
401///
402/// For the Ibex this gets called when an interrupt occurs while the chip is
403/// in kernel mode.
404#[export_name = "_start_trap_rust_from_kernel"]
405pub unsafe extern "C" fn start_trap_rust() {
406    match mcause::Trap::from(CSR.mcause.extract()) {
407        mcause::Trap::Interrupt(interrupt) => {
408            handle_interrupt(interrupt);
409        }
410        mcause::Trap::Exception(exception) => {
411            handle_exception(exception);
412        }
413    }
414}
415
416/// Function that gets called if an interrupt occurs while an app was running.
417///
418/// mcause is passed in, and this function should correctly handle disabling the
419/// interrupt that fired so that it does not trigger again.
420#[export_name = "_disable_interrupt_trap_rust_from_app"]
421pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
422    match mcause::Trap::from(mcause_val as usize) {
423        mcause::Trap::Interrupt(interrupt) => {
424            handle_interrupt(interrupt);
425        }
426        _ => {
427            panic!("unexpected non-interrupt\n");
428        }
429    }
430}
431
432pub unsafe fn configure_trap_handler() {
433    // The common _start_trap handler uses mscratch to determine
434    // whether we are executing kernel or process code. Set to `0` to
435    // indicate we're in the kernel right now.
436    CSR.mscratch.set(0);
437
438    // The Ibex CPU does not support non-vectored trap entries.
439    CSR.mtvec.write(
440        mtvec::trap_addr.val(_earlgrey_start_trap_vectored as usize >> 2) + mtvec::mode::Vectored,
441    );
442}
443
444// Mock implementation for crate tests that does not include the section
445// specifier, as the test will not use our linker script, and the host
446// compilation environment may not allow the section name.
447#[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
448pub extern "C" fn _earlgrey_start_trap_vectored() {
449    use core::hint::unreachable_unchecked;
450    unsafe {
451        unreachable_unchecked();
452    }
453}
454
455#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
456#[link_section = ".riscv.trap_vectored"]
457#[unsafe(naked)]
458pub extern "C" fn _earlgrey_start_trap_vectored() -> ! {
459    use core::arch::naked_asm;
460    // According to the Ibex user manual:
461    // [NMI] has interrupt ID 31, i.e., it has the highest priority of all
462    // interrupts and the core jumps to the trap-handler base address (in
463    // mtvec) plus 0x7C to handle the NMI.
464    //
465    // Below are 32 (non-compressed) jumps to cover the entire possible
466    // range of vectored traps.
467    naked_asm!(
468        "
469    j {start_trap}
470    j {start_trap}
471    j {start_trap}
472    j {start_trap}
473    j {start_trap}
474    j {start_trap}
475    j {start_trap}
476    j {start_trap}
477    j {start_trap}
478    j {start_trap}
479    j {start_trap}
480    j {start_trap}
481    j {start_trap}
482    j {start_trap}
483    j {start_trap}
484    j {start_trap}
485    j {start_trap}
486    j {start_trap}
487    j {start_trap}
488    j {start_trap}
489    j {start_trap}
490    j {start_trap}
491    j {start_trap}
492    j {start_trap}
493    j {start_trap}
494    j {start_trap}
495    j {start_trap}
496    j {start_trap}
497    j {start_trap}
498    j {start_trap}
499    j {start_trap}
500    j {start_trap}
501        ",
502        start_trap = sym rv32i::_start_trap,
503    );
504}
505
506/// Array used to track the "trap handler active" state per hart.
507///
508/// The `riscv` crate requires chip crates to allocate an array to
509/// track whether any given hart is currently in a trap handler. The
510/// array must be zero-initialized.
511#[export_name = "_trap_handler_active"]
512static mut TRAP_HANDLER_ACTIVE: [usize; 1] = [0; 1];