/** @file | |
Main SEC phase code. Transitions to PEI. | |
Copyright (c) 2008 - 2015, Intel Corporation. All rights reserved.<BR> | |
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> | |
Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiPei.h> | |
#include <Library/BaseLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/PeiServicesLib.h> | |
#include <Library/PcdLib.h> | |
#include <Library/CpuLib.h> | |
#include <Library/DebugAgentLib.h> | |
#include <Library/IoLib.h> | |
#include <Library/PeCoffLib.h> | |
#include <Library/PeCoffGetEntryPointLib.h> | |
#include <Library/PeCoffExtraActionLib.h> | |
#include <Library/ExtractGuidedSectionLib.h> | |
#include <Library/LocalApicLib.h> | |
#include <Library/CpuExceptionHandlerLib.h> | |
#include <Ppi/TemporaryRamSupport.h> | |
#include <Ppi/MpInitLibDep.h> | |
#include <Library/TdxHelperLib.h> | |
#include <Library/CcProbeLib.h> | |
#include "AmdSev.h" | |
#define SEC_IDT_ENTRY_COUNT 34 | |
typedef struct _SEC_IDT_TABLE { | |
EFI_PEI_SERVICES *PeiService; | |
IA32_IDT_GATE_DESCRIPTOR IdtTable[SEC_IDT_ENTRY_COUNT]; | |
} SEC_IDT_TABLE; | |
VOID | |
EFIAPI | |
SecStartupPhase2 ( | |
IN VOID *Context | |
); | |
EFI_STATUS | |
EFIAPI | |
TemporaryRamMigration ( | |
IN CONST EFI_PEI_SERVICES **PeiServices, | |
IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase, | |
IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase, | |
IN UINTN CopySize | |
); | |
// | |
// | |
// | |
EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI mTemporaryRamSupportPpi = { | |
TemporaryRamMigration | |
}; | |
EFI_PEI_PPI_DESCRIPTOR mPrivateDispatchTableMp[] = { | |
{ | |
(EFI_PEI_PPI_DESCRIPTOR_PPI), | |
&gEfiTemporaryRamSupportPpiGuid, | |
&mTemporaryRamSupportPpi | |
}, | |
{ | |
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEfiPeiMpInitLibMpDepPpiGuid, | |
NULL | |
}, | |
}; | |
EFI_PEI_PPI_DESCRIPTOR mPrivateDispatchTableUp[] = { | |
{ | |
(EFI_PEI_PPI_DESCRIPTOR_PPI), | |
&gEfiTemporaryRamSupportPpiGuid, | |
&mTemporaryRamSupportPpi | |
}, | |
{ | |
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEfiPeiMpInitLibUpDepPpiGuid, | |
NULL | |
}, | |
}; | |
// | |
// Template of an IDT entry pointing to 10:FFFFFFE4h. | |
// | |
IA32_IDT_GATE_DESCRIPTOR mIdtEntryTemplate = { | |
{ // Bits | |
0xffe4, // OffsetLow | |
0x10, // Selector | |
0x0, // Reserved_0 | |
IA32_IDT_GATE_TYPE_INTERRUPT_32, // GateType | |
0xffff // OffsetHigh | |
} | |
}; | |
/** | |
Locates the main boot firmware volume. | |
@param[in,out] BootFv On input, the base of the BootFv | |
On output, the decompressed main firmware volume | |
@retval EFI_SUCCESS The main firmware volume was located and decompressed | |
@retval EFI_NOT_FOUND The main firmware volume was not found | |
**/ | |
EFI_STATUS | |
FindMainFv ( | |
IN OUT EFI_FIRMWARE_VOLUME_HEADER **BootFv | |
) | |
{ | |
EFI_FIRMWARE_VOLUME_HEADER *Fv; | |
UINTN Distance; | |
ASSERT (((UINTN)*BootFv & EFI_PAGE_MASK) == 0); | |
Fv = *BootFv; | |
Distance = (UINTN)(*BootFv)->FvLength; | |
do { | |
Fv = (EFI_FIRMWARE_VOLUME_HEADER *)((UINT8 *)Fv - EFI_PAGE_SIZE); | |
Distance += EFI_PAGE_SIZE; | |
if (Distance > SIZE_32MB) { | |
return EFI_NOT_FOUND; | |
} | |
if (Fv->Signature != EFI_FVH_SIGNATURE) { | |
continue; | |
} | |
if ((UINTN)Fv->FvLength > Distance) { | |
continue; | |
} | |
*BootFv = Fv; | |
return EFI_SUCCESS; | |
} while (TRUE); | |
} | |
/** | |
Locates a section within a series of sections | |
with the specified section type. | |
The Instance parameter indicates which instance of the section | |
type to return. (0 is first instance, 1 is second...) | |
@param[in] Sections The sections to search | |
@param[in] SizeOfSections Total size of all sections | |
@param[in] SectionType The section type to locate | |
@param[in] Instance The section instance number | |
@param[out] FoundSection The FFS section if found | |
@retval EFI_SUCCESS The file and section was found | |
@retval EFI_NOT_FOUND The file and section was not found | |
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted | |
**/ | |
EFI_STATUS | |
FindFfsSectionInstance ( | |
IN VOID *Sections, | |
IN UINTN SizeOfSections, | |
IN EFI_SECTION_TYPE SectionType, | |
IN UINTN Instance, | |
OUT EFI_COMMON_SECTION_HEADER **FoundSection | |
) | |
{ | |
EFI_PHYSICAL_ADDRESS CurrentAddress; | |
UINT32 Size; | |
EFI_PHYSICAL_ADDRESS EndOfSections; | |
EFI_COMMON_SECTION_HEADER *Section; | |
EFI_PHYSICAL_ADDRESS EndOfSection; | |
// | |
// Loop through the FFS file sections within the PEI Core FFS file | |
// | |
EndOfSection = (EFI_PHYSICAL_ADDRESS)(UINTN)Sections; | |
EndOfSections = EndOfSection + SizeOfSections; | |
for ( ; ;) { | |
if (EndOfSection == EndOfSections) { | |
break; | |
} | |
CurrentAddress = (EndOfSection + 3) & ~(3ULL); | |
if (CurrentAddress >= EndOfSections) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
Section = (EFI_COMMON_SECTION_HEADER *)(UINTN)CurrentAddress; | |
Size = SECTION_SIZE (Section); | |
if (Size < sizeof (*Section)) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
EndOfSection = CurrentAddress + Size; | |
if (EndOfSection > EndOfSections) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
// | |
// Look for the requested section type | |
// | |
if (Section->Type == SectionType) { | |
if (Instance == 0) { | |
*FoundSection = Section; | |
return EFI_SUCCESS; | |
} else { | |
Instance--; | |
} | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Locates a section within a series of sections | |
with the specified section type. | |
@param[in] Sections The sections to search | |
@param[in] SizeOfSections Total size of all sections | |
@param[in] SectionType The section type to locate | |
@param[out] FoundSection The FFS section if found | |
@retval EFI_SUCCESS The file and section was found | |
@retval EFI_NOT_FOUND The file and section was not found | |
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted | |
**/ | |
EFI_STATUS | |
FindFfsSectionInSections ( | |
IN VOID *Sections, | |
IN UINTN SizeOfSections, | |
IN EFI_SECTION_TYPE SectionType, | |
OUT EFI_COMMON_SECTION_HEADER **FoundSection | |
) | |
{ | |
return FindFfsSectionInstance ( | |
Sections, | |
SizeOfSections, | |
SectionType, | |
0, | |
FoundSection | |
); | |
} | |
/** | |
Locates a FFS file with the specified file type and a section | |
within that file with the specified section type. | |
@param[in] Fv The firmware volume to search | |
@param[in] FileType The file type to locate | |
@param[in] SectionType The section type to locate | |
@param[out] FoundSection The FFS section if found | |
@retval EFI_SUCCESS The file and section was found | |
@retval EFI_NOT_FOUND The file and section was not found | |
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted | |
**/ | |
EFI_STATUS | |
FindFfsFileAndSection ( | |
IN EFI_FIRMWARE_VOLUME_HEADER *Fv, | |
IN EFI_FV_FILETYPE FileType, | |
IN EFI_SECTION_TYPE SectionType, | |
OUT EFI_COMMON_SECTION_HEADER **FoundSection | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_PHYSICAL_ADDRESS CurrentAddress; | |
EFI_PHYSICAL_ADDRESS EndOfFirmwareVolume; | |
EFI_FFS_FILE_HEADER *File; | |
UINT32 Size; | |
EFI_PHYSICAL_ADDRESS EndOfFile; | |
if (Fv->Signature != EFI_FVH_SIGNATURE) { | |
DEBUG ((DEBUG_ERROR, "FV at %p does not have FV header signature\n", Fv)); | |
return EFI_VOLUME_CORRUPTED; | |
} | |
CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Fv; | |
EndOfFirmwareVolume = CurrentAddress + Fv->FvLength; | |
// | |
// Loop through the FFS files in the Boot Firmware Volume | |
// | |
for (EndOfFile = CurrentAddress + Fv->HeaderLength; ; ) { | |
CurrentAddress = (EndOfFile + 7) & ~(7ULL); | |
if (CurrentAddress > EndOfFirmwareVolume) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
File = (EFI_FFS_FILE_HEADER *)(UINTN)CurrentAddress; | |
Size = FFS_FILE_SIZE (File); | |
if (Size < (sizeof (*File) + sizeof (EFI_COMMON_SECTION_HEADER))) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
EndOfFile = CurrentAddress + Size; | |
if (EndOfFile > EndOfFirmwareVolume) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
// | |
// Look for the request file type | |
// | |
if (File->Type != FileType) { | |
continue; | |
} | |
Status = FindFfsSectionInSections ( | |
(VOID *)(File + 1), | |
(UINTN)EndOfFile - (UINTN)(File + 1), | |
SectionType, | |
FoundSection | |
); | |
if (!EFI_ERROR (Status) || (Status == EFI_VOLUME_CORRUPTED)) { | |
return Status; | |
} | |
} | |
} | |
/** | |
Locates the compressed main firmware volume and decompresses it. | |
@param[in,out] Fv On input, the firmware volume to search | |
On output, the decompressed BOOT/PEI FV | |
@retval EFI_SUCCESS The file and section was found | |
@retval EFI_NOT_FOUND The file and section was not found | |
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted | |
**/ | |
EFI_STATUS | |
DecompressMemFvs ( | |
IN OUT EFI_FIRMWARE_VOLUME_HEADER **Fv | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_GUID_DEFINED_SECTION *Section; | |
UINT32 OutputBufferSize; | |
UINT32 ScratchBufferSize; | |
UINT16 SectionAttribute; | |
UINT32 AuthenticationStatus; | |
VOID *OutputBuffer; | |
VOID *ScratchBuffer; | |
EFI_COMMON_SECTION_HEADER *FvSection; | |
EFI_FIRMWARE_VOLUME_HEADER *PeiMemFv; | |
EFI_FIRMWARE_VOLUME_HEADER *DxeMemFv; | |
UINT32 FvHeaderSize; | |
UINT32 FvSectionSize; | |
FvSection = (EFI_COMMON_SECTION_HEADER *)NULL; | |
Status = FindFfsFileAndSection ( | |
*Fv, | |
EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, | |
EFI_SECTION_GUID_DEFINED, | |
(EFI_COMMON_SECTION_HEADER **)&Section | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Unable to find GUID defined section\n")); | |
return Status; | |
} | |
Status = ExtractGuidedSectionGetInfo ( | |
Section, | |
&OutputBufferSize, | |
&ScratchBufferSize, | |
&SectionAttribute | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Unable to GetInfo for GUIDed section\n")); | |
return Status; | |
} | |
OutputBuffer = (VOID *)((UINT8 *)(UINTN)PcdGet32 (PcdOvmfDxeMemFvBase) + SIZE_1MB); | |
ScratchBuffer = ALIGN_POINTER ((UINT8 *)OutputBuffer + OutputBufferSize, SIZE_1MB); | |
DEBUG (( | |
DEBUG_VERBOSE, | |
"%a: OutputBuffer@%p+0x%x ScratchBuffer@%p+0x%x " | |
"PcdOvmfDecompressionScratchEnd=0x%x\n", | |
__func__, | |
OutputBuffer, | |
OutputBufferSize, | |
ScratchBuffer, | |
ScratchBufferSize, | |
PcdGet32 (PcdOvmfDecompressionScratchEnd) | |
)); | |
ASSERT ( | |
(UINTN)ScratchBuffer + ScratchBufferSize == | |
PcdGet32 (PcdOvmfDecompressionScratchEnd) | |
); | |
Status = ExtractGuidedSectionDecode ( | |
Section, | |
&OutputBuffer, | |
ScratchBuffer, | |
&AuthenticationStatus | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Error during GUID section decode\n")); | |
return Status; | |
} | |
Status = FindFfsSectionInstance ( | |
OutputBuffer, | |
OutputBufferSize, | |
EFI_SECTION_FIRMWARE_VOLUME_IMAGE, | |
0, | |
&FvSection | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Unable to find PEI FV section\n")); | |
return Status; | |
} | |
ASSERT ( | |
SECTION_SIZE (FvSection) == | |
(PcdGet32 (PcdOvmfPeiMemFvSize) + sizeof (*FvSection)) | |
); | |
ASSERT (FvSection->Type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE); | |
PeiMemFv = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)PcdGet32 (PcdOvmfPeiMemFvBase); | |
CopyMem (PeiMemFv, (VOID *)(FvSection + 1), PcdGet32 (PcdOvmfPeiMemFvSize)); | |
if (PeiMemFv->Signature != EFI_FVH_SIGNATURE) { | |
DEBUG ((DEBUG_ERROR, "Extracted FV at %p does not have FV header signature\n", PeiMemFv)); | |
CpuDeadLoop (); | |
return EFI_VOLUME_CORRUPTED; | |
} | |
Status = FindFfsSectionInstance ( | |
OutputBuffer, | |
OutputBufferSize, | |
EFI_SECTION_FIRMWARE_VOLUME_IMAGE, | |
1, | |
&FvSection | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Unable to find DXE FV section\n")); | |
return Status; | |
} | |
ASSERT (FvSection->Type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE); | |
if (IS_SECTION2 (FvSection)) { | |
FvSectionSize = SECTION2_SIZE (FvSection); | |
FvHeaderSize = sizeof (EFI_COMMON_SECTION_HEADER2); | |
} else { | |
FvSectionSize = SECTION_SIZE (FvSection); | |
FvHeaderSize = sizeof (EFI_COMMON_SECTION_HEADER); | |
} | |
ASSERT (FvSectionSize == (PcdGet32 (PcdOvmfDxeMemFvSize) + FvHeaderSize)); | |
DxeMemFv = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)PcdGet32 (PcdOvmfDxeMemFvBase); | |
CopyMem (DxeMemFv, (VOID *)((UINTN)FvSection + FvHeaderSize), PcdGet32 (PcdOvmfDxeMemFvSize)); | |
if (DxeMemFv->Signature != EFI_FVH_SIGNATURE) { | |
DEBUG ((DEBUG_ERROR, "Extracted FV at %p does not have FV header signature\n", DxeMemFv)); | |
CpuDeadLoop (); | |
return EFI_VOLUME_CORRUPTED; | |
} | |
*Fv = PeiMemFv; | |
return EFI_SUCCESS; | |
} | |
/** | |
Locates the PEI Core entry point address | |
@param[in] Fv The firmware volume to search | |
@param[out] PeiCoreEntryPoint The entry point of the PEI Core image | |
@retval EFI_SUCCESS The file and section was found | |
@retval EFI_NOT_FOUND The file and section was not found | |
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted | |
**/ | |
EFI_STATUS | |
FindPeiCoreImageBaseInFv ( | |
IN EFI_FIRMWARE_VOLUME_HEADER *Fv, | |
OUT EFI_PHYSICAL_ADDRESS *PeiCoreImageBase | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_COMMON_SECTION_HEADER *Section; | |
Status = FindFfsFileAndSection ( | |
Fv, | |
EFI_FV_FILETYPE_PEI_CORE, | |
EFI_SECTION_PE32, | |
&Section | |
); | |
if (EFI_ERROR (Status)) { | |
Status = FindFfsFileAndSection ( | |
Fv, | |
EFI_FV_FILETYPE_PEI_CORE, | |
EFI_SECTION_TE, | |
&Section | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Unable to find PEI Core image\n")); | |
return Status; | |
} | |
} | |
*PeiCoreImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)(Section + 1); | |
return EFI_SUCCESS; | |
} | |
/** | |
Reads 8-bits of CMOS data. | |
Reads the 8-bits of CMOS data at the location specified by Index. | |
The 8-bit read value is returned. | |
@param Index The CMOS location to read. | |
@return The value read. | |
**/ | |
STATIC | |
UINT8 | |
CmosRead8 ( | |
IN UINTN Index | |
) | |
{ | |
IoWrite8 (0x70, (UINT8)Index); | |
return IoRead8 (0x71); | |
} | |
STATIC | |
BOOLEAN | |
IsS3Resume ( | |
VOID | |
) | |
{ | |
return (CmosRead8 (0xF) == 0xFE); | |
} | |
STATIC | |
EFI_STATUS | |
GetS3ResumePeiFv ( | |
IN OUT EFI_FIRMWARE_VOLUME_HEADER **PeiFv | |
) | |
{ | |
*PeiFv = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)PcdGet32 (PcdOvmfPeiMemFvBase); | |
return EFI_SUCCESS; | |
} | |
/** | |
Locates the PEI Core entry point address | |
@param[in,out] Fv The firmware volume to search | |
@param[out] PeiCoreEntryPoint The entry point of the PEI Core image | |
@retval EFI_SUCCESS The file and section was found | |
@retval EFI_NOT_FOUND The file and section was not found | |
@retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted | |
**/ | |
VOID | |
FindPeiCoreImageBase ( | |
IN OUT EFI_FIRMWARE_VOLUME_HEADER **BootFv, | |
OUT EFI_PHYSICAL_ADDRESS *PeiCoreImageBase | |
) | |
{ | |
BOOLEAN S3Resume; | |
*PeiCoreImageBase = 0; | |
S3Resume = IsS3Resume (); | |
if (S3Resume && !FeaturePcdGet (PcdSmmSmramRequire)) { | |
// | |
// A malicious runtime OS may have injected something into our previously | |
// decoded PEI FV, but we don't care about that unless SMM/SMRAM is required. | |
// | |
DEBUG ((DEBUG_VERBOSE, "SEC: S3 resume\n")); | |
GetS3ResumePeiFv (BootFv); | |
} else { | |
// | |
// We're either not resuming, or resuming "securely" -- we'll decompress | |
// both PEI FV and DXE FV from pristine flash. | |
// | |
DEBUG (( | |
DEBUG_VERBOSE, | |
"SEC: %a\n", | |
S3Resume ? "S3 resume (with PEI decompression)" : "Normal boot" | |
)); | |
FindMainFv (BootFv); | |
DecompressMemFvs (BootFv); | |
} | |
FindPeiCoreImageBaseInFv (*BootFv, PeiCoreImageBase); | |
} | |
/** | |
Find core image base. | |
**/ | |
EFI_STATUS | |
FindImageBase ( | |
IN EFI_FIRMWARE_VOLUME_HEADER *BootFirmwareVolumePtr, | |
OUT EFI_PHYSICAL_ADDRESS *SecCoreImageBase | |
) | |
{ | |
EFI_PHYSICAL_ADDRESS CurrentAddress; | |
EFI_PHYSICAL_ADDRESS EndOfFirmwareVolume; | |
EFI_FFS_FILE_HEADER *File; | |
UINT32 Size; | |
EFI_PHYSICAL_ADDRESS EndOfFile; | |
EFI_COMMON_SECTION_HEADER *Section; | |
EFI_PHYSICAL_ADDRESS EndOfSection; | |
*SecCoreImageBase = 0; | |
CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)BootFirmwareVolumePtr; | |
EndOfFirmwareVolume = CurrentAddress + BootFirmwareVolumePtr->FvLength; | |
// | |
// Loop through the FFS files in the Boot Firmware Volume | |
// | |
for (EndOfFile = CurrentAddress + BootFirmwareVolumePtr->HeaderLength; ; ) { | |
CurrentAddress = (EndOfFile + 7) & 0xfffffffffffffff8ULL; | |
if (CurrentAddress > EndOfFirmwareVolume) { | |
return EFI_NOT_FOUND; | |
} | |
File = (EFI_FFS_FILE_HEADER *)(UINTN)CurrentAddress; | |
Size = FFS_FILE_SIZE (File); | |
if (Size < sizeof (*File)) { | |
return EFI_NOT_FOUND; | |
} | |
EndOfFile = CurrentAddress + Size; | |
if (EndOfFile > EndOfFirmwareVolume) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Look for SEC Core | |
// | |
if (File->Type != EFI_FV_FILETYPE_SECURITY_CORE) { | |
continue; | |
} | |
// | |
// Loop through the FFS file sections within the FFS file | |
// | |
EndOfSection = (EFI_PHYSICAL_ADDRESS)(UINTN)(File + 1); | |
for ( ; ;) { | |
CurrentAddress = (EndOfSection + 3) & 0xfffffffffffffffcULL; | |
Section = (EFI_COMMON_SECTION_HEADER *)(UINTN)CurrentAddress; | |
Size = SECTION_SIZE (Section); | |
if (Size < sizeof (*Section)) { | |
return EFI_NOT_FOUND; | |
} | |
EndOfSection = CurrentAddress + Size; | |
if (EndOfSection > EndOfFile) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Look for executable sections | |
// | |
if ((Section->Type == EFI_SECTION_PE32) || (Section->Type == EFI_SECTION_TE)) { | |
if (File->Type == EFI_FV_FILETYPE_SECURITY_CORE) { | |
*SecCoreImageBase = (PHYSICAL_ADDRESS)(UINTN)(Section + 1); | |
} | |
break; | |
} | |
} | |
// | |
// SEC Core image found | |
// | |
if (*SecCoreImageBase != 0) { | |
return EFI_SUCCESS; | |
} | |
} | |
} | |
/* | |
Find and return Pei Core entry point. | |
It also find SEC and PEI Core file debug information. It will report them if | |
remote debug is enabled. | |
**/ | |
VOID | |
FindAndReportEntryPoints ( | |
IN EFI_FIRMWARE_VOLUME_HEADER **BootFirmwareVolumePtr, | |
OUT EFI_PEI_CORE_ENTRY_POINT *PeiCoreEntryPoint | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_PHYSICAL_ADDRESS SecCoreImageBase; | |
EFI_PHYSICAL_ADDRESS PeiCoreImageBase; | |
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; | |
// | |
// Find SEC Core and PEI Core image base | |
// | |
Status = FindImageBase (*BootFirmwareVolumePtr, &SecCoreImageBase); | |
ASSERT_EFI_ERROR (Status); | |
FindPeiCoreImageBase (BootFirmwareVolumePtr, &PeiCoreImageBase); | |
ZeroMem ((VOID *)&ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT)); | |
// | |
// Report SEC Core debug information when remote debug is enabled | |
// | |
ImageContext.ImageAddress = SecCoreImageBase; | |
ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageContext.ImageAddress); | |
PeCoffLoaderRelocateImageExtraAction (&ImageContext); | |
// | |
// Report PEI Core debug information when remote debug is enabled | |
// | |
ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)PeiCoreImageBase; | |
ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageContext.ImageAddress); | |
PeCoffLoaderRelocateImageExtraAction (&ImageContext); | |
// | |
// Find PEI Core entry point | |
// | |
Status = PeCoffLoaderGetEntryPoint ((VOID *)(UINTN)PeiCoreImageBase, (VOID **)PeiCoreEntryPoint); | |
if (EFI_ERROR (Status)) { | |
*PeiCoreEntryPoint = 0; | |
} | |
return; | |
} | |
VOID | |
EFIAPI | |
SecCoreStartupWithStack ( | |
IN EFI_FIRMWARE_VOLUME_HEADER *BootFv, | |
IN VOID *TopOfCurrentStack | |
) | |
{ | |
EFI_SEC_PEI_HAND_OFF SecCoreData; | |
SEC_IDT_TABLE IdtTableInStack; | |
IA32_DESCRIPTOR IdtDescriptor; | |
UINT32 Index; | |
volatile UINT8 *Table; | |
#if defined (TDX_GUEST_SUPPORTED) | |
if (CcProbe () == CcGuestTypeIntelTdx) { | |
// | |
// From the security perspective all the external input should be measured before | |
// it is consumed. TdHob and Configuration FV (Cfv) image are passed from VMM | |
// and should be measured here. | |
// | |
if (EFI_ERROR (TdxHelperMeasureTdHob ())) { | |
CpuDeadLoop (); | |
} | |
if (EFI_ERROR (TdxHelperMeasureCfvImage ())) { | |
CpuDeadLoop (); | |
} | |
// | |
// For Td guests, the memory map info is in TdHobLib. It should be processed | |
// first so that the memory is accepted. Otherwise access to the unaccepted | |
// memory will trigger tripple fault. | |
// | |
if (TdxHelperProcessTdHob () != EFI_SUCCESS) { | |
CpuDeadLoop (); | |
} | |
} | |
#endif | |
// | |
// To ensure SMM can't be compromised on S3 resume, we must force re-init of | |
// the BaseExtractGuidedSectionLib. Since this is before library contructors | |
// are called, we must use a loop rather than SetMem. | |
// | |
Table = (UINT8 *)(UINTN)FixedPcdGet64 (PcdGuidedExtractHandlerTableAddress); | |
for (Index = 0; | |
Index < FixedPcdGet32 (PcdGuidedExtractHandlerTableSize); | |
++Index) | |
{ | |
Table[Index] = 0; | |
} | |
// | |
// Initialize IDT - Since this is before library constructors are called, | |
// we use a loop rather than CopyMem. | |
// | |
IdtTableInStack.PeiService = NULL; | |
for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index++) { | |
// | |
// Declare the local variables that actually move the data elements as | |
// volatile to prevent the optimizer from replacing this function with | |
// the intrinsic memcpy() | |
// | |
CONST UINT8 *Src; | |
volatile UINT8 *Dst; | |
UINTN Byte; | |
Src = (CONST UINT8 *)&mIdtEntryTemplate; | |
Dst = (volatile UINT8 *)&IdtTableInStack.IdtTable[Index]; | |
for (Byte = 0; Byte < sizeof (mIdtEntryTemplate); Byte++) { | |
Dst[Byte] = Src[Byte]; | |
} | |
} | |
IdtDescriptor.Base = (UINTN)&IdtTableInStack.IdtTable; | |
IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1); | |
if (SevEsIsEnabled ()) { | |
SevEsProtocolCheck (); | |
// | |
// For SEV-ES guests, the exception handler is needed before calling | |
// ProcessLibraryConstructorList() because some of the library constructors | |
// perform some functions that result in #VC exceptions being generated. | |
// | |
// Due to this code executing before library constructors, *all* library | |
// API calls are theoretically interface contract violations. However, | |
// because this is SEC (executing in flash), those constructors cannot | |
// write variables with static storage duration anyway. Furthermore, only | |
// a small, restricted set of APIs, such as AsmWriteIdtr() and | |
// InitializeCpuExceptionHandlers(), are called, where we require that the | |
// underlying library not require constructors to have been invoked and | |
// that the library instance not trigger any #VC exceptions. | |
// | |
AsmWriteIdtr (&IdtDescriptor); | |
InitializeCpuExceptionHandlers (NULL); | |
} | |
ProcessLibraryConstructorList (); | |
if (!SevEsIsEnabled ()) { | |
// | |
// For non SEV-ES guests, just load the IDTR. | |
// | |
AsmWriteIdtr (&IdtDescriptor); | |
} else { | |
// | |
// Under SEV-ES, the hypervisor can't modify CR0 and so can't enable | |
// caching in order to speed up the boot. Enable caching early for | |
// an SEV-ES guest. | |
// | |
AsmEnableCache (); | |
} | |
#if defined (TDX_GUEST_SUPPORTED) | |
if (CcProbe () == CcGuestTypeIntelTdx) { | |
// | |
// InitializeCpuExceptionHandlers () should be called in Td guests so that | |
// #VE exceptions can be handled correctly. | |
// | |
InitializeCpuExceptionHandlers (NULL); | |
} | |
#endif | |
DEBUG (( | |
DEBUG_INFO, | |
"SecCoreStartupWithStack(0x%x, 0x%x)\n", | |
(UINT32)(UINTN)BootFv, | |
(UINT32)(UINTN)TopOfCurrentStack | |
)); | |
// | |
// Initialize floating point operating environment | |
// to be compliant with UEFI spec. | |
// | |
InitializeFloatingPointUnits (); | |
#if defined (MDE_CPU_X64) | |
// | |
// ASSERT that the Page Tables were set by the reset vector code to | |
// the address we expect. | |
// | |
ASSERT (AsmReadCr3 () == (UINTN)PcdGet32 (PcdOvmfSecPageTablesBase)); | |
#endif | |
// | |
// |-------------| <-- TopOfCurrentStack | |
// | Stack | 32k | |
// |-------------| | |
// | Heap | 32k | |
// |-------------| <-- SecCoreData.TemporaryRamBase | |
// | |
ASSERT ( | |
(UINTN)(PcdGet32 (PcdOvmfSecPeiTempRamBase) + | |
PcdGet32 (PcdOvmfSecPeiTempRamSize)) == | |
(UINTN)TopOfCurrentStack | |
); | |
// | |
// Initialize SEC hand-off state | |
// | |
SecCoreData.DataSize = sizeof (EFI_SEC_PEI_HAND_OFF); | |
SecCoreData.TemporaryRamSize = (UINTN)PcdGet32 (PcdOvmfSecPeiTempRamSize); | |
SecCoreData.TemporaryRamBase = (VOID *)((UINT8 *)TopOfCurrentStack - SecCoreData.TemporaryRamSize); | |
SecCoreData.PeiTemporaryRamBase = SecCoreData.TemporaryRamBase; | |
SecCoreData.PeiTemporaryRamSize = SecCoreData.TemporaryRamSize >> 1; | |
SecCoreData.StackBase = (UINT8 *)SecCoreData.TemporaryRamBase + SecCoreData.PeiTemporaryRamSize; | |
SecCoreData.StackSize = SecCoreData.TemporaryRamSize >> 1; | |
SecCoreData.BootFirmwareVolumeBase = BootFv; | |
SecCoreData.BootFirmwareVolumeSize = (UINTN)BootFv->FvLength; | |
// | |
// Validate the System RAM used in the SEC Phase | |
// | |
SecValidateSystemRam (); | |
// | |
// Make sure the 8259 is masked before initializing the Debug Agent and the debug timer is enabled | |
// | |
IoWrite8 (0x21, 0xff); | |
IoWrite8 (0xA1, 0xff); | |
// | |
// Initialize Local APIC Timer hardware and disable Local APIC Timer | |
// interrupts before initializing the Debug Agent and the debug timer is | |
// enabled. | |
// | |
SecMapApicBaseUnencrypted (); | |
InitializeApicTimer (0, MAX_UINT32, TRUE, 5); | |
DisableApicTimerInterrupt (); | |
// | |
// Initialize Debug Agent to support source level debug in SEC/PEI phases before memory ready. | |
// | |
InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, &SecCoreData, SecStartupPhase2); | |
} | |
/** | |
Caller provided function to be invoked at the end of InitializeDebugAgent(). | |
Entry point to the C language phase of SEC. After the SEC assembly | |
code has initialized some temporary memory and set up the stack, | |
the control is transferred to this function. | |
@param[in] Context The first input parameter of InitializeDebugAgent(). | |
**/ | |
VOID | |
EFIAPI | |
SecStartupPhase2 ( | |
IN VOID *Context | |
) | |
{ | |
EFI_SEC_PEI_HAND_OFF *SecCoreData; | |
EFI_FIRMWARE_VOLUME_HEADER *BootFv; | |
EFI_PEI_CORE_ENTRY_POINT PeiCoreEntryPoint; | |
EFI_PEI_PPI_DESCRIPTOR *EfiPeiPpiDescriptor; | |
SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Context; | |
// | |
// Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug | |
// is enabled. | |
// | |
BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase; | |
FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint); | |
SecCoreData->BootFirmwareVolumeBase = BootFv; | |
SecCoreData->BootFirmwareVolumeSize = (UINTN)BootFv->FvLength; | |
// | |
// Td guest is required to use the MpInitLibUp (unique-processor version). | |
// Other guests use the MpInitLib (multi-processor version). | |
// | |
if (CcProbe () == CcGuestTypeIntelTdx) { | |
EfiPeiPpiDescriptor = (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTableUp; | |
} else { | |
EfiPeiPpiDescriptor = (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTableMp; | |
} | |
// | |
// Transfer the control to the PEI core | |
// | |
(*PeiCoreEntryPoint)(SecCoreData, EfiPeiPpiDescriptor); | |
// | |
// If we get here then the PEI Core returned, which is not recoverable. | |
// | |
ASSERT (FALSE); | |
CpuDeadLoop (); | |
} | |
EFI_STATUS | |
EFIAPI | |
TemporaryRamMigration ( | |
IN CONST EFI_PEI_SERVICES **PeiServices, | |
IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase, | |
IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase, | |
IN UINTN CopySize | |
) | |
{ | |
IA32_DESCRIPTOR IdtDescriptor; | |
VOID *OldHeap; | |
VOID *NewHeap; | |
VOID *OldStack; | |
VOID *NewStack; | |
DEBUG_AGENT_CONTEXT_POSTMEM_SEC DebugAgentContext; | |
BOOLEAN OldStatus; | |
BASE_LIBRARY_JUMP_BUFFER JumpBuffer; | |
DEBUG (( | |
DEBUG_INFO, | |
"TemporaryRamMigration(0x%Lx, 0x%Lx, 0x%Lx)\n", | |
TemporaryMemoryBase, | |
PermanentMemoryBase, | |
(UINT64)CopySize | |
)); | |
OldHeap = (VOID *)(UINTN)TemporaryMemoryBase; | |
NewHeap = (VOID *)((UINTN)PermanentMemoryBase + (CopySize >> 1)); | |
OldStack = (VOID *)((UINTN)TemporaryMemoryBase + (CopySize >> 1)); | |
NewStack = (VOID *)(UINTN)PermanentMemoryBase; | |
DebugAgentContext.HeapMigrateOffset = (UINTN)NewHeap - (UINTN)OldHeap; | |
DebugAgentContext.StackMigrateOffset = (UINTN)NewStack - (UINTN)OldStack; | |
OldStatus = SaveAndSetDebugTimerInterrupt (FALSE); | |
InitializeDebugAgent (DEBUG_AGENT_INIT_POSTMEM_SEC, (VOID *)&DebugAgentContext, NULL); | |
// | |
// Migrate Heap | |
// | |
CopyMem (NewHeap, OldHeap, CopySize >> 1); | |
// | |
// Migrate Stack | |
// | |
CopyMem (NewStack, OldStack, CopySize >> 1); | |
// | |
// Rebase IDT table in permanent memory | |
// | |
AsmReadIdtr (&IdtDescriptor); | |
IdtDescriptor.Base = IdtDescriptor.Base - (UINTN)OldStack + (UINTN)NewStack; | |
AsmWriteIdtr (&IdtDescriptor); | |
// | |
// Use SetJump()/LongJump() to switch to a new stack. | |
// | |
if (SetJump (&JumpBuffer) == 0) { | |
#if defined (MDE_CPU_IA32) | |
JumpBuffer.Esp = JumpBuffer.Esp + DebugAgentContext.StackMigrateOffset; | |
JumpBuffer.Ebp = JumpBuffer.Ebp + DebugAgentContext.StackMigrateOffset; | |
#endif | |
#if defined (MDE_CPU_X64) | |
JumpBuffer.Rsp = JumpBuffer.Rsp + DebugAgentContext.StackMigrateOffset; | |
JumpBuffer.Rbp = JumpBuffer.Rbp + DebugAgentContext.StackMigrateOffset; | |
#endif | |
LongJump (&JumpBuffer, (UINTN)-1); | |
} | |
SaveAndSetDebugTimerInterrupt (OldStatus); | |
return EFI_SUCCESS; | |
} |