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