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