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};
5#[cfg(feature = "common-os")]
6use core::{mem, slice};
7
8use hermit_entry::boot_info::{PlatformInfo, RawBootInfo};
9use memory_addresses::PhysAddr;
10use x86_64::registers::control::{Cr0, Cr4};
11
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(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;
32#[cfg(target_os = "none")]
33mod start;
34pub mod switch;
35#[cfg(feature = "common-os")]
36mod syscall;
37pub(crate) mod systemtime;
38#[cfg(feature = "vga")]
39pub mod vga;
40
41pub fn get_ram_address() -> PhysAddr {
42 PhysAddr::new(env::boot_info().hardware_info.phys_addr_range.start)
43}
44
45#[cfg(feature = "smp")]
46pub fn get_possible_cpus() -> u32 {
47 use core::cmp;
48
49 match env::boot_info().platform_info {
50 PlatformInfo::Uhyve { num_cpus, .. } => cmp::max(
52 u32::try_from(num_cpus.get()).unwrap(),
53 get_processor_count(),
54 ),
55 _ => apic::local_apic_id_count(),
56 }
57}
58
59#[cfg(feature = "smp")]
60pub fn get_processor_count() -> u32 {
61 CPU_ONLINE.load(Ordering::Acquire)
62}
63
64#[cfg(not(feature = "smp"))]
65pub fn get_processor_count() -> u32 {
66 1
67}
68
69pub fn is_uhyve_with_pci() -> bool {
70 matches!(
71 env::boot_info().platform_info,
72 PlatformInfo::Uhyve { has_pci: true, .. }
73 )
74}
75
76pub fn args() -> Option<&'static str> {
77 match env::boot_info().platform_info {
78 PlatformInfo::Multiboot { command_line, .. }
79 | PlatformInfo::LinuxBootParams { command_line, .. } => command_line,
80 _ => None,
81 }
82}
83
84#[cfg(target_os = "none")]
86pub fn boot_processor_init() {
87 processor::detect_features();
88 processor::configure();
89
90 if cfg!(feature = "vga") && !env::is_uhyve() {
91 #[cfg(feature = "vga")]
92 vga::init();
93 }
94
95 crate::mm::init();
96 crate::mm::print_information();
97 CoreLocal::get().add_irq_counter();
98 env::init();
99 gdt::add_current_core();
100 interrupts::load_idt();
101 pic::init();
102
103 processor::detect_frequency();
104 crate::logging::KERNEL_LOGGER.set_time(true);
105 processor::print_information();
106 debug!("Cr0 = {:?}", Cr0::read());
107 debug!("Cr4 = {:?}", Cr4::read());
108 interrupts::install();
109 systemtime::init();
110
111 if !env::is_uhyve() {
112 #[cfg(feature = "acpi")]
113 acpi::init();
114 }
115 if is_uhyve_with_pci() || !is_uhyve() {
116 #[cfg(feature = "pci")]
117 pci::init();
118 }
119
120 apic::init();
121 scheduler::install_timer_handler();
122 finish_processor_init();
123}
124
125#[cfg(all(target_os = "none", feature = "smp"))]
127pub fn application_processor_init() {
128 CoreLocal::install();
129 processor::configure();
130 gdt::add_current_core();
131 interrupts::load_idt();
132 if processor::supports_x2apic() {
133 apic::init_x2apic();
134 }
135 apic::init_local_apic();
136 debug!("Cr0 = {:?}", Cr0::read());
137 debug!("Cr4 = {:?}", Cr4::read());
138 finish_processor_init();
139}
140
141fn finish_processor_init() {
142 if env::is_uhyve() {
143 apic::add_local_apic_id(core_id() as u8);
148
149 apic::init_next_processor_variables();
152 }
153}
154
155pub fn boot_next_processor() {
156 let cpu_online = CPU_ONLINE.fetch_add(1, Ordering::Release);
159
160 if !env::is_uhyve() {
161 if cpu_online == 0 {
162 #[cfg(all(target_os = "none", feature = "smp"))]
163 apic::boot_application_processors();
164 }
165
166 if !cfg!(feature = "smp") {
167 apic::print_information();
168 }
169 }
170}
171
172pub fn print_statistics() {
173 interrupts::print_statistics();
174}
175
176pub static CPU_ONLINE: AtomicU32 = AtomicU32::new(0);
180
181pub static CURRENT_STACK_ADDRESS: AtomicPtr<u8> = AtomicPtr::new(ptr::null_mut());
182
183#[cfg(target_os = "none")]
184#[inline(never)]
185#[unsafe(no_mangle)]
186unsafe extern "C" fn pre_init(boot_info: Option<&'static RawBootInfo>, cpu_id: u32) -> ! {
187 use x86_64::registers::control::Cr0Flags;
188
189 unsafe {
191 Cr0::update(|flags| flags.remove(Cr0Flags::CACHE_DISABLE | Cr0Flags::NOT_WRITE_THROUGH));
192 }
193
194 if cpu_id == 0 {
195 env::set_boot_info(*boot_info.unwrap());
196
197 crate::boot_processor_main()
198 } else {
199 #[cfg(not(feature = "smp"))]
200 {
201 let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into()));
202 let preamble = format_args!("[ ][{cpu_id}][{style}ERROR{style:#}]");
203 println!(
204 "{preamble} Secondary core booted, but Hermit was not built with SMP support!"
205 );
206 loop {
207 processor::halt();
208 }
209 }
210 #[cfg(feature = "smp")]
211 crate::application_processor_main();
212 }
213}
214
215#[cfg(feature = "common-os")]
216const LOADER_START: usize = 0x0100_0000_0000;
217#[cfg(feature = "common-os")]
218const LOADER_STACK_SIZE: usize = 0x8000;
219
220#[cfg(feature = "common-os")]
221pub fn load_application<F, T>(code_size: u64, tls_size: u64, func: F) -> T
222where
223 F: FnOnce(&'static mut [u8], Option<&'static mut [u8]>) -> T,
224{
225 use core::slice;
226
227 use align_address::Align;
228 use free_list::PageLayout;
229 use memory_addresses::VirtAddr;
230 use x86_64::structures::paging::{PageSize, Size4KiB as BasePageSize};
231
232 use crate::arch::x86_64::mm::paging::{self, PageTableEntryFlags, PageTableEntryFlagsExt};
233 use crate::mm::{FrameAlloc, PageRangeAllocator};
234
235 let code_size = (code_size as usize + LOADER_STACK_SIZE).align_up(BasePageSize::SIZE as usize);
236 let layout = PageLayout::from_size_align(code_size, BasePageSize::SIZE as usize).unwrap();
237 let frame_range = FrameAlloc::allocate(layout).unwrap();
238 let physaddr = PhysAddr::from(frame_range.start());
239
240 let mut flags = PageTableEntryFlags::empty();
241 flags.normal().writable().user().execute_enable();
242 paging::map::<BasePageSize>(
243 VirtAddr::from(LOADER_START),
244 physaddr,
245 code_size / BasePageSize::SIZE as usize,
246 flags,
247 );
248
249 let loader_start_ptr = ptr::with_exposed_provenance_mut(LOADER_START);
250 let code_slice = unsafe { slice::from_raw_parts_mut(loader_start_ptr, code_size) };
251
252 if tls_size > 0 {
253 let tcb_size = mem::size_of::<*mut ()>();
258 let tls_offset = tls_size as usize;
259
260 let tls_memsz = (tls_offset + tcb_size).align_up(BasePageSize::SIZE as usize);
261 let layout = PageLayout::from_size(tls_memsz).unwrap();
262 let frame_range = FrameAlloc::allocate(layout).unwrap();
263 let physaddr = PhysAddr::from(frame_range.start());
264
265 let mut flags = PageTableEntryFlags::empty();
266 flags.normal().writable().user().execute_disable();
267 let tls_virt = VirtAddr::from(LOADER_START + code_size + BasePageSize::SIZE as usize);
268 paging::map::<BasePageSize>(
269 tls_virt,
270 physaddr,
271 tls_memsz / BasePageSize::SIZE as usize,
272 flags,
273 );
274 let block =
275 unsafe { slice::from_raw_parts_mut(tls_virt.as_mut_ptr(), tls_offset + tcb_size) };
276 for elem in block.iter_mut() {
277 *elem = 0;
278 }
279
280 let thread_ptr = block[tls_offset..].as_mut_ptr().cast::<()>();
282 unsafe {
283 thread_ptr.cast::<*mut ()>().write(thread_ptr);
284 }
285 crate::arch::x86_64::kernel::processor::writefs(thread_ptr.expose_provenance());
286
287 func(code_slice, Some(block))
288 } else {
289 func(code_slice, None)
290 }
291}
292
293#[cfg(feature = "common-os")]
294pub unsafe fn jump_to_user_land(entry_point: usize, code_size: usize, arg: &[&str]) -> ! {
295 use alloc::ffi::CString;
296
297 use align_address::Align;
298 use x86_64::structures::paging::{PageSize, Size4KiB as BasePageSize};
299
300 use crate::arch::x86_64::kernel::scheduler::TaskStacks;
301
302 info!("Create new file descriptor table");
303 core_scheduler().recreate_objmap().unwrap();
304
305 let entry_point: usize = LOADER_START | entry_point;
306 let stack_pointer: usize = LOADER_START
307 + (code_size + LOADER_STACK_SIZE).align_up(BasePageSize::SIZE.try_into().unwrap())
308 - 8;
309
310 let stack_pointer = stack_pointer - 128 - arg.len() * mem::size_of::<*mut u8>();
311 let stack_ptr = ptr::with_exposed_provenance_mut::<*mut u8>(stack_pointer);
312 let argv = unsafe { slice::from_raw_parts_mut(stack_ptr, arg.len()) };
313 let len = arg.iter().fold(0, |acc, x| acc + x.len() + 1);
314 let stack_pointer = (stack_pointer - len).align_down(16) - mem::size_of::<usize>();
316
317 let mut pos: usize = 0;
318 for (i, s) in arg.iter().enumerate() {
319 let s = CString::new(*s).unwrap();
320 let bytes = s.as_bytes_with_nul();
321 argv[i] = ptr::with_exposed_provenance_mut::<u8>(stack_pointer + pos);
322 pos += bytes.len();
323
324 unsafe {
325 argv[i].copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
326 }
327 }
328
329 debug!("Jump to user space at 0x{entry_point:x}, stack pointer 0x{stack_pointer:x}");
330
331 unsafe {
332 asm!(
333 "and rsp, {0}",
334 "swapgs",
335 "push {1}",
336 "push {2}",
337 "push {3}",
338 "push {4}",
339 "push {5}",
340 "mov rdi, {6}",
341 "mov rsi, {7}",
342 "iretq",
343 const u64::MAX - (TaskStacks::MARKER_SIZE as u64 - 1),
344 const 0x23usize,
345 in(reg) stack_pointer,
346 const 0x1202u64,
347 const 0x2busize,
348 in(reg) entry_point,
349 in(reg) argv.len(),
350 in(reg) argv.as_ptr(),
351 options(nostack, noreturn)
352 );
353 }
354}