373 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			373 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 * PROJECT:         ExectOS
 | 
						|
 * COPYRIGHT:       See COPYING.md in the top level directory
 | 
						|
 * FILE:            xtldr/arch/amd64/memory.cc
 | 
						|
 * DESCRIPTION:     XT Boot Loader AMD64 specific memory management
 | 
						|
 * DEVELOPERS:      Rafal Kupiec <belliash@codingworkshop.eu.org>
 | 
						|
 *                  Aiken Harris <harraiken91@gmail.com>
 | 
						|
 */
 | 
						|
 | 
						|
#include <xtldr.hh>
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Maps boot loader related code and builds page map.
 | 
						|
 *
 | 
						|
 * @param PageMap
 | 
						|
 *        Supplies a pointer to the page mapping structure.
 | 
						|
 *
 | 
						|
 * @param SelfMapAddress
 | 
						|
 *        Supplies a virtual address of the page tables.
 | 
						|
 *
 | 
						|
 * @return This routine returns a status code.
 | 
						|
 *
 | 
						|
 * @since XT 1.0
 | 
						|
 */
 | 
						|
XTCDECL
 | 
						|
EFI_STATUS
 | 
						|
Memory::BuildPageMap(IN PXTBL_PAGE_MAPPING PageMap,
 | 
						|
                     IN ULONG_PTR SelfMapAddress)
 | 
						|
{
 | 
						|
    PLIST_ENTRY ListEntry, ModulesList, ModulesListEntry;
 | 
						|
    PXTBL_MEMORY_MAPPING Mapping;
 | 
						|
    PXTBL_MODULE_INFO ModuleInfo;
 | 
						|
    EFI_PHYSICAL_ADDRESS Address;
 | 
						|
    PVOID LoaderBase;
 | 
						|
    ULONGLONG LoaderSize;
 | 
						|
    EFI_STATUS Status;
 | 
						|
 | 
						|
    /* Allocate pages for the Page Map */
 | 
						|
    Status = AllocatePages(AllocateAnyPages, 1, &Address);
 | 
						|
    if(Status != STATUS_EFI_SUCCESS)
 | 
						|
    {
 | 
						|
        /* Memory allocation failure */
 | 
						|
        return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Assign and zero-fill memory used by page mappings */
 | 
						|
    PageMap->PtePointer = (PVOID)(UINT_PTR)Address;
 | 
						|
    RTL::Memory::ZeroMemory(PageMap->PtePointer, EFI_PAGE_SIZE);
 | 
						|
 | 
						|
    /* Add page mapping itself to memory mapping */
 | 
						|
    Status = Memory::SelfMapPml(PageMap, SelfMapAddress);
 | 
						|
    if(Status != STATUS_EFI_SUCCESS)
 | 
						|
    {
 | 
						|
        /* PML mapping failed */
 | 
						|
        return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Map the trampoline code area */
 | 
						|
    Status = MapVirtualMemory(PageMap, (PVOID)MM_TRAMPOLINE_ADDRESS,(PVOID)MM_TRAMPOLINE_ADDRESS,
 | 
						|
                              1, LoaderFirmwareTemporary);
 | 
						|
    if(Status != STATUS_EFI_SUCCESS)
 | 
						|
    {
 | 
						|
        /* Mapping trampoline code failed */
 | 
						|
        return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Get list of XTLDR modules */
 | 
						|
    ModulesList = Protocol::GetModulesList();
 | 
						|
    ModulesListEntry = ModulesList->Flink;
 | 
						|
    while(ModulesListEntry != ModulesList)
 | 
						|
    {
 | 
						|
        /* Get module info */
 | 
						|
        ModuleInfo = CONTAIN_RECORD(ModulesListEntry, XTBL_MODULE_INFO, Flink);
 | 
						|
 | 
						|
        /* Map module code */
 | 
						|
        Status = MapVirtualMemory(PageMap, ModuleInfo->ModuleBase, ModuleInfo->ModuleBase,
 | 
						|
                                  EFI_SIZE_TO_PAGES(ModuleInfo->ModuleSize), LoaderFirmwareTemporary);
 | 
						|
 | 
						|
        /* Check if mapping succeeded */
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Mapping module code failed */
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Get next module */
 | 
						|
        ModulesListEntry = ModulesListEntry->Flink;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Get boot loader image information */
 | 
						|
    XtLoader::GetLoaderImageInformation(&LoaderBase, &LoaderSize);
 | 
						|
 | 
						|
    /* Make sure boot loader image base and size are set */
 | 
						|
    if(LoaderBase && LoaderSize)
 | 
						|
    {
 | 
						|
        /* Map boot loader code as well */
 | 
						|
        Status = MapVirtualMemory(PageMap, LoaderBase, LoaderBase,
 | 
						|
                                  EFI_SIZE_TO_PAGES(LoaderSize), LoaderFirmwareTemporary);
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Mapping boot loader code failed */
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        /* Boot loader image information re not available */
 | 
						|
        return STATUS_EFI_PROTOCOL_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Iterate through and map all the mappings*/
 | 
						|
    Debug::Print(L"Mapping and dumping EFI memory:\n");
 | 
						|
    ListEntry = PageMap->MemoryMap.Flink;
 | 
						|
    while(ListEntry != &PageMap->MemoryMap)
 | 
						|
    {
 | 
						|
        /* Take mapping from the list */
 | 
						|
        Mapping = CONTAIN_RECORD(ListEntry, XTBL_MEMORY_MAPPING, ListEntry);
 | 
						|
 | 
						|
        /* Check if virtual address is set */
 | 
						|
        if(Mapping->VirtualAddress)
 | 
						|
        {
 | 
						|
            /* Dump memory mapping */
 | 
						|
            Debug::Print(L"   Type=%02lu, PhysicalBase=%.16P, VirtualBase=%.16P, Pages=%llu\n", Mapping->MemoryType,
 | 
						|
                         Mapping->PhysicalAddress, Mapping->VirtualAddress, Mapping->NumberOfPages);
 | 
						|
 | 
						|
            /* Map memory */
 | 
						|
            Status = MapPage(PageMap, (UINT_PTR)Mapping->VirtualAddress,
 | 
						|
                             (UINT_PTR)Mapping->PhysicalAddress, Mapping->NumberOfPages);
 | 
						|
            if(Status != STATUS_EFI_SUCCESS)
 | 
						|
            {
 | 
						|
                /* Memory mapping failed */
 | 
						|
                return Status;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /* Take next element */
 | 
						|
        ListEntry = ListEntry->Flink;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Return success */
 | 
						|
    return STATUS_EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Returns next level of the Page Table.
 | 
						|
 *
 | 
						|
 * @param PageMap
 | 
						|
 *        Supplies a pointer to the page mapping structure.
 | 
						|
 *
 | 
						|
 * @param PageTable
 | 
						|
 *        Supplies a pointer to the current Page Table.
 | 
						|
 *
 | 
						|
 * @param Entry
 | 
						|
 *        Supplies an index of the current Page Table entry.
 | 
						|
 *
 | 
						|
 * @param NextPageTable
 | 
						|
 *        Supplies a pointer to the memory area where the next Page Table level is returned.
 | 
						|
 *
 | 
						|
 * @return This routine returns a status code.
 | 
						|
 *
 | 
						|
 * @since XT 1.0
 | 
						|
 */
 | 
						|
XTCDECL
 | 
						|
EFI_STATUS
 | 
						|
Memory::GetNextPageTable(IN PXTBL_PAGE_MAPPING PageMap,
 | 
						|
                         IN PVOID PageTable,
 | 
						|
                         IN SIZE_T Entry,
 | 
						|
                         OUT PVOID *NextPageTable)
 | 
						|
{
 | 
						|
    EFI_PHYSICAL_ADDRESS Address;
 | 
						|
    ULONGLONG PmlPointer = 0;
 | 
						|
    PHARDWARE_PTE PmlTable;
 | 
						|
    EFI_STATUS Status;
 | 
						|
 | 
						|
    PmlTable = (PHARDWARE_PTE)PageTable;
 | 
						|
 | 
						|
    /* Check if this is a valid table */
 | 
						|
    if(PmlTable[Entry].Valid)
 | 
						|
    {
 | 
						|
        /* Get PML pointer */
 | 
						|
        PmlPointer = PmlTable[Entry].PageFrameNumber;
 | 
						|
        PmlPointer <<= EFI_PAGE_SHIFT;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        /* Allocate pages for new PML entry */
 | 
						|
        Status = AllocatePages(AllocateAnyPages, 1, &Address);
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Memory allocation failure */
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Add new memory mapping */
 | 
						|
        Status = MapVirtualMemory(PageMap, NULLPTR, (PVOID)(UINT_PTR)Address, 1, LoaderMemoryData);
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Memory mapping failure */
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Fill allocated memory with zeros */
 | 
						|
        RTL::Memory::ZeroMemory((PVOID)(ULONGLONG)Address, EFI_PAGE_SIZE);
 | 
						|
 | 
						|
        /* Set paging entry settings */
 | 
						|
        PmlTable[Entry].PageFrameNumber = Address / EFI_PAGE_SIZE;
 | 
						|
        PmlTable[Entry].Valid = 1;
 | 
						|
        PmlTable[Entry].Writable = 1;
 | 
						|
        PmlPointer = (ULONGLONG)Address;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Set next Page Map Level (PML) */
 | 
						|
    *NextPageTable = (PVOID)(ULONGLONG)PmlPointer;
 | 
						|
 | 
						|
    /* Return success */
 | 
						|
    return STATUS_EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Does the actual virtual memory mapping.
 | 
						|
 *
 | 
						|
 * @param PageMap
 | 
						|
 *        Supplies a pointer to the page mapping structure.
 | 
						|
 *
 | 
						|
 * @param VirtualAddress
 | 
						|
 *        Supplies a virtual address of the mapping.
 | 
						|
 *
 | 
						|
 * @param PhysicalAddress
 | 
						|
 *        Supplies a physical address of the mapping.
 | 
						|
 *
 | 
						|
 * @param NumberOfPages
 | 
						|
 *        Supplies a number of the pages of the mapping.
 | 
						|
 *
 | 
						|
 * @return This routine returns a status code.
 | 
						|
 *
 | 
						|
 * @since XT 1.0
 | 
						|
 */
 | 
						|
XTCDECL
 | 
						|
EFI_STATUS
 | 
						|
Memory::MapPage(IN PXTBL_PAGE_MAPPING PageMap,
 | 
						|
                IN ULONG_PTR VirtualAddress,
 | 
						|
                IN ULONG_PTR PhysicalAddress,
 | 
						|
                IN ULONG NumberOfPages)
 | 
						|
{
 | 
						|
    PVOID Pml1, Pml2, Pml3, Pml4, Pml5;
 | 
						|
    SIZE_T Pml1Entry, Pml2Entry, Pml3Entry, Pml4Entry, Pml5Entry;
 | 
						|
    PHARDWARE_PTE PmlTable;
 | 
						|
    SIZE_T PageFrameNumber;
 | 
						|
    EFI_STATUS Status;
 | 
						|
 | 
						|
    /* Set the Page Frame Number (PFN) */
 | 
						|
    PageFrameNumber = PhysicalAddress >> EFI_PAGE_SHIFT;
 | 
						|
 | 
						|
    /* Do the recursive mapping */
 | 
						|
    while(NumberOfPages > 0)
 | 
						|
    {
 | 
						|
        /* Calculate the indices in the various Page Tables from the virtual address */
 | 
						|
        Pml5Entry = (VirtualAddress & ((ULONGLONG)0x1FF << MM_P5I_SHIFT)) >> MM_P5I_SHIFT;
 | 
						|
        Pml4Entry = (VirtualAddress & ((ULONGLONG)0x1FF << MM_PXI_SHIFT)) >> MM_PXI_SHIFT;
 | 
						|
        Pml3Entry = (VirtualAddress & ((ULONGLONG)0x1FF << MM_PPI_SHIFT)) >> MM_PPI_SHIFT;
 | 
						|
        Pml2Entry = (VirtualAddress & ((ULONGLONG)0x1FF << MM_PDI_SHIFT)) >> MM_PDI_SHIFT;
 | 
						|
        Pml1Entry = (VirtualAddress & ((ULONGLONG)0x1FF << MM_PTI_SHIFT)) >> MM_PTI_SHIFT;
 | 
						|
 | 
						|
        /* Check page map level */
 | 
						|
        if(PageMap->PageMapLevel == 5)
 | 
						|
        {
 | 
						|
            /* Five level Page Map */
 | 
						|
            Pml5 = PageMap->PtePointer;
 | 
						|
 | 
						|
            /* Get PML4 */
 | 
						|
            Status = GetNextPageTable(PageMap, Pml5, Pml5Entry, &Pml4);
 | 
						|
            if(Status != STATUS_EFI_SUCCESS)
 | 
						|
            {
 | 
						|
                /* Memory mapping failure */
 | 
						|
                return Status;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            /* Four level Page Map */
 | 
						|
            Pml4 = PageMap->PtePointer;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Get PML3 */
 | 
						|
        Status = GetNextPageTable(PageMap, Pml4, Pml4Entry, &Pml3);
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Memory mapping failure */
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Get PML 2 */
 | 
						|
        Status = GetNextPageTable(PageMap, Pml3, Pml3Entry, &Pml2);
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Memory mapping failure */
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Get PML1 */
 | 
						|
        Status = GetNextPageTable(PageMap, Pml2, Pml2Entry, &Pml1);
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Memory mapping failure */
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Set paging entry settings */
 | 
						|
        PmlTable = (PHARDWARE_PTE)Pml1;
 | 
						|
        RTL::Memory::ZeroMemory(&PmlTable[Pml1Entry], sizeof(HARDWARE_PTE));
 | 
						|
        PmlTable[Pml1Entry].PageFrameNumber = PageFrameNumber;
 | 
						|
        PmlTable[Pml1Entry].Valid = 1;
 | 
						|
        PmlTable[Pml1Entry].Writable = 1;
 | 
						|
 | 
						|
        /* Take next virtual address and PFN */
 | 
						|
        VirtualAddress += EFI_PAGE_SIZE;
 | 
						|
        PageFrameNumber++;
 | 
						|
 | 
						|
        /* Decrease number of pages left */
 | 
						|
        NumberOfPages--;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Return success */
 | 
						|
    return STATUS_EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates a recursive self mapping for all PML levels.
 | 
						|
 *
 | 
						|
 * @param PageMap
 | 
						|
 *        Supplies a pointer to the page mapping structure.
 | 
						|
 *
 | 
						|
 * @param SelfMapAddress
 | 
						|
 *        Supplies a virtual address of the page tables.
 | 
						|
 *
 | 
						|
 * @return This routine returns a status code.
 | 
						|
 *
 | 
						|
 * @since XT 1.0
 | 
						|
 */
 | 
						|
XTCDECL
 | 
						|
EFI_STATUS
 | 
						|
Memory::SelfMapPml(IN PXTBL_PAGE_MAPPING PageMap,
 | 
						|
                   IN ULONG_PTR SelfMapAddress)
 | 
						|
{
 | 
						|
    PHARDWARE_PTE PmlBase;
 | 
						|
    ULONGLONG PmlIndex;
 | 
						|
 | 
						|
    /* Initialize PML base pointer */
 | 
						|
    PmlBase = (PHARDWARE_PTE)PageMap->PtePointer;
 | 
						|
 | 
						|
    /* Check page map level */
 | 
						|
    if(PageMap->PageMapLevel == 5)
 | 
						|
    {
 | 
						|
        /* Calculate PML index based on provided self map address for PML5 */
 | 
						|
        PmlIndex = (SelfMapAddress >> MM_P5I_SHIFT) & 0x1FF;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        /* Calculate PML index based on provided self map address for PML4 */
 | 
						|
        PmlIndex = (SelfMapAddress >> MM_PXI_SHIFT) & 0x1FF;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Add self-mapping */
 | 
						|
    RTL::Memory::ZeroMemory(&PmlBase[PmlIndex], sizeof(HARDWARE_PTE));
 | 
						|
    PmlBase[PmlIndex].PageFrameNumber = (UINT_PTR)PageMap->PtePointer / EFI_PAGE_SIZE;
 | 
						|
    PmlBase[PmlIndex].Valid = 1;
 | 
						|
    PmlBase[PmlIndex].Writable = 1;
 | 
						|
 | 
						|
    /* Return success */
 | 
						|
    return STATUS_EFI_SUCCESS;
 | 
						|
}
 |