#![cfg_attr(not(test), no_std)]
#![feature(doc_auto_cfg)]
#[macro_use]
extern crate axlog;
#[cfg(all(target_os = "none", not(test)))]
mod lang_items;
mod trap;
#[cfg(feature = "smp")]
mod mp;
#[cfg(feature = "smp")]
pub use self::mp::rust_main_secondary;
const LOGO: &str = r#"
d8888 .d88888b. .d8888b.
d88888 d88P" "Y88b d88P Y88b
d88P888 888 888 Y88b.
d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b.
d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b.
d88P 888 888 888 88888888 888 888 "888
d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P
d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P"
"#;
extern "C" {
fn main();
}
struct LogIfImpl;
#[crate_interface::impl_interface]
impl axlog::LogIf for LogIfImpl {
fn console_write_str(s: &str) {
axhal::console::write_bytes(s.as_bytes());
}
fn current_time() -> core::time::Duration {
axhal::time::monotonic_time()
}
fn current_cpu_id() -> Option<usize> {
#[cfg(feature = "smp")]
if is_init_ok() {
Some(axhal::cpu::this_cpu_id())
} else {
None
}
#[cfg(not(feature = "smp"))]
Some(0)
}
fn current_task_id() -> Option<u64> {
if is_init_ok() {
#[cfg(feature = "multitask")]
{
axtask::current_may_uninit().map(|curr| curr.id().as_u64())
}
#[cfg(not(feature = "multitask"))]
None
} else {
None
}
}
}
use core::sync::atomic::{AtomicUsize, Ordering};
static INITED_CPUS: AtomicUsize = AtomicUsize::new(0);
fn is_init_ok() -> bool {
INITED_CPUS.load(Ordering::Acquire) == axconfig::SMP
}
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! {
ax_println!("{}", LOGO);
ax_println!(
"\
arch = {}\n\
platform = {}\n\
target = {}\n\
smp = {}\n\
build_mode = {}\n\
log_level = {}\n\
",
option_env!("AX_ARCH").unwrap_or(""),
option_env!("AX_PLATFORM").unwrap_or(""),
option_env!("AX_TARGET").unwrap_or(""),
option_env!("AX_SMP").unwrap_or(""),
option_env!("AX_MODE").unwrap_or(""),
option_env!("AX_LOG").unwrap_or(""),
);
#[cfg(feature = "rtc")]
ax_println!(
"Boot at {}\n",
chrono::DateTime::from_timestamp_nanos(axhal::time::wall_time_nanos() as _),
);
axlog::init();
axlog::set_max_level(option_env!("AX_LOG").unwrap_or("")); info!("Logging is enabled.");
info!("Primary CPU {} started, dtb = {:#x}.", cpu_id, dtb);
info!("Found physcial memory regions:");
for r in axhal::mem::memory_regions() {
info!(
" [{:x?}, {:x?}) {} ({:?})",
r.paddr,
r.paddr + r.size,
r.name,
r.flags
);
}
#[cfg(feature = "alloc")]
init_allocator();
#[cfg(feature = "paging")]
{
info!("Initialize kernel page table...");
remap_kernel_memory().expect("remap kernel memoy failed");
}
info!("Initialize platform devices...");
axhal::platform_init();
#[cfg(feature = "multitask")]
axtask::init_scheduler();
#[cfg(any(feature = "fs", feature = "net", feature = "display"))]
{
#[allow(unused_variables)]
let all_devices = axdriver::init_drivers();
#[cfg(feature = "fs")]
axfs::init_filesystems(all_devices.block);
#[cfg(feature = "net")]
axnet::init_network(all_devices.net);
#[cfg(feature = "display")]
axdisplay::init_display(all_devices.display);
}
#[cfg(feature = "smp")]
self::mp::start_secondary_cpus(cpu_id);
#[cfg(feature = "irq")]
{
info!("Initialize interrupt handlers...");
init_interrupt();
}
#[cfg(all(feature = "tls", not(feature = "multitask")))]
{
info!("Initialize thread local storage...");
init_tls();
}
info!("Primary CPU {} init OK.", cpu_id);
INITED_CPUS.fetch_add(1, Ordering::Relaxed);
while !is_init_ok() {
core::hint::spin_loop();
}
unsafe { main() };
#[cfg(feature = "multitask")]
axtask::exit(0);
#[cfg(not(feature = "multitask"))]
{
debug!("main task exited: exit_code={}", 0);
axhal::misc::terminate();
}
}
#[cfg(feature = "alloc")]
fn init_allocator() {
use axhal::mem::{memory_regions, phys_to_virt, MemRegionFlags};
info!("Initialize global memory allocator...");
info!(" use {} allocator.", axalloc::global_allocator().name());
let mut max_region_size = 0;
let mut max_region_paddr = 0.into();
for r in memory_regions() {
if r.flags.contains(MemRegionFlags::FREE) && r.size > max_region_size {
max_region_size = r.size;
max_region_paddr = r.paddr;
}
}
for r in memory_regions() {
if r.flags.contains(MemRegionFlags::FREE) && r.paddr == max_region_paddr {
axalloc::global_init(phys_to_virt(r.paddr).as_usize(), r.size);
break;
}
}
for r in memory_regions() {
if r.flags.contains(MemRegionFlags::FREE) && r.paddr != max_region_paddr {
axalloc::global_add_memory(phys_to_virt(r.paddr).as_usize(), r.size)
.expect("add heap memory region failed");
}
}
}
#[cfg(feature = "paging")]
fn remap_kernel_memory() -> Result<(), axhal::paging::PagingError> {
use axhal::mem::{memory_regions, phys_to_virt};
use axhal::paging::PageTable;
use lazyinit::LazyInit;
static KERNEL_PAGE_TABLE: LazyInit<PageTable> = LazyInit::new();
if axhal::cpu::this_cpu_is_bsp() {
let mut kernel_page_table = PageTable::try_new()?;
for r in memory_regions() {
kernel_page_table.map_region(
phys_to_virt(r.paddr),
r.paddr,
r.size,
r.flags.into(),
true,
)?;
}
KERNEL_PAGE_TABLE.init_once(kernel_page_table);
}
unsafe { axhal::arch::write_page_table_root(KERNEL_PAGE_TABLE.root_paddr()) };
Ok(())
}
#[cfg(feature = "irq")]
fn init_interrupt() {
use axhal::time::TIMER_IRQ_NUM;
const PERIODIC_INTERVAL_NANOS: u64 =
axhal::time::NANOS_PER_SEC / axconfig::TICKS_PER_SEC as u64;
#[percpu::def_percpu]
static NEXT_DEADLINE: u64 = 0;
fn update_timer() {
let now_ns = axhal::time::monotonic_time_nanos();
let mut deadline = unsafe { NEXT_DEADLINE.read_current_raw() };
if now_ns >= deadline {
deadline = now_ns + PERIODIC_INTERVAL_NANOS;
}
unsafe { NEXT_DEADLINE.write_current_raw(deadline + PERIODIC_INTERVAL_NANOS) };
axhal::time::set_oneshot_timer(deadline);
}
axhal::irq::register_handler(TIMER_IRQ_NUM, || {
update_timer();
#[cfg(feature = "multitask")]
axtask::on_timer_tick();
});
axhal::arch::enable_irqs();
}
#[cfg(all(feature = "tls", not(feature = "multitask")))]
fn init_tls() {
let main_tls = axhal::tls::TlsArea::alloc();
unsafe { axhal::arch::write_thread_pointer(main_tls.tls_ptr() as usize) };
core::mem::forget(main_tls);
}