diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c new file mode 100644 index 00000000..cfb66473 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c @@ -0,0 +1,209 @@ +/** @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.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> + +#include <Protocol/Capsule.h> +#include <Protocol/DxeSmmReadyToLock.h> +#include <Protocol/VariableLock.h> + +#include <Guid/CapsuleVendor.h> +#include <Guid/AcpiS3Context.h> + +#include <Library/DebugLib.h> +#include <Library/PcdLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/UefiRuntimeLib.h> +#include <Library/BaseLib.h> +#include <Library/UefiLib.h> +#include <Library/BaseMemoryLib.h> + +// +// 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 ((EFI_D_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 (); + } +} |