git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@927 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			435 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			435 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*++
 | |
| 
 | |
| Copyright (c) 2006, Intel Corporation                                                         
 | |
| All rights reserved. This program and the accompanying materials                          
 | |
| are licensed and made available under the terms and conditions of the BSD License         
 | |
| which accompanies this distribution.  The full text of the license may be found at        
 | |
| http://opensource.org/licenses/bsd-license.php                                            
 | |
|                                                                                           
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
 | |
| 
 | |
| Module Name:
 | |
|   VirtualMemory.c
 | |
|   
 | |
| Abstract:
 | |
| 
 | |
|   x64 Virtual Memory Management Services in the form of an IA-32 driver.  
 | |
|   Used to establish a 1:1 Virtual to Physical Mapping that is required to
 | |
|   enter Long Mode (x64 64-bit mode).
 | |
| 
 | |
|   While we make a 1:1 mapping (identity mapping) for all physical pages 
 | |
|   we still need to use the MTRR's to ensure that the cachability attirbutes
 | |
|   for all memory regions is correct.
 | |
| 
 | |
|   The basic idea is to use 2MB page table entries where ever possible. If
 | |
|   more granularity of cachability is required then 4K page tables are used.
 | |
| 
 | |
|   References:
 | |
|     1) IA-32 Intel(R) Atchitecture Software Developer's Manual Volume 1:Basic Architecture, Intel
 | |
|     2) IA-32 Intel(R) Atchitecture Software Developer's Manual Volume 2:Instruction Set Reference, Intel
 | |
|     3) IA-32 Intel(R) Atchitecture Software Developer's Manual Volume 3:System Programmer's Guide, Intel
 | |
|   
 | |
| --*/  
 | |
| 
 | |
| #include "VirtualMemory.h"
 | |
| 
 | |
| x64_MTRR_VARIABLE_RANGE     *mMTRRVariableRange;
 | |
| x64_MTRR_FIXED_RANGE        mMTRRFixedRange;
 | |
| 
 | |
| 
 | |
| //
 | |
| // Physial memory limit values for each of the 11 fixed MTRRs
 | |
| //
 | |
| UINTN mFixedRangeLimit[] = {
 | |
|   0x7FFFF,  // Fixed MTRR  #0 describes 0x00000..0x7FFFF
 | |
|   0x9FFFF,  // Fixed MTRR  #1 describes 0x80000..0x9FFFF
 | |
|   0xBFFFF,  // Fixed MTRR  #2 describes 0xA0000..0xBFFFF
 | |
|   0xC7FFF,  // Fixed MTRR  #3 describes 0xC0000..0xC7FFF
 | |
|   0xCFFFF,  // Fixed MTRR  #4 describes 0xC8000..0xCFFFF
 | |
|   0xD7FFF,  // Fixed MTRR  #5 describes 0xD0000..0xD7FFF
 | |
|   0xDFFFF,  // Fixed MTRR  #6 describes 0xD8000..0xDFFFF
 | |
|   0xE7FFF,  // Fixed MTRR  #7 describes 0xE0000..0xE7FFF
 | |
|   0xEFFFF,  // Fixed MTRR  #8 describes 0xE8000..0xEFFFF
 | |
|   0xF7FFF,  // Fixed MTRR  #9 describes 0xF0000..0xF7FFF
 | |
|   0xFFFFF   // Fixed MTRR #10 describes 0xF8000..0xFFFFF
 | |
| };
 | |
| 
 | |
| //
 | |
| // The size, in bits, of each of the 11 fixed MTRR.
 | |
| //
 | |
| UINTN mFixedRangeShift[] = {
 | |
|   16,   // Fixed MTRR  #0 describes 8, 64 KB ranges
 | |
|   14,   // Fixed MTRR  #1 describes 8, 16 KB ranges
 | |
|   14,   // Fixed MTRR  #2 describes 8, 16 KB ranges
 | |
|   12,   // Fixed MTRR  #3 describes 8, 4 KB ranges
 | |
|   12,   // Fixed MTRR  #4 describes 8, 4 KB ranges
 | |
|   12,   // Fixed MTRR  #5 describes 8, 4 KB ranges
 | |
|   12,   // Fixed MTRR  #6 describes 8, 4 KB ranges
 | |
|   12,   // Fixed MTRR  #7 describes 8, 4 KB ranges
 | |
|   12,   // Fixed MTRR  #8 describes 8, 4 KB ranges
 | |
|   12,   // Fixed MTRR  #9 describes 8, 4 KB ranges
 | |
|   12    // Fixed MTRR #10 describes 8, 4 KB ranges
 | |
| };
 | |
