bootloader_api/
lib.rs

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