springboard-api 3.0.1

Makes a kernel compatible with the `springboard` crate
Documentation
//! Provides the interface to make kernels compatible with the
//! [**`springboard`**](https://docs.rs/springboard/latest/springboard) crate.

#![cfg_attr(not(test), no_std)]
#![deny(unsafe_op_in_unsafe_fn)]
#![warn(missing_docs)]
#![feature(decl_macro)]

pub use self::{config::BootloaderConfig, info::BootInfo};

/// Allows to configure the system environment set up by the bootloader.
pub mod config;
/// Contains the boot information struct sent by the bootloader to the kernel on startup.
pub mod info;

mod concat {
   include!(concat!(env!("OUT_DIR"), "/concat.rs"));
}

mod version_info {
   include!(concat!(env!("OUT_DIR"), "/version_info.rs"));
}

/// Defines the entry point function.
///
/// The function must have the signature `fn(&'static mut BootInfo) -> !`.
///
/// This macro just creates a function named `_start`, which the linker will use as the entry
/// point. The advantage of using this macro instead of providing an own `_start` function is
/// that the macro ensures that the function and argument types are correct.
///
/// ## Configuration
///
/// This macro supports an optional second parameter to configure how the bootloader should
/// boot the kernel. The second parameter needs to be given as `config = ...` and be of type
/// [`&BootloaderConfig`](crate::BootloaderConfig). If not given, the configuration defaults to
/// [`BootloaderConfig::new_default`](crate::BootloaderConfig::new_default).
///
/// ## Examples
///
/// - With default configuration:
///
///   ```no_run
///   #![no_std]
///   #![no_main]
///   # #![feature(lang_items)]
///  
///   springboard_api::start!(main);
///  
///   fn main(bootinfo: &'static mut springboard_api::BootInfo) -> ! {
///       loop {}
///   }
///
///   #[panic_handler]
///   fn panic(_info: &core::panic::PanicInfo) -> ! {
///       loop {}
///   }
///
///   # #[lang = "eh_personality"] fn eh_personality() {} // not needed when disabling unwinding
///   ```
///
///   The name of the entry point function does not matter. For example, instead of `main`, we
///   could also name it `fn my_entry_point(...) -> !`. We would then need to specify
///   `entry_point!(my_entry_point)` of course.
///
/// - With custom configuration:
///
///   ```no_run
///   #![no_std]
///   #![no_main]
///   # #![feature(lang_items)]
///  
///   use springboard_api::{start, BootloaderConfig};
///   
///   pub static BOOTLOADER_CONFIG: BootloaderConfig = {
///       let mut config = BootloaderConfig::new_default();
///       config.kernel_stack_size = 90 * 1024;
///       config
///   };
///
///   start!(main, config = &BOOTLOADER_CONFIG);
///
///   fn main(bootinfo: &'static mut springboard_api::BootInfo) -> ! {
///       loop {}
///   }
///
///   #[panic_handler]
///   fn panic(_info: &core::panic::PanicInfo) -> ! {
///       loop {}
///   }
///
///   # #[lang = "eh_personality"] fn eh_personality() {} // not needed when disabling unwinding
///   ```
///
/// ## Implementation Notes
///
/// - **Start function:** The `entry_point` macro generates a small wrapper function named
///   `_start` (without name mangling) that becomes the actual entry point function of the
///   executable. This function doesn't do anything itself, it just calls into the function
///   that is provided as macro argument. The purpose of this function is to use the correct
///   ABI and parameter types required by this crate. A user-provided `_start` function could
///   silently become incompatible on dependency updates since the Rust compiler cannot
///   check the signature of custom entry point functions.
/// - **Configuration:** Behind the scenes, the configuration struct is serialized using
///   [`BootloaderConfig::serialize`](crate::BootloaderConfig::serialize). The resulting byte
///   array is then stored as a static variable annotated with
///   `#[link_section = ".bootloader-config"]`, which instructs the Rust compiler to store it
///   in a special section of the resulting ELF executable. From there, the bootloader will
///   automatically read it when loading the kernel.
pub macro start {
   ($path:path) => {
      $crate::start!($path, config = &$crate::BootloaderConfig::new_default());
   },

   ($path:path, config = $config:expr) => {
      const _: () = {
         #[link_section = ".bootloader-config"]
         pub static __BOOTLOADER_CONFIG: [u8; $crate::BootloaderConfig::SERIALIZED_LEN] = {
            // validate the type
            let config: &$crate::BootloaderConfig = $config;
            config.serialize()
         };

         #[export_name = "_start"]
         pub extern "C" fn __impl_start(boot_info: &'static mut $crate::BootInfo) -> ! {
            // validate the signature of the program entry point
            let f: fn(&'static mut $crate::BootInfo) -> ! = $path;

            // ensure that the config is used so that the linker keeps it
            $crate::__force_use(&__BOOTLOADER_CONFIG);

            f(boot_info)
         }
      };
   }
}

#[doc(hidden)]
pub fn __force_use(slice: &[u8]) {
   let force_use = slice.as_ptr() as usize;
   unsafe { core::arch::asm!("add {0}, 0", in(reg) force_use, options(nomem, nostack)) };
}