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 self::arch::kernel;
99use self::arch::kernel::core_local::{core_id, core_scheduler};
100use self::arch::kernel::interrupts;
101use crate::scheduler::{PerCoreScheduler, PerCoreSchedulerExt};
102
103#[macro_use]
104mod macros;
105
106#[macro_use]
107mod logging;
108
109pub mod arch;
110#[cfg(all(feature = "common-os", target_arch = "x86_64"))]
111pub mod common_os;
112pub mod config;
113pub mod console;
114mod drivers;
115mod entropy;
116mod env;
117pub mod errno;
118mod executor;
119pub mod fd;
120pub mod fs;
121mod init_buf;
122mod init_cell;
123pub mod io;
124pub mod mm;
125pub mod scheduler;
126#[cfg(feature = "shell")]
127mod shell;
128mod synch;
129pub mod syscalls;
130pub mod time;
131#[cfg(feature = "uhyve")]
132mod uhyve;
133
134mod built_info {
135	include!(concat!(env!("OUT_DIR"), "/built.rs"));
136}
137
138hermit_entry::define_abi_tag!();
139
140#[cfg(target_os = "none")]
141hermit_entry::define_entry_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	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	use crate::config::USER_STACK_SIZE;
237
238	// Initialize the kernel and hardware.
239	mm::claim_initial_heap();
240	hermit_sync::Lazy::force(&console::CONSOLE);
241	env::init();
242	unsafe {
243		logging::init();
244	}
245
246	info!("Welcome to Hermit {}", env!("CARGO_PKG_VERSION"));
247	if let Some(git_version) = built_info::GIT_VERSION {
248		let dirty = if built_info::GIT_DIRTY == Some(true) {
249			" (dirty)"
250		} else {
251			""
252		};
253
254		let opt_level = if built_info::OPT_LEVEL == "3" {
255			format_args!("")
256		} else {
257			format_args!(" (opt-level={})", built_info::OPT_LEVEL)
258		};
259
260		info!("Git version: {git_version}{dirty}{opt_level}");
261	}
262	let arch = built_info::TARGET.split_once('-').unwrap().0;
263	info!("Architecture: {arch}");
264	info!("Enabled features: {}", built_info::FEATURES_LOWERCASE_STR);
265	info!("Built on {}", built_info::BUILT_TIME_UTC);
266
267	info!("Executable start: {:p}", elf_symbols::executable_start());
268	info!("ELF header:       {:p}", elf_symbols::elf_header());
269	info!("Text segment end: {:p}", elf_symbols::text_end());
270	info!("Data segment end: {:p}", elf_symbols::data_end());
271	info!("Executable end:   {:p}", elf_symbols::executable_end());
272
273	if let Some(fdt) = env::fdt() {
274		info!("FDT:\n{fdt:#?}");
275	}
276
277	kernel::boot_processor_init();
278
279	#[cfg(not(target_arch = "riscv64"))]
280	scheduler::add_current_core();
281	interrupts::enable();
282
283	kernel::boot_next_processor();
284
285	#[cfg(feature = "smp")]
286	synch_all_cores();
287
288	if kernel::is_uhyve_with_pci() || !env::is_uhyve() {
289		#[cfg(feature = "pci")]
290		drivers::pci::print_information();
291	}
292
293	// Start the initd task.
294	unsafe { PerCoreScheduler::spawn(initd, 0, scheduler::task::NORMAL_PRIO, 0, USER_STACK_SIZE) };
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	kernel::application_processor_init();
304	#[cfg(not(target_arch = "riscv64"))]
305	scheduler::add_current_core();
306	interrupts::enable();
307	kernel::boot_next_processor();
308
309	debug!("Entering idle loop for application processor");
310
311	synch_all_cores();
312	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 = core_id();
322	panic_println!("[{core_id}][PANIC] {info}\n");
323
324	scheduler::shutdown(1);
325}