1#![feature(extern_item_impls)]
32#![cfg_attr(not(test), no_std)]
33#![allow(missing_abi)]
34
35#[macro_use]
36extern crate ax_log;
37
38extern crate ax_driver as _;
39
40#[cfg(all(target_os = "none", not(feature = "std-compat"), not(test)))]
41mod lang_items;
42#[cfg(all(
43 feature = "stack-protector",
44 any(target_os = "none", target_env = "musl"),
45 not(test)
46))]
47mod stack_protector;
48
49#[cfg(feature = "smp")]
50mod mp;
51
52mod clock_event;
53#[cfg(feature = "paging")]
54mod kernel_mapping;
55mod klib;
56mod preempt;
57mod raw_console;
58
59pub mod console;
60mod devices;
61pub mod emergency_console;
62mod error;
63mod fs;
64pub mod irq;
65mod registers;
66pub mod serial;
67pub mod sync;
68
69#[cfg(all(feature = "net", feature = "fs"))]
70mod unix_ns;
71
72pub use ax_hal as hal;
73pub use error::{RuntimeError, RuntimeResult};
74
75pub fn terminate() -> ! {
80 if let Ok(output) = console::output() {
81 let _ = output.drain();
82 }
83 ax_hal::power::system_off()
84}
85
86pub(crate) mod build_info {
87 include!(concat!(env!("OUT_DIR"), "/build_info.rs"));
88}
89
90#[cfg(feature = "smp")]
91pub use self::mp::rust_main_secondary;
92
93extern crate alloc;
94
95#[cfg(feature = "fs")]
96pub(crate) fn runtime_default_task_stack_size() -> usize {
97 build_info::TASK_STACK_SIZE
98}
99
100fn ticks_per_sec() -> u64 {
101 build_info::TICKS_PER_SEC as u64
102}
103
104const LOGO: &str = r#"
105 d8888 .d88888b. .d8888b.
106 d88888 d88P" "Y88b d88P Y88b
107 d88P888 888 888 Y88b.
108 d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b.
109 d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b.
110 d88P 888 888 888 88888888 888 888 "888
111 d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P
112d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P"
113"#;
114
115#[eii]
116fn ax_app_entry() {
117 #[cfg(not(test))]
118 unsafe extern "C" {
119 safe fn main();
121 }
122 #[cfg(not(test))]
124 main();
125}
126
127struct LogIfImpl;
128
129#[cfg(feature = "paging")]
130fn runtime_page_fault_handler(
131 addr: ax_memory_addr::VirtAddr,
132 flags: ax_hal::trap::PageFaultFlags,
133) -> bool {
134 #[cfg(feature = "stack-guard-page")]
135 if ax_task::diagnose_current_stack_guard_page_fault(addr) {
136 return false;
137 }
138
139 ax_mm::kernel_aspace().lock().handle_page_fault(addr, flags)
140}
141
142#[ax_crate_interface::impl_interface]
143impl ax_log::LogIf for LogIfImpl {
144 fn try_publish(
145 meta: ax_log::RecordMeta,
146 args: core::fmt::Arguments<'_>,
147 ) -> ax_log::PublishStatus {
148 if let Some(status) = serial::try_publish_record(meta, args) {
149 return status;
150 }
151 if let Some(status) = console::try_publish_without_runtime(args) {
152 return status;
153 }
154 let mut writer = PlatformConsoleWriter::default();
155 if core::fmt::write(&mut writer, args).is_ok() {
156 ax_log::PublishStatus::Published
157 } else {
158 ax_log::PublishStatus::Dropped
159 }
160 }
161
162 fn emergency_write(args: core::fmt::Arguments<'_>) -> usize {
163 emergency_console::write_fmt(args)
164 }
165}
166
167#[derive(Default)]
168struct PlatformConsoleWriter {
169 written: usize,
170}
171
172impl core::fmt::Write for PlatformConsoleWriter {
173 fn write_str(&mut self, text: &str) -> core::fmt::Result {
174 ax_hal::console::write_text_bytes(text.as_bytes());
175 self.written = self.written.saturating_add(text.len());
176 Ok(())
177 }
178}
179
180use core::sync::atomic::{AtomicUsize, Ordering};
181
182static INITED_CPUS: AtomicUsize = AtomicUsize::new(0);
184
185fn is_init_ok() -> bool {
186 INITED_CPUS.load(Ordering::Acquire) == ax_hal::cpu_num()
187}
188
189#[cfg_attr(not(test), ax_plat::main)]
200pub fn rust_main(cpu_id: usize, arg: usize) -> ! {
201 ax_hal::percpu::init_primary(cpu_id);
202 ax_alloc::init_percpu_slab(cpu_id);
205 ax_hal::init_early(cpu_id, arg);
206 let log_level = option_env!("AX_LOG").unwrap_or("info");
207
208 ax_println!("{}", LOGO);
209 ax_println!(
210 indoc::indoc! {"
211 arch = {}
212 platform = {}
213 target = {}
214 build_mode = {}
215 log_level = {}
216 backtrace = {}
217 smp = {}
218 "},
219 build_info::ARCH,
220 hal::platform_name(),
221 build_info::TARGET,
222 build_info::MODE,
223 log_level,
224 axbacktrace::is_enabled(),
225 ax_hal::cpu_num()
226 );
227
228 ax_log::init();
229 ax_log::set_max_level(log_level); info!("Logging is enabled.");
231 info!("Primary CPU {cpu_id} started, arg = {arg:#x}.");
232
233 info!("Found physcial memory regions:");
234 for r in ax_hal::mem::memory_regions() {
235 info!(
236 " [{:x?}, {:x?}) {} ({:?})",
237 r.paddr,
238 r.paddr + r.size,
239 r.name,
240 r.flags
241 );
242 }
243
244 init_allocator();
245
246 let (kernel_space_start, kernel_space_size) = ax_hal::mem::kernel_aspace();
247
248 {
249 use core::ops::Range;
250
251 unsafe extern "C" {
252 safe static _stext: [u8; 0];
253 safe static _etext: [u8; 0];
254 }
255
256 let fp_range_start = kernel_space_start.as_usize();
257 let fp_range_end = fp_range_start.saturating_add(kernel_space_size);
258 axbacktrace::init(
259 Range {
260 start: _stext.as_ptr() as usize,
261 end: _etext.as_ptr() as usize,
262 },
263 Range {
264 start: fp_range_start,
265 end: fp_range_end,
266 },
267 );
268 }
269
270 info!(
271 "kernel aspace: [{:#x?}, {:#x?})",
272 kernel_space_start,
273 kernel_space_start + kernel_space_size,
274 );
275
276 #[cfg(feature = "paging")]
277 {
278 ax_mm::init_memory_management();
279 ax_hal::trap::set_page_fault_handler(runtime_page_fault_handler);
280 }
281
282 info!("Initialize platform devices...");
283 ax_hal::init_later(cpu_id, arg);
284 if rdrive::is_initialized() {
285 registers::append_linker_registers();
286 ax_hal::irq::init_boot_irqs(cpu_id)
287 .unwrap_or_else(|err| panic!("failed to initialize boot IRQs: {err:?}"));
288 } else {
289 warn!("rdrive is not initialized; skip pre-kernel driver probe");
290 }
291
292 ax_task::init_scheduler();
293 preempt::release_bootstrap();
294
295 #[cfg(feature = "ipi")]
296 {
297 ax_ipi::init();
298 ax_hal::irq::set_run_on_cpu_sync(ax_ipi_run_on_cpu_sync);
299 }
300
301 info!("Initialize interrupt handlers...");
302 init_interrupt();
303
304 devices::probe_all_devices();
305
306 serial::init(cpu_id);
307
308 match console::activate_before_smp() {
309 console::ConsoleActivation::Active {
310 runtime_index,
311 tty_number,
312 } => info!("runtime console active: serial{runtime_index}, ttyS{tty_number}"),
313 console::ConsoleActivation::RawHal(reason) => {
314 info!("no runtime console selected; keeping the HAL console: {reason:?}")
315 }
316 console::ConsoleActivation::FailedClosed(reason) => {
317 warn!("runtime console unavailable; early console failed closed: {reason:?}")
318 }
319 }
320
321 #[cfg(feature = "rtc")]
322 ax_println!(
323 "Boot at {}\n",
324 chrono::DateTime::from_timestamp_nanos(ax_hal::time::wall_time_nanos() as _),
325 );
326
327 fs::init(ax_hal::boot::bootargs());
328
329 #[cfg(feature = "display")]
330 devices::init_display();
331
332 #[cfg(feature = "input")]
333 devices::init_input();
334
335 #[cfg(feature = "vsock")]
336 devices::init_vsock();
337
338 #[cfg(feature = "smp")]
339 self::mp::start_secondary_cpus(cpu_id);
340
341 ax_ctor_bare::call_ctors();
342
343 info!("Primary CPU {cpu_id} init OK.");
344 INITED_CPUS.fetch_add(1, Ordering::Release);
345
346 while !is_init_ok() {
347 core::hint::spin_loop();
348 }
349
350 #[cfg(feature = "ipi")]
351 ax_ipi::wait_for_all_cpus_ready();
352
353 #[cfg(all(feature = "smp", feature = "ipi"))]
354 fs::online_smp();
355
356 #[cfg(feature = "net")]
361 devices::init_net();
362
363 ax_app_entry();
364 terminate();
365}
366
367fn init_allocator() {
368 use ax_hal::mem::{MemRegionFlags, memory_regions, phys_to_virt};
369
370 info!("Initialize global memory allocator...");
371 info!(" use {} allocator.", ax_alloc::global_allocator().name());
372
373 let mut max_region_size = 0;
389 let mut max_region_paddr = 0.into();
390
391 for r in memory_regions() {
392 if r.flags.contains(MemRegionFlags::FREE) && r.size > max_region_size {
393 max_region_size = r.size;
394 max_region_paddr = r.paddr;
395 }
396 }
397
398 for r in memory_regions() {
399 if r.flags.contains(MemRegionFlags::FREE) && r.paddr == max_region_paddr {
400 ax_alloc::global_init(phys_to_virt(r.paddr).as_usize(), r.size)
401 .expect("initialize global allocator failed");
402 break;
403 }
404 }
405
406 for r in memory_regions() {
407 if r.flags.contains(MemRegionFlags::FREE) && r.paddr != max_region_paddr {
408 ax_alloc::global_add_memory(phys_to_virt(r.paddr).as_usize(), r.size)
409 .expect("add heap memory region failed");
410 }
411 }
412}
413
414fn init_interrupt() {
415 init_percpu_irq(ax_hal::percpu::this_cpu_id());
416
417 ax_hal::asm::enable_irqs();
419
420 #[cfg(feature = "ipi")]
421 {
422 ax_hal::asm::flush_tlb(None);
423 ax_ipi::mark_current_cpu_ready();
424 }
425}
426
427pub(crate) fn init_percpu_irq(cpu_id: usize) {
428 ax_hal::irq::cpu_online(cpu_id).expect("failed to mark CPU online for IRQ framework");
429 ax_hal::irq::init_common_irq_handler();
430
431 if ax_hal::percpu::this_cpu_is_bsp() {
432 let cpus = ax_hal::irq::CpuMask::first_n(ax_hal::cpu_num());
433 ax_hal::irq::request_percpu_irq(ax_hal::time::irq_num(), cpus, timer_irq_handler)
434 .expect("failed to register timer IRQ handler");
435
436 #[cfg(any(feature = "ipi", feature = "wake-ipi"))]
437 ax_hal::irq::request_percpu_irq(ax_hal::irq::ipi_irq(), cpus, ipi_irq_handler)
438 .expect("failed to register IPI IRQ handler");
439 }
440
441 init_timer();
442}
443
444#[cfg(feature = "ipi")]
445unsafe fn ax_ipi_run_on_cpu_sync(
446 cpu: usize,
447 f: unsafe fn(*mut ()),
448 arg: *mut (),
449) -> Result<(), ax_hal::irq::IrqError> {
450 unsafe { ax_ipi::call_on_cpu(ax_hal::irq::CpuId(cpu), f, arg) }
451}
452
453fn periodic_interval_nanos() -> u64 {
454 ax_hal::time::NANOS_PER_SEC / ticks_per_sec()
455}
456
457#[ax_percpu::def_percpu]
458static NEXT_PERIODIC_DEADLINE_NANOS: u64 = 0;
459
460#[ax_percpu::def_percpu]
461static LOCAL_CLOCK_EVENT: clock_event::LocalClockEvent = clock_event::LocalClockEvent::offline();
462
463fn with_periodic_deadline<R>(
464 operation: impl for<'scope> FnOnce(&ax_percpu::CpuPin<'scope>) -> R,
465) -> R {
466 unsafe { ax_percpu::with_cpu_pin(operation) }
470 .unwrap_or_else(|error| panic!("timer CPU-local state is invalid: {error}"))
471}
472
473fn with_local_clock_event<R>(
474 operation: impl for<'exclusive> FnOnce(&ax_percpu::ExclusiveCpu<'exclusive>) -> R,
475) -> R {
476 unsafe { ax_percpu::with_cpu_pin(|pin| ax_percpu::with_exclusive_cpu(pin, operation)) }
479 .unwrap_or_else(|error| panic!("clockevent CPU-local state is invalid: {error}"))
480}
481
482fn commit_clock_event_action(action: clock_event::ClockEventAction) {
483 if let clock_event::ClockEventAction::Program(deadline) = action {
484 ax_hal::time::set_oneshot_timer(deadline);
485 }
486}
487
488fn init_timer() {
489 ax_task::init_timer_service();
490 let now_ns = ax_hal::time::monotonic_time_nanos();
491 with_periodic_deadline(|pin| {
492 NEXT_PERIODIC_DEADLINE_NANOS
493 .write_current(pin, now_ns.saturating_add(periodic_interval_nanos()));
494 });
495 let deadline = next_timer_deadline();
496 let action = with_local_clock_event(|exclusive| {
497 LOCAL_CLOCK_EVENT.with_current_mut(exclusive, |event| event.online(deadline))
498 });
499 commit_clock_event_action(action);
500 ax_hal::time::enable_timer_irq();
501}
502
503fn advance_periodic_timer(now_ns: u64) -> bool {
504 let mut deadline = with_periodic_deadline(|pin| NEXT_PERIODIC_DEADLINE_NANOS.read_current(pin));
505 if deadline == 0 {
506 with_periodic_deadline(|pin| {
507 NEXT_PERIODIC_DEADLINE_NANOS
508 .write_current(pin, now_ns.saturating_add(periodic_interval_nanos()));
509 });
510 return false;
511 }
512 if now_ns < deadline {
513 return false;
514 }
515
516 while deadline <= now_ns {
517 deadline = deadline.saturating_add(periodic_interval_nanos());
518 if deadline == u64::MAX {
519 break;
520 }
521 }
522 with_periodic_deadline(|pin| NEXT_PERIODIC_DEADLINE_NANOS.write_current(pin, deadline));
523 true
524}
525
526fn select_timer_deadline(
527 periodic_deadline_nanos: u64,
528 task_deadline_nanos: Option<u64>,
529 now_nanos: u64,
530 periodic_interval_nanos: u64,
531) -> (u64, u64) {
532 debug_assert_ne!(periodic_interval_nanos, 0);
533 let periodic_deadline_nanos = if periodic_deadline_nanos <= now_nanos {
534 let elapsed_intervals = (now_nanos - periodic_deadline_nanos) / periodic_interval_nanos;
535 periodic_deadline_nanos.saturating_add(
536 periodic_interval_nanos.saturating_mul(elapsed_intervals.saturating_add(1)),
537 )
538 } else {
539 periodic_deadline_nanos
540 };
541 let selected_deadline_nanos = task_deadline_nanos.map_or(periodic_deadline_nanos, |deadline| {
545 let deadline = if deadline <= now_nanos {
546 now_nanos.saturating_add(1)
547 } else {
548 deadline
549 };
550 core::cmp::min(periodic_deadline_nanos, deadline)
551 });
552 (periodic_deadline_nanos, selected_deadline_nanos)
553}
554
555fn next_timer_deadline() -> u64 {
556 let mut periodic_deadline =
557 with_periodic_deadline(|pin| NEXT_PERIODIC_DEADLINE_NANOS.read_current(pin));
558 if periodic_deadline == 0 {
559 let now_ns = ax_hal::time::monotonic_time_nanos();
560 periodic_deadline = now_ns.saturating_add(periodic_interval_nanos());
561 with_periodic_deadline(|pin| {
562 NEXT_PERIODIC_DEADLINE_NANOS.write_current(pin, periodic_deadline)
563 });
564 }
565 let task_deadline = ax_task::next_timer_deadline_nanos();
566 let now_nanos = ax_hal::time::monotonic_time_nanos();
567 let (next_periodic_deadline, deadline) = select_timer_deadline(
568 periodic_deadline,
569 task_deadline,
570 now_nanos,
571 periodic_interval_nanos(),
572 );
573 if next_periodic_deadline != periodic_deadline {
574 with_periodic_deadline(|pin| {
578 NEXT_PERIODIC_DEADLINE_NANOS.write_current(pin, next_periodic_deadline)
579 });
580 }
581
582 deadline
583}
584
585struct ClockEventControlImpl;
586
587#[ax_crate_interface::impl_interface]
588impl ax_task::ClockEventControl for ClockEventControlImpl {
589 fn request_local_reprogram(deadline_nanos: u64) {
590 let _guard = ax_task::sync::PreemptIrqSaveGuard::new();
591 let action = with_local_clock_event(|exclusive| {
592 LOCAL_CLOCK_EVENT
593 .with_current_mut(exclusive, |event| event.request_earlier(deadline_nanos))
594 });
595 commit_clock_event_action(action);
596 }
597}
598
599fn timer_irq_handler(ctx: ax_hal::irq::IrqContext) -> ax_hal::irq::IrqReturn {
600 let _ = ctx;
601 let token = with_local_clock_event(|exclusive| {
602 LOCAL_CLOCK_EVENT.with_current_mut(exclusive, |event| event.claim_irq())
603 });
604 unsafe { ax_hal::time::scheduler_clock_tick() }
607 .expect("current CPU scheduler clock must be online before timer IRQs");
608 let scheduler_tick = advance_periodic_timer(ax_hal::time::monotonic_time_nanos());
609 ax_task::on_timer_irq(scheduler_tick);
610 let deadline = next_timer_deadline();
611 let action = with_local_clock_event(|exclusive| {
612 LOCAL_CLOCK_EVENT.with_current_mut(exclusive, |event| match token {
613 Some(token) => event.finish_irq(token, Some(deadline)),
614 None => event.request_earlier(deadline),
615 })
616 });
617 trace!(
618 "clockevent IRQ CPU {}: token={token:?}, scheduler_tick={}, next_deadline={}, \
619 action={action:?}",
620 ax_hal::percpu::this_cpu_id(),
621 scheduler_tick,
622 deadline
623 );
624 commit_clock_event_action(action);
625 ax_hal::irq::IrqReturn::Handled
626}
627
628#[cfg(feature = "ipi")]
629fn ipi_irq_handler(_ctx: ax_hal::irq::IrqContext) -> ax_hal::irq::IrqReturn {
630 ax_ipi::claim_current_delivery();
631 #[cfg(feature = "smp")]
632 ax_task::handle_ipi_reschedule();
633 ax_ipi::drain_hard_calls()
634 .unwrap_or_else(|error| panic!("failed to continue hard-call draining: {error:?}"));
635 ax_ipi::legacy::drain_current_callbacks();
636 ax_hal::irq::IrqReturn::Handled
637}
638
639#[cfg(all(feature = "wake-ipi", not(feature = "ipi")))]
640fn ipi_irq_handler(_ctx: ax_hal::irq::IrqContext) -> ax_hal::irq::IrqReturn {
641 ax_hal::irq::IrqReturn::Handled
642}
643
644#[cfg(test)]
645mod tests {
646 #[test]
647 fn timer_programming_catches_up_after_a_slow_irq() {
648 let (periodic, selected) = super::select_timer_deadline(100, None, 150, 10);
649 assert_eq!(periodic, 160);
650 assert_eq!(selected, 160);
651 }
652
653 #[test]
654 fn timer_programming_keeps_an_earlier_task_deadline() {
655 let (periodic, selected) = super::select_timer_deadline(100, Some(155), 150, 10);
656 assert_eq!(periodic, 160);
657 assert_eq!(selected, 155);
658 }
659
660 #[test]
661 fn timer_programming_advances_an_expired_budget_limited_deadline() {
662 let (periodic, selected) = super::select_timer_deadline(100, Some(1), 150, 10);
663 assert_eq!(periodic, 160);
664 assert_eq!(selected, 151);
665 }
666
667 #[test]
668 fn fs_init_accepts_bootargs_without_fs_feature() {
669 crate::fs::init(Some("root=/dev/nvme0n1"));
670 }
671}