/** @file
Create the variable to save the base address of page table and stack
for transferring into long mode in IA32 capsule PEI.
Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//
// 8 extra pages for PF handler.
//
#define EXTRA_PAGE_TABLE_PAGES 8
/**
Allocate EfiReservedMemoryType below 4G memory address.
This function allocates EfiReservedMemoryType below 4G memory address.
@param Size Size of memory to allocate.
@return Allocated Address for output.
**/
VOID *
AllocateReservedMemoryBelow4G (
IN UINTN Size
)
{
UINTN Pages;
EFI_PHYSICAL_ADDRESS Address;
EFI_STATUS Status;
VOID *Buffer;
Pages = EFI_SIZE_TO_PAGES (Size);
Address = 0xffffffff;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiReservedMemoryType,
Pages,
&Address
);
ASSERT_EFI_ERROR (Status);
Buffer = (VOID *)(UINTN)Address;
ZeroMem (Buffer, Size);
return Buffer;
}
/**
Register callback function upon VariableLockProtocol
to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
@param[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
VOID
EFIAPI
VariableLockCapsuleLongModeBufferVariable (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
//
// Mark EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to read-only if the Variable Lock protocol exists
//
Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
if (!EFI_ERROR (Status)) {
Status = VariableLock->RequestToLock (VariableLock, EFI_CAPSULE_LONG_MODE_BUFFER_NAME, &gEfiCapsuleVendorGuid);
ASSERT_EFI_ERROR (Status);
}
}
/**
1. Allocate Reserved memory for capsule PEIM to establish a 1:1 Virtual to Physical mapping.
2. Allocate Reserved memroy as a stack for capsule PEIM to transfer from 32-bit mdoe to 64-bit mode.
**/
VOID
EFIAPI
PrepareContextForCapsulePei (
VOID
)
{
UINTN ExtraPageTablePages;
UINT32 RegEax;
UINT32 RegEdx;
UINTN TotalPagesNum;
UINT8 PhysicalAddressBits;
UINT32 NumberOfPml4EntriesNeeded;
UINT32 NumberOfPdpEntriesNeeded;
BOOLEAN Page1GSupport;
EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
EFI_STATUS Status;
VOID *Registration;
//
// Calculate the size of page table, allocate the memory.
//
Page1GSupport = FALSE;
if (PcdGetBool (PcdUse1GPageTable)) {
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
if (RegEax >= 0x80000001) {
AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
if ((RegEdx & BIT26) != 0) {
Page1GSupport = TRUE;
}
}
}
//
// Create 4G page table by default,
// and let PF handler to handle > 4G request.
//
PhysicalAddressBits = 32;
ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;
//
// Calculate the table entries needed.
//
if (PhysicalAddressBits <= 39 ) {
NumberOfPml4EntriesNeeded = 1;
NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
} else {
NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
NumberOfPdpEntriesNeeded = 512;
}
if (!Page1GSupport) {
TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
} else {
TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
}
TotalPagesNum += ExtraPageTablePages;
DEBUG ((DEBUG_INFO, "CapsuleRuntimeDxe X64 TotalPagesNum - 0x%x pages\n", TotalPagesNum));
LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum));
ASSERT (LongModeBuffer.PageTableAddress != 0);
//
// Allocate stack
//
LongModeBuffer.StackSize = PcdGet32 (PcdCapsulePeiLongModeStackSize);
LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize));
ASSERT (LongModeBuffer.StackBaseAddress != 0);
Status = gRT->SetVariable (
EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
&gEfiCapsuleVendorGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (EFI_CAPSULE_LONG_MODE_BUFFER),
&LongModeBuffer
);
if (!EFI_ERROR (Status)) {
//
// Register callback function upon VariableLockProtocol
// to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
//
EfiCreateProtocolNotifyEvent (
&gEdkiiVariableLockProtocolGuid,
TPL_CALLBACK,
VariableLockCapsuleLongModeBufferVariable,
NULL,
&Registration
);
} else {
DEBUG ((DEBUG_ERROR, "FATAL ERROR: CapsuleLongModeBuffer cannot be saved: %r. Capsule in PEI may fail!\n", Status));
gBS->FreePages (LongModeBuffer.StackBaseAddress, EFI_SIZE_TO_PAGES (LongModeBuffer.StackSize));
}
}
/**
Create the variable to save the base address of page table and stack
for transferring into long mode in IA32 capsule PEI.
**/
VOID
SaveLongModeContext (
VOID
)
{
if ((FeaturePcdGet (PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) {
//
// Allocate memory for Capsule IA32 PEIM, it will create page table to transfer to long mode to access capsule above 4GB.
//
PrepareContextForCapsulePei ();
}
}