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