qemu_rv32_virt/
main.rs

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