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	crate::executor::init();
188
189	syscalls::init();
190	fs::init();
191	#[cfg(feature = "shell")]
192	shell::init();
193
194	// Get the application arguments and environment variables.
195	#[cfg(not(test))]
196	let (argc, argv, environ) = syscalls::get_application_parameters();
197
198	// give the IP thread time to initialize the network interface
199	core_scheduler().reschedule();
200
201	if cfg!(feature = "warn-prebuilt") {
202		warn!("This is a prebuilt Hermit kernel.");
203		warn!("For non-default device drivers and features, consider building a custom kernel.");
204	}
205
206	info!("Jumping into application");
207
208	#[cfg(not(test))]
209	unsafe {
210		// And finally start the application.
211		#[cfg(all(not(test), not(any(feature = "nostd", feature = "common-os"))))]
212		runtime_entry(argc, argv, environ);
213		#[cfg(all(not(test), any(feature = "nostd", feature = "common-os")))]
214		main(argc, argv, environ);
215	}
216	#[cfg(test)]
217	test_main();
218}
219
220#[cfg(feature = "smp")]
221fn synch_all_cores() {
222	static CORE_COUNTER: AtomicU32 = AtomicU32::new(0);
223
224	CORE_COUNTER.fetch_add(1, Ordering::SeqCst);
225
226	let possible_cpus = kernel::get_possible_cpus();
227	while CORE_COUNTER.load(Ordering::SeqCst) != possible_cpus {
228		spin_loop();
229	}
230}
231
232/// Entry Point of Hermit for the Boot Processor
233#[cfg(target_os = "none")]
234fn boot_processor_main() -> ! {
235	// Initialize the kernel and hardware.
236	hermit_sync::Lazy::force(&console::CONSOLE);
237	unsafe {
238		logging::init();
239	}
240
241	info!("Welcome to Hermit {}", env!("CARGO_PKG_VERSION"));
242	if let Some(git_version) = built_info::GIT_VERSION {
243		let dirty = if built_info::GIT_DIRTY == Some(true) {
244			" (dirty)"
245		} else {
246			""
247		};
248
249		let opt_level = if built_info::OPT_LEVEL == "3" {
250			format_args!("")
251		} else {
252			format_args!(" (opt-level={})", built_info::OPT_LEVEL)
253		};
254
255		info!("Git version: {git_version}{dirty}{opt_level}");
256	}
257	let arch = built_info::TARGET.split_once('-').unwrap().0;
258	info!("Architecture: {arch}");
259	info!("Enabled features: {}", built_info::FEATURES_LOWERCASE_STR);
260	info!("Built on {}", built_info::BUILT_TIME_UTC);
261
262	info!("Kernel starts at {:p}", env::get_base_address());
263
264	if let Some(fdt) = env::fdt() {
265		info!("FDT:\n{fdt:#?}");
266	}
267
268	unsafe extern "C" {
269		static mut __bss_start: u8;
270	}
271	let bss_ptr = &raw mut __bss_start;
272	info!("BSS starts at {bss_ptr:p}");
273	info!("tls_info = {:#x?}", env::boot_info().load_info.tls_info);
274	arch::boot_processor_init();
275
276	#[cfg(not(target_arch = "riscv64"))]
277	scheduler::add_current_core();
278	interrupts::enable();
279
280	arch::kernel::boot_next_processor();
281
282	#[cfg(feature = "smp")]
283	synch_all_cores();
284
285	if kernel::is_uhyve_with_pci() || !env::is_uhyve() {
286		#[cfg(feature = "pci")]
287		crate::drivers::pci::print_information();
288	}
289
290	// Start the initd task.
291	unsafe {
292		scheduler::PerCoreScheduler::spawn(
293			initd,
294			0,
295			scheduler::task::NORMAL_PRIO,
296			0,
297			USER_STACK_SIZE,
298		)
299	};
300
301	// Run the scheduler loop.
302	PerCoreScheduler::run();
303}
304
305/// Entry Point of Hermit for an Application Processor
306#[cfg(all(target_os = "none", feature = "smp"))]
307fn application_processor_main() -> ! {
308	arch::application_processor_init();
309	#[cfg(not(target_arch = "riscv64"))]
310	scheduler::add_current_core();
311	interrupts::enable();
312	arch::kernel::boot_next_processor();
313
314	debug!("Entering idle loop for application processor");
315
316	synch_all_cores();
317	crate::executor::init();
318
319	// Run the scheduler loop.
320	PerCoreScheduler::run();
321}
322
323#[cfg(target_os = "none")]
324#[panic_handler]
325fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
326	let core_id = crate::arch::core_local::core_id();
327	panic_println!("[{core_id}][PANIC] {info}\n");
328
329	crate::scheduler::shutdown(1);
330}