qemu_rv32_virt_lib/
lib.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#![no_main]
9
10use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
11use kernel::capabilities;
12use kernel::component::Component;
13use kernel::hil;
14use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
15use kernel::platform::KernelResources;
16use kernel::platform::SyscallDriverLookup;
17use kernel::process::ProcessArray;
18use kernel::scheduler::cooperative::CooperativeSched;
19use kernel::utilities::registers::interfaces::ReadWriteable;
20use kernel::{create_capability, debug, static_init};
21use qemu_rv32_virt_chip::chip::{QemuRv32VirtChip, QemuRv32VirtDefaultPeripherals};
22use rv32i::csr;
23
24pub mod io;
25
26pub const NUM_PROCS: usize = 4;
27
28/// Static variables used by io.rs.
29static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
30
31// Reference to the chip for panic dumps.
32static mut CHIP: Option<&'static QemuRv32VirtChip<QemuRv32VirtDefaultPeripherals>> = None;
33
34// Reference to the process printer for panic dumps.
35static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
36    None;
37
38pub type Chip = QemuRv32VirtChip<'static, QemuRv32VirtDefaultPeripherals<'static>>;
39type RngDriver = components::rng::RngRandomComponentType<
40    qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng<'static, 'static>,
41>;
42pub type ScreenHw = qemu_rv32_virt_chip::virtio::devices::virtio_gpu::VirtIOGPU<'static, 'static>;
43
44kernel::stack_size! {0x8000}
45
46/// A structure representing this platform that holds references to all
47/// capsules for this platform. We've included an alarm and console.
48pub struct QemuRv32VirtPlatform {
49    pub pconsole: &'static capsules_core::process_console::ProcessConsole<
50        'static,
51        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
52        capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm<
53            'static,
54            qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>,
55        >,
56        components::process_console::Capability,
57    >,
58    console: &'static capsules_core::console::Console<'static>,
59    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
60        'static,
61        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
62    >,
63    alarm: &'static capsules_core::alarm::AlarmDriver<
64        'static,
65        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
66    >,
67    pub ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
68    scheduler: &'static CooperativeSched<'static>,
69    scheduler_timer: &'static VirtualSchedulerTimer<
70        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
71    >,
72    rng: Option<&'static RngDriver>,
73    virtio_ethernet_tap: Option<
74        &'static capsules_extra::ethernet_tap::EthernetTapDriver<
75            'static,
76            qemu_rv32_virt_chip::virtio::devices::virtio_net::VirtIONet<'static>,
77        >,
78    >,
79    pub virtio_gpu_screen: Option<
80        &'static capsules_extra::screen::screen_adapters::ScreenARGB8888ToMono8BitPage<
81            'static,
82            ScreenHw,
83        >,
84    >,
85    pub virtio_input_keyboard:
86        Option<&'static qemu_rv32_virt_chip::virtio::devices::virtio_input::VirtIOInput<'static>>,
87}
88
89/// Mapping of integer syscalls to objects that implement syscalls.
90impl SyscallDriverLookup for QemuRv32VirtPlatform {
91    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
92    where
93        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
94    {
95        match driver_num {
96            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
97            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
98            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
99            capsules_core::rng::DRIVER_NUM => {
100                if let Some(rng_driver) = self.rng {
101                    f(Some(rng_driver))
102                } else {
103                    f(None)
104                }
105            }
106            capsules_extra::ethernet_tap::DRIVER_NUM => {
107                if let Some(ethernet_tap_driver) = self.virtio_ethernet_tap {
108                    f(Some(ethernet_tap_driver))
109                } else {
110                    f(None)
111                }
112            }
113
114            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
115            _ => f(None),
116        }
117    }
118}
119
120impl
121    KernelResources<
122        qemu_rv32_virt_chip::chip::QemuRv32VirtChip<
123            'static,
124            QemuRv32VirtDefaultPeripherals<'static>,
125        >,
126    > for QemuRv32VirtPlatform
127{
128    type SyscallDriverLookup = Self;
129    type SyscallFilter = ();
130    type ProcessFault = ();
131    type Scheduler = CooperativeSched<'static>;
132    type SchedulerTimer = VirtualSchedulerTimer<
133        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
134    >;
135    type WatchDog = ();
136    type ContextSwitchCallback = ();
137
138    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
139        self
140    }
141    fn syscall_filter(&self) -> &Self::SyscallFilter {
142        &()
143    }
144    fn process_fault(&self) -> &Self::ProcessFault {
145        &()
146    }
147    fn scheduler(&self) -> &Self::Scheduler {
148        self.scheduler
149    }
150    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
151        self.scheduler_timer
152    }
153    fn watchdog(&self) -> &Self::WatchDog {
154        &()
155    }
156    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
157        &()
158    }
159}
160
161/// This is in a separate, inline(never) function so that its stack frame is
162/// removed when this function returns. Otherwise, the stack space used for
163/// these static_inits is wasted.
164#[inline(never)]
165// We allocate a frame-buffer for converting Mono_8BitPage pixel data
166// into an ARGB_8888 format. This can consume a large amount of stack
167// space, as we allocate this buffer with `static_init!()`:
168#[allow(clippy::large_stack_frames, clippy::large_stack_arrays)]
169pub unsafe fn start() -> (
170    &'static kernel::Kernel,
171    QemuRv32VirtPlatform,
172    &'static qemu_rv32_virt_chip::chip::QemuRv32VirtChip<
173        'static,
174        QemuRv32VirtDefaultPeripherals<'static>,
175    >,
176) {
177    // These symbols are defined in the linker script.
178    extern "C" {
179        /// The start of the kernel text (Included only for kernel PMP)
180        static _stext: u8;
181        /// The end of the kernel text (Included only for kernel PMP)
182        static _etext: u8;
183        /// The start of the kernel / app / storage flash (Included only for kernel PMP)
184        static _sflash: u8;
185        /// The end of the kernel / app / storage flash (Included only for kernel PMP)
186        static _eflash: u8;
187        /// The start of the kernel / app RAM (Included only for kernel PMP)
188        static _ssram: u8;
189        /// The end of the kernel / app RAM (Included only for kernel PMP)
190        static _esram: u8;
191    }
192
193    // ---------- BASIC INITIALIZATION -----------
194
195    // Basic setup of the RISC-V IMAC platform
196    rv32i::configure_trap_handler();
197
198    // Set up memory protection immediately after setting the trap handler, to
199    // ensure that much of the board initialization routine runs with ePMP
200    // protection.
201    let epmp = rv32i::pmp::kernel_protection_mml_epmp::KernelProtectionMMLEPMP::new(
202        rv32i::pmp::kernel_protection_mml_epmp::FlashRegion(
203            rv32i::pmp::NAPOTRegionSpec::from_start_end(
204                core::ptr::addr_of!(_sflash),
205                core::ptr::addr_of!(_eflash),
206            )
207            .unwrap(),
208        ),
209        rv32i::pmp::kernel_protection_mml_epmp::RAMRegion(
210            rv32i::pmp::NAPOTRegionSpec::from_start_end(
211                core::ptr::addr_of!(_ssram),
212                core::ptr::addr_of!(_esram),
213            )
214            .unwrap(),
215        ),
216        rv32i::pmp::kernel_protection_mml_epmp::MMIORegion(
217            rv32i::pmp::NAPOTRegionSpec::from_start_size(
218                core::ptr::null::<u8>(), // start
219                0x20000000,              // size
220            )
221            .unwrap(),
222        ),
223        rv32i::pmp::kernel_protection_mml_epmp::KernelTextRegion(
224            rv32i::pmp::TORRegionSpec::from_start_end(
225                core::ptr::addr_of!(_stext),
226                core::ptr::addr_of!(_etext),
227            )
228            .unwrap(),
229        ),
230    )
231    .unwrap();
232
233    // Acquire required capabilities
234    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
235
236    // Create a board kernel instance
237
238    // Create an array to hold process references.
239    let processes = components::process_array::ProcessArrayComponent::new()
240        .finalize(components::process_array_component_static!(NUM_PROCS));
241    PROCESSES = Some(processes);
242
243    // Setup space to store the core kernel data structure.
244    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
245
246    // ---------- QEMU-SYSTEM-RISCV32 "virt" MACHINE PERIPHERALS ----------
247
248    let peripherals = static_init!(
249        QemuRv32VirtDefaultPeripherals,
250        QemuRv32VirtDefaultPeripherals::new(),
251    );
252
253    // Create a shared UART channel for the console and for kernel
254    // debug over the provided memory-mapped 16550-compatible
255    // UART.
256    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
257        .finalize(components::uart_mux_component_static!());
258
259    // Use the RISC-V machine timer timesource
260    let hardware_timer = static_init!(
261        qemu_rv32_virt_chip::chip::QemuRv32VirtClint,
262        qemu_rv32_virt_chip::chip::QemuRv32VirtClint::new(&qemu_rv32_virt_chip::clint::CLINT_BASE)
263    );
264
265    // Create a shared virtualization mux layer on top of a single hardware
266    // alarm.
267    let mux_alarm = static_init!(
268        MuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
269        MuxAlarm::new(hardware_timer)
270    );
271    hil::time::Alarm::set_alarm_client(hardware_timer, mux_alarm);
272
273    // Virtual alarm for the scheduler
274    let systick_virtual_alarm = static_init!(
275        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
276        VirtualMuxAlarm::new(mux_alarm)
277    );
278    systick_virtual_alarm.setup();
279
280    // Virtual alarm and driver for userspace
281    let virtual_alarm_user = static_init!(
282        VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
283        VirtualMuxAlarm::new(mux_alarm)
284    );
285    virtual_alarm_user.setup();
286
287    let alarm = static_init!(
288        capsules_core::alarm::AlarmDriver<
289            'static,
290            VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
291        >,
292        capsules_core::alarm::AlarmDriver::new(
293            virtual_alarm_user,
294            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
295        )
296    );
297    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
298
299    // ---------- VIRTIO PERIPHERAL DISCOVERY ----------
300    //
301    // This board has 8 virtio-mmio (v2 personality required!) devices
302    //
303    // Collect supported VirtIO peripheral indicies and initialize them if they
304    // are found. If there are two instances of a supported peripheral, the one
305    // on a higher-indexed VirtIO transport is used.
306    let (mut virtio_gpu_idx, mut virtio_net_idx, mut virtio_rng_idx, mut virtio_input_idx) =
307        (None, None, None, None);
308    for (i, virtio_device) in peripherals.virtio_mmio.iter().enumerate() {
309        use qemu_rv32_virt_chip::virtio::devices::VirtIODeviceType;
310        match virtio_device.query() {
311            Ok(VirtIODeviceType::GPUDevice) => {
312                virtio_gpu_idx = Some(i);
313            }
314            Ok(VirtIODeviceType::NetworkCard) => {
315                virtio_net_idx = Some(i);
316            }
317            Ok(VirtIODeviceType::EntropySource) => {
318                virtio_rng_idx = Some(i);
319            }
320            Ok(VirtIODeviceType::InputDevice) => {
321                virtio_input_idx = Some(i);
322            }
323            _ => (),
324        }
325    }
326
327    // If there is a VirtIO EntropySource present, use the appropriate VirtIORng
328    // driver and expose it to userspace though the RngDriver
329    let virtio_gpu_screen: Option<
330        &'static capsules_extra::screen::screen_adapters::ScreenARGB8888ToMono8BitPage<
331            'static,
332            ScreenHw,
333        >,
334    > = if let Some(gpu_idx) = virtio_gpu_idx {
335        use qemu_rv32_virt_chip::virtio::devices::virtio_gpu::{
336            VirtIOGPU, MAX_REQ_SIZE, MAX_RESP_SIZE, PIXEL_STRIDE,
337        };
338        use qemu_rv32_virt_chip::virtio::queues::split_queue::{
339            SplitVirtqueue, VirtqueueAvailableRing, VirtqueueDescriptors, VirtqueueUsedRing,
340        };
341        use qemu_rv32_virt_chip::virtio::queues::Virtqueue;
342        use qemu_rv32_virt_chip::virtio::transports::VirtIOTransport;
343
344        // Video output dimensions:
345
346        const VIDEO_WIDTH: usize = 128;
347        const VIDEO_HEIGHT: usize = 128;
348
349        // VirtIO GPU requires a single Virtqueue for sending commands. It can
350        // optionally use a second VirtQueue for cursor commands, which we don't
351        // use (as we don't have the concept of a cursor).
352        //
353        // The VirtIO GPU control queue must be able to hold two descriptors:
354        // one for the request, and another for the response.
355        let descriptors = static_init!(VirtqueueDescriptors<2>, VirtqueueDescriptors::default(),);
356        let available_ring =
357            static_init!(VirtqueueAvailableRing<2>, VirtqueueAvailableRing::default(),);
358        let used_ring = static_init!(VirtqueueUsedRing<2>, VirtqueueUsedRing::default(),);
359        let control_queue = static_init!(
360            SplitVirtqueue<2>,
361            SplitVirtqueue::new(descriptors, available_ring, used_ring),
362        );
363        control_queue.set_transport(&peripherals.virtio_mmio[gpu_idx]);
364
365        // Create required buffers:
366        let req_buffer = static_init!([u8; MAX_REQ_SIZE], [0; MAX_REQ_SIZE]);
367        let resp_buffer = static_init!([u8; MAX_RESP_SIZE], [0; MAX_RESP_SIZE]);
368        // let frame_buffer = static_init!(
369        //     [u8; VIDEO_WIDTH * VIDEO_HEIGHT * PIXEL_STRIDE],
370        //     [0; VIDEO_WIDTH * VIDEO_HEIGHT * PIXEL_STRIDE]
371        // );
372
373        // VirtIO GPU device driver instantiation
374        let gpu = static_init!(
375            VirtIOGPU,
376            VirtIOGPU::new(
377                control_queue,
378                req_buffer,
379                resp_buffer,
380                VIDEO_WIDTH,
381                VIDEO_HEIGHT,
382            )
383            .unwrap()
384        );
385        kernel::deferred_call::DeferredCallClient::register(gpu);
386        control_queue.set_client(gpu);
387
388        // Register the queues and driver with the transport, so interrupts
389        // are routed properly
390        let mmio_queues = static_init!([&'static dyn Virtqueue; 1], [control_queue; 1]);
391        peripherals.virtio_mmio[gpu_idx]
392            .initialize(gpu, mmio_queues)
393            .unwrap();
394
395        // Convert the `ARGB_8888` pixel mode offered by this device into a
396        // pixel mode that the rest of the kernel and userspace understands,
397        // namely the cursed `Mono_8BitPage` mode:
398        let screen_argb_8888_to_mono_8bit_page =
399            components::screen_adapters::ScreenAdapterARGB8888ToMono8BitPageComponent::new(gpu)
400                .finalize(
401                    components::screen_adapter_argb8888_to_mono8bitpage_component_static!(
402                        ScreenHw,
403                        VIDEO_WIDTH,
404                        VIDEO_HEIGHT,
405                        PIXEL_STRIDE
406                    ),
407                );
408
409        gpu.initialize().unwrap();
410
411        Some(screen_argb_8888_to_mono_8bit_page)
412    } else {
413        // No VirtIO GPU device discovered
414        None
415    };
416
417    // If there is a VirtIO EntropySource present, use the appropriate VirtIORng
418    // driver and expose it to userspace though the RngDriver
419    let virtio_rng: Option<&'static qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng> =
420        if let Some(rng_idx) = virtio_rng_idx {
421            use qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng;
422            use qemu_rv32_virt_chip::virtio::queues::split_queue::{
423                SplitVirtqueue, VirtqueueAvailableRing, VirtqueueDescriptors, VirtqueueUsedRing,
424            };
425            use qemu_rv32_virt_chip::virtio::queues::Virtqueue;
426            use qemu_rv32_virt_chip::virtio::transports::VirtIOTransport;
427
428            // EntropySource requires a single Virtqueue for retrieved entropy
429            let descriptors =
430                static_init!(VirtqueueDescriptors<1>, VirtqueueDescriptors::default(),);
431            let available_ring =
432                static_init!(VirtqueueAvailableRing<1>, VirtqueueAvailableRing::default(),);
433            let used_ring = static_init!(VirtqueueUsedRing<1>, VirtqueueUsedRing::default(),);
434            let queue = static_init!(
435                SplitVirtqueue<1>,
436                SplitVirtqueue::new(descriptors, available_ring, used_ring),
437            );
438            queue.set_transport(&peripherals.virtio_mmio[rng_idx]);
439
440            // VirtIO EntropySource device driver instantiation
441            let rng = static_init!(VirtIORng, VirtIORng::new(queue));
442            kernel::deferred_call::DeferredCallClient::register(rng);
443            queue.set_client(rng);
444
445            // Register the queues and driver with the transport, so interrupts
446            // are routed properly
447            let mmio_queues = static_init!([&'static dyn Virtqueue; 1], [queue; 1]);
448            peripherals.virtio_mmio[rng_idx]
449                .initialize(rng, mmio_queues)
450                .unwrap();
451
452            // Provide an internal randomness buffer
453            let rng_buffer = static_init!([u8; 64], [0; 64]);
454            rng.provide_buffer(rng_buffer)
455                .expect("rng: providing initial buffer failed");
456
457            Some(rng)
458        } else {
459            // No VirtIO EntropySource discovered
460            None
461        };
462
463    // If there is a VirtIO NetworkCard present, use the appropriate VirtIONet
464    // driver, and expose this device through the Ethernet Tap driver
465    // (forwarding raw Ethernet frames from and to userspace).
466    let virtio_ethernet_tap: Option<
467        &'static capsules_extra::ethernet_tap::EthernetTapDriver<
468            'static,
469            qemu_rv32_virt_chip::virtio::devices::virtio_net::VirtIONet<'static>,
470        >,
471    > = if let Some(net_idx) = virtio_net_idx {
472        use capsules_extra::ethernet_tap::EthernetTapDriver;
473        use kernel::hil::ethernet::EthernetAdapterDatapath;
474        use qemu_rv32_virt_chip::virtio::devices::virtio_net::VirtIONet;
475        use qemu_rv32_virt_chip::virtio::queues::split_queue::{
476            SplitVirtqueue, VirtqueueAvailableRing, VirtqueueDescriptors, VirtqueueUsedRing,
477        };
478        use qemu_rv32_virt_chip::virtio::queues::Virtqueue;
479        use qemu_rv32_virt_chip::virtio::transports::VirtIOTransport;
480
481        // A VirtIO NetworkCard requires 2 Virtqueues:
482        // - a TX Virtqueue with buffers for outgoing packets
483        // - a RX Virtqueue where incoming packet buffers are
484        //   placed and filled by the device
485
486        // TX Virtqueue
487        let tx_descriptors =
488            static_init!(VirtqueueDescriptors<2>, VirtqueueDescriptors::default(),);
489        let tx_available_ring =
490            static_init!(VirtqueueAvailableRing<2>, VirtqueueAvailableRing::default(),);
491        let tx_used_ring = static_init!(VirtqueueUsedRing<2>, VirtqueueUsedRing::default(),);
492        let tx_queue = static_init!(
493            SplitVirtqueue<2>,
494            SplitVirtqueue::new(tx_descriptors, tx_available_ring, tx_used_ring),
495        );
496        tx_queue.set_transport(&peripherals.virtio_mmio[net_idx]);
497
498        // RX Virtqueue
499        let rx_descriptors =
500            static_init!(VirtqueueDescriptors<2>, VirtqueueDescriptors::default(),);
501        let rx_available_ring =
502            static_init!(VirtqueueAvailableRing<2>, VirtqueueAvailableRing::default(),);
503        let rx_used_ring = static_init!(VirtqueueUsedRing<2>, VirtqueueUsedRing::default(),);
504        let rx_queue = static_init!(
505            SplitVirtqueue<2>,
506            SplitVirtqueue::new(rx_descriptors, rx_available_ring, rx_used_ring),
507        );
508        rx_queue.set_transport(&peripherals.virtio_mmio[net_idx]);
509
510        // Incoming and outgoing packets are prefixed by a 12-byte
511        // VirtIO specific header
512        let tx_header_buf = static_init!([u8; 12], [0; 12]);
513        let rx_header_buf = static_init!([u8; 12], [0; 12]);
514
515        // Currently, provide a single receive buffer to write
516        // incoming packets into
517        let rx_buffer = static_init!([u8; 1526], [0; 1526]);
518
519        // Instantiate the VirtIONet (NetworkCard) driver and set the queues
520        let virtio_net = static_init!(
521            VirtIONet<'static>,
522            VirtIONet::new(tx_queue, tx_header_buf, rx_queue, rx_header_buf, rx_buffer),
523        );
524        tx_queue.set_client(virtio_net);
525        rx_queue.set_client(virtio_net);
526
527        // Register the queues and driver with the transport, so
528        // interrupts are routed properly
529        let mmio_queues = static_init!([&'static dyn Virtqueue; 2], [rx_queue, tx_queue]);
530        peripherals.virtio_mmio[net_idx]
531            .initialize(virtio_net, mmio_queues)
532            .unwrap();
533
534        // Instantiate the userspace tap network driver over this device:
535        let virtio_ethernet_tap_tx_buffer = static_init!(
536            [u8; capsules_extra::ethernet_tap::MAX_MTU],
537            [0; capsules_extra::ethernet_tap::MAX_MTU],
538        );
539        let virtio_ethernet_tap = static_init!(
540            EthernetTapDriver<'static, VirtIONet<'static>>,
541            EthernetTapDriver::new(
542                virtio_net,
543                board_kernel.create_grant(
544                    capsules_extra::ethernet_tap::DRIVER_NUM,
545                    &memory_allocation_cap
546                ),
547                virtio_ethernet_tap_tx_buffer,
548            ),
549        );
550        virtio_net.set_client(virtio_ethernet_tap);
551
552        // This enables reception on the underlying device:
553        virtio_ethernet_tap.initialize();
554
555        Some(virtio_ethernet_tap as &'static EthernetTapDriver<'static, VirtIONet<'static>>)
556    } else {
557        // No VirtIO NetworkCard discovered
558        None
559    };
560
561    let virtio_input_keyboard: Option<
562        &'static qemu_rv32_virt_chip::virtio::devices::virtio_input::VirtIOInput,
563    > = if let Some(input_idx) = virtio_input_idx {
564        use qemu_rv32_virt_chip::virtio::devices::virtio_input::VirtIOInput;
565        use qemu_rv32_virt_chip::virtio::queues::split_queue::{
566            SplitVirtqueue, VirtqueueAvailableRing, VirtqueueDescriptors, VirtqueueUsedRing,
567        };
568        use qemu_rv32_virt_chip::virtio::queues::Virtqueue;
569        use qemu_rv32_virt_chip::virtio::transports::VirtIOTransport;
570
571        // Event Virtqueue
572        let event_descriptors =
573            static_init!(VirtqueueDescriptors<3>, VirtqueueDescriptors::default(),);
574        let event_available_ring =
575            static_init!(VirtqueueAvailableRing<3>, VirtqueueAvailableRing::default(),);
576        let event_used_ring = static_init!(VirtqueueUsedRing<3>, VirtqueueUsedRing::default(),);
577        let event_queue = static_init!(
578            SplitVirtqueue<3>,
579            SplitVirtqueue::new(event_descriptors, event_available_ring, event_used_ring),
580        );
581        event_queue.set_transport(&peripherals.virtio_mmio[input_idx]);
582
583        // Status Virtqueue
584        let status_descriptors =
585            static_init!(VirtqueueDescriptors<1>, VirtqueueDescriptors::default(),);
586        let status_available_ring =
587            static_init!(VirtqueueAvailableRing<1>, VirtqueueAvailableRing::default(),);
588        let status_used_ring = static_init!(VirtqueueUsedRing<1>, VirtqueueUsedRing::default(),);
589        let status_queue = static_init!(
590            SplitVirtqueue<1>,
591            SplitVirtqueue::new(status_descriptors, status_available_ring, status_used_ring),
592        );
593        status_queue.set_transport(&peripherals.virtio_mmio[input_idx]);
594
595        // Buffers to store events from the keyboard.
596        let event_buf1 = static_init!([u8; 8], [0; 8]);
597        let event_buf2 = static_init!([u8; 8], [0; 8]);
598        let event_buf3 = static_init!([u8; 8], [0; 8]);
599        let status_buf = static_init!([u8; 128], [0; 128]);
600
601        // Instantiate the input driver
602        let virtio_input = static_init!(
603            VirtIOInput<'static>,
604            VirtIOInput::new(event_queue, status_queue, status_buf),
605        );
606        event_queue.set_client(virtio_input);
607        status_queue.set_client(virtio_input);
608
609        // Register the queues and driver with the transport, so
610        // interrupts are routed properly
611        let mmio_queues = static_init!([&'static dyn Virtqueue; 2], [event_queue, status_queue]);
612        peripherals.virtio_mmio[input_idx]
613            .initialize(virtio_input, mmio_queues)
614            .unwrap();
615
616        virtio_input.provide_buffers(event_buf1, event_buf2, event_buf3);
617
618        Some(virtio_input)
619    } else {
620        // No Input device
621        None
622    };
623
624    // ---------- INITIALIZE CHIP, ENABLE INTERRUPTS ---------
625
626    let chip = static_init!(
627        QemuRv32VirtChip<QemuRv32VirtDefaultPeripherals>,
628        QemuRv32VirtChip::new(peripherals, hardware_timer, epmp),
629    );
630    CHIP = Some(chip);
631
632    // Need to enable all interrupts for Tock Kernel
633    chip.enable_plic_interrupts();
634
635    // enable interrupts globally
636    csr::CSR
637        .mie
638        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET + csr::mie::mie::mtimer::SET);
639    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
640
641    // ---------- FINAL SYSTEM INITIALIZATION ----------
642
643    // Create the process printer used in panic prints, etc.
644    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
645        .finalize(components::process_printer_text_component_static!());
646    PROCESS_PRINTER = Some(process_printer);
647
648    // Initialize the kernel's process console.
649    let pconsole = components::process_console::ProcessConsoleComponent::new(
650        board_kernel,
651        uart_mux,
652        mux_alarm,
653        process_printer,
654        None,
655    )
656    .finalize(components::process_console_component_static!(
657        qemu_rv32_virt_chip::chip::QemuRv32VirtClint
658    ));
659
660    // Setup the console.
661    let console = components::console::ConsoleComponent::new(
662        board_kernel,
663        capsules_core::console::DRIVER_NUM,
664        uart_mux,
665    )
666    .finalize(components::console_component_static!());
667    // Create the debugger object that handles calls to `debug!()`.
668    components::debug_writer::DebugWriterComponent::new(
669        uart_mux,
670        create_capability!(capabilities::SetDebugWriterCapability),
671    )
672    .finalize(components::debug_writer_component_static!());
673
674    let lldb = components::lldb::LowLevelDebugComponent::new(
675        board_kernel,
676        capsules_core::low_level_debug::DRIVER_NUM,
677        uart_mux,
678    )
679    .finalize(components::low_level_debug_component_static!());
680
681    // ---------- RNG ----------
682
683    // Userspace RNG driver over the VirtIO EntropySource
684    let rng_driver = virtio_rng.map(|rng| {
685        components::rng::RngRandomComponent::new(board_kernel, capsules_core::rng::DRIVER_NUM, rng)
686            .finalize(components::rng_random_component_static!(
687                qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng
688            ))
689    });
690
691    // ---------- SCHEDULER ----------
692
693    let scheduler = components::sched::cooperative::CooperativeComponent::new(processes)
694        .finalize(components::cooperative_component_static!(NUM_PROCS));
695
696    let scheduler_timer = static_init!(
697        VirtualSchedulerTimer<
698            VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
699        >,
700        VirtualSchedulerTimer::new(systick_virtual_alarm)
701    );
702
703    let platform = QemuRv32VirtPlatform {
704        pconsole,
705        console,
706        alarm,
707        lldb,
708        scheduler,
709        scheduler_timer,
710        rng: rng_driver,
711        virtio_ethernet_tap,
712        virtio_gpu_screen,
713        virtio_input_keyboard,
714        ipc: kernel::ipc::IPC::new(
715            board_kernel,
716            kernel::ipc::DRIVER_NUM,
717            &memory_allocation_cap,
718        ),
719    };
720
721    debug!("QEMU RISC-V 32-bit \"virt\" machine, initialization complete.");
722
723    // This board dynamically discovers VirtIO devices like a randomness source
724    // or a network card. Print a message indicating whether or not each such
725    // device and corresponding userspace driver is present:
726    if virtio_gpu_screen.is_some() {
727        debug!("- Found VirtIO GPUDevice, enabling video output");
728    } else {
729        debug!("- VirtIO GPUDevice not found, disabling video output");
730    }
731    if virtio_rng.is_some() {
732        debug!("- Found VirtIO EntropySource device, enabling RngDriver");
733    } else {
734        debug!("- VirtIO EntropySource device not found, disabling RngDriver");
735    }
736    if virtio_ethernet_tap.is_some() {
737        debug!("- Found VirtIO NetworkCard device, enabling EthernetTapDriver");
738    } else {
739        debug!("- VirtIO NetworkCard device not found, disabling EthernetTapDriver");
740    }
741    if virtio_input_keyboard.is_some() {
742        debug!("- Found VirtIO Input device, enabling Input");
743    } else {
744        debug!("- VirtIO Input device not found, disabling Input");
745    }
746
747    (board_kernel, platform, chip)
748}