Home | History | Annotate | Download | only in X64
      1 /** @file
      2   x64 Virtual Memory Management Services in the form of an IA-32 driver.
      3   Used to establish a 1:1 Virtual to Physical Mapping that is required to
      4   enter Long Mode (x64 64-bit mode).
      5 
      6   While we make a 1:1 mapping (identity mapping) for all physical pages
      7   we still need to use the MTRR's to ensure that the cachability attributes
      8   for all memory regions is correct.
      9 
     10   The basic idea is to use 2MB page table entries where ever possible. If
     11   more granularity of cachability is required then 4K page tables are used.
     12 
     13   References:
     14     1) IA-32 Intel(R) Architecture Software Developer's Manual Volume 1:Basic Architecture, Intel
     15     2) IA-32 Intel(R) Architecture Software Developer's Manual Volume 2:Instruction Set Reference, Intel
     16     3) IA-32 Intel(R) Architecture Software Developer's Manual Volume 3:System Programmer's Guide, Intel
     17 
     18 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
     19 This program and the accompanying materials
     20 are licensed and made available under the terms and conditions of the BSD License
     21 which accompanies this distribution.  The full text of the license may be found at
     22 http://opensource.org/licenses/bsd-license.php
     23 
     24 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     25 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     26 
     27 **/
     28 
     29 #include "DxeIpl.h"
     30 #include "VirtualMemory.h"
     31 
     32 /**
     33   Enable Execute Disable Bit.
     34 
     35 **/
     36 VOID
     37 EnableExecuteDisableBit (
     38   VOID
     39   )
     40 {
     41   UINT64           MsrRegisters;
     42 
     43   MsrRegisters = AsmReadMsr64 (0xC0000080);
     44   MsrRegisters |= BIT11;
     45   AsmWriteMsr64 (0xC0000080, MsrRegisters);
     46 }
     47 
     48 /**
     49   Split 2M page to 4K.
     50 
     51   @param[in]      PhysicalAddress       Start physical address the 2M page covered.
     52   @param[in, out] PageEntry2M           Pointer to 2M page entry.
     53   @param[in]      StackBase             Stack base address.
     54   @param[in]      StackSize             Stack size.
     55 
     56 **/
     57 VOID
     58 Split2MPageTo4K (
     59   IN EFI_PHYSICAL_ADDRESS               PhysicalAddress,
     60   IN OUT UINT64                         *PageEntry2M,
     61   IN EFI_PHYSICAL_ADDRESS               StackBase,
     62   IN UINTN                              StackSize
     63   )
     64 {
     65   EFI_PHYSICAL_ADDRESS                  PhysicalAddress4K;
     66   UINTN                                 IndexOfPageTableEntries;
     67   PAGE_TABLE_4K_ENTRY                   *PageTableEntry;
     68 
     69   PageTableEntry = AllocatePages (1);
     70   //
     71   // Fill in 2M page entry.
     72   //
     73   *PageEntry2M = (UINT64) (UINTN) PageTableEntry | IA32_PG_P | IA32_PG_RW;
     74 
     75   PhysicalAddress4K = PhysicalAddress;
     76   for (IndexOfPageTableEntries = 0; IndexOfPageTableEntries < 512; IndexOfPageTableEntries++, PageTableEntry++, PhysicalAddress4K += SIZE_4KB) {
     77     //
     78     // Fill in the Page Table entries
     79     //
     80     PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K;
     81     PageTableEntry->Bits.ReadWrite = 1;
     82     PageTableEntry->Bits.Present = 1;
     83     if ((PhysicalAddress4K >= StackBase) && (PhysicalAddress4K < StackBase + StackSize)) {
     84       //
     85       // Set Nx bit for stack.
     86       //
     87       PageTableEntry->Bits.Nx = 1;
     88     }
     89   }
     90 }
     91 
     92 /**
     93   Split 1G page to 2M.
     94 
     95   @param[in]      PhysicalAddress       Start physical address the 1G page covered.
     96   @param[in, out] PageEntry1G           Pointer to 1G page entry.
     97   @param[in]      StackBase             Stack base address.
     98   @param[in]      StackSize             Stack size.
     99 
    100 **/
    101 VOID
    102 Split1GPageTo2M (
    103   IN EFI_PHYSICAL_ADDRESS               PhysicalAddress,
    104   IN OUT UINT64                         *PageEntry1G,
    105   IN EFI_PHYSICAL_ADDRESS               StackBase,
    106   IN UINTN                              StackSize
    107   )
    108 {
    109   EFI_PHYSICAL_ADDRESS                  PhysicalAddress2M;
    110   UINTN                                 IndexOfPageDirectoryEntries;
    111   PAGE_TABLE_ENTRY                      *PageDirectoryEntry;
    112 
    113   PageDirectoryEntry = AllocatePages (1);
    114   //
    115   // Fill in 1G page entry.
    116   //
    117   *PageEntry1G = (UINT64) (UINTN) PageDirectoryEntry | IA32_PG_P | IA32_PG_RW;
    118 
    119   PhysicalAddress2M = PhysicalAddress;
    120   for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) {
    121     if ((PhysicalAddress2M < StackBase + StackSize) && ((PhysicalAddress2M + SIZE_2MB) > StackBase)) {
    122       //
    123       // Need to split this 2M page that covers stack range.
    124       //
    125       Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
    126     } else {
    127       //
    128       // Fill in the Page Directory entries
    129       //
    130       PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M;
    131       PageDirectoryEntry->Bits.ReadWrite = 1;
    132       PageDirectoryEntry->Bits.Present = 1;
    133       PageDirectoryEntry->Bits.MustBe1 = 1;
    134     }
    135   }
    136 }
    137 
    138 /**
    139   Allocates and fills in the Page Directory and Page Table Entries to
    140   establish a 1:1 Virtual to Physical mapping.
    141 
    142   @param[in] StackBase  Stack base address.
    143   @param[in] StackSize  Stack size.
    144 
    145   @return The address of 4 level page map.
    146 
    147 **/
    148 UINTN
    149 CreateIdentityMappingPageTables (
    150   IN EFI_PHYSICAL_ADDRESS   StackBase,
    151   IN UINTN                  StackSize
    152   )
    153 {
    154   UINT32                                        RegEax;
    155   UINT32                                        RegEdx;
    156   UINT8                                         PhysicalAddressBits;
    157   EFI_PHYSICAL_ADDRESS                          PageAddress;
    158   UINTN                                         IndexOfPml4Entries;
    159   UINTN                                         IndexOfPdpEntries;
    160   UINTN                                         IndexOfPageDirectoryEntries;
    161   UINT32                                        NumberOfPml4EntriesNeeded;
    162   UINT32                                        NumberOfPdpEntriesNeeded;
    163   PAGE_MAP_AND_DIRECTORY_POINTER                *PageMapLevel4Entry;
    164   PAGE_MAP_AND_DIRECTORY_POINTER                *PageMap;
    165   PAGE_MAP_AND_DIRECTORY_POINTER                *PageDirectoryPointerEntry;
    166   PAGE_TABLE_ENTRY                              *PageDirectoryEntry;
    167   UINTN                                         TotalPagesNum;
    168   UINTN                                         BigPageAddress;
    169   VOID                                          *Hob;
    170   BOOLEAN                                       Page1GSupport;
    171   PAGE_TABLE_1G_ENTRY                           *PageDirectory1GEntry;
    172 
    173   Page1GSupport = FALSE;
    174   if (PcdGetBool(PcdUse1GPageTable)) {
    175     AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
    176     if (RegEax >= 0x80000001) {
    177       AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
    178       if ((RegEdx & BIT26) != 0) {
    179         Page1GSupport = TRUE;
    180       }
    181     }
    182   }
    183 
    184   //
    185   // Get physical address bits supported.
    186   //
    187   Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
    188   if (Hob != NULL) {
    189     PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
    190   } else {
    191     AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
    192     if (RegEax >= 0x80000008) {
    193       AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
    194       PhysicalAddressBits = (UINT8) RegEax;
    195     } else {
    196       PhysicalAddressBits = 36;
    197     }
    198   }
    199 
    200   //
    201   // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
    202   //
    203   ASSERT (PhysicalAddressBits <= 52);
    204   if (PhysicalAddressBits > 48) {
    205     PhysicalAddressBits = 48;
    206   }
    207 
    208   //
    209   // Calculate the table entries needed.
    210   //
    211   if (PhysicalAddressBits <= 39 ) {
    212     NumberOfPml4EntriesNeeded = 1;
    213     NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
    214   } else {
    215     NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
    216     NumberOfPdpEntriesNeeded = 512;
    217   }
    218 
    219   //
    220   // Pre-allocate big pages to avoid later allocations.
    221   //
    222   if (!Page1GSupport) {
    223     TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
    224   } else {
    225     TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
    226   }
    227   BigPageAddress = (UINTN) AllocatePages (TotalPagesNum);
    228   ASSERT (BigPageAddress != 0);
    229 
    230   //
    231   // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
    232   //
    233   PageMap         = (VOID *) BigPageAddress;
    234   BigPageAddress += SIZE_4KB;
    235 
    236   PageMapLevel4Entry = PageMap;
    237   PageAddress        = 0;
    238   for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
    239     //
    240     // Each PML4 entry points to a page of Page Directory Pointer entires.
    241     // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
    242     //
    243     PageDirectoryPointerEntry = (VOID *) BigPageAddress;
    244     BigPageAddress += SIZE_4KB;
    245 
    246     //
    247     // Make a PML4 Entry
    248     //
    249     PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;
    250     PageMapLevel4Entry->Bits.ReadWrite = 1;
    251     PageMapLevel4Entry->Bits.Present = 1;
    252 
    253     if (Page1GSupport) {
    254       PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry;
    255 
    256       for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
    257         if (PcdGetBool (PcdSetNxForStack) && (PageAddress < StackBase + StackSize) && ((PageAddress + SIZE_1GB) > StackBase)) {
    258           Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, StackBase, StackSize);
    259         } else {
    260           //
    261           // Fill in the Page Directory entries
    262           //
    263           PageDirectory1GEntry->Uint64 = (UINT64)PageAddress;
    264           PageDirectory1GEntry->Bits.ReadWrite = 1;
    265           PageDirectory1GEntry->Bits.Present = 1;
    266           PageDirectory1GEntry->Bits.MustBe1 = 1;
    267         }
    268       }
    269     } else {
    270       for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
    271         //
    272         // Each Directory Pointer entries points to a page of Page Directory entires.
    273         // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
    274         //
    275         PageDirectoryEntry = (VOID *) BigPageAddress;
    276         BigPageAddress += SIZE_4KB;
    277 
    278         //
    279         // Fill in a Page Directory Pointer Entries
    280         //
    281         PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry;
    282         PageDirectoryPointerEntry->Bits.ReadWrite = 1;
    283         PageDirectoryPointerEntry->Bits.Present = 1;
    284 
    285         for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
    286           if (PcdGetBool (PcdSetNxForStack) && (PageAddress < StackBase + StackSize) && ((PageAddress + SIZE_2MB) > StackBase)) {
    287             //
    288             // Need to split this 2M page that covers stack range.
    289             //
    290             Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
    291           } else {
    292             //
    293             // Fill in the Page Directory entries
    294             //
    295             PageDirectoryEntry->Uint64 = (UINT64)PageAddress;
    296             PageDirectoryEntry->Bits.ReadWrite = 1;
    297             PageDirectoryEntry->Bits.Present = 1;
    298             PageDirectoryEntry->Bits.MustBe1 = 1;
    299           }
    300         }
    301       }
    302 
    303       for (; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
    304         ZeroMem (
    305           PageDirectoryPointerEntry,
    306           sizeof(PAGE_MAP_AND_DIRECTORY_POINTER)
    307           );
    308       }
    309     }
    310   }
    311 
    312   //
    313   // For the PML4 entries we are not using fill in a null entry.
    314   //
    315   for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {
    316     ZeroMem (
    317       PageMapLevel4Entry,
    318       sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
    319       );
    320   }
    321 
    322   if (PcdGetBool (PcdSetNxForStack)) {
    323     EnableExecuteDisableBit ();
    324   }
    325 
    326   return (UINTN)PageMap;
    327 }
    328 
    329