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