Skip to main content

hermit/
lib.rs

1//! The Hermit kernel.
2//!
3//! This _library operating system_ (libOS) compiles to a static library
4//! (libhermit.a) that applications can link against to create a _Unikernel_.
5//!
6//! The API documented here does not matter to such an application.
7//! Such an application would use it's languages standard library which
8//! internally calls this kernel's system call functions ([`syscalls`]).
9//!
10//! # Using Hermit
11//!
12//! To run a Rust application with Hermit, see [hermit-rs].
13//!
14//! To run a C or C++ application with Hermit, see [hermit-c].
15//!
16//! # Building the kernel manually
17//!
18//! You can build the kernel with default features for x86-64 like this:
19//!
20//! ```sh
21//! cargo xtask build --arch x86_64
22//! ```
23//!
24//! For more information, run:
25//!
26//! ```
27//! cargo xtask build --help
28//! ```
29//!
30//! # Features
31//!
32#![cfg_attr(
33	not(feature = "document-features"),
34	doc = "Activate the `document-features` Cargo feature to see feature docs here."
35)]
36#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
37//!
38//! [hermit-rs]: https://github.com/hermit-os/hermit-rs
39//! [hermit-c]: https://github.com/hermit-os/hermit-c
40
41#![allow(clippy::missing_safety_doc)]
42#![cfg_attr(
43	any(target_arch = "aarch64", target_arch = "riscv64"),
44	allow(incomplete_features)
45)]
46#![cfg_attr(target_arch = "x86_64", feature(abi_x86_interrupt))]
47#![feature(allocator_api)]
48#![cfg_attr(docsrs, feature(doc_cfg))]
49#![cfg_attr(
50	all(
51		not(any(feature = "common-os", feature = "nostd")),
52		not(target_arch = "riscv64"),
53	),
54	feature(linkage)
55)]
56#![feature(linked_list_cursors)]
57#![feature(never_type)]
58#![cfg_attr(
59	any(target_arch = "aarch64", target_arch = "riscv64"),
60	feature(specialization)
61)]
62#![cfg_attr(
63	all(
64		not(any(feature = "common-os", feature = "nostd")),
65		not(target_arch = "riscv64"),
66	),
67	feature(thread_local)
68)]
69#![cfg_attr(target_os = "none", no_std)]
70#![cfg_attr(target_os = "none", feature(custom_test_frameworks))]
71#![cfg_attr(all(target_os = "none", test), test_runner(crate::test_runner))]
72#![cfg_attr(
73	all(target_os = "none", test),
74	reexport_test_harness_main = "test_main"
75)]
76#![cfg_attr(all(target_os = "none", test), no_main)]
77// FIXME: move this to `Cargo.toml` once stable
78#![feature(strict_provenance_lints)]
79#![warn(implicit_provenance_casts)]
80
81// EXTERNAL CRATES
82#[macro_use]
83extern crate alloc;
84#[macro_use]
85extern crate bitflags;
86#[macro_use]
87extern crate log;
88#[cfg(not(target_os = "none"))]
89#[macro_use]
90extern crate std;
91
92#[cfg(feature = "smp")]
93use core::hint::spin_loop;
94#[cfg(feature = "smp")]
95use core::sync::atomic::{AtomicU32, Ordering};
96
97use self::arch::kernel;
98use self::arch::kernel::core_local::{core_id, core_scheduler};
99use self::arch::kernel::interrupts;
100use crate::scheduler::{PerCoreScheduler, PerCoreSchedulerExt};
101
102#[macro_use]
103mod macros;
104
105#[macro_use]
106mod logging;
107
108pub mod arch;
109#[cfg(all(feature = "common-os", target_arch = "x86_64"))]
110pub mod common_os;
111pub mod config;
112pub mod console;
113mod drivers;
114mod entropy;
115mod env;
116pub mod errno;
117mod executor;
118pub mod fd;
119pub mod fs;
120mod init_buf;
121mod init_cell;
122pub mod io;
123pub mod mm;
124pub mod scheduler;
125#[cfg(feature = "shell")]
126mod shell;
127mod synch;
128pub mod syscalls;
129pub mod time;
130#[cfg(feature = "uhyve")]
131mod uhyve;
132
133mod built_info {
134	include!(concat!(env!("OUT_DIR"), "/built.rs"));
135}
136
137hermit_entry::define_abi_tag!();
138
139#[cfg(target_os = "none")]
140hermit_entry::define_entry_version!();
141
142#[cfg(test)]
143#[cfg(target_os = "none")]
144#[unsafe(no_mangle)]
145extern "C" fn runtime_entry(_argc: i32, _argv: *const *const u8, _env: *const *const u8) -> ! {
146	println!("Executing hermit unittests. Any arguments are dropped");
147	test_main();
148	core_scheduler().exit(0)
149}
150
151//https://github.com/rust-lang/rust/issues/50297#issuecomment-524180479
152#[cfg(test)]
153pub fn test_runner(tests: &[&dyn Fn()]) {
154	println!("Running {} tests", tests.len());
155	for test in tests {
156		test();
157	}
158	core_scheduler().exit(0)
159}
160
161#[cfg(target_os = "none")]
162#[test_case]
163fn trivial_test() {
164	println!("Test test test");
165	panic!("Test called");
166}
167
168/// Entry point of a kernel thread, which initialize the libos
169#[cfg(target_os = "none")]
170extern "C" fn initd(_arg: usize) {
171	unsafe extern "C" {
172		#[cfg(all(not(test), not(any(feature = "nostd", feature = "common-os"))))]
173		fn runtime_entry(argc: i32, argv: *const *const u8, env: *const *const u8) -> !;
174		#[cfg(all(not(test), any(feature = "nostd", feature = "common-os")))]
175		fn main(argc: i32, argv: *const *const u8, env: *const *const u8);
176	}
177
178	// Initialize Drivers
179	drivers::init();
180	// The filesystem needs to be initialized before network to allow writing packet captures to a file.
181	fs::init();
182	executor::init();
183
184	syscalls::init();
185	#[cfg(feature = "shell")]
186	shell::init();
187
188	// Get the application arguments and environment variables.
189	#[cfg(not(test))]
190	let (argc, argv, environ) = syscalls::get_application_parameters();
191
192	// give the IP thread time to initialize the network interface
193	core_scheduler().reschedule();
194
195	if cfg!(feature = "warn-prebuilt") {
196		warn!("This is a prebuilt Hermit kernel.");
197		warn!("For non-default device drivers and features, consider building a custom kernel.");
198	}
199
200	info!("Jumping into application");
201
202	#[cfg(not(test))]
203	unsafe {
204		// And finally start the application.
205		#[cfg(all(not(test), not(any(feature = "nostd", feature = "common-os"))))]
206		runtime_entry(argc, argv, environ);
207		#[cfg(all(not(test), any(feature = "nostd", feature = "common-os")))]
208		main(argc, argv, environ);
209	}
210	#[cfg(test)]
211	test_main();
212}
213
214#[cfg(feature = "smp")]
215fn synch_all_cores() {
216	static CORE_COUNTER: AtomicU32 = AtomicU32::new(0);
217
218	CORE_COUNTER.fetch_add(1, Ordering::SeqCst);
219
220	let possible_cpus = kernel::get_possible_cpus();
221	while CORE_COUNTER.load(Ordering::SeqCst) != possible_cpus {
222		spin_loop();
223	}
224}
225
226/// Entry Point of Hermit for the Boot Processor
227#[cfg(target_os = "none")]
228fn boot_processor_main() -> ! {
229	use crate::config::USER_STACK_SIZE;
230
231	// Initialize the kernel and hardware.
232	mm::claim_initial_heap();
233	hermit_sync::Lazy::force(&console::CONSOLE);
234	env::init();
235	unsafe {
236		logging::init();
237	}
238
239	info!("Welcome to Hermit {}", env!("CARGO_PKG_VERSION"));
240	if let Some(git_version) = built_info::GIT_VERSION {
241		let dirty = if built_info::GIT_DIRTY == Some(true) {
242			" (dirty)"
243		} else {
244			""
245		};
246
247		let opt_level = if built_info::OPT_LEVEL == "3" {
248			format_args!("")
249		} else {
250			format_args!(" (opt-level={})", built_info::OPT_LEVEL)
251		};
252
253		info!("Git version: {git_version}{dirty}{opt_level}");
254	}
255	let arch = built_info::TARGET.split_once('-').unwrap().0;
256	info!("Architecture: {arch}");
257	info!("Enabled features: {}", built_info::FEATURES_LOWERCASE_STR);
258	info!("Built on {}", built_info::BUILT_TIME_UTC);
259
260	info!("Executable start: {:p}", elf_symbols::executable_start());
261	info!("ELF header:       {:p}", elf_symbols::elf_header());
262	info!("Text segment end: {:p}", elf_symbols::text_end());
263	info!("Data segment end: {:p}", elf_symbols::data_end());
264	info!("Executable end:   {:p}", elf_symbols::executable_end());
265
266	if let Some(fdt) = env::fdt() {
267		info!("FDT:\n{fdt:#?}");
268	}
269
270	kernel::boot_processor_init();
271
272	#[cfg(not(target_arch = "riscv64"))]
273	scheduler::add_current_core();
274	interrupts::enable();
275
276	kernel::boot_next_processor();
277
278	#[cfg(feature = "smp")]
279	synch_all_cores();
280
281	#[cfg(feature = "pci")]
282	drivers::pci::print_information();
283
284	// Start the initd task.
285	unsafe { PerCoreScheduler::spawn(initd, 0, scheduler::task::NORMAL_PRIO, 0, USER_STACK_SIZE) };
286
287	// Run the scheduler loop.
288	PerCoreScheduler::run();
289}
290
291/// Entry Point of Hermit for an Application Processor
292#[cfg(all(target_os = "none", feature = "smp"))]
293fn application_processor_main() -> ! {
294	kernel::application_processor_init();
295	#[cfg(not(target_arch = "riscv64"))]
296	scheduler::add_current_core();
297	interrupts::enable();
298	kernel::boot_next_processor();
299
300	debug!("Entering idle loop for application processor");
301
302	synch_all_cores();
303	executor::init();
304
305	// Run the scheduler loop.
306	PerCoreScheduler::run();
307}
308
309#[cfg(target_os = "none")]
310#[panic_handler]
311fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
312	let core_id = core_id();
313	panic_println!("[{core_id}][PANIC] {info}\n");
314
315	scheduler::shutdown(1);
316}