use core::ptr::NonNull;

use ax_errno::AxError;
use ax_plat::mem::phys_to_virt;
use somehal::{KernelOp, setup::*};

#[somehal::entry(Kernel)]
fn main() -> ! {
    let mut args = 0;
    if let Some(fdt) = somehal::fdt_addr_phys() {
        args = fdt;
    }

    ax_plat::call_main(0, args)
}

#[somehal::secondary_entry]
fn secondary_main() {
    #[cfg(feature = "smp")]
    ax_plat::call_secondary_main(meta.cpu_idx);
}

pub struct Kernel;

impl KernelOp for Kernel {}

impl MmioOp for Kernel {
    fn ioremap(&self, addr: MmioAddr, size: usize) -> Result<MmioRaw, MapError> {
        let virt = match axklib::mem::iomap(addr.as_usize().into(), size) {
            Ok(v) => v,
            Err(AxError::AlreadyExists) => {
                // If the region is already mapped, just return the existing mapping.
                phys_to_virt(addr.as_usize().into())
            }
            Err(e) => {
                error!("Failed to map MMIO region at {addr:?} with size {size:#x}: {e:?}");
                return Err(MapError::Invalid);
            }
        };
        Ok(unsafe { MmioRaw::new(addr, NonNull::new(virt.as_mut_ptr()).unwrap(), size) })
    }

    fn iounmap(&self, _mmio: &MmioRaw) {}
}
