Skip to main content

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