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