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}