/**
 * PROJECT:         ExectOS
 * COPYRIGHT:       See COPYING.md in the top level directory
 * FILE:            xtoskrnl/mm/i686/pte.cc
 * DESCRIPTION:     Page Table Entry (PTE) for i686 support
 * DEVELOPERS:      Aiken Harris <harraiken91@gmail.com>
 */

#include <xtos.hh>


/**
 * Checks if the virtual address is valid and mapped in the page tables.
 *
 * @param VirtualAddress
 *        The virtual address to check.
 *
 * @return This routine returns TRUE if the address is valid, or FALSE otherwise.
 *
 * @since XT 1.0
 */
XTAPI
BOOLEAN
MM::Pte::AddressValid(IN PVOID VirtualAddress)
{
    /* Check if PDE and PTE are valid */
    if(!MM::Paging::PteValid(MM::Paging::GetPdeAddress(VirtualAddress)) ||
       !MM::Paging::PteValid(MM::Paging::GetPteAddress(VirtualAddress)))
    {
        /* Invalid PDE or PTE, return FALSE */
        return FALSE;
    }

    /* Address is valid, return TRUE */
    return TRUE;
}

/**
 * Retrieves the base virtual address of the system PTEs.
 *
 * @return This routine returns a pointer to the first PTE in the system PTE space.
 *
 * @since XT 1.0
 */
XTAPI
PMMPTE
MM::Pte::GetSystemPteBaseAddress(VOID)
{
    return MM::Paging::GetPteAddress(NULLPTR);
}

/**
 * Performs the initial setup of the system's page table hierarchy.
 *
 * @return This routine does not return any value.
 *
 * @since XT 1.0
 */
XTAPI
VOID
MM::Pte::InitializePageTable(VOID)
{
    PMMPTE EndSpacePte, PointerPte;
    PMMMEMORY_LAYOUT MemoryLayout;
    CPUID_REGISTERS CpuRegisters;
    MMPTE TemplatePte;

    /* Retrieve current paging mode and memory layout */
    MemoryLayout = MM::Manager::GetMemoryLayout();

    /* Get CPU features */
    CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES;
    AR::CpuFunctions::CpuId(&CpuRegisters);

    /* Check if Paging Global Extensions (PGE) is supported */
    if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PGE)
    {
        /* Enable the Global Paging (PGE) feature */
        AR::CpuFunctions::WriteControlRegister(4, AR::CpuFunctions::ReadControlRegister(4) | CR4_PGE);
    }

    /* Get the PD user-space range for both legacy and PAE paging */
    PointerPte = (PMMPTE)MM::Paging::GetPdeAddress(0);
    EndSpacePte = (PMMPTE)MM::Paging::GetPdeAddress(MemoryLayout->UserSpaceEnd);

    /* Clear all top-level entries mapping the user address space */
    while(PointerPte <= EndSpacePte)
    {
        MM::Paging::ClearPte(PointerPte);
        PointerPte = MM::Paging::GetNextPte(PointerPte);
    }

    /* Flush the TLB to invalidate all non-global entries */
    AR::CpuFunctions::FlushTlb();

    /* Create a template PTE for mapping kernel pages */
    MM::Paging::ClearPte(&TemplatePte);
    MM::Paging::SetPte(&TemplatePte, 0, MM_PTE_READWRITE | MM_PTE_CACHE_ENABLE);

    /* Map the kernel's PD entries */
    MM::Pte::MapPDE(MemoryLayout->NonPagedSystemPoolStart, (PVOID)MM_HIGHEST_SYSTEM_ADDRESS, &TemplatePte);
}

/**
 * Maps a range of virtual addresses at the PPE (Page Directory Pointer Entry) level.
 *
 * @param StartAddress
 *        The beginning of the virtual address range to map.
 *
 * @param EndAddress
 *        The end of the virtual address range to map.
 *
 * @param TemplatePpe
 *        A template PPE to use for creating new entries.
 *
 * @return This routine does not return any value.
 *
 * @since XT 1.0
 */
XTAPI
VOID
MM::Pte::MapPPE(IN PVOID StartAddress,
                IN PVOID EndAddress,
                IN PMMPPE TemplatePpe)
{
    /* Just a stub on i686 platform */
    return;
}
