Skip to main content

hermit/arch/x86_64/kernel/
mod.rs

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