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.atomic(|| {
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.atomic(|| {
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
268    fn mpu(&self) -> &Self::MPU {
269        &self.mpu
270    }
271
272    fn userspace_kernel_boundary(&self) -> &SysCall {
273        &self.userspace_kernel_boundary
274    }
275
276    fn service_pending_interrupts(&self) {
277        loop {
278            if self.plic.get_saved_interrupts().is_some() {
279                unsafe {
280                    self.handle_plic_interrupts();
281                }
282            }
283
284            if self.plic.get_saved_interrupts().is_none() {
285                break;
286            }
287        }
288
289        // Re-enable all MIE interrupts that we care about. Since we looped
290        // until we handled them all, we can re-enable all of them.
291        CSR.mie.modify(mie::mext::SET + mie::mtimer::CLEAR);
292        self.plic.enable_all();
293    }
294
295    fn has_pending_interrupts(&self) -> bool {
296        self.plic.get_saved_interrupts().is_some()
297    }
298
299    fn sleep(&self) {
300        unsafe {
301            self.pwrmgr.enable_low_power();
302            self.check_until_true_or_interrupt(|| self.pwrmgr.check_clock_propagation(), None);
303            rv32i::support::wfi();
304        }
305    }
306
307    unsafe fn atomic<F, R>(&self, f: F) -> R
308    where
309        F: FnOnce() -> R,
310    {
311        rv32i::support::atomic(f)
312    }
313
314    unsafe fn print_state(&self, writer: &mut dyn Write) {
315        let _ = writer.write_fmt(format_args!(
316            "\r\n---| OpenTitan Earlgrey configuration for {} |---",
317            CFG::NAME
318        ));
319        rv32i::print_riscv_state(writer);
320        let _ = writer.write_fmt(format_args!("{}", self.mpu.pmp));
321    }
322}
323
324fn handle_exception(exception: mcause::Exception) {
325    match exception {
326        mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
327
328        // Breakpoints occur from the tests running on hardware
329        mcause::Exception::Breakpoint => loop {
330            unsafe { rv32i::support::wfi() }
331        },
332
333        mcause::Exception::InstructionMisaligned
334        | mcause::Exception::InstructionFault
335        | mcause::Exception::IllegalInstruction
336        | mcause::Exception::LoadMisaligned
337        | mcause::Exception::LoadFault
338        | mcause::Exception::StoreMisaligned
339        | mcause::Exception::StoreFault
340        | mcause::Exception::MachineEnvCall
341        | mcause::Exception::InstructionPageFault
342        | mcause::Exception::LoadPageFault
343        | mcause::Exception::StorePageFault
344        | mcause::Exception::Unknown => {
345            panic!("fatal exception: {:?}: {:#x}", exception, CSR.mtval.get());
346        }
347    }
348}
349
350unsafe fn handle_interrupt(intr: mcause::Interrupt) {
351    match intr {
352        mcause::Interrupt::UserSoft
353        | mcause::Interrupt::UserTimer
354        | mcause::Interrupt::UserExternal => {
355            panic!("unexpected user-mode interrupt");
356        }
357        mcause::Interrupt::SupervisorExternal
358        | mcause::Interrupt::SupervisorTimer
359        | mcause::Interrupt::SupervisorSoft => {
360            panic!("unexpected supervisor-mode interrupt");
361        }
362
363        mcause::Interrupt::MachineSoft => {
364            CSR.mie.modify(mie::msoft::CLEAR);
365        }
366        mcause::Interrupt::MachineTimer => {
367            CSR.mie.modify(mie::mtimer::CLEAR);
368        }
369        mcause::Interrupt::MachineExternal => {
370            // We received an interrupt, disable interrupts while we handle them
371            CSR.mie.modify(mie::mext::CLEAR);
372
373            // Claim the interrupt, unwrap() as we know an interrupt exists
374            // Once claimed this interrupt won't fire until it's completed
375            // NOTE: The interrupt is no longer pending in the PLIC
376            loop {
377                let interrupt = (*addr_of!(PLIC)).next_pending();
378
379                match interrupt {
380                    Some(irq) => {
381                        // Safe as interrupts are disabled
382                        (*addr_of!(PLIC)).save_interrupt(irq);
383                    }
384                    None => {
385                        // Enable generic interrupts
386                        CSR.mie.modify(mie::mext::SET);
387                        break;
388                    }
389                }
390            }
391        }
392
393        mcause::Interrupt::Unknown(_) => {
394            panic!("interrupt of unknown cause");
395        }
396    }
397}
398
399/// Trap handler for board/chip specific code.
400///
401/// For the Ibex this gets called when an interrupt occurs while the chip is
402/// in kernel mode.
403#[export_name = "_start_trap_rust_from_kernel"]
404pub unsafe extern "C" fn start_trap_rust() {
405    match mcause::Trap::from(CSR.mcause.extract()) {
406        mcause::Trap::Interrupt(interrupt) => {
407            handle_interrupt(interrupt);
408        }
409        mcause::Trap::Exception(exception) => {
410            handle_exception(exception);
411        }
412    }
413}
414
415/// Function that gets called if an interrupt occurs while an app was running.
416///
417/// mcause is passed in, and this function should correctly handle disabling the
418/// interrupt that fired so that it does not trigger again.
419#[export_name = "_disable_interrupt_trap_rust_from_app"]
420pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
421    match mcause::Trap::from(mcause_val as usize) {
422        mcause::Trap::Interrupt(interrupt) => {
423            handle_interrupt(interrupt);
424        }
425        _ => {
426            panic!("unexpected non-interrupt\n");
427        }
428    }
429}
430
431pub unsafe fn configure_trap_handler() {
432    // The common _start_trap handler uses mscratch to determine
433    // whether we are executing kernel or process code. Set to `0` to
434    // indicate we're in the kernel right now.
435    CSR.mscratch.set(0);
436
437    // The Ibex CPU does not support non-vectored trap entries.
438    CSR.mtvec.write(
439        mtvec::trap_addr.val(_earlgrey_start_trap_vectored as usize >> 2) + mtvec::mode::Vectored,
440    );
441}
442
443// Mock implementation for crate tests that does not include the section
444// specifier, as the test will not use our linker script, and the host
445// compilation environment may not allow the section name.
446#[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
447pub extern "C" fn _earlgrey_start_trap_vectored() {
448    use core::hint::unreachable_unchecked;
449    unsafe {
450        unreachable_unchecked();
451    }
452}
453
454#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
455extern "C" {
456    pub fn _earlgrey_start_trap_vectored();
457}
458
459#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
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.
467core::arch::global_asm!(
468    "
469            .section .riscv.trap_vectored, \"ax\"
470            .globl _start_trap_vectored
471          _earlgrey_start_trap_vectored:
472
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            j {start_trap}
502            j {start_trap}
503            j {start_trap}
504            j {start_trap}
505    ",
506    start_trap = sym rv32i::_start_trap,
507);