mirror of https://github.com/acidanthera/audk.git
693 lines
20 KiB
C
693 lines
20 KiB
C
/** @file
|
|
Page Fault (#PF) handler for X64 processors
|
|
|
|
Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
|
|
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.
|
|
|
|
**/
|
|
|
|
#include "PiSmmCpuDxeSmm.h"
|
|
|
|
#define PAGE_TABLE_PAGES 8
|
|
#define ACC_MAX_BIT BIT3
|
|
LIST_ENTRY mPagePool = INITIALIZE_LIST_HEAD_VARIABLE (mPagePool);
|
|
SPIN_LOCK mPFLock;
|
|
BOOLEAN m1GPageTableSupport = FALSE;
|
|
|
|
/**
|
|
Check if 1-GByte pages is supported by processor or not.
|
|
|
|
@retval TRUE 1-GByte pages is supported.
|
|
@retval FALSE 1-GByte pages is not supported.
|
|
|
|
**/
|
|
BOOLEAN
|
|
Is1GPageSupport (
|
|
VOID
|
|
)
|
|
{
|
|
UINT32 RegEax;
|
|
UINT32 RegEdx;
|
|
|
|
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
|
|
if (RegEax >= 0x80000001) {
|
|
AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
|
|
if ((RegEdx & BIT26) != 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Set sub-entries number in entry.
|
|
|
|
@param[in, out] Entry Pointer to entry
|
|
@param[in] SubEntryNum Sub-entries number based on 0:
|
|
0 means there is 1 sub-entry under this entry
|
|
0x1ff means there is 512 sub-entries under this entry
|
|
|
|
**/
|
|
VOID
|
|
SetSubEntriesNum (
|
|
IN OUT UINT64 *Entry,
|
|
IN UINT64 SubEntryNum
|
|
)
|
|
{
|
|
//
|
|
// Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
|
|
//
|
|
*Entry = BitFieldWrite64 (*Entry, 52, 60, SubEntryNum);
|
|
}
|
|
|
|
/**
|
|
Return sub-entries number in entry.
|
|
|
|
@param[in] Entry Pointer to entry
|
|
|
|
@return Sub-entries number based on 0:
|
|
0 means there is 1 sub-entry under this entry
|
|
0x1ff means there is 512 sub-entries under this entry
|
|
**/
|
|
UINT64
|
|
GetSubEntriesNum (
|
|
IN UINT64 *Entry
|
|
)
|
|
{
|
|
//
|
|
// Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
|
|
//
|
|
return BitFieldRead64 (*Entry, 52, 60);
|
|
}
|
|
|
|
/**
|
|
Create PageTable for SMM use.
|
|
|
|
@return The address of PML4 (to set CR3).
|
|
|
|
**/
|
|
UINT32
|
|
SmmInitPageTable (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_PHYSICAL_ADDRESS Pages;
|
|
UINT64 *PTEntry;
|
|
LIST_ENTRY *FreePage;
|
|
UINTN Index;
|
|
UINTN PageFaultHandlerHookAddress;
|
|
IA32_IDT_GATE_DESCRIPTOR *IdtEntry;
|
|
|
|
//
|
|
// Initialize spin lock
|
|
//
|
|
InitializeSpinLock (&mPFLock);
|
|
|
|
m1GPageTableSupport = Is1GPageSupport ();
|
|
//
|
|
// Generate PAE page table for the first 4GB memory space
|
|
//
|
|
Pages = Gen4GPageTable (PAGE_TABLE_PAGES + 1, FALSE);
|
|
|
|
//
|
|
// Set IA32_PG_PMNT bit to mask this entry
|
|
//
|
|
PTEntry = (UINT64*)(UINTN)Pages;
|
|
for (Index = 0; Index < 4; Index++) {
|
|
PTEntry[Index] |= IA32_PG_PMNT;
|
|
}
|
|
|
|
//
|
|
// Fill Page-Table-Level4 (PML4) entry
|
|
//
|
|
PTEntry = (UINT64*)(UINTN)(Pages - EFI_PAGES_TO_SIZE (PAGE_TABLE_PAGES + 1));
|
|
*PTEntry = Pages + PAGE_ATTRIBUTE_BITS;
|
|
ZeroMem (PTEntry + 1, EFI_PAGE_SIZE - sizeof (*PTEntry));
|
|
//
|
|
// Set sub-entries number
|
|
//
|
|
SetSubEntriesNum (PTEntry, 3);
|
|
|
|
//
|
|
// Add remaining pages to page pool
|
|
//
|
|
FreePage = (LIST_ENTRY*)(PTEntry + EFI_PAGE_SIZE / sizeof (*PTEntry));
|
|
while ((UINTN)FreePage < Pages) {
|
|
InsertTailList (&mPagePool, FreePage);
|
|
FreePage += EFI_PAGE_SIZE / sizeof (*FreePage);
|
|
}
|
|
|
|
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
|
|
//
|
|
// Set own Page Fault entry instead of the default one, because SMM Profile
|
|
// feature depends on IRET instruction to do Single Step
|
|
//
|
|
PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;
|
|
IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;
|
|
IdtEntry += EXCEPT_IA32_PAGE_FAULT;
|
|
IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;
|
|
IdtEntry->Bits.Reserved_0 = 0;
|
|
IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
|
|
IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);
|
|
IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32);
|
|
IdtEntry->Bits.Reserved_1 = 0;
|
|
} else {
|
|
//
|
|
// Register Smm Page Fault Handler
|
|
//
|
|
SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);
|
|
}
|
|
|
|
//
|
|
// Additional SMM IDT initialization for SMM stack guard
|
|
//
|
|
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
|
|
InitializeIDTSmmStackGuard ();
|
|
}
|
|
|
|
//
|
|
// Return the address of PML4 (to set CR3)
|
|
//
|
|
return (UINT32)(UINTN)PTEntry;
|
|
}
|
|
|
|
/**
|
|
Set access record in entry.
|
|
|
|
@param[in, out] Entry Pointer to entry
|
|
@param[in] Acc Access record value
|
|
|
|
**/
|
|
VOID
|
|
SetAccNum (
|
|
IN OUT UINT64 *Entry,
|
|
IN UINT64 Acc
|
|
)
|
|
{
|
|
//
|
|
// Access record is saved in BIT9 to BIT11 (reserved field) in Entry
|
|
//
|
|
*Entry = BitFieldWrite64 (*Entry, 9, 11, Acc);
|
|
}
|
|
|
|
/**
|
|
Return access record in entry.
|
|
|
|
@param[in] Entry Pointer to entry
|
|
|
|
@return Access record value.
|
|
|
|
**/
|
|
UINT64
|
|
GetAccNum (
|
|
IN UINT64 *Entry
|
|
)
|
|
{
|
|
//
|
|
// Access record is saved in BIT9 to BIT11 (reserved field) in Entry
|
|
//
|
|
return BitFieldRead64 (*Entry, 9, 11);
|
|
}
|
|
|
|
/**
|
|
Return and update the access record in entry.
|
|
|
|
@param[in, out] Entry Pointer to entry
|
|
|
|
@return Access record value.
|
|
|
|
**/
|
|
UINT64
|
|
GetAndUpdateAccNum (
|
|
IN OUT UINT64 *Entry
|
|
)
|
|
{
|
|
UINT64 Acc;
|
|
|
|
Acc = GetAccNum (Entry);
|
|
if ((*Entry & IA32_PG_A) != 0) {
|
|
//
|
|
// If this entry has been accessed, clear access flag in Entry and update access record
|
|
// to the initial value 7, adding ACC_MAX_BIT is to make it larger than others
|
|
//
|
|
*Entry &= ~(UINT64)(UINTN)IA32_PG_A;
|
|
SetAccNum (Entry, 0x7);
|
|
return (0x7 + ACC_MAX_BIT);
|
|
} else {
|
|
if (Acc != 0) {
|
|
//
|
|
// If the access record is not the smallest value 0, minus 1 and update the access record field
|
|
//
|
|
SetAccNum (Entry, Acc - 1);
|
|
}
|
|
}
|
|
return Acc;
|
|
}
|
|
|
|
/**
|
|
Reclaim free pages for PageFault handler.
|
|
|
|
Search the whole entries tree to find the leaf entry that has the smallest
|
|
access record value. Insert the page pointed by this leaf entry into the
|
|
page pool. And check its upper entries if need to be inserted into the page
|
|
pool or not.
|
|
|
|
**/
|
|
VOID
|
|
ReclaimPages (
|
|
VOID
|
|
)
|
|
{
|
|
UINT64 *Pml4;
|
|
UINT64 *Pdpt;
|
|
UINT64 *Pdt;
|
|
UINTN Pml4Index;
|
|
UINTN PdptIndex;
|
|
UINTN PdtIndex;
|
|
UINTN MinPml4;
|
|
UINTN MinPdpt;
|
|
UINTN MinPdt;
|
|
UINT64 MinAcc;
|
|
UINT64 Acc;
|
|
UINT64 SubEntriesNum;
|
|
BOOLEAN PML4EIgnore;
|
|
BOOLEAN PDPTEIgnore;
|
|
UINT64 *ReleasePageAddress;
|
|
|
|
Pml4 = NULL;
|
|
Pdpt = NULL;
|
|
Pdt = NULL;
|
|
MinAcc = (UINT64)-1;
|
|
MinPml4 = (UINTN)-1;
|
|
MinPdpt = (UINTN)-1;
|
|
MinPdt = (UINTN)-1;
|
|
Acc = 0;
|
|
ReleasePageAddress = 0;
|
|
|
|
//
|
|
// First, find the leaf entry has the smallest access record value
|
|
//
|
|
Pml4 = (UINT64*)(UINTN)(AsmReadCr3 () & gPhyMask);
|
|
for (Pml4Index = 0; Pml4Index < EFI_PAGE_SIZE / sizeof (*Pml4); Pml4Index++) {
|
|
if ((Pml4[Pml4Index] & IA32_PG_P) == 0 || (Pml4[Pml4Index] & IA32_PG_PMNT) != 0) {
|
|
//
|
|
// If the PML4 entry is not present or is masked, skip it
|
|
//
|
|
continue;
|
|
}
|
|
Pdpt = (UINT64*)(UINTN)(Pml4[Pml4Index] & gPhyMask);
|
|
PML4EIgnore = FALSE;
|
|
for (PdptIndex = 0; PdptIndex < EFI_PAGE_SIZE / sizeof (*Pdpt); PdptIndex++) {
|
|
if ((Pdpt[PdptIndex] & IA32_PG_P) == 0 || (Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {
|
|
//
|
|
// If the PDPT entry is not present or is masked, skip it
|
|
//
|
|
if ((Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {
|
|
//
|
|
// If the PDPT entry is masked, we will ignore checking the PML4 entry
|
|
//
|
|
PML4EIgnore = TRUE;
|
|
}
|
|
continue;
|
|
}
|
|
if ((Pdpt[PdptIndex] & IA32_PG_PS) == 0) {
|
|
//
|
|
// It's not 1-GByte pages entry, it should be a PDPT entry,
|
|
// we will not check PML4 entry more
|
|
//
|
|
PML4EIgnore = TRUE;
|
|
Pdt = (UINT64*)(UINTN)(Pdpt[PdptIndex] & gPhyMask);
|
|
PDPTEIgnore = FALSE;
|
|
for (PdtIndex = 0; PdtIndex < EFI_PAGE_SIZE / sizeof(*Pdt); PdtIndex++) {
|
|
if ((Pdt[PdtIndex] & IA32_PG_P) == 0 || (Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {
|
|
//
|
|
// If the PD entry is not present or is masked, skip it
|
|
//
|
|
if ((Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {
|
|
//
|
|
// If the PD entry is masked, we will not PDPT entry more
|
|
//
|
|
PDPTEIgnore = TRUE;
|
|
}
|
|
continue;
|
|
}
|
|
if ((Pdt[PdtIndex] & IA32_PG_PS) == 0) {
|
|
//
|
|
// It's not 2 MByte page table entry, it should be PD entry
|
|
// we will find the entry has the smallest access record value
|
|
//
|
|
PDPTEIgnore = TRUE;
|
|
Acc = GetAndUpdateAccNum (Pdt + PdtIndex);
|
|
if (Acc < MinAcc) {
|
|
//
|
|
// If the PD entry has the smallest access record value,
|
|
// save the Page address to be released
|
|
//
|
|
MinAcc = Acc;
|
|
MinPml4 = Pml4Index;
|
|
MinPdpt = PdptIndex;
|
|
MinPdt = PdtIndex;
|
|
ReleasePageAddress = Pdt + PdtIndex;
|
|
}
|
|
}
|
|
}
|
|
if (!PDPTEIgnore) {
|
|
//
|
|
// If this PDPT entry has no PDT entries pointer to 4 KByte pages,
|
|
// it should only has the entries point to 2 MByte Pages
|
|
//
|
|
Acc = GetAndUpdateAccNum (Pdpt + PdptIndex);
|
|
if (Acc < MinAcc) {
|
|
//
|
|
// If the PDPT entry has the smallest access record value,
|
|
// save the Page address to be released
|
|
//
|
|
MinAcc = Acc;
|
|
MinPml4 = Pml4Index;
|
|
MinPdpt = PdptIndex;
|
|
MinPdt = (UINTN)-1;
|
|
ReleasePageAddress = Pdpt + PdptIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!PML4EIgnore) {
|
|
//
|
|
// If PML4 entry has no the PDPT entry pointer to 2 MByte pages,
|
|
// it should only has the entries point to 1 GByte Pages
|
|
//
|
|
Acc = GetAndUpdateAccNum (Pml4 + Pml4Index);
|
|
if (Acc < MinAcc) {
|
|
//
|
|
// If the PML4 entry has the smallest access record value,
|
|
// save the Page address to be released
|
|
//
|
|
MinAcc = Acc;
|
|
MinPml4 = Pml4Index;
|
|
MinPdpt = (UINTN)-1;
|
|
MinPdt = (UINTN)-1;
|
|
ReleasePageAddress = Pml4 + Pml4Index;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Make sure one PML4/PDPT/PD entry is selected
|
|
//
|
|
ASSERT (MinAcc != (UINT64)-1);
|
|
|
|
//
|
|
// Secondly, insert the page pointed by this entry into page pool and clear this entry
|
|
//
|
|
InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(*ReleasePageAddress & gPhyMask));
|
|
*ReleasePageAddress = 0;
|
|
|
|
//
|
|
// Lastly, check this entry's upper entries if need to be inserted into page pool
|
|
// or not
|
|
//
|
|
while (TRUE) {
|
|
if (MinPdt != (UINTN)-1) {
|
|
//
|
|
// If 4 KByte Page Table is released, check the PDPT entry
|
|
//
|
|
Pdpt = (UINT64*)(UINTN)(Pml4[MinPml4] & gPhyMask);
|
|
SubEntriesNum = GetSubEntriesNum(Pdpt + MinPdpt);
|
|
if (SubEntriesNum == 0) {
|
|
//
|
|
// Release the empty Page Directory table if there was no more 4 KByte Page Table entry
|
|
// clear the Page directory entry
|
|
//
|
|
InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pdpt[MinPdpt] & gPhyMask));
|
|
Pdpt[MinPdpt] = 0;
|
|
//
|
|
// Go on checking the PML4 table
|
|
//
|
|
MinPdt = (UINTN)-1;
|
|
continue;
|
|
}
|
|
//
|
|
// Update the sub-entries filed in PDPT entry and exit
|
|
//
|
|
SetSubEntriesNum (Pdpt + MinPdpt, SubEntriesNum - 1);
|
|
break;
|
|
}
|
|
if (MinPdpt != (UINTN)-1) {
|
|
//
|
|
// One 2MB Page Table is released or Page Directory table is released, check the PML4 entry
|
|
//
|
|
SubEntriesNum = GetSubEntriesNum (Pml4 + MinPml4);
|
|
if (SubEntriesNum == 0) {
|
|
//
|
|
// Release the empty PML4 table if there was no more 1G KByte Page Table entry
|
|
// clear the Page directory entry
|
|
//
|
|
InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pml4[MinPml4] & gPhyMask));
|
|
Pml4[MinPml4] = 0;
|
|
MinPdpt = (UINTN)-1;
|
|
continue;
|
|
}
|
|
//
|
|
// Update the sub-entries filed in PML4 entry and exit
|
|
//
|
|
SetSubEntriesNum (Pml4 + MinPml4, SubEntriesNum - 1);
|
|
break;
|
|
}
|
|
//
|
|
// PLM4 table has been released before, exit it
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Allocate free Page for PageFault handler use.
|
|
|
|
@return Page address.
|
|
|
|
**/
|
|
UINT64
|
|
AllocPage (
|
|
VOID
|
|
)
|
|
{
|
|
UINT64 RetVal;
|
|
|
|
if (IsListEmpty (&mPagePool)) {
|
|
//
|
|
// If page pool is empty, reclaim the used pages and insert one into page pool
|
|
//
|
|
ReclaimPages ();
|
|
}
|
|
|
|
//
|
|
// Get one free page and remove it from page pool
|
|
//
|
|
RetVal = (UINT64)(UINTN)mPagePool.ForwardLink;
|
|
RemoveEntryList (mPagePool.ForwardLink);
|
|
//
|
|
// Clean this page and return
|
|
//
|
|
ZeroMem ((VOID*)(UINTN)RetVal, EFI_PAGE_SIZE);
|
|
return RetVal;
|
|
}
|
|
|
|
/**
|
|
Page Fault handler for SMM use.
|
|
|
|
**/
|
|
VOID
|
|
SmiDefaultPFHandler (
|
|
VOID
|
|
)
|
|
{
|
|
UINT64 *PageTable;
|
|
UINT64 *Pml4;
|
|
UINT64 PFAddress;
|
|
UINTN StartBit;
|
|
UINTN EndBit;
|
|
UINT64 PTIndex;
|
|
UINTN Index;
|
|
SMM_PAGE_SIZE_TYPE PageSize;
|
|
UINTN NumOfPages;
|
|
UINTN PageAttribute;
|
|
EFI_STATUS Status;
|
|
UINT64 *UpperEntry;
|
|
|
|
//
|
|
// Set default SMM page attribute
|
|
//
|
|
PageSize = SmmPageSize2M;
|
|
NumOfPages = 1;
|
|
PageAttribute = 0;
|
|
|
|
EndBit = 0;
|
|
Pml4 = (UINT64*)(AsmReadCr3 () & gPhyMask);
|
|
PFAddress = AsmReadCr2 ();
|
|
|
|
Status = GetPlatformPageTableAttribute (PFAddress, &PageSize, &NumOfPages, &PageAttribute);
|
|
//
|
|
// If platform not support page table attribute, set default SMM page attribute
|
|
//
|
|
if (Status != EFI_SUCCESS) {
|
|
PageSize = SmmPageSize2M;
|
|
NumOfPages = 1;
|
|
PageAttribute = 0;
|
|
}
|
|
if (PageSize >= MaxSmmPageSizeType) {
|
|
PageSize = SmmPageSize2M;
|
|
}
|
|
if (NumOfPages > 512) {
|
|
NumOfPages = 512;
|
|
}
|
|
|
|
switch (PageSize) {
|
|
case SmmPageSize4K:
|
|
//
|
|
// BIT12 to BIT20 is Page Table index
|
|
//
|
|
EndBit = 12;
|
|
break;
|
|
case SmmPageSize2M:
|
|
//
|
|
// BIT21 to BIT29 is Page Directory index
|
|
//
|
|
EndBit = 21;
|
|
PageAttribute |= (UINTN)IA32_PG_PS;
|
|
break;
|
|
case SmmPageSize1G:
|
|
if (!m1GPageTableSupport) {
|
|
DEBUG ((EFI_D_ERROR, "1-GByte pages is not supported!"));
|
|
ASSERT (FALSE);
|
|
}
|
|
//
|
|
// BIT30 to BIT38 is Page Directory Pointer Table index
|
|
//
|
|
EndBit = 30;
|
|
PageAttribute |= (UINTN)IA32_PG_PS;
|
|
break;
|
|
default:
|
|
ASSERT (FALSE);
|
|
}
|
|
|
|
//
|
|
// If execute-disable is enabled, set NX bit
|
|
//
|
|
if (mXdEnabled) {
|
|
PageAttribute |= IA32_PG_NX;
|
|
}
|
|
|
|
for (Index = 0; Index < NumOfPages; Index++) {
|
|
PageTable = Pml4;
|
|
UpperEntry = NULL;
|
|
for (StartBit = 39; StartBit > EndBit; StartBit -= 9) {
|
|
PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);
|
|
if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
|
|
//
|
|
// If the entry is not present, allocate one page from page pool for it
|
|
//
|
|
PageTable[PTIndex] = AllocPage () | PAGE_ATTRIBUTE_BITS;
|
|
} else {
|
|
//
|
|
// Save the upper entry address
|
|
//
|
|
UpperEntry = PageTable + PTIndex;
|
|
}
|
|
//
|
|
// BIT9 to BIT11 of entry is used to save access record,
|
|
// initialize value is 7
|
|
//
|
|
PageTable[PTIndex] |= (UINT64)IA32_PG_A;
|
|
SetAccNum (PageTable + PTIndex, 7);
|
|
PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);
|
|
}
|
|
|
|
PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);
|
|
if ((PageTable[PTIndex] & IA32_PG_P) != 0) {
|
|
//
|
|
// Check if the entry has already existed, this issue may occur when the different
|
|
// size page entries created under the same entry
|
|
//
|
|
DEBUG ((EFI_D_ERROR, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable, PTIndex, PageTable[PTIndex]));
|
|
DEBUG ((EFI_D_ERROR, "New page table overlapped with old page table!\n"));
|
|
ASSERT (FALSE);
|
|
}
|
|
//
|
|
// Fill the new entry
|
|
//
|
|
PageTable[PTIndex] = (PFAddress & gPhyMask & ~((1ull << EndBit) - 1)) |
|
|
PageAttribute | IA32_PG_A | PAGE_ATTRIBUTE_BITS;
|
|
if (UpperEntry != NULL) {
|
|
SetSubEntriesNum (UpperEntry, GetSubEntriesNum (UpperEntry) + 1);
|
|
}
|
|
//
|
|
// Get the next page address if we need to create more page tables
|
|
//
|
|
PFAddress += (1ull << EndBit);
|
|
}
|
|
}
|
|
|
|
/**
|
|
ThePage Fault handler wrapper for SMM use.
|
|
|
|
@param InterruptType Defines the type of interrupt or exception that
|
|
occurred on the processor.This parameter is processor architecture specific.
|
|
@param SystemContext A pointer to the processor context when
|
|
the interrupt occurred on the processor.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
SmiPFHandler (
|
|
IN EFI_EXCEPTION_TYPE InterruptType,
|
|
IN EFI_SYSTEM_CONTEXT SystemContext
|
|
)
|
|
{
|
|
UINTN PFAddress;
|
|
|
|
ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);
|
|
|
|
AcquireSpinLock (&mPFLock);
|
|
|
|
PFAddress = AsmReadCr2 ();
|
|
|
|
//
|
|
// If a page fault occurs in SMRAM range, it should be in a SMM stack guard page.
|
|
//
|
|
if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&
|
|
(PFAddress >= mCpuHotPlugData.SmrrBase) &&
|
|
(PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {
|
|
DEBUG ((EFI_D_ERROR, "SMM stack overflow!\n"));
|
|
CpuDeadLoop ();
|
|
}
|
|
|
|
//
|
|
// If a page fault occurs in SMM range
|
|
//
|
|
if ((PFAddress < mCpuHotPlugData.SmrrBase) ||
|
|
(PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {
|
|
if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) {
|
|
DEBUG ((EFI_D_ERROR, "Code executed on IP(0x%lx) out of SMM range after SMM is locked!\n", PFAddress));
|
|
DEBUG_CODE (
|
|
DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp);
|
|
);
|
|
CpuDeadLoop ();
|
|
}
|
|
}
|
|
|
|
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
|
|
SmmProfilePFHandler (
|
|
SystemContext.SystemContextX64->Rip,
|
|
SystemContext.SystemContextX64->ExceptionData
|
|
);
|
|
} else {
|
|
SmiDefaultPFHandler ();
|
|
}
|
|
|
|
ReleaseSpinLock (&mPFLock);
|
|
}
|