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