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