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