/** @file Main SEC phase code. Transitions to PEI. Copyright (c) 2008 - 2015, Intel Corporation. All rights reserved.
(C) Copyright 2016 Hewlett Packard Enterprise Development LP
Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 mPrivateDispatchTable[] = { { (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEfiTemporaryRamSupportPpiGuid, &mTemporaryRamSupportPpi }, }; // // 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", __FUNCTION__, 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; } /** Handle an SEV-ES/GHCB protocol check failure. Notify the hypervisor using the VMGEXIT instruction that the SEV-ES guest wishes to be terminated. @param[in] ReasonCode Reason code to provide to the hypervisor for the termination request. **/ STATIC VOID SevEsProtocolFailure ( IN UINT8 ReasonCode ) { MSR_SEV_ES_GHCB_REGISTER Msr; // // Use the GHCB MSR Protocol to request termination by the hypervisor // Msr.GhcbPhysicalAddress = 0; Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST; Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB; Msr.GhcbTerminate.ReasonCode = ReasonCode; AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress); AsmVmgExit (); ASSERT (FALSE); CpuDeadLoop (); } /** Validate the SEV-ES/GHCB protocol level. Verify that the level of SEV-ES/GHCB protocol supported by the hypervisor and the guest intersect. If they don't intersect, request termination. **/ STATIC VOID SevEsProtocolCheck ( VOID ) { MSR_SEV_ES_GHCB_REGISTER Msr; GHCB *Ghcb; // // Use the GHCB MSR Protocol to obtain the GHCB SEV-ES Information for // protocol checking // Msr.GhcbPhysicalAddress = 0; Msr.GhcbInfo.Function = GHCB_INFO_SEV_INFO_GET; AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress); AsmVmgExit (); Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB); if (Msr.GhcbInfo.Function != GHCB_INFO_SEV_INFO) { SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL); } if (Msr.GhcbProtocol.SevEsProtocolMin > Msr.GhcbProtocol.SevEsProtocolMax) { SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL); } if ((Msr.GhcbProtocol.SevEsProtocolMin > GHCB_VERSION_MAX) || (Msr.GhcbProtocol.SevEsProtocolMax < GHCB_VERSION_MIN)) { SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL); } // // SEV-ES protocol checking succeeded, set the initial GHCB address // Msr.GhcbPhysicalAddress = FixedPcdGet32 (PcdOvmfSecGhcbBase); AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress); Ghcb = Msr.Ghcb; SetMem (Ghcb, sizeof (*Ghcb), 0); // // Set the version to the maximum that can be supported // Ghcb->ProtocolVersion = MIN (Msr.GhcbProtocol.SevEsProtocolMax, GHCB_VERSION_MAX); Ghcb->GhcbUsage = GHCB_STANDARD_USAGE; } /** Determine if SEV-ES is active. During early booting, SEV-ES support code will set a flag to indicate that SEV-ES is enabled. Return the value of this flag as an indicator that SEV-ES is enabled. @retval TRUE SEV-ES is enabled @retval FALSE SEV-ES is not enabled **/ STATIC BOOLEAN SevEsIsEnabled ( VOID ) { SEC_SEV_ES_WORK_AREA *SevEsWorkArea; SevEsWorkArea = (SEC_SEV_ES_WORK_AREA *) FixedPcdGet32 (PcdSevEsWorkAreaBase); return ((SevEsWorkArea != NULL) && (SevEsWorkArea->SevEsEnabled != 0)); } 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; // // 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 ++) { UINT8 *Src; UINT8 *Dst; UINTN Byte; Src = (UINT8 *) &mIdtEntryTemplate; Dst = (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 (NULL, NULL); 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 (); } 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; // // 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. // 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; 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; // // Transfer the control to the PEI core // (*PeiCoreEntryPoint) (SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable); // // 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; }