Skip to main content

hermit/
rt.rs

1use crate::arch::kernel;
2use crate::arch::kernel::core_local::{core_id, core_scheduler};
3use crate::arch::kernel::interrupts;
4use crate::env::{self, StartInfo};
5use crate::scheduler::{PerCoreScheduler, PerCoreSchedulerExt};
6use crate::{console, drivers, executor, fs, logging, mm, scheduler, syscalls};
7
8mod built_info {
9	include!(concat!(env!("OUT_DIR"), "/built.rs"));
10}
11
12#[cfg(feature = "hermit-entry")]
13hermit_entry::define_abi_tag!();
14
15#[cfg(feature = "hermit-entry")]
16hermit_entry::define_entry_version!();
17
18#[cfg(test)]
19#[unsafe(no_mangle)]
20extern "C" fn runtime_entry(_argc: i32, _argv: *const *const u8, _env: *const *const u8) -> ! {
21	println!("Executing hermit unittests. Any arguments are dropped");
22	crate::test_main();
23	core_scheduler().exit(0)
24}
25
26//https://github.com/rust-lang/rust/issues/50297#issuecomment-524180479
27#[cfg(test)]
28pub fn test_runner(tests: &[&dyn Fn()]) {
29	println!("Running {} tests", tests.len());
30	for test in tests {
31		test();
32	}
33	core_scheduler().exit(0)
34}
35
36#[test_case]
37fn trivial_test() {
38	println!("Test test test");
39	panic!("Test called");
40}
41
42/// Entry point of a kernel thread, which initialize the libos
43extern "C" fn initd(_arg: usize) {
44	unsafe extern "C" {
45		#[cfg(all(not(test), not(any(feature = "nostd", feature = "common-os"))))]
46		fn runtime_entry(argc: i32, argv: *const *const u8, env: *const *const u8) -> !;
47		#[cfg(all(not(test), any(feature = "nostd", feature = "common-os")))]
48		fn main(argc: i32, argv: *const *const u8, env: *const *const u8);
49	}
50
51	// Initialize Drivers
52	drivers::init();
53	// The filesystem needs to be initialized before network to allow writing packet captures to a file.
54	fs::init();
55	executor::init();
56
57	syscalls::init();
58	#[cfg(feature = "shell")]
59	crate::shell::init();
60
61	// Get the application arguments and environment variables.
62	#[cfg(not(test))]
63	let (argc, argv, environ) = syscalls::get_application_parameters();
64
65	// give the IP thread time to initialize the network interface
66	core_scheduler().reschedule();
67
68	if cfg!(feature = "warn-prebuilt") {
69		warn!("This is a prebuilt Hermit kernel.");
70		warn!("For non-default device drivers and features, consider building a custom kernel.");
71	}
72
73	info!("Jumping into application");
74
75	#[cfg(not(test))]
76	unsafe {
77		// And finally start the application.
78		#[cfg(all(not(test), not(any(feature = "nostd", feature = "common-os"))))]
79		runtime_entry(argc, argv, environ);
80		#[cfg(all(not(test), any(feature = "nostd", feature = "common-os")))]
81		main(argc, argv, environ);
82	}
83	#[cfg(test)]
84	crate::test_main();
85}
86
87#[cfg(feature = "smp")]
88fn synch_all_cores() {
89	use core::hint;
90	use core::sync::atomic::{AtomicU32, Ordering};
91
92	static CORE_COUNTER: AtomicU32 = AtomicU32::new(0);
93
94	CORE_COUNTER.fetch_add(1, Ordering::SeqCst);
95
96	let possible_cpus = kernel::get_possible_cpus();
97	while CORE_COUNTER.load(Ordering::SeqCst) != possible_cpus {
98		hint::spin_loop();
99	}
100}
101
102/// Entry Point of Hermit for the Boot Processor
103pub fn boot_processor_main() -> ! {
104	use crate::config::USER_STACK_SIZE;
105
106	// Initialize the kernel and hardware.
107	mm::claim_initial_heap();
108	hermit_sync::Lazy::force(&console::CONSOLE);
109	env::init();
110	unsafe {
111		logging::init();
112	}
113
114	info!("Welcome to Hermit {}", env!("CARGO_PKG_VERSION"));
115	if let Some(git_version) = built_info::GIT_VERSION {
116		let dirty = if built_info::GIT_DIRTY == Some(true) {
117			" (dirty)"
118		} else {
119			""
120		};
121
122		let opt_level = if built_info::OPT_LEVEL == "3" {
123			format_args!("")
124		} else {
125			format_args!(" (opt-level={})", built_info::OPT_LEVEL)
126		};
127
128		info!("Git version: {git_version}{dirty}{opt_level}");
129	}
130	let arch = built_info::TARGET.split_once('-').unwrap().0;
131	info!("Architecture: {arch}");
132	info!("Enabled features: {}", built_info::FEATURES_LOWERCASE_STR);
133	info!("Built on {}", built_info::BUILT_TIME_UTC);
134
135	info!("Executable start: {:p}", elf_symbols::executable_start());
136	info!("ELF header:       {:p}", elf_symbols::elf_header());
137	info!("Text segment end: {:p}", elf_symbols::text_end());
138	info!("Data segment end: {:p}", elf_symbols::data_end());
139	info!("Executable end:   {:p}", elf_symbols::executable_end());
140
141	info!("{}", env::start_info().display());
142
143	kernel::boot_processor_init();
144
145	#[cfg(not(target_arch = "riscv64"))]
146	scheduler::add_current_core();
147	interrupts::enable();
148
149	kernel::boot_next_processor();
150
151	#[cfg(feature = "smp")]
152	synch_all_cores();
153
154	#[cfg(feature = "pci")]
155	drivers::pci::print_information();
156
157	// Start the initd task.
158	unsafe { PerCoreScheduler::spawn(initd, 0, scheduler::task::NORMAL_PRIO, 0, USER_STACK_SIZE) };
159
160	// Run the scheduler loop.
161	PerCoreScheduler::run();
162}
163
164/// Entry Point of Hermit for an Application Processor
165#[cfg(feature = "smp")]
166pub fn application_processor_main() -> ! {
167	kernel::application_processor_init();
168	#[cfg(not(target_arch = "riscv64"))]
169	scheduler::add_current_core();
170	interrupts::enable();
171	kernel::boot_next_processor();
172
173	debug!("Entering idle loop for application processor");
174
175	synch_all_cores();
176	executor::init();
177
178	// Run the scheduler loop.
179	PerCoreScheduler::run();
180}
181
182#[panic_handler]
183fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
184	let core_id = core_id();
185	panic_println!("[{core_id}][PANIC] {info}\n");
186
187	scheduler::shutdown(1);
188}