X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FUniversal%2FAcpi%2FBootScriptExecutorDxe%2FX64%2FSetIdtEntry.c;h=d433cf128ccff9f89cdb9474ee285a875c2fb035;hp=db11697e7c00c7f5a1ed90d7ba0e08caa3b97a4d;hb=ab1a5a58c95937998f3ecf384a8940130511e234;hpb=d0bf562330e5309a92e55e44063a8ea37ead4d1d
diff --git a/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c b/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c
index db11697e7c..d433cf128c 100644
--- a/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c
+++ b/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c
@@ -3,7 +3,9 @@
Set a IDT entry for interrupt vector 3 for debug purpose for x64 platform
-Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
@@ -15,79 +17,114 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "ScriptExecute.h"
+
//
-// INTERRUPT_GATE_DESCRIPTOR and SetIdtEntry () are used to setup IDT to do debug
+// 8 extra pages for PF handler.
//
-
-#pragma pack(1)
-
-typedef struct {
- UINT16 Offset15To0;
- UINT16 SegmentSelector;
- UINT16 Attributes;
- UINT16 Offset31To16;
- UINT32 Offset63To32;
- UINT32 Reserved;
-} INTERRUPT_GATE_DESCRIPTOR;
-
-#define INTERRUPT_GATE_ATTRIBUTE 0x8e00
-
-#pragma pack()
+#define EXTRA_PAGE_TABLE_PAGES 8
#define IA32_PG_P BIT0
#define IA32_PG_RW BIT1
#define IA32_PG_PS BIT7
-UINT64 mPhyMask;
-BOOLEAN mPage1GSupport;
-VOID *mOriginalHandler;
-UINTN mS3NvsPageTableAddress;
+UINT64 mPhyMask;
+VOID *mOriginalHandler;
+UINTN mPageFaultBuffer;
+UINTN mPageFaultIndex = 0;
+//
+// Store the uplink information for each page being used.
+//
+UINT64 *mPageFaultUplink[EXTRA_PAGE_TABLE_PAGES];
+/**
+ Page fault handler.
+
+**/
VOID
EFIAPI
PageFaultHandlerHook (
VOID
);
+/**
+ Hook IDT with our page fault handler so that the on-demand paging works on page fault.
+
+ @param IdtEntry a pointer to IDT entry
+
+**/
VOID
HookPageFaultHandler (
- IN INTERRUPT_GATE_DESCRIPTOR *IdtEntry
+ IN IA32_IDT_GATE_DESCRIPTOR *IdtEntry
)
{
UINT32 RegEax;
- UINT32 RegEdx;
+ UINT8 PhysicalAddressBits;
+ UINTN PageFaultHandlerHookAddress;
- AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
- mPhyMask = LShiftU64 (1, (UINT8)RegEax) - 1;
- mPhyMask &= (1ull << 48) - SIZE_4KB;
-
- mPage1GSupport = FALSE;
- if (PcdGetBool(PcdUse1GPageTable)) {
- AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
- if (RegEax >= 0x80000001) {
- AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
- if ((RegEdx & BIT26) != 0) {
- mPage1GSupport = TRUE;
- }
- }
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000008) {
+ AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
+ PhysicalAddressBits = (UINT8) RegEax;
+ } else {
+ PhysicalAddressBits = 36;
}
+ mPhyMask = LShiftU64 (1, PhysicalAddressBits) - 1;
+ mPhyMask &= (1ull << 48) - SIZE_4KB;
//
// Set Page Fault entry to catch >4G access
//
- mOriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Offset63To32, 32) + IdtEntry->Offset15To0 + (IdtEntry->Offset31To16 << 16));
- IdtEntry->Offset15To0 = (UINT16)((UINTN)PageFaultHandlerHook);
- IdtEntry->SegmentSelector = (UINT16)AsmReadCs ();
- IdtEntry->Attributes = (UINT16)INTERRUPT_GATE_ATTRIBUTE;
- IdtEntry->Offset31To16 = (UINT16)((UINTN)PageFaultHandlerHook >> 16);
- IdtEntry->Offset63To32 = (UINT32)((UINTN)PageFaultHandlerHook >> 32);
- IdtEntry->Reserved = 0;
+ PageFaultHandlerHookAddress = (UINTN)PageFaultHandlerHook;
+ mOriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Bits.OffsetUpper, 32) + IdtEntry->Bits.OffsetLow + (IdtEntry->Bits.OffsetHigh << 16));
+ IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;
+ IdtEntry->Bits.Selector = (UINT16)AsmReadCs ();
+ 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;
if (mPage1GSupport) {
- mS3NvsPageTableAddress = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(2);
+ mPageFaultBuffer = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(2);
}else {
- mS3NvsPageTableAddress = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(6);
+ mPageFaultBuffer = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(6);
+ }
+ ZeroMem (mPageFaultUplink, sizeof (mPageFaultUplink));
+}
+
+/**
+ The function will check if current waking vector is long mode.
+
+ @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
+
+ @retval TRUE Current context need long mode waking vector.
+ @retval FALSE Current context need not long mode waking vector.
+**/
+BOOLEAN
+IsLongModeWakingVector (
+ IN ACPI_S3_CONTEXT *AcpiS3Context
+ )
+{
+ EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
+
+ Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable));
+ if ((Facs == NULL) ||
+ (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ||
+ ((Facs->FirmwareWakingVector == 0) && (Facs->XFirmwareWakingVector == 0)) ) {
+ // Something wrong with FACS
+ return FALSE;
}
+ if (Facs->XFirmwareWakingVector != 0) {
+ if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&
+ ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) &&
+ ((Facs->Flags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) {
+ // Both BIOS and OS wants 64bit vector
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
}
/**
@@ -101,9 +138,10 @@ SetIdtEntry (
IN ACPI_S3_CONTEXT *AcpiS3Context
)
{
- INTERRUPT_GATE_DESCRIPTOR *IdtEntry;
+ IA32_IDT_GATE_DESCRIPTOR *IdtEntry;
IA32_DESCRIPTOR *IdtDescriptor;
UINTN S3DebugBuffer;
+ EFI_STATUS Status;
//
// Restore IDT for debug
@@ -114,39 +152,77 @@ SetIdtEntry (
//
// Setup the default CPU exception handlers
//
- SetupCpuExceptionHandlers ();
+ Status = InitializeCpuExceptionHandlers (NULL);
+ ASSERT_EFI_ERROR (Status);
+
+ DEBUG_CODE (
+ //
+ // Update IDT entry INT3 if the instruction is valid in it
+ //
+ S3DebugBuffer = (UINTN) (AcpiS3Context->S3DebugBufferAddress);
+ if (*(UINTN *)S3DebugBuffer != (UINTN) -1) {
+ IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (3 * sizeof (IA32_IDT_GATE_DESCRIPTOR)));
+ IdtEntry->Bits.OffsetLow = (UINT16)S3DebugBuffer;
+ IdtEntry->Bits.Selector = (UINT16)AsmReadCs ();
+ IdtEntry->Bits.Reserved_0 = 0;
+ IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
+ IdtEntry->Bits.OffsetHigh = (UINT16)(S3DebugBuffer >> 16);
+ IdtEntry->Bits.OffsetUpper = (UINT32)(S3DebugBuffer >> 32);
+ IdtEntry->Bits.Reserved_1 = 0;
+ }
+ );
//
- // Update IDT entry INT3
+ // If both BIOS and OS wants long mode waking vector,
+ // S3ResumePei should have established 1:1 Virtual to Physical identity mapping page table,
+ // no need to hook page fault handler.
//
- IdtEntry = (INTERRUPT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (3 * sizeof (INTERRUPT_GATE_DESCRIPTOR)));
- S3DebugBuffer = (UINTN) (AcpiS3Context->S3DebugBufferAddress);
-
- IdtEntry->Offset15To0 = (UINT16)S3DebugBuffer;
- IdtEntry->SegmentSelector = (UINT16)AsmReadCs ();
- IdtEntry->Attributes = (UINT16)INTERRUPT_GATE_ATTRIBUTE;
- IdtEntry->Offset31To16 = (UINT16)(S3DebugBuffer >> 16);
- IdtEntry->Offset63To32 = (UINT32)(S3DebugBuffer >> 32);
- IdtEntry->Reserved = 0;
+ if (!IsLongModeWakingVector (AcpiS3Context)) {
+ IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR)));
+ HookPageFaultHandler (IdtEntry);
+ }
+}
- IdtEntry = (INTERRUPT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (14 * sizeof (INTERRUPT_GATE_DESCRIPTOR)));
- HookPageFaultHandler (IdtEntry);
+/**
+ Acquire page for page fault.
- AsmWriteIdtr (IdtDescriptor);
-}
+ @param[in, out] Uplink Pointer to up page table entry.
-UINTN
-GetNewPage (
- IN UINTN PageNum
+**/
+VOID
+AcquirePage (
+ IN OUT UINT64 *Uplink
)
{
- UINTN NewPage;
- NewPage = mS3NvsPageTableAddress;
- ZeroMem ((VOID *)NewPage, EFI_PAGES_TO_SIZE(PageNum));
- mS3NvsPageTableAddress += EFI_PAGES_TO_SIZE(PageNum);
- return NewPage;
+ UINTN Address;
+
+ Address = mPageFaultBuffer + EFI_PAGES_TO_SIZE (mPageFaultIndex);
+ ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1));
+
+ //
+ // Cut the previous uplink if it exists and wasn't overwritten.
+ //
+ if ((mPageFaultUplink[mPageFaultIndex] != NULL) &&
+ ((*mPageFaultUplink[mPageFaultIndex] & ~mAddressEncMask & mPhyMask) == Address)) {
+ *mPageFaultUplink[mPageFaultIndex] = 0;
+ }
+
+ //
+ // Link & Record the current uplink.
+ //
+ *Uplink = Address | mAddressEncMask | IA32_PG_P | IA32_PG_RW;
+ mPageFaultUplink[mPageFaultIndex] = Uplink;
+
+ mPageFaultIndex = (mPageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES;
}
+/**
+ The page fault handler that on-demand read >4G memory/MMIO.
+
+ @retval TRUE The page fault is correctly handled.
+ @retval FALSE The page fault is not handled and is passed through to original handler.
+
+**/
BOOLEAN
EFIAPI
PageFaultHandler (
@@ -170,21 +246,21 @@ PageFaultHandler (
PTIndex = BitFieldRead64 (PFAddress, 39, 47);
// PML4E
if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
- PageTable[PTIndex] = GetNewPage (1) | IA32_PG_P | IA32_PG_RW;
+ AcquirePage (&PageTable[PTIndex]);
}
- PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & mPhyMask);
+ PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & mPhyMask);
PTIndex = BitFieldRead64 (PFAddress, 30, 38);
// PDPTE
if (mPage1GSupport) {
- PageTable[PTIndex] = PFAddress | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
+ PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
} else {
if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
- PageTable[PTIndex] = GetNewPage (1) | IA32_PG_P | IA32_PG_RW;
+ AcquirePage (&PageTable[PTIndex]);
}
- PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & mPhyMask);
+ PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & mPhyMask);
PTIndex = BitFieldRead64 (PFAddress, 21, 29);
// PD
- PageTable[PTIndex] = PFAddress | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
+ PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
}
return TRUE;