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