//! Sets up a memory map using Limine.

use limine::{
    request::{HhdmRequest, KernelAddressRequest, MemoryMapRequest},
    response::MemoryMapResponse,
};
use spin::Lazy;

#[used]
#[unsafe(link_section = ".requests")]
static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new();

#[used]
#[unsafe(link_section = ".requests")]
static HIGHER_HALF_DIRECT_MAP_REQUEST: HhdmRequest = HhdmRequest::new();

#[used]
#[unsafe(link_section = ".requests")]
pub static KERNEL_ADDRESS_REQUEST: KernelAddressRequest =
    KernelAddressRequest::new();

/// ```rs
/// let virt_addr = phys_addr + offset;
/// let phys_addr = virt_addr - offset; // (given VA is in the HHDM). Do not use for executable code.
/// ```
pub static PHYSICAL_MEMORY_OFFSET: Lazy<u64> = Lazy::new(|| {
    HIGHER_HALF_DIRECT_MAP_REQUEST
        .get_response()
        .unwrap()
        .offset()
});

/// Converts virtual addresses in the kernel to a physical address like this:
/// ```rs
/// let phys_addr = virt_addr - virtual_base + physical_base;
/// ```
///
/// Returns (virtual_base, physical_base)
pub static _KERNEL_PHYSICAL_MEMORY_OFFSET: Lazy<(u64, u64)> = Lazy::new(|| {
    let resp = KERNEL_ADDRESS_REQUEST.get_response().unwrap();

    // These are base addresses, using Limine's built in page table.
    (resp.virtual_base(), resp.physical_base())
});

/// Fetches the memory map from Limine.
///
/// # Panics
///
/// Panics if the memory map was not found in MEMORY_MAP_REQUEST.
pub fn get_memory_map() -> &'static MemoryMapResponse {
    MEMORY_MAP_REQUEST.get_response().map_or_else(
        || unreachable!("Could not fetch memory map from Limine."),
        |memory_map| memory_map,
    )
}
