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}