//! Memory management utilities and types

use core::{
    ffi::c_void,
    mem::{align_of, size_of},
};

use libk_mm::{address::PhysicalAddress, device::DeviceMemoryMapping};

/// Offset applied to the physical kernel image when translating it into the virtual address space
pub const KERNEL_VIRT_OFFSET: usize = kernel_arch::KERNEL_VIRT_OFFSET;

/// Reads a value from an arbitrary physical address.
///
/// # Safety
///
/// The caller must ensure the correct origin of the address, its alignment and that the access is
/// properly synchronized.
pub unsafe fn read_memory<T>(address: PhysicalAddress) -> T {
    let io = DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap();
    let address = io.address();

    if address % align_of::<T>() == 0 {
        (address as *const T).read_volatile()
    } else {
        (address as *const T).read_unaligned()
    }
}

/// Writes a value to an arbitrary physical address.
///
/// # Safety
///
/// The caller must ensure the correct origin of the address, its alignment and that the access is
/// properly synchronized.
pub unsafe fn write_memory<T>(address: PhysicalAddress, value: T) {
    let io = DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap();
    let address = io.address();

    if address % align_of::<T>() == 0 {
        (address as *mut T).write_volatile(value)
    } else {
        (address as *mut T).write_unaligned(value)
    }
}

#[no_mangle]
unsafe extern "C" fn memcpy(p0: *mut c_void, p1: *const c_void, len: usize) -> *mut c_void {
    compiler_builtins::mem::memcpy(p0 as _, p1 as _, len) as _
}

#[no_mangle]
unsafe extern "C" fn memcmp(p0: *const c_void, p1: *const c_void, len: usize) -> i32 {
    compiler_builtins::mem::memcmp(p0 as _, p1 as _, len)
}

#[no_mangle]
unsafe extern "C" fn memmove(dst: *mut c_void, src: *const c_void, len: usize) -> *mut c_void {
    compiler_builtins::mem::memmove(dst as _, src as _, len) as _
}

#[no_mangle]
unsafe extern "C" fn memset(dst: *mut c_void, val: i32, len: usize) -> *mut c_void {
    compiler_builtins::mem::memset(dst as _, val, len) as _
}
