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