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