/** @file Main SEC phase code. Transitions to PEI. Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include /** temporary memory to permanent memory and do stack switching. @param[in] PeiServices Pointer to the PEI Services Table. @param[in] TemporaryMemoryBase Temporary Memory Base address. @param[in] PermanentMemoryBase Permanent Memory Base address. @param[in] CopySize The size of memory that needs to be migrated. @retval EFI_SUCCESS Migration successful. **/ STATIC EFI_STATUS EFIAPI TemporaryRamMigration ( IN CONST EFI_PEI_SERVICES **PeiServices, IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase, IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase, IN UINTN CopySize ); STATIC EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI mTemporaryRamSupportPpi = { TemporaryRamMigration }; STATIC EFI_PEI_PPI_DESCRIPTOR mPrivateDispatchTable[] = { { (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEfiTemporaryRamSupportPpiGuid, &mTemporaryRamSupportPpi }, }; /** 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 **/ STATIC 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 **/ STATIC 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 **/ STATIC 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 = *(UINT32 *)File->Size & 0xffffff; 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 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 **/ STATIC 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; } /** 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. **/ STATIC VOID FindAndReportEntryPoints ( IN EFI_FIRMWARE_VOLUME_HEADER **BootFirmwareVolumePtr, OUT EFI_PEI_CORE_ENTRY_POINT *PeiCoreEntryPoint ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS PeiCoreImageBase = 0; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; Status = FindPeiCoreImageBaseInFv (*BootFirmwareVolumePtr, &PeiCoreImageBase); ASSERT (Status == EFI_SUCCESS); ZeroMem ((VOID *)&ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT)); // // 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; } /** Find the peicore entry point and jump to the entry point to execute. @param[in] Context The first input parameter of InitializeDebugAgent(). **/ STATIC 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; DEBUG ((DEBUG_INFO, "Find Pei EntryPoint=%p\n", PeiCoreEntryPoint)); // // Transfer the control to the PEI core // DEBUG ((DEBUG_INFO, "SecStartupPhase2 %p\n", PeiCoreEntryPoint)); (*PeiCoreEntryPoint)(SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable); // // If we get here then the PEI Core returned, which is not recoverable. // ASSERT (FALSE); CpuDeadLoop (); } /** Entry point to the C language phase of SEC. initialize some temporary memory and set up the stack, the control is transferred to this function. @param[in] BootFv The pointer to the PEI FV in memory. @param[in] TopOfCurrentStack Top of Current Stack. **/ VOID EFIAPI SecCoreStartupWithStack ( IN EFI_FIRMWARE_VOLUME_HEADER *BootFv, IN VOID *TopOfCurrentStack ) { EFI_SEC_PEI_HAND_OFF SecCoreData; EFI_FIRMWARE_VOLUME_HEADER *BootPeiFv = (EFI_FIRMWARE_VOLUME_HEADER *)BootFv; DEBUG ((DEBUG_INFO, "Entering C environment\n")); ProcessLibraryConstructorList (); DEBUG (( DEBUG_INFO, "SecCoreStartupWithStack (0x%lx, 0x%lx)\n", (UINTN)BootFv, (UINTN)TopOfCurrentStack )); DEBUG (( DEBUG_INFO, "(0x%lx, 0x%lx)\n", (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase)), (UINTN)(FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) )); // |-------------| <-- TopOfCurrentStack // | BSP Stack | 32k // |-------------| // | BSP Heap | 32k // |-------------| <-- SecCoreData.TemporaryRamBase // | Ap Stack | 384k // |-------------| // | Exception | 64k // |-------------| <-- PcdOvmfSecPeiTempRamBase ASSERT ( (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) == (UINTN)TopOfCurrentStack ); // // Initialize SEC hand-off state // SecCoreData.DataSize = sizeof (EFI_SEC_PEI_HAND_OFF); SecCoreData.TemporaryRamSize = (UINTN)SIZE_64KB; SecCoreData.TemporaryRamBase = (VOID *)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize) - 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 = BootPeiFv; SecCoreData.BootFirmwareVolumeSize = (UINTN)BootPeiFv->FvLength; DEBUG (( DEBUG_INFO, "&SecCoreData.BootFirmwareVolumeBase=%lx SecCoreData.BootFirmwareVolumeBase=%lx\n", (UINT64)&(SecCoreData.BootFirmwareVolumeBase), (UINT64)(SecCoreData.BootFirmwareVolumeBase) )); DEBUG (( DEBUG_INFO, "&SecCoreData.BootFirmwareVolumeSize=%lx SecCoreData.BootFirmwareVolumeSize=%lx\n", (UINT64)&(SecCoreData.BootFirmwareVolumeSize), (UINT64)(SecCoreData.BootFirmwareVolumeSize) )); // // Initialize Debug Agent to support source level debug in SEC/PEI phases before memory ready. // InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, NULL, NULL); SecStartupPhase2 (&SecCoreData); } /** temporary memory to permanent memory and do stack switching. @param[in] PeiServices Pointer to the PEI Services Table. @param[in] TemporaryMemoryBase Temporary Memory Base address. @param[in] PermanentMemoryBase Permanent Memory Base address. @param[in] CopySize The size of memory that needs to be migrated. @retval EFI_SUCCESS Migration successful. **/ STATIC EFI_STATUS EFIAPI TemporaryRamMigration ( IN CONST EFI_PEI_SERVICES **PeiServices, IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase, IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase, IN UINTN CopySize ) { VOID *OldHeap; VOID *NewHeap; VOID *OldStack; VOID *NewStack; 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; // // Migrate Heap // CopyMem (NewHeap, OldHeap, CopySize >> 1); // // Migrate Stack // CopyMem (NewStack, OldStack, CopySize >> 1); // Use SetJump ()/LongJump () to switch to a new stack. // if (SetJump (&JumpBuffer) == 0) { JumpBuffer.SP = JumpBuffer.SP - (UINTN)OldStack + (UINTN)NewStack; LongJump (&JumpBuffer, (UINTN)-1); } return EFI_SUCCESS; }