Skip to main content

hermit/arch/x86_64/kernel/
mod.rs

1#[cfg(feature = "common-os")]
2use core::arch::asm;
3use core::ptr;
4#[cfg(feature = "common-os")]
5use core::slice;
6use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering};
7
8use hermit_entry::boot_info::RawBootInfo;
9use x86_64::registers::control::{Cr0, Cr4};
10
11pub(crate) use self::apic::{set_oneshot_timer, wakeup_core};
12use crate::arch::x86_64::kernel::core_local::*;
13use crate::env;
14
15#[cfg(feature = "acpi")]
16pub mod acpi;
17pub mod apic;
18pub mod core_local;
19pub mod gdt;
20pub mod interrupts;
21#[cfg(feature = "kernel-stack")]
22pub mod kernel_stack;
23#[cfg(all(not(feature = "pci"), feature = "virtio"))]
24pub mod mmio;
25#[cfg(feature = "pci")]
26pub mod pci;
27pub mod pic;
28pub mod pit;
29pub mod processor;
30pub mod scheduler;
31pub mod serial;
32#[cfg(target_os = "none")]
33mod start;
34pub mod switch;
35#[cfg(feature = "common-os")]
36mod syscall;
37pub(crate) mod systemtime;
38#[cfg(feature = "vga")]
39pub mod vga;
40
41#[cfg(feature = "smp")]
42pub fn get_possible_cpus() -> u32 {
43	#[cfg(feature = "uhyve")]
44	if let Some(num_cpus) = env::uhyve_num_cpus() {
45		return num_cpus.get().try_into().unwrap();
46	}
47
48	apic::local_apic_id_count()
49}
50
51#[cfg(feature = "smp")]
52pub fn get_processor_count() -> u32 {
53	CPU_ONLINE.load(Ordering::Acquire)
54}
55
56#[cfg(not(feature = "smp"))]
57pub fn get_processor_count() -> u32 {
58	1
59}
60
61/// Real Boot Processor initialization as soon as we have put the first Welcome message on the screen.
62#[cfg(target_os = "none")]
63pub fn boot_processor_init() {
64	processor::detect_features();
65	processor::configure();
66
67	#[cfg(feature = "vga")]
68	vga::init();
69
70	crate::mm::init();
71	crate::mm::print_information();
72	CoreLocal::get().add_irq_counter();
73	gdt::add_current_core();
74	interrupts::load_idt();
75	pic::init();
76
77	processor::detect_frequency();
78	crate::logging::KERNEL_LOGGER.set_time(true);
79	processor::print_information();
80	debug!("Cr0 = {:?}", Cr0::read());
81	debug!("Cr4 = {:?}", Cr4::read());
82	interrupts::install();
83	systemtime::init();
84
85	#[cfg(feature = "acpi")]
86	acpi::init();
87
88	#[cfg(feature = "pci")]
89	pci::init();
90
91	apic::init();
92	scheduler::install_timer_handler();
93	finish_processor_init();
94}
95
96/// Application Processor initialization
97#[cfg(all(target_os = "none", feature = "smp"))]
98pub fn application_processor_init() {
99	CoreLocal::install();
100	processor::configure();
101	gdt::add_current_core();
102	interrupts::load_idt();
103	if processor::supports_x2apic() {
104		apic::init_x2apic();
105	}
106	apic::init_local_apic();
107	debug!("Cr0 = {:?}", Cr0::read());
108	debug!("Cr4 = {:?}", Cr4::read());
109	finish_processor_init();
110}
111
112fn finish_processor_init() {
113	#[cfg(feature = "uhyve")]
114	if env::is_uhyve() {
115		// uhyve does not use apic::detect_from_acpi and therefore does not know the number of processors and
116		// their APIC IDs in advance.
117		// Therefore, we have to add each booted processor into the CPU_LOCAL_APIC_IDS vector ourselves.
118		// Fortunately, the Local APIC IDs of uhyve are sequential and therefore match the Core IDs.
119		apic::add_local_apic_id(core_id() as u8);
120
121		// uhyve also boots each processor into _start itself and does not use apic::boot_application_processors.
122		// Therefore, the current processor already needs to prepare the processor variables for a possible next processor.
123		#[cfg(feature = "smp")]
124		apic::init_next_processor_variables();
125	}
126}
127
128pub fn boot_next_processor() {
129	// This triggers apic::boot_application_processors (bare-metal/QEMU) or uhyve
130	// to initialize the next processor.
131	let cpu_online = CPU_ONLINE.fetch_add(1, Ordering::Release);
132
133	#[cfg(feature = "uhyve")]
134	if env::is_uhyve() {
135		return;
136	}
137
138	if cpu_online == 0 {
139		#[cfg(all(target_os = "none", feature = "smp"))]
140		apic::boot_application_processors();
141	}
142
143	if !cfg!(feature = "smp") {
144		apic::print_information();
145	}
146}
147
148pub fn print_statistics() {
149	interrupts::print_statistics();
150}
151
152/// `CPU_ONLINE` is the count of CPUs that finished initialization.
153///
154/// It also synchronizes initialization of CPU cores.
155pub static CPU_ONLINE: AtomicU32 = AtomicU32::new(0);
156
157pub static CURRENT_STACK_ADDRESS: AtomicPtr<u8> = AtomicPtr::new(ptr::null_mut());
158
159#[cfg(target_os = "none")]
160#[inline(never)]
161#[unsafe(no_mangle)]
162unsafe extern "C" fn pre_init(boot_info: Option<&'static RawBootInfo>, cpu_id: u32) -> ! {
163	use x86_64::registers::control::Cr0Flags;
164
165	// Enable caching
166	unsafe {
167		Cr0::update(|flags| flags.remove(Cr0Flags::CACHE_DISABLE | Cr0Flags::NOT_WRITE_THROUGH));
168	}
169
170	if cpu_id == 0 {
171		env::set_boot_info(*boot_info.unwrap());
172
173		crate::boot_processor_main()
174	} else {
175		#[cfg(not(feature = "smp"))]
176		{
177			let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into()));
178			let preamble = format_args!("[            ][{cpu_id}][{style}ERROR{style:#}]");
179			println!(
180				"{preamble} Secondary core booted, but Hermit was not built with SMP support!"
181			);
182			loop {
183				processor::halt();
184			}
185		}
186		#[cfg(feature = "smp")]
187		crate::application_processor_main();
188	}
189}
190
191#[cfg(feature = "common-os")]
192const LOADER_START: usize = 0x0100_0000_0000;
193#[cfg(feature = "common-os")]
194const LOADER_STACK_SIZE: usize = 0x8000;
195
196#[cfg(feature = "common-os")]
197pub fn load_application<F, T>(code_size: u64, tls_size: u64, func: F) -> T
198where
199	F: FnOnce(&'static mut [u8], Option<&'static mut [u8]>) -> T,
200{
201	use align_address::Align;
202	use free_list::PageLayout;
203	use memory_addresses::{PhysAddr, VirtAddr};
204	use x86_64::structures::paging::{PageSize, Size4KiB as BasePageSize};
205
206	use crate::arch::x86_64::mm::paging::{self, PageTableEntryFlags, PageTableEntryFlagsExt};
207	use crate::mm::{FrameAlloc, PageRangeAllocator};
208
209	let code_size = (code_size as usize + LOADER_STACK_SIZE).align_up(BasePageSize::SIZE as usize);
210	let layout = PageLayout::from_size_align(code_size, BasePageSize::SIZE as usize).unwrap();
211	let frame_range = FrameAlloc::allocate(layout).unwrap();
212	let physaddr = PhysAddr::from(frame_range.start());
213
214	let mut flags = PageTableEntryFlags::empty();
215	flags.normal().writable().user().execute_enable();
216	paging::map::<BasePageSize>(
217		VirtAddr::from(LOADER_START),
218		physaddr,
219		code_size / BasePageSize::SIZE as usize,
220		flags,
221	);
222
223	let loader_start_ptr = ptr::with_exposed_provenance_mut(LOADER_START);
224	let code_slice = unsafe { slice::from_raw_parts_mut(loader_start_ptr, code_size) };
225
226	if tls_size > 0 {
227		// To access TLS blocks on x86-64, TLS offsets are *subtracted* from the thread register value.
228		// So the thread pointer needs to be `block_ptr + tls_offset`.
229		// GNU style TLS requires `fs:0` to represent the same address as the thread pointer.
230		// Since the thread pointer points to the end of the TLS blocks, we need to store it there.
231		let tcb_size = size_of::<*mut ()>();
232		let tls_offset = tls_size as usize;
233
234		let tls_memsz = (tls_offset + tcb_size).align_up(BasePageSize::SIZE as usize);
235		let layout = PageLayout::from_size(tls_memsz).unwrap();
236		let frame_range = FrameAlloc::allocate(layout).unwrap();
237		let physaddr = PhysAddr::from(frame_range.start());
238
239		let mut flags = PageTableEntryFlags::empty();
240		flags.normal().writable().user().execute_disable();
241		let tls_virt = VirtAddr::from(LOADER_START + code_size + BasePageSize::SIZE as usize);
242		paging::map::<BasePageSize>(
243			tls_virt,
244			physaddr,
245			tls_memsz / BasePageSize::SIZE as usize,
246			flags,
247		);
248		let block =
249			unsafe { slice::from_raw_parts_mut(tls_virt.as_mut_ptr(), tls_offset + tcb_size) };
250		for elem in block.iter_mut() {
251			*elem = 0;
252		}
253
254		// thread_ptr = block_ptr + tls_offset
255		let thread_ptr = block[tls_offset..].as_mut_ptr().cast::<()>();
256		unsafe {
257			thread_ptr.cast::<*mut ()>().write(thread_ptr);
258		}
259		processor::writefs(thread_ptr.expose_provenance());
260
261		func(code_slice, Some(block))
262	} else {
263		func(code_slice, None)
264	}
265}
266
267#[cfg(feature = "common-os")]
268pub unsafe fn jump_to_user_land(entry_point: usize, code_size: usize, arg: &[&str]) -> ! {
269	use alloc::ffi::CString;
270
271	use align_address::Align;
272	use x86_64::structures::paging::{PageSize, Size4KiB as BasePageSize};
273
274	use crate::arch::x86_64::kernel::scheduler::TaskStacks;
275
276	info!("Create new file descriptor table");
277	core_scheduler().recreate_objmap().unwrap();
278
279	let entry_point: usize = LOADER_START | entry_point;
280	let stack_pointer: usize = LOADER_START
281		+ (code_size + LOADER_STACK_SIZE).align_up(BasePageSize::SIZE.try_into().unwrap())
282		- 8;
283
284	let stack_pointer = stack_pointer - 128 /* red zone */ - arg.len() * size_of::<*mut u8>();
285	let stack_ptr = ptr::with_exposed_provenance_mut::<*mut u8>(stack_pointer);
286	let argv = unsafe { slice::from_raw_parts_mut(stack_ptr, arg.len()) };
287	let len = arg.iter().fold(0, |acc, x| acc + x.len() + 1);
288	// align stack pointer to fulfill the requirements of the x86_64 ABI
289	let stack_pointer = (stack_pointer - len).align_down(16) - size_of::<usize>();
290
291	let mut pos: usize = 0;
292	for (i, s) in arg.iter().enumerate() {
293		let s = CString::new(*s).unwrap();
294		let bytes = s.as_bytes_with_nul();
295		argv[i] = ptr::with_exposed_provenance_mut::<u8>(stack_pointer + pos);
296		pos += bytes.len();
297
298		unsafe {
299			argv[i].copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
300		}
301	}
302
303	debug!("Jump to user space at 0x{entry_point:x}, stack pointer 0x{stack_pointer:x}");
304
305	unsafe {
306		asm!(
307			"and rsp, {0}",
308			"swapgs",
309			"push {1}",
310			"push {2}",
311			"push {3}",
312			"push {4}",
313			"push {5}",
314			"mov rdi, {6}",
315			"mov rsi, {7}",
316			"iretq",
317			const u64::MAX - (TaskStacks::MARKER_SIZE as u64 - 1),
318			const 0x23usize,
319			in(reg) stack_pointer,
320			const 0x1202u64,
321			const 0x2busize,
322			in(reg) entry_point,
323			in(reg) argv.len(),
324			in(reg) argv.as_ptr(),
325			options(nostack, noreturn)
326		);
327	}
328}