qemu_rv32_virt/
main.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//! Board file for qemu-system-riscv32 "virt" machine type
6
7#![no_std]
8// Disable this attribute when documenting, as a workaround for
9// https://github.com/rust-lang/rust/issues/62184.
10#![cfg_attr(not(doc), no_main)]
11
12use core::ptr::addr_of;
13use core::ptr::addr_of_mut;
14
15use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
16use kernel::capabilities;
17use kernel::component::Component;
18use kernel::hil;
19use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
20use kernel::platform::KernelResources;
21use kernel::platform::SyscallDriverLookup;
22use kernel::scheduler::cooperative::CooperativeSched;
23use kernel::utilities::registers::interfaces::ReadWriteable;
24use kernel::{create_capability, debug, static_init};
25use qemu_rv32_virt_chip::chip::{QemuRv32VirtChip, QemuRv32VirtDefaultPeripherals};
26use rv32i::csr;
27
28pub mod io;
29
30pub const NUM_PROCS: usize = 4;
31
32// Actual memory for holding the active process structures. Need an empty list
33// at least.
34static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
35    [None; NUM_PROCS];
36
37// Reference to the chip for panic dumps.
38static mut CHIP: Option<&'static QemuRv32VirtChip<QemuRv32VirtDefaultPeripherals>> = None;
39
40// Reference to the process printer for panic dumps.
41static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
42    None;
43
44// How should the kernel respond when a process faults.
45const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
46    capsules_system::process_policies::PanicFaultPolicy {};
47
48/// Dummy buffer that causes the linker to reserve enough space for the stack.
49#[no_mangle]
50#[link_section = ".stack_buffer"]
51pub static mut STACK_MEMORY: [u8; 0x8000] = [0; 0x8000];
52
53/// A structure representing this platform that holds references to all
54/// capsules for this platform. We've included an alarm and console.
55struct QemuRv32VirtPlatform {
56    pconsole: &'static capsules_core::process_console::ProcessConsole<
57        'static,
58        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
59        capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm<
60            'static,
61            qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>,
62        >,
63        components::process_console::Capability,
64    >,
65    console: &'static capsules_core::console::Console<'static>,
66    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
67        'static,
68        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
69    >,
70    alarm: &'static capsules_core::alarm::AlarmDriver<
71        'static,
72        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
73    >,
74    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
75    scheduler: &'static CooperativeSched<'static>,
76    scheduler_timer: &'static VirtualSchedulerTimer<
77        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
78    >,
79    virtio_rng: Option<
80        &'static capsules_core::rng::RngDriver<
81            'static,
82            qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng<'static, 'static>,
83        >,
84    >,
85}
86
87/// Mapping of integer syscalls to objects that implement syscalls.
88impl SyscallDriverLookup for QemuRv32VirtPlatform {
89    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
90    where
91        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
92    {
93        match driver_num {
94            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
95            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
96            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
97            capsules_core::rng::DRIVER_NUM => {
98                if let Some(rng_driver) = self.virtio_rng {
99                    f(Some(rng_driver))
100                } else {
101                    f(None)
102                }
103            }
104            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
105            _ => f(None),
106        }
107    }
108}
109
110impl
111    KernelResources<
112        qemu_rv32_virt_chip::chip::QemuRv32VirtChip<
113            'static,
114            QemuRv32VirtDefaultPeripherals<'static>,
115        >,
116    > for QemuRv32VirtPlatform
117{
118    type SyscallDriverLookup = Self;
119    type SyscallFilter = ();
120    type ProcessFault = ();
121    type Scheduler = CooperativeSched<'static>;
122    type SchedulerTimer = VirtualSchedulerTimer<
123        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
124    >;
125    type WatchDog = ();
126    type ContextSwitchCallback = ();
127
128    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
129        self
130    }
131    fn syscall_filter(&self) -> &Self::SyscallFilter {
132        &()
133    }
134    fn process_fault(&self) -> &Self::ProcessFault {
135        &()
136    }
137    fn scheduler(&self) -> &Self::Scheduler {
138        self.scheduler
139    }
140    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
141        self.scheduler_timer
142    }
143    fn watchdog(&self) -> &Self::WatchDog {
144        &()
145    }
146    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
147        &()
148    }
149}
150
151/// This is in a separate, inline(never) function so that its stack frame is
152/// removed when this function returns. Otherwise, the stack space used for
153/// these static_inits is wasted.
154#[inline(never)]
155unsafe fn start() -> (
156    &'static kernel::Kernel,
157    QemuRv32VirtPlatform,
158    &'static qemu_rv32_virt_chip::chip::QemuRv32VirtChip<
159        'static,
160        QemuRv32VirtDefaultPeripherals<'static>,
161    >,
162) {
163    // These symbols are defined in the linker script.
164    extern "C" {
165        /// Beginning of the ROM region containing app images.
166        static _sapps: u8;
167        /// End of the ROM region containing app images.
168        static _eapps: u8;
169        /// Beginning of the RAM region for app memory.
170        static mut _sappmem: u8;
171        /// End of the RAM region for app memory.
172        static _eappmem: u8;
173        /// The start of the kernel text (Included only for kernel PMP)
174        static _stext: u8;
175        /// The end of the kernel text (Included only for kernel PMP)
176        static _etext: u8;
177        /// The start of the kernel / app / storage flash (Included only for kernel PMP)
178        static _sflash: u8;
179        /// The end of the kernel / app / storage flash (Included only for kernel PMP)
180        static _eflash: u8;
181        /// The start of the kernel / app RAM (Included only for kernel PMP)
182        static _ssram: u8;
183        /// The end of the kernel / app RAM (Included only for kernel PMP)
184        static _esram: u8;
185    }
186
187    // ---------- BASIC INITIALIZATION -----------
188
189    // Basic setup of the RISC-V IMAC platform
190    rv32i::configure_trap_handler();
191
192    // Set up memory protection immediately after setting the trap handler, to
193    // ensure that much of the board initialization routine runs with ePMP
194    // protection.
195    let epmp = rv32i::pmp::kernel_protection_mml_epmp::KernelProtectionMMLEPMP::new(
196        rv32i::pmp::kernel_protection_mml_epmp::FlashRegion(
197            rv32i::pmp::NAPOTRegionSpec::new(
198                core::ptr::addr_of!(_sflash),
199                core::ptr::addr_of!(_eflash) as usize - core::ptr::addr_of!(_sflash) as usize,
200            )
201            .unwrap(),
202        ),
203        rv32i::pmp::kernel_protection_mml_epmp::RAMRegion(
204            rv32i::pmp::NAPOTRegionSpec::new(
205                core::ptr::addr_of!(_ssram),
206                core::ptr::addr_of!(_esram) as usize - core::ptr::addr_of!(_ssram) as usize,
207            )
208            .unwrap(),
209        ),
210        rv32i::pmp::kernel_protection_mml_epmp::MMIORegion(
211            rv32i::pmp::NAPOTRegionSpec::new(
212                core::ptr::null::<u8>(), // start
213                0x20000000,              // size
214            )
215            .unwrap(),
216        ),
217        rv32i::pmp::kernel_protection_mml_epmp::KernelTextRegion(
218            rv32i::pmp::TORRegionSpec::new(
219                core::ptr::addr_of!(_stext),
220                core::ptr::addr_of!(_etext),
221            )
222            .unwrap(),
223        ),
224    )
225    .unwrap();
226
227    // Acquire required capabilities
228    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
229    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
230
231    // Create a board kernel instance
232    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
233
234    // ---------- QEMU-SYSTEM-RISCV32 "virt" MACHINE PERIPHERALS ----------
235
236    let peripherals = static_init!(
237        QemuRv32VirtDefaultPeripherals,
238        QemuRv32VirtDefaultPeripherals::new(),
239    );
240
241    // Create a shared UART channel for the console and for kernel
242    // debug over the provided memory-mapped 16550-compatible
243    // UART.
244    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
245        .finalize(components::uart_mux_component_static!());
246
247    // Use the RISC-V machine timer timesource
248    let hardware_timer = static_init!(
249        qemu_rv32_virt_chip::chip::QemuRv32VirtClint,
250        qemu_rv32_virt_chip::chip::QemuRv32VirtClint::new(&qemu_rv32_virt_chip::clint::CLINT_BASE)
251    );
252
253    // Create a shared virtualization mux layer on top of a single hardware
254    // alarm.
255    let mux_alarm = static_init!(
256        MuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
257        MuxAlarm::new(hardware_timer)
258    );
259    hil::time::Alarm::set_alarm_client(hardware_timer, mux_alarm);
260
261    // Virtual alarm for the scheduler
262    let systick_virtual_alarm = static_init!(
263        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
264        VirtualMuxAlarm::new(mux_alarm)
265    );
266    systick_virtual_alarm.setup();
267
268    // Virtual alarm and driver for userspace
269    let virtual_alarm_user = static_init!(
270        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
271        VirtualMuxAlarm::new(mux_alarm)
272    );
273    virtual_alarm_user.setup();
274
275    let alarm = static_init!(
276        capsules_core::alarm::AlarmDriver<
277            'static,
278            VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
279        >,
280        capsules_core::alarm::AlarmDriver::new(
281            virtual_alarm_user,
282            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
283        )
284    );
285    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
286
287    // ---------- VIRTIO PERIPHERAL DISCOVERY ----------
288    //
289    // This board has 8 virtio-mmio (v2 personality required!) devices
290    //
291    // Collect supported VirtIO peripheral indicies and initialize them if they
292    // are found. If there are two instances of a supported peripheral, the one
293    // on a higher-indexed VirtIO transport is used.
294    let (mut virtio_net_idx, mut virtio_rng_idx) = (None, None);
295    for (i, virtio_device) in peripherals.virtio_mmio.iter().enumerate() {
296        use qemu_rv32_virt_chip::virtio::devices::VirtIODeviceType;
297        match virtio_device.query() {
298            Some(VirtIODeviceType::NetworkCard) => {
299                virtio_net_idx = Some(i);
300            }
301            Some(VirtIODeviceType::EntropySource) => {
302                virtio_rng_idx = Some(i);
303            }
304            _ => (),
305        }
306    }
307
308    // If there is a VirtIO EntropySource present, use the appropriate VirtIORng
309    // driver and expose it to userspace though the RngDriver
310    let virtio_rng_driver: Option<
311        &'static capsules_core::rng::RngDriver<
312            'static,
313            qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng<'static, 'static>,
314        >,
315    > = if let Some(rng_idx) = virtio_rng_idx {
316        use kernel::hil::rng::Rng;
317        use qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng;
318        use qemu_rv32_virt_chip::virtio::queues::split_queue::{
319            SplitVirtqueue, VirtqueueAvailableRing, VirtqueueDescriptors, VirtqueueUsedRing,
320        };
321        use qemu_rv32_virt_chip::virtio::queues::Virtqueue;
322        use qemu_rv32_virt_chip::virtio::transports::VirtIOTransport;
323
324        // EntropySource requires a single Virtqueue for retrieved entropy
325        let descriptors = static_init!(VirtqueueDescriptors<1>, VirtqueueDescriptors::default(),);
326        let available_ring =
327            static_init!(VirtqueueAvailableRing<1>, VirtqueueAvailableRing::default(),);
328        let used_ring = static_init!(VirtqueueUsedRing<1>, VirtqueueUsedRing::default(),);
329        let queue = static_init!(
330            SplitVirtqueue<1>,
331            SplitVirtqueue::new(descriptors, available_ring, used_ring),
332        );
333        queue.set_transport(&peripherals.virtio_mmio[rng_idx]);
334
335        // VirtIO EntropySource device driver instantiation
336        let rng = static_init!(VirtIORng, VirtIORng::new(queue));
337        kernel::deferred_call::DeferredCallClient::register(rng);
338        queue.set_client(rng);
339
340        // Register the queues and driver with the transport, so interrupts
341        // are routed properly
342        let mmio_queues = static_init!([&'static dyn Virtqueue; 1], [queue; 1]);
343        peripherals.virtio_mmio[rng_idx]
344            .initialize(rng, mmio_queues)
345            .unwrap();
346
347        // Provide an internal randomness buffer
348        let rng_buffer = static_init!([u8; 64], [0; 64]);
349        rng.provide_buffer(rng_buffer)
350            .expect("rng: providing initial buffer failed");
351
352        // Userspace RNG driver over the VirtIO EntropySource
353        let rng_driver = static_init!(
354            capsules_core::rng::RngDriver<VirtIORng>,
355            capsules_core::rng::RngDriver::new(
356                rng,
357                board_kernel.create_grant(capsules_core::rng::DRIVER_NUM, &memory_allocation_cap),
358            ),
359        );
360        rng.set_client(rng_driver);
361
362        Some(rng_driver as &'static capsules_core::rng::RngDriver<VirtIORng>)
363    } else {
364        // No VirtIO EntropySource discovered
365        None
366    };
367
368    // If there is a VirtIO NetworkCard present, use the appropriate VirtIONet
369    // driver. Currently this is not used, as work on the userspace network
370    // driver and kernel network stack is in progress.
371    //
372    // A template dummy driver is provided to verify basic functionality of this
373    // interface.
374    let _virtio_net_if: Option<
375        &'static qemu_rv32_virt_chip::virtio::devices::virtio_net::VirtIONet<'static>,
376    > = if let Some(net_idx) = virtio_net_idx {
377        use qemu_rv32_virt_chip::virtio::devices::virtio_net::VirtIONet;
378        use qemu_rv32_virt_chip::virtio::queues::split_queue::{
379            SplitVirtqueue, VirtqueueAvailableRing, VirtqueueDescriptors, VirtqueueUsedRing,
380        };
381        use qemu_rv32_virt_chip::virtio::queues::Virtqueue;
382        use qemu_rv32_virt_chip::virtio::transports::VirtIOTransport;
383
384        // A VirtIO NetworkCard requires 2 Virtqueues:
385        // - a TX Virtqueue with buffers for outgoing packets
386        // - a RX Virtqueue where incoming packet buffers are
387        //   placed and filled by the device
388
389        // TX Virtqueue
390        let tx_descriptors =
391            static_init!(VirtqueueDescriptors<2>, VirtqueueDescriptors::default(),);
392        let tx_available_ring =
393            static_init!(VirtqueueAvailableRing<2>, VirtqueueAvailableRing::default(),);
394        let tx_used_ring = static_init!(VirtqueueUsedRing<2>, VirtqueueUsedRing::default(),);
395        let tx_queue = static_init!(
396            SplitVirtqueue<2>,
397            SplitVirtqueue::new(tx_descriptors, tx_available_ring, tx_used_ring),
398        );
399        tx_queue.set_transport(&peripherals.virtio_mmio[net_idx]);
400
401        // RX Virtqueue
402        let rx_descriptors =
403            static_init!(VirtqueueDescriptors<2>, VirtqueueDescriptors::default(),);
404        let rx_available_ring =
405            static_init!(VirtqueueAvailableRing<2>, VirtqueueAvailableRing::default(),);
406        let rx_used_ring = static_init!(VirtqueueUsedRing<2>, VirtqueueUsedRing::default(),);
407        let rx_queue = static_init!(
408            SplitVirtqueue<2>,
409            SplitVirtqueue::new(rx_descriptors, rx_available_ring, rx_used_ring),
410        );
411        rx_queue.set_transport(&peripherals.virtio_mmio[net_idx]);
412
413        // Incoming and outgoing packets are prefixed by a 12-byte
414        // VirtIO specific header
415        let tx_header_buf = static_init!([u8; 12], [0; 12]);
416        let rx_header_buf = static_init!([u8; 12], [0; 12]);
417
418        // Currently, provide a single receive buffer to write
419        // incoming packets into
420        let rx_buffer = static_init!([u8; 1526], [0; 1526]);
421
422        // Instantiate the VirtIONet (NetworkCard) driver and set
423        // the queues
424        let virtio_net = static_init!(
425            VirtIONet<'static>,
426            VirtIONet::new(
427                0,
428                tx_queue,
429                tx_header_buf,
430                rx_queue,
431                rx_header_buf,
432                rx_buffer,
433            ),
434        );
435        tx_queue.set_client(virtio_net);
436        rx_queue.set_client(virtio_net);
437
438        // Register the queues and driver with the transport, so
439        // interrupts are routed properly
440        let mmio_queues = static_init!([&'static dyn Virtqueue; 2], [rx_queue, tx_queue]);
441        peripherals.virtio_mmio[net_idx]
442            .initialize(virtio_net, mmio_queues)
443            .unwrap();
444
445        // Don't forget to enable RX once when integrating this into a
446        // proper Ethernet stack:
447        // virtio_net.enable_rx();
448
449        // TODO: When we have a proper Ethernet driver available for userspace,
450        // return that. For now, just return a reference to the raw VirtIONet
451        // driver:
452        Some(virtio_net as &'static VirtIONet)
453    } else {
454        // No VirtIO NetworkCard discovered
455        None
456    };
457
458    // ---------- INITIALIZE CHIP, ENABLE INTERRUPTS ---------
459
460    let chip = static_init!(
461        QemuRv32VirtChip<QemuRv32VirtDefaultPeripherals>,
462        QemuRv32VirtChip::new(peripherals, hardware_timer, epmp),
463    );
464    CHIP = Some(chip);
465
466    // Need to enable all interrupts for Tock Kernel
467    chip.enable_plic_interrupts();
468
469    // enable interrupts globally
470    csr::CSR
471        .mie
472        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET + csr::mie::mie::mtimer::SET);
473    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
474
475    // ---------- FINAL SYSTEM INITIALIZATION ----------
476
477    // Create the process printer used in panic prints, etc.
478    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
479        .finalize(components::process_printer_text_component_static!());
480    PROCESS_PRINTER = Some(process_printer);
481
482    // Initialize the kernel's process console.
483    let pconsole = components::process_console::ProcessConsoleComponent::new(
484        board_kernel,
485        uart_mux,
486        mux_alarm,
487        process_printer,
488        None,
489    )
490    .finalize(components::process_console_component_static!(
491        qemu_rv32_virt_chip::chip::QemuRv32VirtClint
492    ));
493
494    // Setup the console.
495    let console = components::console::ConsoleComponent::new(
496        board_kernel,
497        capsules_core::console::DRIVER_NUM,
498        uart_mux,
499    )
500    .finalize(components::console_component_static!());
501    // Create the debugger object that handles calls to `debug!()`.
502    components::debug_writer::DebugWriterComponent::new(uart_mux)
503        .finalize(components::debug_writer_component_static!());
504
505    let lldb = components::lldb::LowLevelDebugComponent::new(
506        board_kernel,
507        capsules_core::low_level_debug::DRIVER_NUM,
508        uart_mux,
509    )
510    .finalize(components::low_level_debug_component_static!());
511
512    let scheduler =
513        components::sched::cooperative::CooperativeComponent::new(&*addr_of!(PROCESSES))
514            .finalize(components::cooperative_component_static!(NUM_PROCS));
515
516    let scheduler_timer = static_init!(
517        VirtualSchedulerTimer<
518            VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
519        >,
520        VirtualSchedulerTimer::new(systick_virtual_alarm)
521    );
522
523    let platform = QemuRv32VirtPlatform {
524        pconsole,
525        console,
526        alarm,
527        lldb,
528        scheduler,
529        scheduler_timer,
530        virtio_rng: virtio_rng_driver,
531        ipc: kernel::ipc::IPC::new(
532            board_kernel,
533            kernel::ipc::DRIVER_NUM,
534            &memory_allocation_cap,
535        ),
536    };
537
538    // Start the process console:
539    let _ = platform.pconsole.start();
540
541    debug!("QEMU RISC-V 32-bit \"virt\" machine, initialization complete.");
542    debug!("Entering main loop.");
543
544    // ---------- PROCESS LOADING, SCHEDULER LOOP ----------
545
546    kernel::process::load_processes(
547        board_kernel,
548        chip,
549        core::slice::from_raw_parts(
550            core::ptr::addr_of!(_sapps),
551            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
552        ),
553        core::slice::from_raw_parts_mut(
554            core::ptr::addr_of_mut!(_sappmem),
555            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
556        ),
557        &mut *addr_of_mut!(PROCESSES),
558        &FAULT_RESPONSE,
559        &process_mgmt_cap,
560    )
561    .unwrap_or_else(|err| {
562        debug!("Error loading processes!");
563        debug!("{:?}", err);
564    });
565
566    (board_kernel, platform, chip)
567}
568
569/// Main function called after RAM initialized.
570#[no_mangle]
571pub unsafe fn main() {
572    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
573
574    let (board_kernel, platform, chip) = start();
575    board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
576}