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