use bootloader::bootinfo::{BootInfo, MemoryRegionType};
use core::sync::atomic::*;
use log::*;

pub mod board;
pub mod consts;
pub mod cpu;
pub mod driver;
pub mod gdt;
pub mod idt;
pub mod interrupt;
pub mod io;
pub mod ipi;
pub mod memory;
pub mod paging;
pub mod rand;
pub mod syscall;
pub mod timer;

static AP_CAN_INIT: AtomicBool = AtomicBool::new(false);

/// The entry point of kernel
#[no_mangle] // don't mangle the name of this function
pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! {
    let cpu_id = cpu::id();
    println!("Hello world! from CPU {}!", cpu_id);

    if cpu_id != 0 {
        while !AP_CAN_INIT.load(Ordering::Relaxed) {
            spin_loop_hint();
        }
        other_start();
    }

    // First init log mod, so that we can print log info.
    crate::logging::init();
    info!("{:#x?}", boot_info);
    assert_eq!(
        boot_info.physical_memory_offset as usize,
        consts::PHYSICAL_MEMORY_OFFSET
    );

    // Init trap handling.
    idt::init();
    // setup fast syscall in x86_64
    interrupt::fast_syscall::init();

    // Init physical memory management and heap.
    memory::init(boot_info);

    // Now heap is available

    // Init GDT
    gdt::init();
    //get local apic id of cpu
    cpu::init();
    // Use IOAPIC instead of PIC, use APIC Timer instead of PIT, init serial&keyboard in x86_64
    driver::init(boot_info);
    // init pci/bus-based devices ,e.g. Intel 10Gb NIC, ...
    crate::drivers::init();
    // init cpu scheduler and process manager, and add user shell app in process manager
    crate::process::init();

    // wake up other CPUs
    AP_CAN_INIT.store(true, Ordering::Relaxed);

    // call the first main function in kernel.
    crate::kmain();
}

/// The entry point for other processors
fn other_start() -> ! {
    // init trap handling.
    idt::init();
    // init gdt
    gdt::init();
    // init local apic
    cpu::init();
    // setup fast syscall in x86_64
    interrupt::fast_syscall::init();
    // call the first main function in kernel.
    crate::kmain();
}
