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.atomic(|| {
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
96    fn mpu(&self) -> &Self::MPU {
97        &self.pmp
98    }
99
100    fn userspace_kernel_boundary(&self) -> &SysCall {
101        &self.userspace_kernel_boundary
102    }
103
104    fn service_pending_interrupts(&self) {
105        loop {
106            let mip = CSR.mip.extract();
107
108            // Check if the timer interrupt is pending
109            if mip.is_set(mip::mtimer) {
110                self.mtimer.handle_interrupt();
111            }
112            if self.pic.get_saved_interrupts().is_some() {
113                unsafe {
114                    self.handle_pic_interrupts();
115                }
116            }
117
118            if !mip.any_matching_bits_set(mip::mtimer::SET)
119                && self.pic.get_saved_interrupts().is_none()
120            {
121                break;
122            }
123        }
124
125        // Re-enable all MIE interrupts that we care about. Since we looped
126        // until we handled them all, we can re-enable all of them.
127        CSR.mie.modify(mie::mext::SET + mie::mtimer::SET);
128    }
129
130    fn has_pending_interrupts(&self) -> bool {
131        let mip = CSR.mip.extract();
132        self.pic.get_saved_interrupts().is_some() || mip.any_matching_bits_set(mip::mtimer::SET)
133    }
134
135    fn sleep(&self) {
136        unsafe {
137            rv32i::support::wfi();
138        }
139    }
140
141    unsafe fn atomic<F, R>(&self, f: F) -> R
142    where
143        F: FnOnce() -> R,
144    {
145        rv32i::support::atomic(f)
146    }
147
148    unsafe fn print_state(&self, writer: &mut dyn Write) {
149        rv32i::print_riscv_state(writer);
150    }
151}
152
153fn handle_exception(exception: mcause::Exception) {
154    match exception {
155        mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
156
157        mcause::Exception::InstructionMisaligned
158        | mcause::Exception::InstructionFault
159        | mcause::Exception::IllegalInstruction
160        | mcause::Exception::Breakpoint
161        | mcause::Exception::LoadMisaligned
162        | mcause::Exception::LoadFault
163        | mcause::Exception::StoreMisaligned
164        | mcause::Exception::StoreFault
165        | mcause::Exception::MachineEnvCall
166        | mcause::Exception::InstructionPageFault
167        | mcause::Exception::LoadPageFault
168        | mcause::Exception::StorePageFault
169        | mcause::Exception::Unknown => {
170            panic!("fatal exception: {:?}: {:#x}", exception, CSR.mtval.get());
171        }
172    }
173}
174
175unsafe fn handle_interrupt(intr: mcause::Interrupt) {
176    match intr {
177        mcause::Interrupt::UserSoft
178        | mcause::Interrupt::UserTimer
179        | mcause::Interrupt::UserExternal => {
180            panic!("unexpected user-mode interrupt");
181        }
182        mcause::Interrupt::SupervisorExternal
183        | mcause::Interrupt::SupervisorTimer
184        | mcause::Interrupt::SupervisorSoft => {
185            panic!("unexpected supervisor-mode interrupt");
186        }
187
188        mcause::Interrupt::MachineSoft => {
189            CSR.mie.modify(mie::msoft::CLEAR);
190        }
191        mcause::Interrupt::MachineTimer => {
192            CSR.mie.modify(mie::mtimer::CLEAR);
193        }
194        mcause::Interrupt::MachineExternal => {
195            // We received an interrupt, disable interrupts while we handle them
196            CSR.mie.modify(mie::mext::CLEAR);
197
198            // Claim the interrupt, unwrap() as we know an interrupt exists
199            // Once claimed this interrupt won't fire until it's completed
200            // NOTE: The interrupt is no longer pending in the PIC
201            loop {
202                let interrupt = (*addr_of!(PIC)).next_pending();
203
204                match interrupt {
205                    Some(irq) => {
206                        // Safe as interrupts are disabled
207                        (*addr_of!(PIC)).save_interrupt(irq);
208                    }
209                    None => {
210                        // Enable generic interrupts
211                        CSR.mie.modify(mie::mext::SET);
212                        break;
213                    }
214                }
215            }
216        }
217
218        mcause::Interrupt::Unknown(_) => {
219            panic!("interrupt of unknown cause");
220        }
221    }
222}
223
224/// Trap handler for board/chip specific code.
225///
226/// This gets called when an interrupt occurs while the chip is
227/// in kernel mode.
228///
229/// # Safety
230/// Accesses CSRs.
231#[export_name = "_start_trap_rust_from_kernel"]
232pub unsafe extern "C" fn start_trap_rust() {
233    match mcause::Trap::from(CSR.mcause.extract()) {
234        mcause::Trap::Interrupt(interrupt) => {
235            handle_interrupt(interrupt);
236        }
237        mcause::Trap::Exception(exception) => {
238            handle_exception(exception);
239        }
240    }
241}
242
243/// Function that gets called if an interrupt occurs while an app was running.
244///
245/// mcause is passed in, and this function should correctly handle disabling the
246/// interrupt that fired so that it does not trigger again.
247#[export_name = "_disable_interrupt_trap_rust_from_app"]
248pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
249    match mcause::Trap::from(mcause_val as usize) {
250        mcause::Trap::Interrupt(interrupt) => unsafe {
251            handle_interrupt(interrupt);
252        },
253        _ => {
254            panic!("unexpected non-interrupt\n");
255        }
256    }
257}