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