hermit/arch/x86_64/kernel/
mod.rs1#[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(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 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#[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#[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 apic::add_local_apic_id(core_id() as u8);
204
205 apic::init_next_processor_variables();
208 }
209}
210
211pub fn boot_next_processor() {
212 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
232pub 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 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 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 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 - 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 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}