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.with_interrupts_disabled(|| {
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.with_interrupts_disabled(|| {
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 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 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 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 CSR.mie.modify(mie::mext::CLEAR);
373
374 loop {
378 let interrupt = (*addr_of!(PLIC)).next_pending();
379
380 match interrupt {
381 Some(irq) => {
382 (*addr_of!(PLIC)).save_interrupt(irq);
384 }
385 None => {
386 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#[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#[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 CSR.mscratch.set(0);
437
438 CSR.mtvec.write(
440 mtvec::trap_addr.val(_earlgrey_start_trap_vectored as usize >> 2) + mtvec::mode::Vectored,
441 );
442}
443
444#[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 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#[export_name = "_trap_handler_active"]
512static mut TRAP_HANDLER_ACTIVE: [usize; 1] = [0; 1];