From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga12.intel.com (mga12.intel.com [192.55.52.136]) by mx.groups.io with SMTP id smtpd.web10.25718.1639489320336796392 for ; Tue, 14 Dec 2021 05:42:00 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@intel.com header.s=intel header.b=iyReacC2; spf=pass (domain: intel.com, ip: 192.55.52.136, mailfrom: min.m.xu@intel.com) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1639489320; x=1671025320; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=3eqtMyfuR7fNfYfG2i+TdPlXu63Wi+Qqj6bi6JWqeCI=; b=iyReacC2Q8CxvZXBdbv7N8uf+HBSZVaJg2E+HU1F3GWwZJ4JZp3IXkoP w0dA5ccIbjX9oENWQ2/OH+2aBm/Ro+w+n1Q6uA8LstDg8IBRGE76rGOD3 AvWcXST1ur82KKk11X9E89BNe4sKVaJ1VFNI90Mh6fTpv+P1pDWfOWA6y yRAYUKmz64P2qxPkYPPVN8pHTfUePAgxx6O4fmlRBOFIUVAeqsntFO7dH ZE6bRTl9P70BCTVZgc9Wv3jZzkblnEkfIqcx6tfaXzmBNaG9tg5PWV/Xt I0Qv+RHrvoD0ke19XX99CrvwJC/UFyN6al0VK5umrGRAAQtxtiVUkoPzW w==; X-IronPort-AV: E=McAfee;i="6200,9189,10197"; a="218993587" X-IronPort-AV: E=Sophos;i="5.88,205,1635231600"; d="scan'208";a="218993587" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga106.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Dec 2021 05:41:51 -0800 X-IronPort-AV: E=Sophos;i="5.88,205,1635231600"; d="scan'208";a="465094031" Received: from mxu9-mobl1.ccr.corp.intel.com ([10.255.30.115]) by orsmga006-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Dec 2021 05:41:47 -0800 From: "Min Xu" To: devel@edk2.groups.io Cc: Min Xu , Michael D Kinney , Brijesh Singh , Erdem Aktas , James Bottomley , Jiewen Yao , Tom Lendacky , Gerd Hoffmann , Leif Lindholm , Ard Biesheuvel , Abner Chang , Daniel Schaefer Subject: [PATCH 02/10] EmbeddedPkg/PrePiLib: Update PrePiLib Date: Tue, 14 Dec 2021 21:41:18 +0800 Message-Id: <20211214134126.869-3-min.m.xu@intel.com> X-Mailer: git-send-email 2.29.2.windows.2 In-Reply-To: <20211214134126.869-1-min.m.xu@intel.com> References: <20211214134126.869-1-min.m.xu@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429 There are below new functions added in EmbeddedPkg/PrePiLib. 1. FfsAnyFvFindFileByName This function is to find the file by name in the FvImage. 2. FfsDecompressSection This function is to decompress a compressed section. 3. FfsProcessFvFileEx This function is to decompress a compressed section and create FvHob/Fv2Hob for all the FvImages. Cc: Michael D Kinney Cc: Brijesh Singh Cc: Erdem Aktas Cc: James Bottomley Cc: Jiewen Yao Cc: Tom Lendacky Cc: Gerd Hoffmann Cc: Leif Lindholm Cc: Ard Biesheuvel Cc: Abner Chang Cc: Daniel Schaefer Signed-off-by: Min Xu --- EmbeddedPkg/Include/Library/PrePiLib.h | 21 +- EmbeddedPkg/Library/PrePiLib/FwVol.c | 399 ++++++++++++++++++++++++- 2 files changed, 418 insertions(+), 2 deletions(-) diff --git a/EmbeddedPkg/Include/Library/PrePiLib.h b/EmbeddedPkg/Include/Library/PrePiLib.h index 7b2cea296f1c..80698e559cdf 100644 --- a/EmbeddedPkg/Include/Library/PrePiLib.h +++ b/EmbeddedPkg/Include/Library/PrePiLib.h @@ -180,6 +180,9 @@ FfsAnyFvFindFirstFile ( /** Get Fv image from the FV type file, then add FV & FV2 Hob. + This function can handle the situation that a compressed + section contains multi-FvImages and create FV/FV2 Hob for + all the FvImages. @param FileHandle File handle of a Fv type file. @@ -190,7 +193,7 @@ FfsAnyFvFindFirstFile ( **/ EFI_STATUS EFIAPI -FfsProcessFvFile ( +FfsProcessFvFileEx ( IN EFI_PEI_FILE_HANDLE FvFileHandle ); @@ -735,4 +738,20 @@ DecompressFirstFv ( VOID ); +/** + * This function find the file by GUID name from a FvImage. + * + * @param Name GUID name of the file + * @param VolumeHandle The handle of the Fv + * @param FileHandle The handle of the File + * @return EFI_STATUS Successfully find the file. + */ +EFI_STATUS +EFIAPI +FfsAnyFvFindFileByName ( + IN CONST EFI_GUID *Name, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ); + #endif diff --git a/EmbeddedPkg/Library/PrePiLib/FwVol.c b/EmbeddedPkg/Library/PrePiLib/FwVol.c index 92ae68f0d382..e714f4876ff5 100644 --- a/EmbeddedPkg/Library/PrePiLib/FwVol.c +++ b/EmbeddedPkg/Library/PrePiLib/FwVol.c @@ -335,7 +335,7 @@ FfsProcessSection ( Status = UefiDecompressGetInfo ( CompressedData, - CompressedDataLength, + (UINT32)CompressedDataLength, &DstBufferSize, &ScratchBufferSize ); @@ -850,6 +850,403 @@ FfsProcessFvFile ( &FvImageInfo.FvName, &(((EFI_FFS_FILE_HEADER *)FvFileHandle)->Name) ); + return EFI_SUCCESS; +} + +/** + * This function find the file by GUID name from a FvImage. + * + * @param Name GUID name of the file + * @param VolumeHandle The handle of the Fv + * @param FileHandle The handle of the File + * @return EFI_STATUS Successfully find the file. + */ +EFI_STATUS +EFIAPI +FfsAnyFvFindFileByName ( + IN CONST EFI_GUID *Name, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + UINTN Instance; + + // + // Search every FV for the file by name + // + Instance = 0; + *FileHandle = NULL; + + while (1) { + Status = FfsFindNextVolume (Instance++, VolumeHandle); + if (EFI_ERROR (Status)) { + break; + } + + Status = FfsFindFileByName (Name, *VolumeHandle, FileHandle); + if (!EFI_ERROR (Status)) { + break; + } + } + + DEBUG ((DEBUG_INFO, "FfsAnyFvFindFileByName with name = %g, %r\n", Name, Status)); + + return Status; +} + +/** + * This function decompress the compressed section. + * + * @param FileHandle File handle + * @param OutputBuffer Pointer to the decompressed data + * @param OutputBufferSize Pointer to the size of the decompressed data + * @return EFI_STATUS + */ +EFI_STATUS +FfsDecompressSection ( + IN VOID *FileHandle, + OUT VOID **OutputBuffer, + OUT UINT32 *OutputBufferSize + ) +{ + EFI_STATUS Status; + UINT32 SectionLength; + UINT32 ParsedLength; + EFI_COMPRESSION_SECTION *CompressionSection; + EFI_COMPRESSION_SECTION2 *CompressionSection2; + UINT32 DstBufferSize; + VOID *ScratchBuffer; + UINT32 ScratchBufferSize; + VOID *DstBuffer; + UINT16 SectionAttribute; + UINT32 AuthenticationStatus; + CHAR8 *CompressedData; + UINTN CompressedDataLength; + EFI_COMMON_SECTION_HEADER *Section; + UINT32 SectionSize; + EFI_FFS_FILE_HEADER *FfsFileHeader; + + *OutputBuffer = NULL; + *OutputBufferSize = 0; + ParsedLength = 0; + Status = EFI_NOT_FOUND; + + FfsFileHeader = (EFI_FFS_FILE_HEADER *)(FileHandle); + // + // Size is 24 bits wide so mask upper 8 bits. + // Does not include FfsFileHeader header size + // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned. + // + Section = (EFI_COMMON_SECTION_HEADER *)(FfsFileHeader + 1); + SectionSize = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF; + SectionSize -= sizeof (EFI_FFS_FILE_HEADER); + + while (ParsedLength < SectionSize) { + if (IS_SECTION2 (Section)) { + ASSERT (SECTION2_SIZE (Section) > 0x00FFFFFF); + } + + DEBUG ((DEBUG_INFO, "Check section type=%x\n", Section->Type)); + + if ((Section->Type == EFI_SECTION_COMPRESSION) || (Section->Type == EFI_SECTION_GUID_DEFINED)) { + DEBUG ((DEBUG_INFO, "It is a compressed section.\n")); + + if (Section->Type == EFI_SECTION_COMPRESSION) { + if (IS_SECTION2 (Section)) { + CompressionSection2 = (EFI_COMPRESSION_SECTION2 *)Section; + SectionLength = SECTION2_SIZE (Section); + + if (CompressionSection2->CompressionType != EFI_STANDARD_COMPRESSION) { + return EFI_UNSUPPORTED; + } + + CompressedData = (CHAR8 *)((EFI_COMPRESSION_SECTION2 *)Section + 1); + CompressedDataLength = (UINT32)SectionLength - sizeof (EFI_COMPRESSION_SECTION2); + } else { + CompressionSection = (EFI_COMPRESSION_SECTION *)Section; + SectionLength = SECTION_SIZE (Section); + + if (CompressionSection->CompressionType != EFI_STANDARD_COMPRESSION) { + return EFI_UNSUPPORTED; + } + + CompressedData = (CHAR8 *)((EFI_COMPRESSION_SECTION *)Section + 1); + CompressedDataLength = (UINT32)SectionLength - sizeof (EFI_COMPRESSION_SECTION); + } + + Status = UefiDecompressGetInfo ( + CompressedData, + (UINT32)CompressedDataLength, + &DstBufferSize, + &ScratchBufferSize + ); + } else if (Section->Type == EFI_SECTION_GUID_DEFINED) { + Status = ExtractGuidedSectionGetInfo ( + Section, + &DstBufferSize, + &ScratchBufferSize, + &SectionAttribute + ); + } + + if (EFI_ERROR (Status)) { + // + // GetInfo failed + // + DEBUG ((DEBUG_ERROR, "Decompress GetInfo Failed - %r\n", Status)); + return EFI_NOT_FOUND; + } + + // + // Allocate scratch buffer + // + ScratchBuffer = (VOID *)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (ScratchBufferSize)); + if (ScratchBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Allocate destination buffer, extra one page for adjustment + // + DstBuffer = (VOID *)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (DstBufferSize) + 1); + if (DstBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // DstBuffer still is one section. Adjust DstBuffer offset, skip EFI section header + // to make section data at page alignment. + // + if (IS_SECTION2 (Section)) { + DstBuffer = (UINT8 *)DstBuffer + EFI_PAGE_SIZE - sizeof (EFI_COMMON_SECTION_HEADER2); + } else { + DstBuffer = (UINT8 *)DstBuffer + EFI_PAGE_SIZE - sizeof (EFI_COMMON_SECTION_HEADER); + } + + // + // Call decompress function + // + if (Section->Type == EFI_SECTION_COMPRESSION) { + if (IS_SECTION2 (Section)) { + CompressedData = (CHAR8 *)((EFI_COMPRESSION_SECTION2 *)Section + 1); + } else { + CompressedData = (CHAR8 *)((EFI_COMPRESSION_SECTION *)Section + 1); + } + + Status = UefiDecompress ( + CompressedData, + DstBuffer, + ScratchBuffer + ); + } else if (Section->Type == EFI_SECTION_GUID_DEFINED) { + Status = ExtractGuidedSectionDecode ( + Section, + &DstBuffer, + ScratchBuffer, + &AuthenticationStatus + ); + } + + if (EFI_ERROR (Status)) { + // + // Decompress failed + // + DEBUG ((DEBUG_ERROR, "Decompress Failed - %r\n", Status)); + return EFI_NOT_FOUND; + } else { + *OutputBuffer = DstBuffer; + *OutputBufferSize = DstBufferSize; + DEBUG (( + DEBUG_INFO, + "Decompressed data is at %x, %x\n", + DstBuffer, + DstBufferSize + )); + return EFI_SUCCESS; + } + } + + if (IS_SECTION2 (Section)) { + SectionLength = SECTION2_SIZE (Section); + } else { + SectionLength = SECTION_SIZE (Section); + } + + // + // SectionLength is adjusted it is 4 byte aligned. + // Go to the next section + // + SectionLength = GET_OCCUPIED_SIZE (SectionLength, 4); + ASSERT (SectionLength != 0); + ParsedLength += SectionLength; + Section = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)Section + SectionLength); + DEBUG ((DEBUG_INFO, "Go to next section.\n")); + } + + return EFI_NOT_FOUND; +} + +#define MAX_FV_IMAGES 8 +/** + Get Fv image from the FV type file, then add FV & FV2 Hob. + This function can handle the situation that a compressed + section contains multi-FvImages and create FV/FV2 Hob for + all the FvImages. + + We assume there are at most MAX_FV_IMAGES (8) FvImages in + a compressed section. If it is not the case, it can be + expanded to a larger one. + + @param FileHandle File handle of a Fv type file. + + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully to process it. + +**/ +EFI_STATUS +EFIAPI +FfsProcessFvFileEx ( + IN EFI_PEI_FILE_HANDLE FvFileHandle + ) +{ + EFI_STATUS Status; + EFI_FV_INFO FvImageInfo; + UINT32 FvAlignment; + VOID *FvBuffer; + EFI_PEI_HOB_POINTERS HobFv2; + UINT32 ParsedLength; + VOID *DecompressBuffer; + UINT32 DecompressBufferSize; + UINT32 FvImagesCnt; + UINT32 SectionLength; + UINTN FvImageHandles[MAX_FV_IMAGES]; + UINT32 Index; + IN EFI_COMMON_SECTION_HEADER *Section; + + FvBuffer = NULL; + DecompressBuffer = NULL; + DecompressBufferSize = 0; + + // + // Check if this EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE file has already + // been extracted. + // + HobFv2.Raw = GetHobList (); + while ((HobFv2.Raw = GetNextHob (EFI_HOB_TYPE_FV2, HobFv2.Raw)) != NULL) { + if (CompareGuid (&(((EFI_FFS_FILE_HEADER *)FvFileHandle)->Name), &HobFv2.FirmwareVolume2->FileName)) { + // + // this FILE has been dispatched, it will not be dispatched again. + // + return EFI_SUCCESS; + } + + HobFv2.Raw = GET_NEXT_HOB (HobFv2); + } + + // + // Decompress section + // + Status = FfsDecompressSection (FvFileHandle, (VOID **)&DecompressBuffer, &DecompressBufferSize); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to decompress section. %r\n", Status)); + ASSERT (FALSE); + return Status; + } + + // + // Find all the FvImages in the decompressed buffer + // + ParsedLength = 0; + FvImagesCnt = 0; + Section = (EFI_COMMON_SECTION_HEADER *)(DecompressBuffer); + + while (ParsedLength < DecompressBufferSize && FvImagesCnt < MAX_FV_IMAGES) { + if (IS_SECTION2 (Section)) { + ASSERT (SECTION2_SIZE (Section) > 0x00FFFFFF); + } + + if (Section->Type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) { + if (IS_SECTION2 (Section)) { + FvImageHandles[FvImagesCnt++] = (UINTN)(VOID *)((UINT8 *)Section + sizeof (EFI_COMMON_SECTION_HEADER2)); + } else { + FvImageHandles[FvImagesCnt++] = (UINTN)(VOID *)((UINT8 *)Section + sizeof (EFI_COMMON_SECTION_HEADER)); + } + } + + if (IS_SECTION2 (Section)) { + SectionLength = SECTION2_SIZE (Section); + } else { + SectionLength = SECTION_SIZE (Section); + } + + // + // SectionLength is adjusted it is 4 byte aligned. + // Go to the next section + // + SectionLength = GET_OCCUPIED_SIZE (SectionLength, 4); + ASSERT (SectionLength != 0); + ParsedLength += SectionLength; + Section = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)Section + SectionLength); + } + + if (FvImagesCnt == 0) { + ASSERT (FALSE); + DEBUG ((DEBUG_ERROR, "Cannot find FvImages.\n")); + return EFI_NOT_FOUND; + } + + DEBUG ((DEBUG_INFO, "Collect FvImageInfo\n")); + for (Index = 0; Index < FvImagesCnt; Index++) { + // + // Collect FvImage Info. + // + ZeroMem (&FvImageInfo, sizeof (FvImageInfo)); + Status = FfsGetVolumeInfo ((VOID *)FvImageHandles[Index], &FvImageInfo); + ASSERT_EFI_ERROR (Status); + DEBUG ((DEBUG_INFO, " Fv Name=%g, Format=%g, Size=0x%x\n", FvImageInfo.FvName, FvImageInfo.FvFormat, FvImageInfo.FvSize)); + + // + // FvAlignment must be more than 8 bytes required by FvHeader structure. + // + FvAlignment = 1 << ((FvImageInfo.FvAttributes & EFI_FVB2_ALIGNMENT) >> 16); + if (FvAlignment < 8) { + FvAlignment = 8; + } + + // + // Check FvImage + // + if ((UINTN)FvImageInfo.FvStart % FvAlignment != 0) { + FvBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINT32)FvImageInfo.FvSize), FvAlignment); + if (FvBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (FvBuffer, FvImageInfo.FvStart, (UINTN)FvImageInfo.FvSize); + // + // Update FvImageInfo after reload FvImage to new aligned memory + // + FfsGetVolumeInfo ((EFI_PEI_FV_HANDLE)FvBuffer, &FvImageInfo); + } + + // + // Inform HOB consumer phase, i.e. DXE core, the existence of this FV + // + BuildFvHob ((EFI_PHYSICAL_ADDRESS)(UINTN)FvImageInfo.FvStart, FvImageInfo.FvSize); + + // + // Makes the encapsulated volume show up in DXE phase to skip processing of + // encapsulated file again. + // + BuildFv2Hob ( + (EFI_PHYSICAL_ADDRESS)(UINTN)FvImageInfo.FvStart, + FvImageInfo.FvSize, + &FvImageInfo.FvName, + &(((EFI_FFS_FILE_HEADER *)FvFileHandle)->Name) + ); + } return EFI_SUCCESS; } -- 2.29.2.windows.2