1use 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 self.atomic(|| {
200 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 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 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 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 CSR.mie.modify(mie::mext::CLEAR);
372
373 loop {
377 let interrupt = (*addr_of!(PLIC)).next_pending();
378
379 match interrupt {
380 Some(irq) => {
381 (*addr_of!(PLIC)).save_interrupt(irq);
383 }
384 None => {
385 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#[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#[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 CSR.mscratch.set(0);
436
437 CSR.mtvec.write(
439 mtvec::trap_addr.val(_earlgrey_start_trap_vectored as usize >> 2) + mtvec::mode::Vectored,
440 );
441}
442
443#[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")))]
460core::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);