| 
 | |
| 
 | |
| UINTN mPowerOf2[] = {
 | |
|   1,
 | |
|   2,
 | |
|   4,
 | |
|   8,
 | |
|   16,
 | |
|   32,
 | |
|   64,
 | |
|   128,
 | |
|   256,
 | |
|   512
 | |
| };
 | |
| 
 | |
| x64_MTRR_MEMORY_TYPE
 | |
| EfiGetMTRRMemoryType (
 | |
|   IN  EFI_PHYSICAL_ADDRESS      Address
 | |
|   )
 | |
| /*++
 | |
| 
 | |
| Routine Description:
 | |
| 
 | |
|   Retrieves the memory type from the MTRR that describes a physical address.
 | |
| 
 | |
| Arguments:
 | |
| 
 | |
|   VariableRange - Set of Variable MTRRs
 | |
| 
 | |
|   FixedRange    - Set of Fixed MTRRs
 | |
| 
 | |
|   Address       - The physical address for which the MTRR memory type is being retrieved
 | |
| 
 | |
| Returns:
 | |
| 
 | |
|   The MTRR Memory Type for the physical memory specified by Address.
 | |
| 
 | |
| --*/
 | |
| {
 | |
|   UINTN                   Index;
 | |
|   UINTN                   TypeIndex;
 | |
|   BOOLEAN                 Found;
 | |
|   x64_MTRR_MEMORY_TYPE  VariableType;
 | |
|   EFI_PHYSICAL_ADDRESS    MaskBase;
 | |
|   EFI_PHYSICAL_ADDRESS    PhysMask;
 | |
| 
 | |
|   //
 | |
|   // If the MTRRs are disabled, then return the Uncached Memory Type
 | |
|   //
 | |
|   if (mMTRRFixedRange.DefaultType.Bits.E == 0) {
 | |
|     return Uncached;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If the CPU supports Fixed MTRRs and the Fixed MTRRs are enabled, then 
 | |
|   // see if Address falls into one of the Fixed MTRRs
 | |
|   //
 | |
|   if (mMTRRFixedRange.Capabilities.Bits.FIX && mMTRRFixedRange.DefaultType.Bits.FE) {
 | |
|     //
 | |
|     // Loop though 11 fixed MTRRs
 | |
|     //
 | |
|     for (Index = 0; Index < 11; Index++) {
 | |
|       //
 | |
|       // Check for a matching range
 | |
|       //
 | |
|       if (Address <= mFixedRangeLimit[Index]) {
 | |
|         //
 | |
|         // Compute the offset address into the MTRR bu subtrating the base address of the MTRR
 | |
|         //
 | |
|         if (Index > 0) {
 | |
|           Address = Address - (mFixedRangeLimit[Index-1] + 1);
 | |
|         }
 | |
|         //
 | |
|         // Retrieve the index into the MTRR to extract the memory type.  The range is 0..7
 | |
|         //
 | |
|         TypeIndex = (UINTN)RShiftU64 (Address, mFixedRangeShift[Index]);
 | |
|         
 | |
|         //
 | |
|         // Retrieve and return the memory type for the matching range
 | |
|         //
 | |
|         return mMTRRFixedRange.Fixed[Index].Type[TypeIndex];
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If Address was not found in a Fixed MTRR, then search the Variable MTRRs
 | |
|   //
 | |
|   for (Index = 0, Found = FALSE, VariableType = WriteBack; Index < mMTRRFixedRange.Capabilities.Bits.VCNT; Index++) {
 | |
|     //
 | |
|     // BugBug: __aullshr complier error
 | |
|     //
 | |
|     if ((mMTRRVariableRange[Index].PhysMask.Uint64 & 0x800) == 0x800) {    
 | |
|     //if (mMTRRVariableRange[Index].PhysMask.Bits.Valid == 1) {
 | |
|       PhysMask = mMTRRVariableRange[Index].PhysMask.Uint64 & ~0xfff;
 | |
|       MaskBase = PhysMask & (mMTRRVariableRange[Index].PhysBase.Uint64 & ~0xfff);
 | |
|       if (MaskBase == (PhysMask & Address)) {
 | |
|         //
 | |
|         // Check to see how many matches we find
 | |
|         //
 | |
|         Found = TRUE;
 | |
|         if ((mMTRRVariableRange[Index].PhysBase.Bits.Type == Uncached) || (VariableType == Uncached)) {
 | |
|           //
 | |
|           // If any matching region uses UC, the memory region is UC
 | |
|           //
 | |
|           VariableType = Uncached;
 | |
|         } else if ((mMTRRVariableRange[Index].PhysBase.Bits.Type == WriteThrough) || (VariableType == WriteThrough)){
 | |
|           //
 | |
|           // If it's WT and WB then set it to WT. If it's WT and other type it's undefined
 | |
|           //
 | |
|           VariableType = WriteThrough;
 | |
|         } else {
 | |
|           VariableType = mMTRRVariableRange[Index].PhysBase.Bits.Type;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   if (Found) {
 | |
|     return VariableType;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Address was not found in the Fixed or Variable MTRRs, so return the default memory type
 | |
|   //
 | |
|   return mMTRRFixedRange.DefaultType.Bits.Type;
 | |
| }
 | |
| 
 | |
| 
 | |
| BOOLEAN
 | |
| CanNotUse2MBPage (
 | |
|   IN  EFI_PHYSICAL_ADDRESS  BaseAddress
 | |
|   )
 | |
| /*++
 | |
| 
 | |
| Routine Description:
 | |
|   Test to see if a 2MB aligned page has all the same attributes. If a 2MB page
 | |
|   has more than one attibute type it needs to be split into multiple 4K pages.
 | |
| 
 | |
| Arguments:
 | |
|   BaseAddress - 2MB aligned address to check out
 | |
| 
 | |
| Returns:
 | |
|   TRUE  - This 2MB address range (BaseAddress) can NOT be mapped by a 2MB page
 | |
|   FALSE - This 2MB address range can be mapped by a 2MB page
 | |
| 
 | |
| --*/
 | |
| {
 | |
|   UINTN                   Index;
 | |
|   x64_MTRR_MEMORY_TYPE  MemoryType;
 | |
|   x64_MTRR_MEMORY_TYPE  PreviousMemoryType;
 | |
|   
 | |
|   //
 | |
|   // Address needs to be 2MB aligned
 | |
|   //
 | |
|   ASSERT ((BaseAddress & 0x1fffff) == 0);
 | |
| 
 | |
|   PreviousMemoryType = -1;
 | |
|   for (Index = 0; Index < 512; Index++, BaseAddress += 0x1000) {
 | |
|     MemoryType = EfiGetMTRRMemoryType (BaseAddress);
 | |
|     if ((Index != 0) && (MemoryType != PreviousMemoryType)) {
 | |
|       return TRUE;
 | |
|     }
 | |
| 
 | |
|     PreviousMemoryType = MemoryType;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // All the pages had the same type
 | |
|   //
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| VOID
 | |
| Convert2MBPageTo4KPages (  
 | |
|   IN  x64_PAGE_TABLE_ENTRY_2M   *PageDirectoryEntry2MB, 
 | |
|   IN  EFI_PHYSICAL_ADDRESS        PageAddress
 | |
|   )
 | |
| /*++
 | |
| 
 | |
| Routine Description:
 | |
|  Convert a single 2MB page entry to 512 4K page entries. The attributes for 
 | |
|  the 4K pages are read from the MTRR registers.
 | |
| 
 | |
| Arguments:
 | |
|   PageDirectoryEntry2MB - Page directory entry for PageAddress
 | |
|   PageAddress           - 2MB algined address of region to convert
 | |
| 
 | |
| Returns:
 | |
|   None
 | |
| 
 | |
| --*/
 | |
| {
 | |
|   EFI_PHYSICAL_ADDRESS                          Address;
 | |
|   x64_PAGE_DIRECTORY_ENTRY_4K                   *PageDirectoryEntry4k;
 | |
|   x64_PAGE_TABLE_ENTRY_4K                       *PageTableEntry;
 | |
|   UINTN                                         Index1;
 | |
| 
 | |
|   //
 | |
|   // Allocate the page table entry for the 4K pages
 | |
|   //
 | |
|   PageTableEntry = (x64_PAGE_TABLE_ENTRY_4K *) AllocatePages (1);
 | |
| 
 | |
|   ASSERT (PageTableEntry != NULL);
 | |
| 
 | |
|   //
 | |
|   // Convert PageDirectoryEntry2MB into a 4K Page Directory
 | |
|   //
 | |
|   PageDirectoryEntry4k = (x64_PAGE_DIRECTORY_ENTRY_4K *)PageDirectoryEntry2MB;
 | |
|   PageDirectoryEntry2MB->Uint64 = (UINT64)PageTableEntry;
 | |
|   PageDirectoryEntry2MB->Bits.ReadWrite = 1;
 | |
|   PageDirectoryEntry2MB->Bits.Present = 1;
 | |
|   
 | |
|   //
 | |
|   // Fill in the 4K page entries with the attributes from the MTRRs
 | |
|   //
 | |
|   for (Index1 = 0, Address = PageAddress; Index1 < 512; Index1++, PageTableEntry++, Address += 0x1000) {
 | |
|     PageTableEntry->Uint64 = (UINT64)Address;
 | |
|     PageTableEntry->Bits.ReadWrite = 1;
 | |
|     PageTableEntry->Bits.Present = 1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| EFI_PHYSICAL_ADDRESS
 | |
| CreateIdentityMappingPageTables (
 | |
|   IN UINT32                NumberOfProcessorPhysicalAddressBits
 | |
|   )
 | |
| /*++
 | |
| 
 | |
| Routine Description:
 | |
| 
 | |
|   Allocates and fills in the Page Directory and Page Table Entries to
 | |
|   establish a 1:1 Virtual to Physical mapping for physical memory from
 | |
|   0 to 4GB.  Memory above 4GB is not mapped.  The MTRRs are used to 
 | |
|   determine the cachability of the physical memory regions
 | |
| 
 | |
| Arguments:
 | |
| 
 | |
|   NumberOfProcessorPhysicalAddressBits - Number of processor address bits to use.
 | |
|                                          Limits the number of page table entries 
 | |
|                                          to the physical address space.
 | |
| 
 | |
| Returns:
 | |
|   EFI_OUT_OF_RESOURCES  There are not enough resources to allocate the Page Tables
 | |
| 
 | |
|   EFI_SUCCESS           The 1:1 Virtual to Physical identity mapping was created
 | |
| 
 | |
| --*/
 | |
| {  
 | |
|   EFI_PHYSICAL_ADDRESS                          PageAddress;
 | |
|   UINTN                                         Index;
 | |
|   UINTN                                         MaxBitsSupported;
 | |
|   UINTN                                         Index1;
 | |
|   UINTN                                         Index2;
 | |
|   x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K     *PageMapLevel4Entry;
 | |
|   x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K     *PageMap;
 | |
|   x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K     *PageDirectoryPointerEntry;
 | |
|   x64_PAGE_TABLE_ENTRY_2M                       *PageDirectoryEntry2MB;
 | |
| 
 | |
| 
 | |
|   //
 | |
|   //  Page Table structure 4 level 4K, 3 level 2MB.
 | |
|   //
 | |
|   //                   PageMapLevel4Entry        : bits 47-39
 | |
|   //                   PageDirectoryPointerEntry : bits 38-30
 | |
|   //  Page Table 2MB : PageDirectoryEntry2M      : bits 29-21
 | |
|   //  Page Table 4K  : PageDirectoryEntry4K      : bits 29 - 21
 | |
|   //                   PageTableEntry            : bits 20 - 12
 | |
|   //
 | |
|   // Strategy is to map every thing in the processor address space using 
 | |
|   //  2MB pages. If more  granularity is required the 2MB page will get 
 | |
|   //  converted to set of 4K pages. 
 | |
|   //
 | |
| 
 | |
|   //
 | |
|   // By architecture only one PageMapLevel4 exists - so lets allocate storgage for it.
 | |
|   //
 | |
|   PageMap = PageMapLevel4Entry = (x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *) AllocatePages (1);
 | |
|   ASSERT (PageMap != NULL);
 | |
|   PageAddress = 0;
 | |
| 
 | |
|   //
 | |
|   // The number of page-map Level-4 Offset entries is based on the number of 
 | |
|   // physical address bits. Less than equal to 38 bits only takes one entry.
 | |
|   // 512 entries represents 48 address bits. 
 | |
|   //
 | |
|   if (NumberOfProcessorPhysicalAddressBits <= 38) {
 | |
|     MaxBitsSupported = 1;
 | |
|   } else {
 | |
|     MaxBitsSupported = mPowerOf2[NumberOfProcessorPhysicalAddressBits - 39];
 | |
|   }
 | |
| 
 | |
|   for (Index = 0; Index < MaxBitsSupported; Index++, PageMapLevel4Entry++) {
 | |
|     //
 | |
|     // Each PML4 entry points to a page of Page Directory Pointer entires.
 | |
|     //  So lets allocate space for them and fill them in in the Index1 loop.
 | |
|     //  
 | |
|     PageDirectoryPointerEntry = (x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *) AllocatePages (1);
 | |
|     ASSERT (PageDirectoryPointerEntry != NULL);
 | |
| 
 | |
|     //
 | |
|     // Make a PML4 Entry
 | |
|     //
 | |
|     PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;
 | |
|     PageMapLevel4Entry->Bits.ReadWrite = 1;
 | |
|     PageMapLevel4Entry->Bits.Present = 1;
 | |
| 
 | |
|     for (Index1 = 0; Index1 < 512; Index1++, PageDirectoryPointerEntry++) {
 | |
|       //
 | |
|       // Each Directory Pointer entries points to a page of Page Directory entires.
 | |
|       //  So lets allocate space for them and fill them in in the Index2 loop.
 | |
|       //       
 | |
|       PageDirectoryEntry2MB = (x64_PAGE_TABLE_ENTRY_2M *) AllocatePages (1);
 | |
|       ASSERT (PageDirectoryEntry2MB != NULL);
 | |
| 
 | |
|       //
 | |
|       // Fill in a Page Directory Pointer Entries
 | |
|       //
 | |
|       PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry2MB;
 | |
|       PageDirectoryPointerEntry->Bits.ReadWrite = 1;
 | |
|       PageDirectoryPointerEntry->Bits.Present = 1;
 | |
| 
 | |
|       for (Index2 = 0; Index2 < 512; Index2++, PageDirectoryEntry2MB++, PageAddress += 0x200000) {
 | |
|         //
 | |
|         // Fill in the Page Directory entries
 | |
|         //
 | |
|         PageDirectoryEntry2MB->Uint64 = (UINT64)PageAddress;
 | |
|         PageDirectoryEntry2MB->Bits.ReadWrite = 1;
 | |
|         PageDirectoryEntry2MB->Bits.Present = 1;
 | |
|         PageDirectoryEntry2MB->Bits.MustBe1 = 1;
 | |
| 
 | |
|         if (CanNotUse2MBPage (PageAddress)) {
 | |
|           //
 | |
|           // Check to see if all 2MB has the same mapping. If not convert
 | |
|           //  to 4K pages by adding the 4th level of page table entries
 | |
|           //
 | |
|           Convert2MBPageTo4KPages (PageDirectoryEntry2MB, PageAddress);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // For the PML4 entries we are not using fill in a null entry.
 | |
|   //  for now we just copy the first entry.
 | |
|   //
 | |
|   for (; Index < 512; Index++, PageMapLevel4Entry++) {
 | |
|   //    EfiCopyMem (PageMapLevel4Entry, PageMap, sizeof (x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K));
 | |
|      CopyMem (PageMapLevel4Entry,
 | |
|               PageMap,
 | |
|               sizeof (x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K)
 | |
|               );
 | |
|   }
 | |
| 
 | |
|   return (EFI_PHYSICAL_ADDRESS)PageMap;
 | |
| }
 | |
| 
 |