Home | History | Annotate | Download | only in X64
      1 /** @file
      2 Page Fault (#PF) handler for X64 processors
      3 
      4 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "PiSmmCpuDxeSmm.h"
     16 
     17 #define PAGE_TABLE_PAGES            8
     18 #define ACC_MAX_BIT                 BIT3
     19 LIST_ENTRY                          mPagePool = INITIALIZE_LIST_HEAD_VARIABLE (mPagePool);
     20 BOOLEAN                             m1GPageTableSupport = FALSE;
     21 UINT8                               mPhysicalAddressBits;
     22 BOOLEAN                             mCpuSmmStaticPageTable;
     23 
     24 /**
     25   Check if 1-GByte pages is supported by processor or not.
     26 
     27   @retval TRUE   1-GByte pages is supported.
     28   @retval FALSE  1-GByte pages is not supported.
     29 
     30 **/
     31 BOOLEAN
     32 Is1GPageSupport (
     33   VOID
     34   )
     35 {
     36   UINT32         RegEax;
     37   UINT32         RegEdx;
     38 
     39   AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
     40   if (RegEax >= 0x80000001) {
     41     AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
     42     if ((RegEdx & BIT26) != 0) {
     43       return TRUE;
     44     }
     45   }
     46   return FALSE;
     47 }
     48 
     49 /**
     50   Set sub-entries number in entry.
     51 
     52   @param[in, out] Entry        Pointer to entry
     53   @param[in]      SubEntryNum  Sub-entries number based on 0:
     54                                0 means there is 1 sub-entry under this entry
     55                                0x1ff means there is 512 sub-entries under this entry
     56 
     57 **/
     58 VOID
     59 SetSubEntriesNum (
     60   IN OUT UINT64               *Entry,
     61   IN     UINT64               SubEntryNum
     62   )
     63 {
     64   //
     65   // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
     66   //
     67   *Entry = BitFieldWrite64 (*Entry, 52, 60, SubEntryNum);
     68 }
     69 
     70 /**
     71   Return sub-entries number in entry.
     72 
     73   @param[in] Entry        Pointer to entry
     74 
     75   @return Sub-entries number based on 0:
     76           0 means there is 1 sub-entry under this entry
     77           0x1ff means there is 512 sub-entries under this entry
     78 **/
     79 UINT64
     80 GetSubEntriesNum (
     81   IN UINT64            *Entry
     82   )
     83 {
     84   //
     85   // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
     86   //
     87   return BitFieldRead64 (*Entry, 52, 60);
     88 }
     89 
     90 /**
     91   Calculate the maximum support address.
     92 
     93   @return the maximum support address.
     94 **/
     95 UINT8
     96 CalculateMaximumSupportAddress (
     97   VOID
     98   )
     99 {
    100   UINT32                                        RegEax;
    101   UINT8                                         PhysicalAddressBits;
    102   VOID                                          *Hob;
    103 
    104   //
    105   // Get physical address bits supported.
    106   //
    107   Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
    108   if (Hob != NULL) {
    109     PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
    110   } else {
    111     AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
    112     if (RegEax >= 0x80000008) {
    113       AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
    114       PhysicalAddressBits = (UINT8) RegEax;
    115     } else {
    116       PhysicalAddressBits = 36;
    117     }
    118   }
    119 
    120   //
    121   // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
    122   //
    123   ASSERT (PhysicalAddressBits <= 52);
    124   if (PhysicalAddressBits > 48) {
    125     PhysicalAddressBits = 48;
    126   }
    127   return PhysicalAddressBits;
    128 }
    129 
    130 /**
    131   Set static page table.
    132 
    133   @param[in] PageTable     Address of page table.
    134 **/
    135 VOID
    136 SetStaticPageTable (
    137   IN UINTN               PageTable
    138   )
    139 {
    140   UINT64                                        PageAddress;
    141   UINTN                                         NumberOfPml4EntriesNeeded;
    142   UINTN                                         NumberOfPdpEntriesNeeded;
    143   UINTN                                         IndexOfPml4Entries;
    144   UINTN                                         IndexOfPdpEntries;
    145   UINTN                                         IndexOfPageDirectoryEntries;
    146   UINT64                                        *PageMapLevel4Entry;
    147   UINT64                                        *PageMap;
    148   UINT64                                        *PageDirectoryPointerEntry;
    149   UINT64                                        *PageDirectory1GEntry;
    150   UINT64                                        *PageDirectoryEntry;
    151 
    152   if (mPhysicalAddressBits <= 39 ) {
    153     NumberOfPml4EntriesNeeded = 1;
    154     NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (mPhysicalAddressBits - 30));
    155   } else {
    156     NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (mPhysicalAddressBits - 39));
    157     NumberOfPdpEntriesNeeded = 512;
    158   }
    159 
    160   //
    161   // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
    162   //
    163   PageMap         = (VOID *) PageTable;
    164 
    165   PageMapLevel4Entry = PageMap;
    166   PageAddress        = 0;
    167   for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
    168     //
    169     // Each PML4 entry points to a page of Page Directory Pointer entries.
    170     //
    171     PageDirectoryPointerEntry = (UINT64 *) ((*PageMapLevel4Entry) & gPhyMask);
    172     if (PageDirectoryPointerEntry == NULL) {
    173       PageDirectoryPointerEntry = AllocatePageTableMemory (1);
    174       ASSERT(PageDirectoryPointerEntry != NULL);
    175       ZeroMem (PageDirectoryPointerEntry, EFI_PAGES_TO_SIZE(1));
    176 
    177       *PageMapLevel4Entry = ((UINTN)PageDirectoryPointerEntry & gPhyMask)  | PAGE_ATTRIBUTE_BITS;
    178     }
    179 
    180     if (m1GPageTableSupport) {
    181       PageDirectory1GEntry = PageDirectoryPointerEntry;
    182       for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
    183         if (IndexOfPml4Entries == 0 && IndexOfPageDirectoryEntries < 4) {
    184           //
    185           // Skip the < 4G entries
    186           //
    187           continue;
    188         }
    189         //
    190         // Fill in the Page Directory entries
    191         //
    192         *PageDirectory1GEntry = (PageAddress & gPhyMask) | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;
    193       }
    194     } else {
    195       PageAddress = BASE_4GB;
    196       for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
    197         if (IndexOfPml4Entries == 0 && IndexOfPdpEntries < 4) {
    198           //
    199           // Skip the < 4G entries
    200           //
    201           continue;
    202         }
    203         //
    204         // Each Directory Pointer entries points to a page of Page Directory entires.
    205         // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
    206         //
    207         PageDirectoryEntry = (UINT64 *) ((*PageDirectoryPointerEntry) & gPhyMask);
    208         if (PageDirectoryEntry == NULL) {
    209           PageDirectoryEntry = AllocatePageTableMemory (1);
    210           ASSERT(PageDirectoryEntry != NULL);
    211           ZeroMem (PageDirectoryEntry, EFI_PAGES_TO_SIZE(1));
    212 
    213           //
    214           // Fill in a Page Directory Pointer Entries
    215           //
    216           *PageDirectoryPointerEntry = (UINT64)(UINTN)PageDirectoryEntry | PAGE_ATTRIBUTE_BITS;
    217         }
    218 
    219         for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
    220           //
    221           // Fill in the Page Directory entries
    222           //
    223           *PageDirectoryEntry = (UINT64)PageAddress | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;
    224         }
    225       }
    226     }
    227   }
    228 }
    229 
    230 /**
    231   Create PageTable for SMM use.
    232 
    233   @return The address of PML4 (to set CR3).
    234 
    235 **/
    236 UINT32
    237 SmmInitPageTable (
    238   VOID
    239   )
    240 {
    241   EFI_PHYSICAL_ADDRESS              Pages;
    242   UINT64                            *PTEntry;
    243   LIST_ENTRY                        *FreePage;
    244   UINTN                             Index;
    245   UINTN                             PageFaultHandlerHookAddress;
    246   IA32_IDT_GATE_DESCRIPTOR          *IdtEntry;
    247   EFI_STATUS                        Status;
    248 
    249   //
    250   // Initialize spin lock
    251   //
    252   InitializeSpinLock (mPFLock);
    253 
    254   mCpuSmmStaticPageTable = PcdGetBool (PcdCpuSmmStaticPageTable);
    255   m1GPageTableSupport = Is1GPageSupport ();
    256   DEBUG ((DEBUG_INFO, "1GPageTableSupport - 0x%x\n", m1GPageTableSupport));
    257   DEBUG ((DEBUG_INFO, "PcdCpuSmmStaticPageTable - 0x%x\n", mCpuSmmStaticPageTable));
    258 
    259   mPhysicalAddressBits = CalculateMaximumSupportAddress ();
    260   DEBUG ((DEBUG_INFO, "PhysicalAddressBits - 0x%x\n", mPhysicalAddressBits));
    261   //
    262   // Generate PAE page table for the first 4GB memory space
    263   //
    264   Pages = Gen4GPageTable (FALSE);
    265 
    266   //
    267   // Set IA32_PG_PMNT bit to mask this entry
    268   //
    269   PTEntry = (UINT64*)(UINTN)Pages;
    270   for (Index = 0; Index < 4; Index++) {
    271     PTEntry[Index] |= IA32_PG_PMNT;
    272   }
    273 
    274   //
    275   // Fill Page-Table-Level4 (PML4) entry
    276   //
    277   PTEntry = (UINT64*)AllocatePageTableMemory (1);
    278   ASSERT (PTEntry != NULL);
    279   *PTEntry = Pages | PAGE_ATTRIBUTE_BITS;
    280   ZeroMem (PTEntry + 1, EFI_PAGE_SIZE - sizeof (*PTEntry));
    281 
    282   //
    283   // Set sub-entries number
    284   //
    285   SetSubEntriesNum (PTEntry, 3);
    286 
    287   if (mCpuSmmStaticPageTable) {
    288     SetStaticPageTable ((UINTN)PTEntry);
    289   } else {
    290     //
    291     // Add pages to page pool
    292     //
    293     FreePage = (LIST_ENTRY*)AllocatePageTableMemory (PAGE_TABLE_PAGES);
    294     ASSERT (FreePage != NULL);
    295     for (Index = 0; Index < PAGE_TABLE_PAGES; Index++) {
    296       InsertTailList (&mPagePool, FreePage);
    297       FreePage += EFI_PAGE_SIZE / sizeof (*FreePage);
    298     }
    299   }
    300 
    301   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
    302     //
    303     // Set own Page Fault entry instead of the default one, because SMM Profile
    304     // feature depends on IRET instruction to do Single Step
    305     //
    306     PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;
    307     IdtEntry  = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;
    308     IdtEntry += EXCEPT_IA32_PAGE_FAULT;
    309     IdtEntry->Bits.OffsetLow      = (UINT16)PageFaultHandlerHookAddress;
    310     IdtEntry->Bits.Reserved_0     = 0;
    311     IdtEntry->Bits.GateType       = IA32_IDT_GATE_TYPE_INTERRUPT_32;
    312     IdtEntry->Bits.OffsetHigh     = (UINT16)(PageFaultHandlerHookAddress >> 16);
    313     IdtEntry->Bits.OffsetUpper    = (UINT32)(PageFaultHandlerHookAddress >> 32);
    314     IdtEntry->Bits.Reserved_1     = 0;
    315   } else {
    316     //
    317     // Register Smm Page Fault Handler
    318     //
    319     Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);
    320     ASSERT_EFI_ERROR (Status);
    321   }
    322 
    323   //
    324   // Additional SMM IDT initialization for SMM stack guard
    325   //
    326   if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
    327     InitializeIDTSmmStackGuard ();
    328   }
    329 
    330   //
    331   // Return the address of PML4 (to set CR3)
    332   //
    333   return (UINT32)(UINTN)PTEntry;
    334 }
    335 
    336 /**
    337   Set access record in entry.
    338 
    339   @param[in, out] Entry        Pointer to entry
    340   @param[in]      Acc          Access record value
    341 
    342 **/
    343 VOID
    344 SetAccNum (
    345   IN OUT UINT64               *Entry,
    346   IN     UINT64               Acc
    347   )
    348 {
    349   //
    350   // Access record is saved in BIT9 to BIT11 (reserved field) in Entry
    351   //
    352   *Entry = BitFieldWrite64 (*Entry, 9, 11, Acc);
    353 }
    354 
    355 /**
    356   Return access record in entry.
    357 
    358   @param[in] Entry        Pointer to entry
    359 
    360   @return Access record value.
    361 
    362 **/
    363 UINT64
    364 GetAccNum (
    365   IN UINT64            *Entry
    366   )
    367 {
    368   //
    369   // Access record is saved in BIT9 to BIT11 (reserved field) in Entry
    370   //
    371   return BitFieldRead64 (*Entry, 9, 11);
    372 }
    373 
    374 /**
    375   Return and update the access record in entry.
    376 
    377   @param[in, out]  Entry    Pointer to entry
    378 
    379   @return Access record value.
    380 
    381 **/
    382 UINT64
    383 GetAndUpdateAccNum (
    384   IN OUT UINT64      *Entry
    385   )
    386 {
    387   UINT64         Acc;
    388 
    389   Acc = GetAccNum (Entry);
    390   if ((*Entry & IA32_PG_A) != 0) {
    391     //
    392     // If this entry has been accessed, clear access flag in Entry and update access record
    393     // to the initial value 7, adding ACC_MAX_BIT is to make it larger than others
    394     //
    395     *Entry &= ~(UINT64)(UINTN)IA32_PG_A;
    396     SetAccNum (Entry, 0x7);
    397     return (0x7 + ACC_MAX_BIT);
    398   } else {
    399     if (Acc != 0) {
    400       //
    401       // If the access record is not the smallest value 0, minus 1 and update the access record field
    402       //
    403       SetAccNum (Entry, Acc - 1);
    404     }
    405   }
    406   return Acc;
    407 }
    408 
    409 /**
    410   Reclaim free pages for PageFault handler.
    411 
    412   Search the whole entries tree to find the leaf entry that has the smallest
    413   access record value. Insert the page pointed by this leaf entry into the
    414   page pool. And check its upper entries if need to be inserted into the page
    415   pool or not.
    416 
    417 **/
    418 VOID
    419 ReclaimPages (
    420   VOID
    421   )
    422 {
    423   UINT64                       *Pml4;
    424   UINT64                       *Pdpt;
    425   UINT64                       *Pdt;
    426   UINTN                        Pml4Index;
    427   UINTN                        PdptIndex;
    428   UINTN                        PdtIndex;
    429   UINTN                        MinPml4;
    430   UINTN                        MinPdpt;
    431   UINTN                        MinPdt;
    432   UINT64                       MinAcc;
    433   UINT64                       Acc;
    434   UINT64                       SubEntriesNum;
    435   BOOLEAN                      PML4EIgnore;
    436   BOOLEAN                      PDPTEIgnore;
    437   UINT64                       *ReleasePageAddress;
    438 
    439   Pml4 = NULL;
    440   Pdpt = NULL;
    441   Pdt  = NULL;
    442   MinAcc  = (UINT64)-1;
    443   MinPml4 = (UINTN)-1;
    444   MinPdpt = (UINTN)-1;
    445   MinPdt  = (UINTN)-1;
    446   Acc     = 0;
    447   ReleasePageAddress = 0;
    448 
    449   //
    450   // First, find the leaf entry has the smallest access record value
    451   //
    452   Pml4 = (UINT64*)(UINTN)(AsmReadCr3 () & gPhyMask);
    453   for (Pml4Index = 0; Pml4Index < EFI_PAGE_SIZE / sizeof (*Pml4); Pml4Index++) {
    454     if ((Pml4[Pml4Index] & IA32_PG_P) == 0 || (Pml4[Pml4Index] & IA32_PG_PMNT) != 0) {
    455       //
    456       // If the PML4 entry is not present or is masked, skip it
    457       //
    458       continue;
    459     }
    460     Pdpt = (UINT64*)(UINTN)(Pml4[Pml4Index] & gPhyMask);
    461     PML4EIgnore = FALSE;
    462     for (PdptIndex = 0; PdptIndex < EFI_PAGE_SIZE / sizeof (*Pdpt); PdptIndex++) {
    463       if ((Pdpt[PdptIndex] & IA32_PG_P) == 0 || (Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {
    464         //
    465         // If the PDPT entry is not present or is masked, skip it
    466         //
    467         if ((Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {
    468           //
    469           // If the PDPT entry is masked, we will ignore checking the PML4 entry
    470           //
    471           PML4EIgnore = TRUE;
    472         }
    473         continue;
    474       }
    475       if ((Pdpt[PdptIndex] & IA32_PG_PS) == 0) {
    476         //
    477         // It's not 1-GByte pages entry, it should be a PDPT entry,
    478         // we will not check PML4 entry more
    479         //
    480         PML4EIgnore = TRUE;
    481         Pdt =  (UINT64*)(UINTN)(Pdpt[PdptIndex] & gPhyMask);
    482         PDPTEIgnore = FALSE;
    483         for (PdtIndex = 0; PdtIndex < EFI_PAGE_SIZE / sizeof(*Pdt); PdtIndex++) {
    484           if ((Pdt[PdtIndex] & IA32_PG_P) == 0 || (Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {
    485             //
    486             // If the PD entry is not present or is masked, skip it
    487             //
    488             if ((Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {
    489               //
    490               // If the PD entry is masked, we will not PDPT entry more
    491               //
    492               PDPTEIgnore = TRUE;
    493             }
    494             continue;
    495           }
    496           if ((Pdt[PdtIndex] & IA32_PG_PS) == 0) {
    497             //
    498             // It's not 2 MByte page table entry, it should be PD entry
    499             // we will find the entry has the smallest access record value
    500             //
    501             PDPTEIgnore = TRUE;
    502             Acc = GetAndUpdateAccNum (Pdt + PdtIndex);
    503             if (Acc < MinAcc) {
    504               //
    505               // If the PD entry has the smallest access record value,
    506               // save the Page address to be released
    507               //
    508               MinAcc  = Acc;
    509               MinPml4 = Pml4Index;
    510               MinPdpt = PdptIndex;
    511               MinPdt  = PdtIndex;
    512               ReleasePageAddress = Pdt + PdtIndex;
    513             }
    514           }
    515         }
    516         if (!PDPTEIgnore) {
    517           //
    518           // If this PDPT entry has no PDT entries pointer to 4 KByte pages,
    519           // it should only has the entries point to 2 MByte Pages
    520           //
    521           Acc = GetAndUpdateAccNum (Pdpt + PdptIndex);
    522           if (Acc < MinAcc) {
    523             //
    524             // If the PDPT entry has the smallest access record value,
    525             // save the Page address to be released
    526             //
    527             MinAcc  = Acc;
    528             MinPml4 = Pml4Index;
    529             MinPdpt = PdptIndex;
    530             MinPdt  = (UINTN)-1;
    531             ReleasePageAddress = Pdpt + PdptIndex;
    532           }
    533         }
    534       }
    535     }
    536     if (!PML4EIgnore) {
    537       //
    538       // If PML4 entry has no the PDPT entry pointer to 2 MByte pages,
    539       // it should only has the entries point to 1 GByte Pages
    540       //
    541       Acc = GetAndUpdateAccNum (Pml4 + Pml4Index);
    542       if (Acc < MinAcc) {
    543         //
    544         // If the PML4 entry has the smallest access record value,
    545         // save the Page address to be released
    546         //
    547         MinAcc  = Acc;
    548         MinPml4 = Pml4Index;
    549         MinPdpt = (UINTN)-1;
    550         MinPdt  = (UINTN)-1;
    551         ReleasePageAddress = Pml4 + Pml4Index;
    552       }
    553     }
    554   }
    555   //
    556   // Make sure one PML4/PDPT/PD entry is selected
    557   //
    558   ASSERT (MinAcc != (UINT64)-1);
    559 
    560   //
    561   // Secondly, insert the page pointed by this entry into page pool and clear this entry
    562   //
    563   InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(*ReleasePageAddress & gPhyMask));
    564   *ReleasePageAddress = 0;
    565 
    566   //
    567   // Lastly, check this entry's upper entries if need to be inserted into page pool
    568   // or not
    569   //
    570   while (TRUE) {
    571     if (MinPdt != (UINTN)-1) {
    572       //
    573       // If 4 KByte Page Table is released, check the PDPT entry
    574       //
    575       Pdpt = (UINT64*)(UINTN)(Pml4[MinPml4] & gPhyMask);
    576       SubEntriesNum = GetSubEntriesNum(Pdpt + MinPdpt);
    577       if (SubEntriesNum == 0) {
    578         //
    579         // Release the empty Page Directory table if there was no more 4 KByte Page Table entry
    580         // clear the Page directory entry
    581         //
    582         InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pdpt[MinPdpt] & gPhyMask));
    583         Pdpt[MinPdpt] = 0;
    584         //
    585         // Go on checking the PML4 table
    586         //
    587         MinPdt = (UINTN)-1;
    588         continue;
    589       }
    590       //
    591       // Update the sub-entries filed in PDPT entry and exit
    592       //
    593       SetSubEntriesNum (Pdpt + MinPdpt, SubEntriesNum - 1);
    594       break;
    595     }
    596     if (MinPdpt != (UINTN)-1) {
    597       //
    598       // One 2MB Page Table is released or Page Directory table is released, check the PML4 entry
    599       //
    600       SubEntriesNum = GetSubEntriesNum (Pml4 + MinPml4);
    601       if (SubEntriesNum == 0) {
    602         //
    603         // Release the empty PML4 table if there was no more 1G KByte Page Table entry
    604         // clear the Page directory entry
    605         //
    606         InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pml4[MinPml4] & gPhyMask));
    607         Pml4[MinPml4] = 0;
    608         MinPdpt = (UINTN)-1;
    609         continue;
    610       }
    611       //
    612       // Update the sub-entries filed in PML4 entry and exit
    613       //
    614       SetSubEntriesNum (Pml4 + MinPml4, SubEntriesNum - 1);
    615       break;
    616     }
    617     //
    618     // PLM4 table has been released before, exit it
    619     //
    620     break;
    621   }
    622 }
    623 
    624 /**
    625   Allocate free Page for PageFault handler use.
    626 
    627   @return Page address.
    628 
    629 **/
    630 UINT64
    631 AllocPage (
    632   VOID
    633   )
    634 {
    635   UINT64                            RetVal;
    636 
    637   if (IsListEmpty (&mPagePool)) {
    638     //
    639     // If page pool is empty, reclaim the used pages and insert one into page pool
    640     //
    641     ReclaimPages ();
    642   }
    643 
    644   //
    645   // Get one free page and remove it from page pool
    646   //
    647   RetVal = (UINT64)(UINTN)mPagePool.ForwardLink;
    648   RemoveEntryList (mPagePool.ForwardLink);
    649   //
    650   // Clean this page and return
    651   //
    652   ZeroMem ((VOID*)(UINTN)RetVal, EFI_PAGE_SIZE);
    653   return RetVal;
    654 }
    655 
    656 /**
    657   Page Fault handler for SMM use.
    658 
    659 **/
    660 VOID
    661 SmiDefaultPFHandler (
    662   VOID
    663   )
    664 {
    665   UINT64                            *PageTable;
    666   UINT64                            *Pml4;
    667   UINT64                            PFAddress;
    668   UINTN                             StartBit;
    669   UINTN                             EndBit;
    670   UINT64                            PTIndex;
    671   UINTN                             Index;
    672   SMM_PAGE_SIZE_TYPE                PageSize;
    673   UINTN                             NumOfPages;
    674   UINTN                             PageAttribute;
    675   EFI_STATUS                        Status;
    676   UINT64                            *UpperEntry;
    677 
    678   //
    679   // Set default SMM page attribute
    680   //
    681   PageSize = SmmPageSize2M;
    682   NumOfPages = 1;
    683   PageAttribute = 0;
    684 
    685   EndBit = 0;
    686   Pml4 = (UINT64*)(AsmReadCr3 () & gPhyMask);
    687   PFAddress = AsmReadCr2 ();
    688 
    689   Status = GetPlatformPageTableAttribute (PFAddress, &PageSize, &NumOfPages, &PageAttribute);
    690   //
    691   // If platform not support page table attribute, set default SMM page attribute
    692   //
    693   if (Status != EFI_SUCCESS) {
    694     PageSize = SmmPageSize2M;
    695     NumOfPages = 1;
    696     PageAttribute = 0;
    697   }
    698   if (PageSize >= MaxSmmPageSizeType) {
    699     PageSize = SmmPageSize2M;
    700   }
    701   if (NumOfPages > 512) {
    702     NumOfPages = 512;
    703   }
    704 
    705   switch (PageSize) {
    706   case SmmPageSize4K:
    707     //
    708     // BIT12 to BIT20 is Page Table index
    709     //
    710     EndBit = 12;
    711     break;
    712   case SmmPageSize2M:
    713     //
    714     // BIT21 to BIT29 is Page Directory index
    715     //
    716     EndBit = 21;
    717     PageAttribute |= (UINTN)IA32_PG_PS;
    718     break;
    719   case SmmPageSize1G:
    720     if (!m1GPageTableSupport) {
    721       DEBUG ((DEBUG_ERROR, "1-GByte pages is not supported!"));
    722       ASSERT (FALSE);
    723     }
    724     //
    725     // BIT30 to BIT38 is Page Directory Pointer Table index
    726     //
    727     EndBit = 30;
    728     PageAttribute |= (UINTN)IA32_PG_PS;
    729     break;
    730   default:
    731     ASSERT (FALSE);
    732   }
    733 
    734   //
    735   // If execute-disable is enabled, set NX bit
    736   //
    737   if (mXdEnabled) {
    738     PageAttribute |= IA32_PG_NX;
    739   }
    740 
    741   for (Index = 0; Index < NumOfPages; Index++) {
    742     PageTable  = Pml4;
    743     UpperEntry = NULL;
    744     for (StartBit = 39; StartBit > EndBit; StartBit -= 9) {
    745       PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);
    746       if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
    747         //
    748         // If the entry is not present, allocate one page from page pool for it
    749         //
    750         PageTable[PTIndex] = AllocPage () | PAGE_ATTRIBUTE_BITS;
    751       } else {
    752         //
    753         // Save the upper entry address
    754         //
    755         UpperEntry = PageTable + PTIndex;
    756       }
    757       //
    758       // BIT9 to BIT11 of entry is used to save access record,
    759       // initialize value is 7
    760       //
    761       PageTable[PTIndex] |= (UINT64)IA32_PG_A;
    762       SetAccNum (PageTable + PTIndex, 7);
    763       PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);
    764     }
    765 
    766     PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);
    767     if ((PageTable[PTIndex] & IA32_PG_P) != 0) {
    768       //
    769       // Check if the entry has already existed, this issue may occur when the different
    770       // size page entries created under the same entry
    771       //
    772       DEBUG ((DEBUG_ERROR, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable, PTIndex, PageTable[PTIndex]));
    773       DEBUG ((DEBUG_ERROR, "New page table overlapped with old page table!\n"));
    774       ASSERT (FALSE);
    775     }
    776     //
    777     // Fill the new entry
    778     //
    779     PageTable[PTIndex] = (PFAddress & gPhyMask & ~((1ull << EndBit) - 1)) |
    780                          PageAttribute | IA32_PG_A | PAGE_ATTRIBUTE_BITS;
    781     if (UpperEntry != NULL) {
    782       SetSubEntriesNum (UpperEntry, GetSubEntriesNum (UpperEntry) + 1);
    783     }
    784     //
    785     // Get the next page address if we need to create more page tables
    786     //
    787     PFAddress += (1ull << EndBit);
    788   }
    789 }
    790 
    791 /**
    792   ThePage Fault handler wrapper for SMM use.
    793 
    794   @param  InterruptType    Defines the type of interrupt or exception that
    795                            occurred on the processor.This parameter is processor architecture specific.
    796   @param  SystemContext    A pointer to the processor context when
    797                            the interrupt occurred on the processor.
    798 **/
    799 VOID
    800 EFIAPI
    801 SmiPFHandler (
    802     IN EFI_EXCEPTION_TYPE   InterruptType,
    803     IN EFI_SYSTEM_CONTEXT   SystemContext
    804   )
    805 {
    806   UINTN             PFAddress;
    807   UINTN             GuardPageAddress;
    808   UINTN             CpuIndex;
    809 
    810   ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);
    811 
    812   AcquireSpinLock (mPFLock);
    813 
    814   PFAddress = AsmReadCr2 ();
    815 
    816   if (mCpuSmmStaticPageTable && (PFAddress >= LShiftU64 (1, (mPhysicalAddressBits - 1)))) {
    817     DEBUG ((DEBUG_ERROR, "Do not support address 0x%lx by processor!\n", PFAddress));
    818     CpuDeadLoop ();
    819   }
    820 
    821   //
    822   // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,
    823   // or SMM page protection violation.
    824   //
    825   if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&
    826       (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {
    827     CpuIndex = GetCpuIndex ();
    828     GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);
    829     if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&
    830         (PFAddress >= GuardPageAddress) &&
    831         (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) {
    832       DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));
    833     } else {
    834       DEBUG ((DEBUG_ERROR, "SMM exception data - 0x%lx(", SystemContext.SystemContextX64->ExceptionData));
    835       DEBUG ((DEBUG_ERROR, "I:%x, R:%x, U:%x, W:%x, P:%x",
    836         (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0,
    837         (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_RSVD) != 0,
    838         (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_US) != 0,
    839         (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_WR) != 0,
    840         (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_P) != 0
    841         ));
    842       DEBUG ((DEBUG_ERROR, ")\n"));
    843       if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) {
    844         DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%lx)\n", PFAddress));
    845         DEBUG_CODE (
    846           DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp);
    847         );
    848       } else {
    849         DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%lx)\n", PFAddress));
    850         DEBUG_CODE (
    851           DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);
    852         );
    853       }
    854     }
    855     CpuDeadLoop ();
    856   }
    857 
    858   //
    859   // If a page fault occurs in SMM range
    860   //
    861   if ((PFAddress < mCpuHotPlugData.SmrrBase) ||
    862       (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {
    863     if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) {
    864       DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%lx) out of SMM range after SMM is locked!\n", PFAddress));
    865       DEBUG_CODE (
    866         DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp);
    867       );
    868       CpuDeadLoop ();
    869     }
    870     if (IsSmmCommBufferForbiddenAddress (PFAddress)) {
    871       DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%lx)!\n", PFAddress));
    872       DEBUG_CODE (
    873         DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);
    874       );
    875       CpuDeadLoop ();
    876     }
    877   }
    878 
    879   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
    880     SmmProfilePFHandler (
    881       SystemContext.SystemContextX64->Rip,
    882       SystemContext.SystemContextX64->ExceptionData
    883       );
    884   } else {
    885     SmiDefaultPFHandler ();
    886   }
    887 
    888   ReleaseSpinLock (mPFLock);
    889 }
    890 
    891 /**
    892   This function sets memory attribute for page table.
    893 **/
    894 VOID
    895 SetPageTableAttributes (
    896   VOID
    897   )
    898 {
    899   UINTN                 Index2;
    900   UINTN                 Index3;
    901   UINTN                 Index4;
    902   UINT64                *L1PageTable;
    903   UINT64                *L2PageTable;
    904   UINT64                *L3PageTable;
    905   UINT64                *L4PageTable;
    906   BOOLEAN               IsSplitted;
    907   BOOLEAN               PageTableSplitted;
    908 
    909   if (!mCpuSmmStaticPageTable) {
    910     return ;
    911   }
    912 
    913   DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
    914 
    915   //
    916   // Disable write protection, because we need mark page table to be write protected.
    917   // We need *write* page table memory, to mark itself to be *read only*.
    918   //
    919   AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);
    920 
    921   do {
    922     DEBUG ((DEBUG_INFO, "Start...\n"));
    923     PageTableSplitted = FALSE;
    924 
    925     L4PageTable = (UINT64 *)GetPageTableBase ();
    926     SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L4PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
    927     PageTableSplitted = (PageTableSplitted || IsSplitted);
    928 
    929     for (Index4 = 0; Index4 < SIZE_4KB/sizeof(UINT64); Index4++) {
    930       L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64);
    931       if (L3PageTable == NULL) {
    932         continue;
    933       }
    934 
    935       SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
    936       PageTableSplitted = (PageTableSplitted || IsSplitted);
    937 
    938       for (Index3 = 0; Index3 < SIZE_4KB/sizeof(UINT64); Index3++) {
    939         if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
    940           // 1G
    941           continue;
    942         }
    943         L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);
    944         if (L2PageTable == NULL) {
    945           continue;
    946         }
    947 
    948         SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
    949         PageTableSplitted = (PageTableSplitted || IsSplitted);
    950 
    951         for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {
    952           if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
    953             // 2M
    954             continue;
    955           }
    956           L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);
    957           if (L1PageTable == NULL) {
    958             continue;
    959           }
    960           SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
    961           PageTableSplitted = (PageTableSplitted || IsSplitted);
    962         }
    963       }
    964     }
    965   } while (PageTableSplitted);
    966 
    967   //
    968   // Enable write protection, after page table updated.
    969   //
    970   AsmWriteCr0 (AsmReadCr0() | CR0_WP);
    971 
    972   return ;
    973 }
    974