veer_el2/
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// Copyright (c) 2024 Antmicro <www.antmicro.com>
5
6//! High-level setup and interrupt mapping for the chip.
7
8use crate::machine_timer::Clint;
9use core::fmt::Write;
10use core::ptr::addr_of;
11use kernel::platform::chip::{Chip, InterruptService};
12use kernel::utilities::registers::interfaces::{ReadWriteable, Readable};
13use kernel::utilities::StaticRef;
14use rv32i::csr::{mcause, mie::mie, mip::mip, CSR};
15use rv32i::pmp::{simple::SimplePMP, PMPUserMPU};
16use rv32i::syscall::SysCall;
17
18use crate::pic::Pic;
19use crate::pic::PicRegisters;
20
21pub const PIC_BASE: StaticRef<PicRegisters> =
22    unsafe { StaticRef::new(0xf00c_0000 as *const PicRegisters) };
23
24pub static mut PIC: Pic = Pic::new(PIC_BASE);
25
26pub struct VeeR<'a, I: InterruptService + 'a> {
27    userspace_kernel_boundary: SysCall,
28    pic: &'a Pic,
29    mtimer: &'static Clint<'static>,
30    pic_interrupt_service: &'a I,
31    pmp: PMPUserMPU<4, SimplePMP<8>>,
32}
33
34pub struct VeeRDefaultPeripherals {
35    pub sim_uart: crate::uart::SimUartType,
36}
37
38impl VeeRDefaultPeripherals {
39    pub fn new() -> Self {
40        Self {
41            sim_uart: crate::uart::SimUartType::new(),
42        }
43    }
44
45    pub fn init(&'static self) {
46        kernel::deferred_call::DeferredCallClient::register(&self.sim_uart);
47    }
48}
49
50impl Default for VeeRDefaultPeripherals {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56impl InterruptService for VeeRDefaultPeripherals {
57    unsafe fn service_interrupt(&self, _interrupt: u32) -> bool {
58        true
59    }
60}
61
62impl<'a, I: InterruptService + 'a> VeeR<'a, I> {
63    /// # Safety
64    /// Accesses memory-mapped registers.
65    pub unsafe fn new(pic_interrupt_service: &'a I, mtimer: &'static Clint) -> Self {
66        Self {
67            userspace_kernel_boundary: SysCall::new(),
68            pic: &*addr_of!(PIC),
69            mtimer,
70            pic_interrupt_service,
71            pmp: PMPUserMPU::new(SimplePMP::new().unwrap()),
72        }
73    }
74
75    pub fn enable_pic_interrupts(&self) {
76        self.pic.enable_all();
77    }
78
79    unsafe fn handle_pic_interrupts(&self) {
80        while let Some(interrupt) = self.pic.get_saved_interrupts() {
81            if !self.pic_interrupt_service.service_interrupt(interrupt) {
82                panic!("Unhandled interrupt {}", interrupt);
83            }
84            self.with_interrupts_disabled(|| {
85                // Safe as interrupts are disabled
86                self.pic.complete(interrupt);
87            });
88        }
89    }
90}
91
92impl<'a, I: InterruptService + 'a> kernel::platform::chip::Chip for VeeR<'a, I> {
93    type MPU = PMPUserMPU<4, SimplePMP<8>>;
94    type UserspaceKernelBoundary = SysCall;
95    type ThreadIdProvider = rv32i::thread_id::RiscvThreadIdProvider;
96
97    fn mpu(&self) -> &Self::MPU {
98        &self.pmp
99    }
100
101    fn userspace_kernel_boundary(&self) -> &SysCall {
102        &self.userspace_kernel_boundary
103    }
104
105    fn service_pending_interrupts(&self) {
106        loop {
107            let mip = CSR.mip.extract();
108
109            // Check if the timer interrupt is pending
110            if mip.is_set(mip::mtimer) {
111                self.mtimer.handle_interrupt();
112            }
113            if self.pic.get_saved_interrupts().is_some() {
114                unsafe {
115                    self.handle_pic_interrupts();
116                }
117            }
118
119            if !mip.any_matching_bits_set(mip::mtimer::SET)
120                && self.pic.get_saved_interrupts().is_none()
121            {
122                break;
123            }
124        }
125
126        // Re-enable all MIE interrupts that we care about. Since we looped
127        // until we handled them all, we can re-enable all of them.
128        CSR.mie.modify(mie::mext::SET + mie::mtimer::SET);
129    }
130
131    fn has_pending_interrupts(&self) -> bool {
132        let mip = CSR.mip.extract();
133        self.pic.get_saved_interrupts().is_some() || mip.any_matching_bits_set(mip::mtimer::SET)
134    }
135
136    fn sleep(&self) {
137        unsafe {
138            rv32i::support::wfi();
139        }
140    }
141
142    unsafe fn with_interrupts_disabled<F, R>(&self, f: F) -> R
143    where
144        F: FnOnce() -> R,
145    {
146        rv32i::support::with_interrupts_disabled(f)
147    }
148
149    unsafe fn print_state(&self, writer: &mut dyn Write) {
150        rv32i::print_riscv_state(writer);
151    }
152}
153
154fn handle_exception(exception: mcause::Exception) {
155    match exception {
156        mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
157
158        mcause::Exception::InstructionMisaligned
159        | mcause::Exception::InstructionFault
160        | mcause::Exception::IllegalInstruction
161        | mcause::Exception::Breakpoint
162        | mcause::Exception::LoadMisaligned
163        | mcause::Exception::LoadFault
164        | mcause::Exception::StoreMisaligned
165        | mcause::Exception::StoreFault
166        | mcause::Exception::MachineEnvCall
167        | mcause::Exception::InstructionPageFault
168        | mcause::Exception::LoadPageFault
169        | mcause::Exception::StorePageFault
170        | mcause::Exception::Unknown => {
171            panic!("fatal exception: {:?}: {:#x}", exception, CSR.mtval.get());
172        }
173    }
174}
175
176unsafe fn handle_interrupt(intr: mcause::Interrupt) {
177    match intr {
178        mcause::Interrupt::UserSoft
179        | mcause::Interrupt::UserTimer
180        | mcause::Interrupt::UserExternal => {
181            panic!("unexpected user-mode interrupt");
182        }
183        mcause::Interrupt::SupervisorExternal
184        | mcause::Interrupt::SupervisorTimer
185        | mcause::Interrupt::SupervisorSoft => {
186            panic!("unexpected supervisor-mode interrupt");
187        }
188
189        mcause::Interrupt::MachineSoft => {
190            CSR.mie.modify(mie::msoft::CLEAR);
191        }
192        mcause::Interrupt::MachineTimer => {
193            CSR.mie.modify(mie::mtimer::CLEAR);
194        }
195        mcause::Interrupt::MachineExternal => {
196            // We received an interrupt, disable interrupts while we handle them
197            CSR.mie.modify(mie::mext::CLEAR);
198
199            // Claim the interrupt, unwrap() as we know an interrupt exists
200            // Once claimed this interrupt won't fire until it's completed
201            // NOTE: The interrupt is no longer pending in the PIC
202            loop {
203                let interrupt = (*addr_of!(PIC)).next_pending();
204
205                match interrupt {
206                    Some(irq) => {
207                        // Safe as interrupts are disabled
208                        (*addr_of!(PIC)).save_interrupt(irq);
209                    }
210                    None => {
211                        // Enable generic interrupts
212                        CSR.mie.modify(mie::mext::SET);
213                        break;
214                    }
215                }
216            }
217        }
218
219        mcause::Interrupt::Unknown(_) => {
220            panic!("interrupt of unknown cause");
221        }
222    }
223}
224
225/// Trap handler for board/chip specific code.
226///
227/// This gets called when an interrupt occurs while the chip is
228/// in kernel mode.
229///
230/// # Safety
231/// Accesses CSRs.
232#[export_name = "_start_trap_rust_from_kernel"]
233pub unsafe extern "C" fn start_trap_rust() {
234    match mcause::Trap::from(CSR.mcause.extract()) {
235        mcause::Trap::Interrupt(interrupt) => {
236            handle_interrupt(interrupt);
237        }
238        mcause::Trap::Exception(exception) => {
239            handle_exception(exception);
240        }
241    }
242}
243
244/// Function that gets called if an interrupt occurs while an app was running.
245///
246/// mcause is passed in, and this function should correctly handle disabling the
247/// interrupt that fired so that it does not trigger again.
248#[export_name = "_disable_interrupt_trap_rust_from_app"]
249pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
250    match mcause::Trap::from(mcause_val as usize) {
251        mcause::Trap::Interrupt(interrupt) => unsafe {
252            handle_interrupt(interrupt);
253        },
254        _ => {
255            panic!("unexpected non-interrupt\n");
256        }
257    }
258}
259
260/// Array used to track the "trap handler active" state per hart.
261///
262/// The `riscv` crate requires chip crates to allocate an array to
263/// track whether any given hart is currently in a trap handler. The
264/// array must be zero-initialized.
265#[export_name = "_trap_handler_active"]
266static mut TRAP_HANDLER_ACTIVE: [usize; 1] = [0; 1];