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