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