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(fuzzy_provenance_casts)]
80#![warn(lossy_provenance_casts)]
81
82// EXTERNAL CRATES
83#[macro_use]
84extern crate alloc;
85#[macro_use]
86extern crate bitflags;
87#[macro_use]
88extern crate log;
89#[cfg(not(target_os = "none"))]
90#[macro_use]
91extern crate std;
92
93#[cfg(feature = "smp")]
94use core::hint::spin_loop;
95#[cfg(feature = "smp")]
96use core::sync::atomic::{AtomicU32, Ordering};
97
98use arch::core_local::*;
99
100pub(crate) use crate::arch::*;
101pub use crate::config::DEFAULT_STACK_SIZE;
102pub(crate) use crate::config::*;
103use crate::scheduler::{PerCoreScheduler, PerCoreSchedulerExt};
104
105#[macro_use]
106mod macros;
107
108#[macro_use]
109mod logging;
110
111pub mod arch;
112mod config;
113pub mod console;
114mod drivers;
115mod entropy;
116mod env;
117pub mod errno;
118mod executor;
119pub mod fd;
120pub mod fs;
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
131mod built_info {
132	include!(concat!(env!("OUT_DIR"), "/built.rs"));
133}
134
135hermit_entry::define_abi_tag!();
136
137#[cfg(target_os = "none")]
138hermit_entry::define_entry_version!();
139
140#[cfg(target_os = "none")]
141hermit_entry::define_uhyve_interface_version!(uhyve_interface::UHYVE_INTERFACE_VERSION);
142
143#[cfg(test)]
144#[cfg(target_os = "none")]
145#[unsafe(no_mangle)]
146extern "C" fn runtime_entry(_argc: i32, _argv: *const *const u8, _env: *const *const u8) -> ! {
147	println!("Executing hermit unittests. Any arguments are dropped");
148	test_main();
149	core_scheduler().exit(0)
150}
151
152//https://github.com/rust-lang/rust/issues/50297#issuecomment-524180479
153#[cfg(test)]
154pub fn test_runner(tests: &[&dyn Fn()]) {
155	println!("Running {} tests", tests.len());
156	for test in tests {
157		test();
158	}
159	core_scheduler().exit(0)
160}
161
162#[cfg(target_os = "none")]
163#[test_case]
164fn trivial_test() {
165	println!("Test test test");
166	panic!("Test called");
167}
168
169/// Entry point of a kernel thread, which initialize the libos
170#[cfg(target_os = "none")]
171extern "C" fn initd(_arg: usize) {
172	unsafe extern "C" {
173		#[cfg(all(not(test), not(any(feature = "nostd", feature = "common-os"))))]
174		fn runtime_entry(argc: i32, argv: *const *const u8, env: *const *const u8) -> !;
175		#[cfg(all(not(test), any(feature = "nostd", feature = "common-os")))]
176		fn main(argc: i32, argv: *const *const u8, env: *const *const u8);
177	}
178
179	if env::is_uhyve() {
180		info!("Hermit is running on uhyve!");
181	} else {
182		info!("Hermit is running on common system!");
183	}
184
185	// Initialize Drivers
186	drivers::init();
187	// The filesystem needs to be initialized before network to allow writing packet captures to a file.
188	fs::init();
189	crate::executor::init();
190
191	syscalls::init();
192	#[cfg(feature = "shell")]
193	shell::init();
194
195	// Get the application arguments and environment variables.
196	#[cfg(not(test))]
197	let (argc, argv, environ) = syscalls::get_application_parameters();
198
199	// give the IP thread time to initialize the network interface
200	core_scheduler().reschedule();
201
202	if cfg!(feature = "warn-prebuilt") {
203		warn!("This is a prebuilt Hermit kernel.");
204		warn!("For non-default device drivers and features, consider building a custom kernel.");
205	}
206
207	info!("Jumping into application");
208
209	#[cfg(not(test))]
210	unsafe {
211		// And finally start the application.
212		#[cfg(all(not(test), not(any(feature = "nostd", feature = "common-os"))))]
213		runtime_entry(argc, argv, environ);
214		#[cfg(all(not(test), any(feature = "nostd", feature = "common-os")))]
215		main(argc, argv, environ);
216	}
217	#[cfg(test)]
218	test_main();
219}
220
221#[cfg(feature = "smp")]
222fn synch_all_cores() {
223	static CORE_COUNTER: AtomicU32 = AtomicU32::new(0);
224
225	CORE_COUNTER.fetch_add(1, Ordering::SeqCst);
226
227	let possible_cpus = kernel::get_possible_cpus();
228	while CORE_COUNTER.load(Ordering::SeqCst) != possible_cpus {
229		spin_loop();
230	}
231}
232
233/// Entry Point of Hermit for the Boot Processor
234#[cfg(target_os = "none")]
235fn boot_processor_main() -> ! {
236	// Initialize the kernel and hardware.
237	hermit_sync::Lazy::force(&console::CONSOLE);
238	unsafe {
239		logging::init();
240	}
241
242	info!("Welcome to Hermit {}", env!("CARGO_PKG_VERSION"));
243	if let Some(git_version) = built_info::GIT_VERSION {
244		let dirty = if built_info::GIT_DIRTY == Some(true) {
245			" (dirty)"
246		} else {
247			""
248		};
249
250		let opt_level = if built_info::OPT_LEVEL == "3" {
251			format_args!("")
252		} else {
253			format_args!(" (opt-level={})", built_info::OPT_LEVEL)
254		};
255
256		info!("Git version: {git_version}{dirty}{opt_level}");
257	}
258	let arch = built_info::TARGET.split_once('-').unwrap().0;
259	info!("Architecture: {arch}");
260	info!("Enabled features: {}", built_info::FEATURES_LOWERCASE_STR);
261	info!("Built on {}", built_info::BUILT_TIME_UTC);
262
263	env::log_segments();
264
265	if let Some(fdt) = env::fdt() {
266		info!("FDT:\n{fdt:#?}");
267	}
268
269	arch::boot_processor_init();
270
271	#[cfg(not(target_arch = "riscv64"))]
272	scheduler::add_current_core();
273	interrupts::enable();
274
275	arch::kernel::boot_next_processor();
276
277	#[cfg(feature = "smp")]
278	synch_all_cores();
279
280	if kernel::is_uhyve_with_pci() || !env::is_uhyve() {
281		#[cfg(feature = "pci")]
282		crate::drivers::pci::print_information();
283	}
284
285	// Start the initd task.
286	unsafe {
287		scheduler::PerCoreScheduler::spawn(
288			initd,
289			0,
290			scheduler::task::NORMAL_PRIO,
291			0,
292			USER_STACK_SIZE,
293		)
294	};
295
296	// Run the scheduler loop.
297	PerCoreScheduler::run();
298}
299
300/// Entry Point of Hermit for an Application Processor
301#[cfg(all(target_os = "none", feature = "smp"))]
302fn application_processor_main() -> ! {
303	arch::application_processor_init();
304	#[cfg(not(target_arch = "riscv64"))]
305	scheduler::add_current_core();
306	interrupts::enable();
307	arch::kernel::boot_next_processor();
308
309	debug!("Entering idle loop for application processor");
310
311	synch_all_cores();
312	crate::executor::init();
313
314	// Run the scheduler loop.
315	PerCoreScheduler::run();
316}
317
318#[cfg(target_os = "none")]
319#[panic_handler]
320fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
321	let core_id = crate::arch::core_local::core_id();
322	panic_println!("[{core_id}][PANIC] {info}\n");
323
324	crate::scheduler::shutdown(1);
325}