springboard_api/
lib.rs

1//! Provides the interface to make kernels compatible with the
2//! [**`springboard`**](https://docs.rs/springboard/latest/springboard) crate.
3
4#![cfg_attr(not(test), no_std)]
5#![deny(unsafe_op_in_unsafe_fn)]
6#![warn(missing_docs)]
7#![feature(decl_macro)]
8
9pub use self::{config::BootloaderConfig, info::BootInfo};
10
11/// Allows to configure the system environment set up by the bootloader.
12pub mod config;
13/// Contains the boot information struct sent by the bootloader to the kernel on startup.
14pub mod info;
15
16mod concat {
17   include!(concat!(env!("OUT_DIR"), "/concat.rs"));
18}
19
20mod version_info {
21   include!(concat!(env!("OUT_DIR"), "/version_info.rs"));
22}
23
24/// Defines the entry point function.
25///
26/// The function must have the signature `fn(&'static mut BootInfo) -> !`.
27///
28/// This macro just creates a function named `_start`, which the linker will use as the entry
29/// point. The advantage of using this macro instead of providing an own `_start` function is
30/// that the macro ensures that the function and argument types are correct.
31///
32/// ## Configuration
33///
34/// This macro supports an optional second parameter to configure how the bootloader should
35/// boot the kernel. The second parameter needs to be given as `config = ...` and be of type
36/// [`&BootloaderConfig`](crate::BootloaderConfig). If not given, the configuration defaults to
37/// [`BootloaderConfig::new_default`](crate::BootloaderConfig::new_default).
38///
39/// ## Examples
40///
41/// - With default configuration:
42///
43///   ```no_run
44///   #![no_std]
45///   #![no_main]
46///   # #![feature(lang_items)]
47///  
48///   springboard_api::start!(main);
49///  
50///   fn main(bootinfo: &'static mut springboard_api::BootInfo) -> ! {
51///       loop {}
52///   }
53///
54///   #[panic_handler]
55///   fn panic(_info: &core::panic::PanicInfo) -> ! {
56///       loop {}
57///   }
58///
59///   # #[lang = "eh_personality"] fn eh_personality() {} // not needed when disabling unwinding
60///   ```
61///
62///   The name of the entry point function does not matter. For example, instead of `main`, we
63///   could also name it `fn my_entry_point(...) -> !`. We would then need to specify
64///   `entry_point!(my_entry_point)` of course.
65///
66/// - With custom configuration:
67///
68///   ```no_run
69///   #![no_std]
70///   #![no_main]
71///   # #![feature(lang_items)]
72///  
73///   use springboard_api::{start, BootloaderConfig};
74///   
75///   pub static BOOTLOADER_CONFIG: BootloaderConfig = {
76///       let mut config = BootloaderConfig::new_default();
77///       config.kernel_stack_size = 90 * 1024;
78///       config
79///   };
80///
81///   start!(main, config = &BOOTLOADER_CONFIG);
82///
83///   fn main(bootinfo: &'static mut springboard_api::BootInfo) -> ! {
84///       loop {}
85///   }
86///
87///   #[panic_handler]
88///   fn panic(_info: &core::panic::PanicInfo) -> ! {
89///       loop {}
90///   }
91///
92///   # #[lang = "eh_personality"] fn eh_personality() {} // not needed when disabling unwinding
93///   ```
94///
95/// ## Implementation Notes
96///
97/// - **Start function:** The `entry_point` macro generates a small wrapper function named
98///   `_start` (without name mangling) that becomes the actual entry point function of the
99///   executable. This function doesn't do anything itself, it just calls into the function
100///   that is provided as macro argument. The purpose of this function is to use the correct
101///   ABI and parameter types required by this crate. A user-provided `_start` function could
102///   silently become incompatible on dependency updates since the Rust compiler cannot
103///   check the signature of custom entry point functions.
104/// - **Configuration:** Behind the scenes, the configuration struct is serialized using
105///   [`BootloaderConfig::serialize`](crate::BootloaderConfig::serialize). The resulting byte
106///   array is then stored as a static variable annotated with
107///   `#[link_section = ".bootloader-config"]`, which instructs the Rust compiler to store it
108///   in a special section of the resulting ELF executable. From there, the bootloader will
109///   automatically read it when loading the kernel.
110pub macro start {
111   ($path:path) => {
112      $crate::start!($path, config = &$crate::BootloaderConfig::new_default());
113   },
114
115   ($path:path, config = $config:expr) => {
116      const _: () = {
117         #[link_section = ".bootloader-config"]
118         pub static __BOOTLOADER_CONFIG: [u8; $crate::BootloaderConfig::SERIALIZED_LEN] = {
119            // validate the type
120            let config: &$crate::BootloaderConfig = $config;
121            config.serialize()
122         };
123
124         #[export_name = "_start"]
125         pub extern "C" fn __impl_start(boot_info: &'static mut $crate::BootInfo) -> ! {
126            // validate the signature of the program entry point
127            let f: fn(&'static mut $crate::BootInfo) -> ! = $path;
128
129            // ensure that the config is used so that the linker keeps it
130            $crate::__force_use(&__BOOTLOADER_CONFIG);
131
132            f(boot_info)
133         }
134      };
135   }
136}
137
138#[doc(hidden)]
139pub fn __force_use(slice: &[u8]) {
140   let force_use = slice.as_ptr() as usize;
141   unsafe { core::arch::asm!("add {0}, 0", in(reg) force_use, options(nomem, nostack)) };
142}