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