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