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