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