299 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 * PROJECT:         ExectOS
 | 
						|
 * COPYRIGHT:       See COPYING.md in the top level directory
 | 
						|
 * FILE:            xtldr/amd64/memory.cc
 | 
						|
 * DESCRIPTION:     EFI memory management for AMD64 target
 | 
						|
 * DEVELOPERS:      Rafal Kupiec <belliash@codingworkshop.eu.org>
 | 
						|
 *                  Aiken Harris <harraiken91@gmail.com>
 | 
						|
 */
 | 
						|
 | 
						|
#include <xtos.hh>
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Determines the appropriate paging level (PML) for the AMD64 architecture.
 | 
						|
 *
 | 
						|
 * @param Parameters
 | 
						|
 *        A pointer to the wide character string containing the kernel boot parameters.
 | 
						|
 *
 | 
						|
 * @return This routine returns the appropriate page map level (5 if LA57 is enabled, 4 otherwise).
 | 
						|
 *
 | 
						|
 * @since XT 1.0
 | 
						|
 */
 | 
						|
XTCDECL
 | 
						|
ULONG
 | 
						|
Xtos::DeterminePagingLevel(IN CONST PWCHAR Parameters)
 | 
						|
{
 | 
						|
    CPUID_REGISTERS CpuRegisters;
 | 
						|
 | 
						|
    /* Prepare CPUID registers to query for STD7 features */
 | 
						|
    XtLdrProtocol->Memory.ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS));
 | 
						|
    CpuRegisters.Leaf = CPUID_GET_VENDOR_STRING;
 | 
						|
 | 
						|
    /* Query CPUID */
 | 
						|
    XtLdrProtocol->Cpu.CpuId(&CpuRegisters);
 | 
						|
 | 
						|
    /* Verify if the CPU supports the STD7 feature leaf (0x00000007) */
 | 
						|
    if(CpuRegisters.Eax >= CPUID_GET_STANDARD7_FEATURES)
 | 
						|
    {
 | 
						|
        /* Prepare CPUID registers to query for LA57 support */
 | 
						|
        XtLdrProtocol->Memory.ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS));
 | 
						|
        CpuRegisters.Leaf = CPUID_GET_STANDARD7_FEATURES;
 | 
						|
 | 
						|
        /* Query CPUID */
 | 
						|
        XtLdrProtocol->Cpu.CpuId(&CpuRegisters);
 | 
						|
 | 
						|
        /* Check if eXtended Physical Addressing (XPA) is enabled and if LA57 is supported by the CPU */
 | 
						|
        if((CpuRegisters.Ecx & CPUID_FEATURES_ECX_LA57) &&
 | 
						|
           !(XtLdrProtocol->BootUtils.GetBooleanParameter(Parameters, L"NOXPA")))
 | 
						|
        {
 | 
						|
            /* Enable LA57 (PML5) */
 | 
						|
            return 5;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* Disable LA57 and use PML4 by default */
 | 
						|
    return 4;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Builds the actual memory mapping page table and enables paging. This routine exits EFI boot services as well.
 | 
						|
 *
 | 
						|
 * @param PageMap
 | 
						|
 *        Supplies a pointer to the page mapping structure.
 | 
						|
 *
 | 
						|
 * @return This routine returns a status code.
 | 
						|
 *
 | 
						|
 * @since XT 1.0
 | 
						|
 */
 | 
						|
XTCDECL
 | 
						|
EFI_STATUS
 | 
						|
Xtos::EnablePaging(IN PXTBL_PAGE_MAPPING PageMap)
 | 
						|
{
 | 
						|
    EFI_STATUS Status;
 | 
						|
    EFI_PHYSICAL_ADDRESS TrampolineAddress;
 | 
						|
    PXT_TRAMPOLINE_ENTRY TrampolineEntry;
 | 
						|
    ULONG_PTR TrampolineSize;
 | 
						|
    PVOID TrampolineCode;
 | 
						|
 | 
						|
    /* Build page map */
 | 
						|
    Status = XtLdrProtocol->Memory.BuildPageMap(PageMap, (PageMap->PageMapLevel > 4) ? MM_P5E_LA57_BASE : MM_PXE_BASE);
 | 
						|
    if(Status != STATUS_EFI_SUCCESS)
 | 
						|
    {
 | 
						|
        /* Failed to build page map */
 | 
						|
        XtLdrProtocol->Debug.Print(L"Failed to build page map (Status code: %zX)\n", Status);
 | 
						|
        return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Map memory for hardware layer */
 | 
						|
    Status = MapHardwareMemoryPool(PageMap);
 | 
						|
    if(Status != STATUS_EFI_SUCCESS)
 | 
						|
    {
 | 
						|
        /* Failed to map memory for hardware layer */
 | 
						|
        XtLdrProtocol->Debug.Print(L"Failed to map memory for hardware leyer (Status code: %zX)\n", Status);
 | 
						|
        return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Check the configured page map level to set the LA57 state accordingly */
 | 
						|
    if(PageMap->PageMapLevel == 5)
 | 
						|
    {
 | 
						|
        /* Get the trampoline code information */
 | 
						|
        XtLdrProtocol->BootUtils.GetTrampolineInformation(TrampolineEnableXpa, &TrampolineCode, &TrampolineSize);
 | 
						|
        if(TrampolineCode == NULLPTR || TrampolineSize == 0)
 | 
						|
        {
 | 
						|
            /* Failed to get trampoline information */
 | 
						|
            XtLdrProtocol->Debug.Print(L"Failed to get trampoline information\n");
 | 
						|
            return STATUS_EFI_INVALID_PARAMETER;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Set the address of the trampoline code below 1MB */
 | 
						|
        TrampolineAddress = MM_TRAMPOLINE_ADDRESS;
 | 
						|
 | 
						|
        /* Allocate pages for the trampoline */
 | 
						|
        Status = XtLdrProtocol->Memory.AllocatePages(AllocateAddress, EFI_SIZE_TO_PAGES(TrampolineSize), &TrampolineAddress);
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Failed to allocate memory for trampoline code */
 | 
						|
            XtLdrProtocol->Debug.Print(L"Failed to allocate memory for trampoline code (Status code: %zX)\n", Status);
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Set the trampoline entry point and copy its code into the allocated buffer */
 | 
						|
        TrampolineEntry = (PXT_TRAMPOLINE_ENTRY)(UINT_PTR)TrampolineAddress;
 | 
						|
        XtLdrProtocol->Memory.CopyMemory((PVOID)TrampolineEntry, TrampolineCode, TrampolineSize);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Exit EFI Boot Services */
 | 
						|
    XtLdrProtocol->Debug.Print(L"Exiting EFI boot services\n");
 | 
						|
    Status = XtLdrProtocol->Utils.ExitBootServices();
 | 
						|
    if(Status != STATUS_EFI_SUCCESS)
 | 
						|
    {
 | 
						|
        /* Failed to exit boot services */
 | 
						|
        XtLdrProtocol->Debug.Print(L"Failed to exit boot services (Status code: %zX)\n", Status);
 | 
						|
        return STATUS_EFI_ABORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Check the configured page map level to set the LA57 state accordingly */
 | 
						|
    if(PageMap->PageMapLevel == 5)
 | 
						|
    {
 | 
						|
        /* Enable Linear Address 57-bit (LA57) extension */
 | 
						|
        XtLdrProtocol->Debug.Print(L"Enabling Linear Address 57-bit (LA57)\n");
 | 
						|
 | 
						|
        /* Execute the trampoline to enable LA57 and write PML5 to CR3 */
 | 
						|
        TrampolineEntry((UINT64)PageMap->PtePointer);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        /* Disable Linear Address 57-bit (LA57) extension */
 | 
						|
        XtLdrProtocol->Debug.Print(L"Disabling Linear Address 57-bit (LA57)\n");
 | 
						|
 | 
						|
        /* Write PML4 to CR3 and enable paging */
 | 
						|
        XtLdrProtocol->Cpu.WriteControlRegister(3, (UINT_PTR)PageMap->PtePointer);
 | 
						|
        XtLdrProtocol->Cpu.WriteControlRegister(0, XtLdrProtocol->Cpu.ReadControlRegister(0) | CR0_PG);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Return success */
 | 
						|
    return STATUS_EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Maps the page table for hardware layer addess space.
 | 
						|
 *
 | 
						|
 * @param PageMap
 | 
						|
 *        Supplies a pointer to the page mapping structure.
 | 
						|
 *
 | 
						|
 * @return This routine returns a status code.
 | 
						|
 *
 | 
						|
 * @since XT 1.0
 | 
						|
 */
 | 
						|
XTCDECL
 | 
						|
EFI_STATUS
 | 
						|
Xtos::MapHardwareMemoryPool(IN PXTBL_PAGE_MAPPING PageMap)
 | 
						|
{
 | 
						|
    PHARDWARE_PTE P5eBase, PdeBase, PpeBase, PxeBase;
 | 
						|
    EFI_PHYSICAL_ADDRESS Address;
 | 
						|
    EFI_STATUS Status;
 | 
						|
 | 
						|
    if(PageMap->PageMapLevel == 5)
 | 
						|
    {
 | 
						|
        /* Get P5E (PML5) base address */
 | 
						|
        P5eBase = (PHARDWARE_PTE)PageMap->PtePointer;
 | 
						|
 | 
						|
        /* Check if P5E entry already exists */
 | 
						|
        if(!P5eBase[(MM_HARDWARE_VA_START >> MM_P5I_SHIFT) & 0x1FF].Valid)
 | 
						|
        {
 | 
						|
            /* No valid P5E, allocate memory */
 | 
						|
            Status = XtLdrProtocol->Memory.AllocatePages(AllocateAnyPages, 1, &Address);
 | 
						|
            if(Status != STATUS_EFI_SUCCESS)
 | 
						|
            {
 | 
						|
                /* Memory allocation failure, return error */
 | 
						|
                return Status;
 | 
						|
            }
 | 
						|
 | 
						|
            /* Zero fill memory used by P5E */
 | 
						|
            XtLdrProtocol->Memory.ZeroMemory((PVOID)Address, EFI_PAGE_SIZE);
 | 
						|
 | 
						|
            /* Make P5E valid */
 | 
						|
            P5eBase[(MM_HARDWARE_VA_START >> MM_P5I_SHIFT) & 0x1FF].Valid = 1;
 | 
						|
            P5eBase[(MM_HARDWARE_VA_START >> MM_P5I_SHIFT) & 0x1FF].PageFrameNumber = Address / EFI_PAGE_SIZE;
 | 
						|
            P5eBase[(MM_HARDWARE_VA_START >> MM_P5I_SHIFT) & 0x1FF].Writable = 1;
 | 
						|
 | 
						|
            /* Set PXE base address */
 | 
						|
            PxeBase = (PHARDWARE_PTE)(UINT_PTR)Address;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            /* Set PXE base address based on existing P5E */
 | 
						|
            PxeBase = (PHARDWARE_PTE)((P5eBase[(MM_HARDWARE_VA_START >> MM_P5I_SHIFT) & 0x1FF].PageFrameNumber) << EFI_PAGE_SHIFT);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        /* Get PXE (PML4) base address */
 | 
						|
        PxeBase = (PHARDWARE_PTE)PageMap->PtePointer;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Check if PXE entry already exists */
 | 
						|
    if(!PxeBase[(MM_HARDWARE_VA_START >> MM_PXI_SHIFT) & 0x1FF].Valid)
 | 
						|
    {
 | 
						|
        /* No valid PXE, allocate memory */
 | 
						|
        Status = XtLdrProtocol->Memory.AllocatePages(AllocateAnyPages, 1, &Address);
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Memory allocation failure, return error */
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Zero fill memory used by PXE */
 | 
						|
        XtLdrProtocol->Memory.ZeroMemory((PVOID)Address, EFI_PAGE_SIZE);
 | 
						|
 | 
						|
        /* Make PXE valid */
 | 
						|
        PxeBase[(MM_HARDWARE_VA_START >> MM_PXI_SHIFT) & 0x1FF].Valid = 1;
 | 
						|
        PxeBase[(MM_HARDWARE_VA_START >> MM_PXI_SHIFT) & 0x1FF].PageFrameNumber = Address / EFI_PAGE_SIZE;
 | 
						|
        PxeBase[(MM_HARDWARE_VA_START >> MM_PXI_SHIFT) & 0x1FF].Writable = 1;
 | 
						|
 | 
						|
        /* Set PPE base address */
 | 
						|
        PpeBase = (PHARDWARE_PTE)(UINT_PTR)Address;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        /* Set PPE base address based on existing PXE */
 | 
						|
        PpeBase = (PHARDWARE_PTE)((PxeBase[(MM_HARDWARE_VA_START >> MM_PXI_SHIFT) & 0x1FF].PageFrameNumber) << EFI_PAGE_SHIFT);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Check if PPE entry already exists */
 | 
						|
    if(!PpeBase[(MM_HARDWARE_VA_START >> MM_PPI_SHIFT) & 0x1FF].Valid)
 | 
						|
    {
 | 
						|
        /* No valid PPE, allocate memory */
 | 
						|
        Status = XtLdrProtocol->Memory.AllocatePages(AllocateAnyPages, 1, &Address);
 | 
						|
        if(Status != STATUS_EFI_SUCCESS)
 | 
						|
        {
 | 
						|
            /* Memory allocation failure, return error */
 | 
						|
            return Status;
 | 
						|
        }
 | 
						|
 | 
						|
        /* Zero fill memory used by PPE */
 | 
						|
        XtLdrProtocol->Memory.ZeroMemory((PVOID)Address, EFI_PAGE_SIZE);
 | 
						|
 | 
						|
        /* Make PPE valid */
 | 
						|
        PpeBase[(MM_HARDWARE_VA_START >> MM_PPI_SHIFT) & 0x1FF].Valid = 1;
 | 
						|
        PpeBase[(MM_HARDWARE_VA_START >> MM_PPI_SHIFT) & 0x1FF].PageFrameNumber = Address / EFI_PAGE_SIZE;
 | 
						|
        PpeBase[(MM_HARDWARE_VA_START >> MM_PPI_SHIFT) & 0x1FF].Writable = 1;
 | 
						|
 | 
						|
        /* Set PDE base address */
 | 
						|
        PdeBase = (PHARDWARE_PTE)Address;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        /* Set PDE base address, based on existing PPE */
 | 
						|
        PdeBase = (PHARDWARE_PTE)((PpeBase[(MM_HARDWARE_VA_START >> MM_PPI_SHIFT) & 0x1FF].PageFrameNumber) << EFI_PAGE_SHIFT);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Loop through 2 PDE entries */
 | 
						|
    for(UINT Index = 0 ; Index < 2 ; Index++)
 | 
						|
    {
 | 
						|
        /* Check if PDE entry already exists */
 | 
						|
        if(!PdeBase[((MM_HARDWARE_VA_START >> MM_PDI_SHIFT) & 0x1FF) + Index].Valid)
 | 
						|
        {
 | 
						|
            /* No valid PDE, allocate memory */
 | 
						|
            Status = XtLdrProtocol->Memory.AllocatePages(AllocateAnyPages, 1, &Address);
 | 
						|
            if(Status != STATUS_EFI_SUCCESS)
 | 
						|
            {
 | 
						|
                /* Memory allocation failure, return error */
 | 
						|
                return Status;
 | 
						|
            }
 | 
						|
 | 
						|
            /* Zero fill memory used by PDE */
 | 
						|
            XtLdrProtocol->Memory.ZeroMemory((PVOID)Address, EFI_PAGE_SIZE);
 | 
						|
 | 
						|
            /* Make PDE valid */
 | 
						|
            PdeBase[((MM_HARDWARE_VA_START >> MM_PDI_SHIFT) & 0x1FF) + Index].Valid = 1;
 | 
						|
            PdeBase[((MM_HARDWARE_VA_START >> MM_PDI_SHIFT) & 0x1FF) + Index].PageFrameNumber = Address / EFI_PAGE_SIZE;
 | 
						|
            PdeBase[((MM_HARDWARE_VA_START >> MM_PDI_SHIFT) & 0x1FF) + Index].Writable = 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* Return success */
 | 
						|
    return STATUS_EFI_SUCCESS;
 | 
						|
}
 |