hermit/
lib.rs

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