diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c new file mode 100644 index 00000000..60c1101b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c @@ -0,0 +1,325 @@ +/** @file + This is the implementation to save ACPI S3 Context. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/HobLib.h> +#include <Library/LockBoxLib.h> +#include <Library/PcdLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiLib.h> +#include <Guid/AcpiS3Context.h> +#include <IndustryStandard/Acpi.h> +#include <Protocol/LockBox.h> + +// +// 8 extra pages for PF handler. +// +#define EXTRA_PAGE_TABLE_PAGES 8 + +EFI_GUID mAcpiS3IdtrProfileGuid = { + 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d } +}; + +/** + Allocate memory below 4G memory address. + + This function allocates memory below 4G memory address. + + @param MemoryType Memory type of memory to allocate. + @param Size Size of memory to allocate. + + @return Allocated address for output. + +**/ +VOID* +AllocateMemoryBelow4G ( + IN EFI_MEMORY_TYPE MemoryType, + 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, + MemoryType, + Pages, + &Address + ); + ASSERT_EFI_ERROR (Status); + + Buffer = (VOID *) (UINTN) Address; + ZeroMem (Buffer, Size); + + return Buffer; +} + +/** + The function will check if long mode waking vector is supported. + + @param[in] Facs Pointer to FACS table. + + @retval TRUE Long mode waking vector is supported. + @retval FALSE Long mode waking vector is not supported. + +**/ +BOOLEAN +IsLongModeWakingVectorSupport ( + IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs + ) +{ + if ((Facs == NULL) || + (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ) { + // + // Something wrong with FACS. + // + return FALSE; + } + if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) && + ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0)) { + // + // BIOS supports 64bit waking vector. + // + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + return TRUE; + } + } + return FALSE; +} + +/** + Allocates page table buffer. + + @param[in] LongModeWakingVectorSupport Support long mode waking vector or not. + + If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1 + virtual to physical mapping page table when long mode waking vector is supported, otherwise + create 4G page table when long mode waking vector is not supported and let PF handler to + handle > 4G request. + If BootScriptExector driver will not run in 64-bit mode, this function will do nothing. + + @return Page table base address. + +**/ +EFI_PHYSICAL_ADDRESS +S3AllocatePageTablesBuffer ( + IN BOOLEAN LongModeWakingVectorSupport + ) +{ + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + UINTN ExtraPageTablePages; + UINT32 RegEax; + UINT32 RegEdx; + UINT8 PhysicalAddressBits; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress; + UINTN TotalPageTableSize; + VOID *Hob; + BOOLEAN Page1GSupport; + + 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; + } + } + } + + // + // Get physical address bits supported. + // + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + if (Hob != NULL) { + PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; + } else { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000008) { + AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); + PhysicalAddressBits = (UINT8) RegEax; + } else { + PhysicalAddressBits = 36; + } + } + + // + // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses. + // + ASSERT (PhysicalAddressBits <= 52); + if (PhysicalAddressBits > 48) { + PhysicalAddressBits = 48; + } + + ExtraPageTablePages = 0; + if (!LongModeWakingVectorSupport) { + // + // Create 4G page table when BIOS does not support long mode waking vector, + // 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; + } + + // + // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs. + // + if (!Page1GSupport) { + TotalPageTableSize = 1 + NumberOfPml4EntriesNeeded + NumberOfPml4EntriesNeeded * NumberOfPdpEntriesNeeded; + } else { + TotalPageTableSize = 1 + NumberOfPml4EntriesNeeded; + } + + TotalPageTableSize += ExtraPageTablePages; + DEBUG ((DEBUG_INFO, "AcpiS3ContextSave TotalPageTableSize - 0x%x pages\n", TotalPageTableSize)); + + // + // By architecture only one PageMapLevel4 exists - so lets allocate storage for it. + // + S3NvsPageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGES_TO_SIZE(TotalPageTableSize)); + ASSERT (S3NvsPageTableAddress != 0); + return S3NvsPageTableAddress; + } else { + // + // If DXE is running 32-bit mode, no need to establish page table. + // + return (EFI_PHYSICAL_ADDRESS) 0; + } +} + +/** + Callback function executed when the EndOfDxe event group is signaled. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context The pointer to the notification function's context, which + is implementation-dependent. +**/ +VOID +EFIAPI +AcpiS3ContextSaveOnEndOfDxe ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer; + ACPI_S3_CONTEXT *AcpiS3Context; + IA32_DESCRIPTOR *Idtr; + IA32_IDT_GATE_DESCRIPTOR *IdtGate; + EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; + VOID *Interface; + + DEBUG ((EFI_D_INFO, "AcpiS3ContextSave!\n")); + + Status = gBS->LocateProtocol (&gEfiLockBoxProtocolGuid, NULL, &Interface); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO | EFI_D_WARN, "ACPI S3 context can't be saved without LockBox!\n")); + goto Done; + } + + AcpiS3Context = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(*AcpiS3Context)); + ASSERT (AcpiS3Context != NULL); + AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context; + + // + // Get ACPI Table because we will save its position to variable + // + Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) EfiLocateFirstAcpiTable ( + EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE + ); + AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS) (UINTN) Facs; + ASSERT (AcpiS3Context->AcpiFacsTable != 0); + + IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR)); + Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100); + Idtr->Base = (UINTN)IdtGate; + Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1); + AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr; + + Status = SaveLockBox ( + &mAcpiS3IdtrProfileGuid, + (VOID *)(UINTN)Idtr, + (UINTN)sizeof(IA32_DESCRIPTOR) + ); + ASSERT_EFI_ERROR (Status); + + Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); + ASSERT_EFI_ERROR (Status); + + // + // Allocate page table + // + AcpiS3Context->S3NvsPageTableAddress = S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs)); + + // + // Allocate stack + // + AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize); + AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize)); + ASSERT (AcpiS3Context->BootScriptStackBase != 0); + + // + // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it. + // + AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE); + SetMem ((VOID *)(UINTN)AcpiS3Context->S3DebugBufferAddress, EFI_PAGE_SIZE, 0xff); + + DEBUG((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context->AcpiFacsTable)); + DEBUG((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context->IdtrProfile)); + DEBUG((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context->S3NvsPageTableAddress)); + DEBUG((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context->S3DebugBufferAddress)); + DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context->BootScriptStackBase)); + DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context->BootScriptStackSize)); + + Status = SaveLockBox ( + &gEfiAcpiVariableGuid, + &AcpiS3ContextBuffer, + sizeof(AcpiS3ContextBuffer) + ); + ASSERT_EFI_ERROR (Status); + + Status = SaveLockBox ( + &gEfiAcpiS3ContextGuid, + (VOID *)(UINTN)AcpiS3Context, + (UINTN)sizeof(*AcpiS3Context) + ); + ASSERT_EFI_ERROR (Status); + + Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); + ASSERT_EFI_ERROR (Status); + +Done: + // + // Close the event, deregistering the callback and freeing resources. + // + Status = gBS->CloseEvent (Event); + ASSERT_EFI_ERROR (Status); +} + |