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