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