UefiCpuPkg/PiSmmCpuDxeSmm: don't free page table pages that are required to handle current page fault

Reclaim may free page table pages that are required to handle current page
fault. This causes a page leak, and, after sufficent number of specific
page fault+reclaim pairs, we run out of reclaimable pages and hit:

ASSERT (MinAcc != (UINT64)-1);

To remedy, prevent pages essential to handling current page fault:
(1) from being considered as reclaim candidates (first reclaim phase)
(2) from being freed as part of "branch cleanup" (second reclaim phase)

Signed-off-by: Damian Nikodem <damian.nikodem@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Jian J Wang <jian.j.wang@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Krzysztof Rusocki <krzysztof.rusocki@intel.com>
This commit is contained in:
Damian Nikodem
2019-08-20 01:48:45 +08:00
committed by Ray Ni
parent ada905ab5c
commit 4201098e97

View File

@@ -544,6 +544,11 @@ ReclaimPages (
UINT64 *ReleasePageAddress; UINT64 *ReleasePageAddress;
IA32_CR4 Cr4; IA32_CR4 Cr4;
BOOLEAN Enable5LevelPaging; BOOLEAN Enable5LevelPaging;
UINT64 PFAddress;
UINT64 PFAddressPml5Index;
UINT64 PFAddressPml4Index;
UINT64 PFAddressPdptIndex;
UINT64 PFAddressPdtIndex;
Pml4 = NULL; Pml4 = NULL;
Pdpt = NULL; Pdpt = NULL;
@@ -555,6 +560,11 @@ ReclaimPages (
MinPdt = (UINTN)-1; MinPdt = (UINTN)-1;
Acc = 0; Acc = 0;
ReleasePageAddress = 0; ReleasePageAddress = 0;
PFAddress = AsmReadCr2 ();
PFAddressPml5Index = BitFieldRead64 (PFAddress, 48, 48 + 8);
PFAddressPml4Index = BitFieldRead64 (PFAddress, 39, 39 + 8);
PFAddressPdptIndex = BitFieldRead64 (PFAddress, 30, 30 + 8);
PFAddressPdtIndex = BitFieldRead64 (PFAddress, 21, 21 + 8);
Cr4.UintN = AsmReadCr4 (); Cr4.UintN = AsmReadCr4 ();
Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1); Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
@@ -629,18 +639,21 @@ ReclaimPages (
// we will find the entry has the smallest access record value // we will find the entry has the smallest access record value
// //
PDPTEIgnore = TRUE; PDPTEIgnore = TRUE;
Acc = GetAndUpdateAccNum (Pdt + PdtIndex); if (PdtIndex != PFAddressPdtIndex || PdptIndex != PFAddressPdptIndex ||
if (Acc < MinAcc) { Pml4Index != PFAddressPml4Index || Pml5Index != PFAddressPml5Index) {
// Acc = GetAndUpdateAccNum (Pdt + PdtIndex);
// If the PD entry has the smallest access record value, if (Acc < MinAcc) {
// save the Page address to be released //
// // If the PD entry has the smallest access record value,
MinAcc = Acc; // save the Page address to be released
MinPml5 = Pml5Index; //
MinPml4 = Pml4Index; MinAcc = Acc;
MinPdpt = PdptIndex; MinPml5 = Pml5Index;
MinPdt = PdtIndex; MinPml4 = Pml4Index;
ReleasePageAddress = Pdt + PdtIndex; MinPdpt = PdptIndex;
MinPdt = PdtIndex;
ReleasePageAddress = Pdt + PdtIndex;
}
} }
} }
} }
@@ -649,18 +662,21 @@ ReclaimPages (
// If this PDPT entry has no PDT entries pointer to 4 KByte pages, // If this PDPT entry has no PDT entries pointer to 4 KByte pages,
// it should only has the entries point to 2 MByte Pages // it should only has the entries point to 2 MByte Pages
// //
Acc = GetAndUpdateAccNum (Pdpt + PdptIndex); if (PdptIndex != PFAddressPdptIndex || Pml4Index != PFAddressPml4Index ||
if (Acc < MinAcc) { Pml5Index != PFAddressPml5Index) {
// Acc = GetAndUpdateAccNum (Pdpt + PdptIndex);
// If the PDPT entry has the smallest access record value, if (Acc < MinAcc) {
// save the Page address to be released //
// // If the PDPT entry has the smallest access record value,
MinAcc = Acc; // save the Page address to be released
MinPml5 = Pml5Index; //
MinPml4 = Pml4Index; MinAcc = Acc;
MinPdpt = PdptIndex; MinPml5 = Pml5Index;
MinPdt = (UINTN)-1; MinPml4 = Pml4Index;
ReleasePageAddress = Pdpt + PdptIndex; MinPdpt = PdptIndex;
MinPdt = (UINTN)-1;
ReleasePageAddress = Pdpt + PdptIndex;
}
} }
} }
} }
@@ -670,18 +686,20 @@ ReclaimPages (
// If PML4 entry has no the PDPT entry pointer to 2 MByte pages, // If PML4 entry has no the PDPT entry pointer to 2 MByte pages,
// it should only has the entries point to 1 GByte Pages // it should only has the entries point to 1 GByte Pages
// //
Acc = GetAndUpdateAccNum (Pml4 + Pml4Index); if (Pml4Index != PFAddressPml4Index || Pml5Index != PFAddressPml5Index) {
if (Acc < MinAcc) { Acc = GetAndUpdateAccNum (Pml4 + Pml4Index);
// if (Acc < MinAcc) {
// If the PML4 entry has the smallest access record value, //
// save the Page address to be released // If the PML4 entry has the smallest access record value,
// // save the Page address to be released
MinAcc = Acc; //
MinPml5 = Pml5Index; MinAcc = Acc;
MinPml4 = Pml4Index; MinPml5 = Pml5Index;
MinPdpt = (UINTN)-1; MinPml4 = Pml4Index;
MinPdt = (UINTN)-1; MinPdpt = (UINTN)-1;
ReleasePageAddress = Pml4 + Pml4Index; MinPdt = (UINTN)-1;
ReleasePageAddress = Pml4 + Pml4Index;
}
} }
} }
} }
@@ -709,7 +727,8 @@ ReclaimPages (
Pml4 = (UINT64 *) (UINTN) (Pml5[MinPml5] & gPhyMask); Pml4 = (UINT64 *) (UINTN) (Pml5[MinPml5] & gPhyMask);
Pdpt = (UINT64*)(UINTN)(Pml4[MinPml4] & ~mAddressEncMask & gPhyMask); Pdpt = (UINT64*)(UINTN)(Pml4[MinPml4] & ~mAddressEncMask & gPhyMask);
SubEntriesNum = GetSubEntriesNum(Pdpt + MinPdpt); SubEntriesNum = GetSubEntriesNum(Pdpt + MinPdpt);
if (SubEntriesNum == 0) { if (SubEntriesNum == 0 &&
(MinPdpt != PFAddressPdptIndex || MinPml4 != PFAddressPml4Index || MinPml5 != PFAddressPml5Index)) {
// //
// Release the empty Page Directory table if there was no more 4 KByte Page Table entry // Release the empty Page Directory table if there was no more 4 KByte Page Table entry
// clear the Page directory entry // clear the Page directory entry
@@ -725,7 +744,7 @@ ReclaimPages (
// //
// Update the sub-entries filed in PDPT entry and exit // Update the sub-entries filed in PDPT entry and exit
// //
SetSubEntriesNum (Pdpt + MinPdpt, SubEntriesNum - 1); SetSubEntriesNum (Pdpt + MinPdpt, (SubEntriesNum - 1) & 0x1FF);
break; break;
} }
if (MinPdpt != (UINTN)-1) { if (MinPdpt != (UINTN)-1) {
@@ -733,7 +752,7 @@ ReclaimPages (
// One 2MB Page Table is released or Page Directory table is released, check the PML4 entry // One 2MB Page Table is released or Page Directory table is released, check the PML4 entry
// //
SubEntriesNum = GetSubEntriesNum (Pml4 + MinPml4); SubEntriesNum = GetSubEntriesNum (Pml4 + MinPml4);
if (SubEntriesNum == 0) { if (SubEntriesNum == 0 && (MinPml4 != PFAddressPml4Index || MinPml5 != PFAddressPml5Index)) {
// //
// Release the empty PML4 table if there was no more 1G KByte Page Table entry // Release the empty PML4 table if there was no more 1G KByte Page Table entry
// clear the Page directory entry // clear the Page directory entry
@@ -746,7 +765,7 @@ ReclaimPages (
// //
// Update the sub-entries filed in PML4 entry and exit // Update the sub-entries filed in PML4 entry and exit
// //
SetSubEntriesNum (Pml4 + MinPml4, SubEntriesNum - 1); SetSubEntriesNum (Pml4 + MinPml4, (SubEntriesNum - 1) & 0x1FF);
break; break;
} }
// //
@@ -919,7 +938,7 @@ SmiDefaultPFHandler (
PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & gPhyMask & ~((1ull << EndBit) - 1)) | PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & gPhyMask & ~((1ull << EndBit) - 1)) |
PageAttribute | IA32_PG_A | PAGE_ATTRIBUTE_BITS; PageAttribute | IA32_PG_A | PAGE_ATTRIBUTE_BITS;
if (UpperEntry != NULL) { if (UpperEntry != NULL) {
SetSubEntriesNum (UpperEntry, GetSubEntriesNum (UpperEntry) + 1); SetSubEntriesNum (UpperEntry, (GetSubEntriesNum (UpperEntry) + 1) & 0x1FF);
} }
// //
// Get the next page address if we need to create more page tables // Get the next page address if we need to create more page tables