qemu_rv32_virt_chip/
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
5//! High-level setup and interrupt mapping for the chip.
6
7use core::fmt::Write;
8use core::ptr::addr_of;
9
10use kernel::debug;
11use kernel::hil::time::Freq10MHz;
12use kernel::platform::chip::{Chip, InterruptService};
13
14use kernel::utilities::registers::interfaces::{ReadWriteable, Readable};
15
16use rv32i::csr::{mcause, mie::mie, mip::mip, CSR};
17
18use crate::plic::PLIC;
19use sifive::plic::Plic;
20
21use crate::interrupts;
22
23use virtio::transports::mmio::VirtIOMMIODevice;
24
25type QemuRv32VirtPMP = rv32i::pmp::PMPUserMPU<
26    5,
27    rv32i::pmp::kernel_protection_mml_epmp::KernelProtectionMMLEPMP<16, 5>,
28>;
29
30pub type QemuRv32VirtClint<'a> = sifive::clint::Clint<'a, Freq10MHz>;
31
32pub struct QemuRv32VirtChip<'a, I: InterruptService + 'a> {
33    userspace_kernel_boundary: rv32i::syscall::SysCall,
34    pmp: QemuRv32VirtPMP,
35    plic: &'a Plic,
36    timer: &'a QemuRv32VirtClint<'a>,
37    plic_interrupt_service: &'a I,
38}
39
40pub struct QemuRv32VirtDefaultPeripherals<'a> {
41    pub uart0: crate::uart::Uart16550<'a>,
42    pub virtio_mmio: [VirtIOMMIODevice; 8],
43}
44
45impl QemuRv32VirtDefaultPeripherals<'_> {
46    pub fn new() -> Self {
47        Self {
48            uart0: crate::uart::Uart16550::new(crate::uart::UART0_BASE),
49            virtio_mmio: [
50                VirtIOMMIODevice::new(crate::virtio_mmio::VIRTIO_MMIO_0_BASE),
51                VirtIOMMIODevice::new(crate::virtio_mmio::VIRTIO_MMIO_1_BASE),
52                VirtIOMMIODevice::new(crate::virtio_mmio::VIRTIO_MMIO_2_BASE),
53                VirtIOMMIODevice::new(crate::virtio_mmio::VIRTIO_MMIO_3_BASE),
54                VirtIOMMIODevice::new(crate::virtio_mmio::VIRTIO_MMIO_4_BASE),
55                VirtIOMMIODevice::new(crate::virtio_mmio::VIRTIO_MMIO_5_BASE),
56                VirtIOMMIODevice::new(crate::virtio_mmio::VIRTIO_MMIO_6_BASE),
57                VirtIOMMIODevice::new(crate::virtio_mmio::VIRTIO_MMIO_7_BASE),
58            ],
59        }
60    }
61}
62
63impl InterruptService for QemuRv32VirtDefaultPeripherals<'_> {
64    unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
65        match interrupt {
66            interrupts::UART0 => self.uart0.handle_interrupt(),
67            interrupts::VIRTIO_MMIO_0 => self.virtio_mmio[0].handle_interrupt(),
68            interrupts::VIRTIO_MMIO_1 => self.virtio_mmio[1].handle_interrupt(),
69            interrupts::VIRTIO_MMIO_2 => self.virtio_mmio[2].handle_interrupt(),
70            interrupts::VIRTIO_MMIO_3 => self.virtio_mmio[3].handle_interrupt(),
71            interrupts::VIRTIO_MMIO_4 => self.virtio_mmio[4].handle_interrupt(),
72            interrupts::VIRTIO_MMIO_5 => self.virtio_mmio[5].handle_interrupt(),
73            interrupts::VIRTIO_MMIO_6 => self.virtio_mmio[6].handle_interrupt(),
74            interrupts::VIRTIO_MMIO_7 => self.virtio_mmio[7].handle_interrupt(),
75            _ => return false,
76        }
77        true
78    }
79}
80
81impl<'a, I: InterruptService + 'a> QemuRv32VirtChip<'a, I> {
82    pub unsafe fn new(
83        plic_interrupt_service: &'a I,
84        timer: &'a QemuRv32VirtClint<'a>,
85        pmp: rv32i::pmp::kernel_protection_mml_epmp::KernelProtectionMMLEPMP<16, 5>,
86    ) -> Self {
87        Self {
88            userspace_kernel_boundary: rv32i::syscall::SysCall::new(),
89            pmp: rv32i::pmp::PMPUserMPU::new(pmp),
90            plic: &*addr_of!(PLIC),
91            timer,
92            plic_interrupt_service,
93        }
94    }
95
96    pub unsafe fn enable_plic_interrupts(&self) {
97        self.plic.disable_all();
98        self.plic.clear_all_pending();
99        self.plic.enable_all();
100    }
101
102    unsafe fn handle_plic_interrupts(&self) {
103        while let Some(interrupt) = self.plic.get_saved_interrupts() {
104            if !self.plic_interrupt_service.service_interrupt(interrupt) {
105                debug!("Pidx {}", interrupt);
106            }
107            self.atomic(|| {
108                self.plic.complete(interrupt);
109            });
110        }
111    }
112}
113
114impl<'a, I: InterruptService + 'a> Chip for QemuRv32VirtChip<'a, I> {
115    type MPU = QemuRv32VirtPMP;
116    type UserspaceKernelBoundary = rv32i::syscall::SysCall;
117
118    fn mpu(&self) -> &Self::MPU {
119        &self.pmp
120    }
121
122    fn userspace_kernel_boundary(&self) -> &rv32i::syscall::SysCall {
123        &self.userspace_kernel_boundary
124    }
125
126    fn service_pending_interrupts(&self) {
127        loop {
128            let mip = CSR.mip.extract();
129
130            if mip.is_set(mip::mtimer) {
131                self.timer.handle_interrupt();
132            }
133            if self.plic.get_saved_interrupts().is_some() {
134                unsafe {
135                    self.handle_plic_interrupts();
136                }
137            }
138
139            if !mip.any_matching_bits_set(mip::mtimer::SET)
140                && self.plic.get_saved_interrupts().is_none()
141            {
142                break;
143            }
144        }
145
146        // Re-enable all MIE interrupts that we care about. Since we looped
147        // until we handled them all, we can re-enable all of them.
148        CSR.mie.modify(mie::mext::SET + mie::mtimer::SET);
149    }
150
151    fn has_pending_interrupts(&self) -> bool {
152        // First check if the global machine timer interrupt is set.
153        // We would also need to check for additional global interrupt bits
154        // if there were to be used for anything in the future.
155        if CSR.mip.is_set(mip::mtimer) {
156            return true;
157        }
158
159        // Then we can check the PLIC.
160        self.plic.get_saved_interrupts().is_some()
161    }
162
163    fn sleep(&self) {
164        unsafe {
165            rv32i::support::wfi();
166        }
167    }
168
169    unsafe fn atomic<F, R>(&self, f: F) -> R
170    where
171        F: FnOnce() -> R,
172    {
173        rv32i::support::atomic(f)
174    }
175
176    unsafe fn print_state(&self, writer: &mut dyn Write) {
177        rv32i::print_riscv_state(writer);
178        let _ = writer.write_fmt(format_args!("{}", self.pmp.pmp));
179    }
180}
181
182fn handle_exception(exception: mcause::Exception) {
183    match exception {
184        mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
185
186        mcause::Exception::InstructionMisaligned
187        | mcause::Exception::InstructionFault
188        | mcause::Exception::IllegalInstruction
189        | mcause::Exception::Breakpoint
190        | mcause::Exception::LoadMisaligned
191        | mcause::Exception::LoadFault
192        | mcause::Exception::StoreMisaligned
193        | mcause::Exception::StoreFault
194        | mcause::Exception::MachineEnvCall
195        | mcause::Exception::InstructionPageFault
196        | mcause::Exception::LoadPageFault
197        | mcause::Exception::StorePageFault
198        | mcause::Exception::Unknown => {
199            panic!("fatal exception");
200        }
201    }
202}
203
204unsafe fn handle_interrupt(intr: mcause::Interrupt) {
205    match intr {
206        mcause::Interrupt::UserSoft
207        | mcause::Interrupt::UserTimer
208        | mcause::Interrupt::UserExternal => {
209            panic!("unexpected user-mode interrupt");
210        }
211        mcause::Interrupt::SupervisorExternal
212        | mcause::Interrupt::SupervisorTimer
213        | mcause::Interrupt::SupervisorSoft => {
214            panic!("unexpected supervisor-mode interrupt");
215        }
216
217        mcause::Interrupt::MachineSoft => {
218            CSR.mie.modify(mie::msoft::CLEAR);
219        }
220        mcause::Interrupt::MachineTimer => {
221            CSR.mie.modify(mie::mtimer::CLEAR);
222        }
223        mcause::Interrupt::MachineExternal => {
224            // We received an interrupt, disable interrupts while we handle them
225            CSR.mie.modify(mie::mext::CLEAR);
226
227            // Claim the interrupt, unwrap() as we know an interrupt exists
228            // Once claimed this interrupt won't fire until it's completed
229            // NOTE: The interrupt is no longer pending in the PLIC
230            loop {
231                let interrupt = (*addr_of!(PLIC)).next_pending();
232
233                match interrupt {
234                    Some(irq) => {
235                        // Safe as interrupts are disabled
236                        (*addr_of!(PLIC)).save_interrupt(irq);
237                    }
238                    None => {
239                        // Enable generic interrupts
240                        CSR.mie.modify(mie::mext::SET);
241
242                        break;
243                    }
244                }
245            }
246        }
247
248        mcause::Interrupt::Unknown(_) => {
249            panic!("interrupt of unknown cause");
250        }
251    }
252}
253
254/// Trap handler for board/chip specific code.
255///
256/// For the qemu-system-riscv32 virt machine this gets called when an
257/// interrupt occurs while the chip is in kernel mode.
258#[export_name = "_start_trap_rust_from_kernel"]
259pub unsafe extern "C" fn start_trap_rust() {
260    match mcause::Trap::from(CSR.mcause.extract()) {
261        mcause::Trap::Interrupt(interrupt) => {
262            handle_interrupt(interrupt);
263        }
264        mcause::Trap::Exception(exception) => {
265            handle_exception(exception);
266        }
267    }
268}
269
270/// Function that gets called if an interrupt occurs while an app was running.
271///
272/// mcause is passed in, and this function should correctly handle disabling the
273/// interrupt that fired so that it does not trigger again.
274#[export_name = "_disable_interrupt_trap_rust_from_app"]
275pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
276    match mcause::Trap::from(mcause_val as usize) {
277        mcause::Trap::Interrupt(interrupt) => {
278            handle_interrupt(interrupt);
279        }
280        _ => {
281            panic!("unexpected non-interrupt\n");
282        }
283    }
284}