/** Fill some other extra space using 0xFF(Erase Value). @param ErasePolarity Fv erase value. @param FileHeader Point to the start of FFS File. @param ExtraLength The pading length. **/ VOID FvAdjustFfsFile ( IN UINT8 ErasePolarity, IN EFI_FFS_FILE_HEADER *FileHeader, IN UINTN ExtraLength ) { UINT8 *Ptr; UINT8 PadingByte; if (IS_FFS_FILE2 (FileHeader)) { Ptr = (UINT8 *) FileHeader + FFS_FILE2_SIZE (FileHeader); } else { Ptr = (UINT8 *) FileHeader + FFS_FILE_SIZE (FileHeader); } if (ErasePolarity == 0) { PadingByte = 0; } else { PadingByte = 0xFF; } // // Fill the non-used space with Padding Byte // SetMem (Ptr, ExtraLength, PadingByte); return ; }
/** Calculate the checksum for a PAD file. @param PadFileHeader The Pad File to be caculeted the checksum. **/ VOID SetPadFileChecksum ( IN EFI_FFS_FILE_HEADER *PadFileHeader ) { if ((PadFileHeader->Attributes & FFS_ATTRIB_CHECKSUM) != 0) { if (IS_FFS_FILE2 (PadFileHeader)) { // // Calculate checksum of Pad File Data // PadFileHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 ((UINT8 *) PadFileHeader + sizeof (EFI_FFS_FILE_HEADER2), FFS_FILE2_SIZE (PadFileHeader) - sizeof (EFI_FFS_FILE_HEADER2)); } else { // // Calculate checksum of Pad File Data // PadFileHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 ((UINT8 *) PadFileHeader + sizeof (EFI_FFS_FILE_HEADER), FFS_FILE_SIZE (PadFileHeader) - sizeof (EFI_FFS_FILE_HEADER)); } } else { PadFileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; } return ; }
static size_t ffs_file_size(const EFI_FFS_FILE_HEADER *ffsfh) { size_t size; if (IS_FFS_FILE2(ffsfh)) size = read_le32(&FFS_FILE2_SIZE(ffsfh)); else { size = read_le8(&ffsfh->Size[0]) << 0; size |= read_le8(&ffsfh->Size[1]) << 8; size |= read_le8(&ffsfh->Size[2]) << 16; } return size; }
static size_t ffs_file_size(const EFI_FFS_FILE_HEADER *ffsfh) { size_t size; if (IS_FFS_FILE2(ffsfh)) size = FFS_FILE2_SIZE(ffsfh); else { size = ffsfh->Size[0] << 0; size |= ffsfh->Size[1] << 8; size |= ffsfh->Size[2] << 16; } return size; }
/** Check if an FV is consistent and allocate cache for it. @param FvDevice A pointer to the FvDevice to be checked. @retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated. @retval EFI_SUCCESS FV is consistent and cache is allocated. @retval EFI_VOLUME_CORRUPTED File system is corrupted. **/ EFI_STATUS FvCheck ( IN OUT FV_DEVICE *FvDevice ) { EFI_STATUS Status; EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExtHeader; EFI_FVB_ATTRIBUTES_2 FvbAttributes; EFI_FV_BLOCK_MAP_ENTRY *BlockMap; FFS_FILE_LIST_ENTRY *FfsFileEntry; EFI_FFS_FILE_HEADER *FfsHeader; UINT8 *CacheLocation; UINTN LbaOffset; UINTN HeaderSize; UINTN Index; EFI_LBA LbaIndex; UINTN Size; EFI_FFS_FILE_STATE FileState; UINT8 *TopFvAddress; UINTN TestLength; EFI_PHYSICAL_ADDRESS PhysicalAddress; BOOLEAN FileCached; UINTN WholeFileSize; EFI_FFS_FILE_HEADER *CacheFfsHeader; FileCached = FALSE; CacheFfsHeader = NULL; Fvb = FvDevice->Fvb; FwVolHeader = FvDevice->FwVolHeader; Status = Fvb->GetAttributes (Fvb, &FvbAttributes); if (EFI_ERROR (Status)) { return Status; } // // Size is the size of the FV minus the head. We have already allocated // the header to check to make sure the volume is valid // Size = (UINTN)(FwVolHeader->FvLength - FwVolHeader->HeaderLength); if ((FvbAttributes & EFI_FVB2_MEMORY_MAPPED) != 0) { FvDevice->IsMemoryMapped = TRUE; Status = Fvb->GetPhysicalAddress (Fvb, &PhysicalAddress); if (EFI_ERROR (Status)) { return Status; } // // Don't cache memory mapped FV really. // FvDevice->CachedFv = (UINT8 *) (UINTN) (PhysicalAddress + FwVolHeader->HeaderLength); } else { FvDevice->IsMemoryMapped = FALSE; FvDevice->CachedFv = AllocatePool (Size); if (FvDevice->CachedFv == NULL) { return EFI_OUT_OF_RESOURCES; } } // // Remember a pointer to the end fo the CachedFv // FvDevice->EndOfCachedFv = FvDevice->CachedFv + Size; if (!FvDevice->IsMemoryMapped) { // // Copy FV minus header into memory using the block map we have all ready // read into memory. // BlockMap = FwVolHeader->BlockMap; CacheLocation = FvDevice->CachedFv; LbaIndex = 0; LbaOffset = 0; HeaderSize = FwVolHeader->HeaderLength; while ((BlockMap->NumBlocks != 0) || (BlockMap->Length != 0)) { Index = 0; Size = BlockMap->Length; if (HeaderSize > 0) { // // Skip header size // for (; Index < BlockMap->NumBlocks && HeaderSize >= BlockMap->Length; Index ++) { HeaderSize -= BlockMap->Length; LbaIndex ++; } // // Check whether FvHeader is crossing the multi block range. // if (Index >= BlockMap->NumBlocks) { BlockMap++; continue; } else if (HeaderSize > 0) { LbaOffset = HeaderSize; Size = BlockMap->Length - HeaderSize; HeaderSize = 0; } } // // read the FV data // for (; Index < BlockMap->NumBlocks; Index ++) { Status = Fvb->Read (Fvb, LbaIndex, LbaOffset, &Size, CacheLocation ); // // Not check EFI_BAD_BUFFER_SIZE, for Size = BlockMap->Length // if (EFI_ERROR (Status)) { goto Done; } LbaIndex++; CacheLocation += Size; // // After we skip Fv Header always read from start of block // LbaOffset = 0; Size = BlockMap->Length; } BlockMap++; } } // // Scan to check the free space & File list // if ((FvbAttributes & EFI_FVB2_ERASE_POLARITY) != 0) { FvDevice->ErasePolarity = 1; } else { FvDevice->ErasePolarity = 0; } // // go through the whole FV cache, check the consistence of the FV. // Make a linked list of all the Ffs file headers // Status = EFI_SUCCESS; InitializeListHead (&FvDevice->FfsFileListHeader); // // Build FFS list // if (FwVolHeader->ExtHeaderOffset != 0) { // // Searching for files starts on an 8 byte aligned boundary after the end of the Extended Header if it exists. // FwVolExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *) (FvDevice->CachedFv + (FwVolHeader->ExtHeaderOffset - FwVolHeader->HeaderLength)); FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FwVolExtHeader + FwVolExtHeader->ExtHeaderSize); FfsHeader = (EFI_FFS_FILE_HEADER *) ALIGN_POINTER (FfsHeader, 8); } else { FfsHeader = (EFI_FFS_FILE_HEADER *) (FvDevice->CachedFv); } TopFvAddress = FvDevice->EndOfCachedFv; while (((UINTN) FfsHeader >= (UINTN) FvDevice->CachedFv) && ((UINTN) FfsHeader <= (UINTN) ((UINTN) TopFvAddress - sizeof (EFI_FFS_FILE_HEADER)))) { if (FileCached) { CoreFreePool (CacheFfsHeader); FileCached = FALSE; } TestLength = TopFvAddress - ((UINT8 *) FfsHeader); if (TestLength > sizeof (EFI_FFS_FILE_HEADER)) { TestLength = sizeof (EFI_FFS_FILE_HEADER); } if (IsBufferErased (FvDevice->ErasePolarity, FfsHeader, TestLength)) { // // We have found the free space so we are done! // goto Done; } if (!IsValidFfsHeader (FvDevice->ErasePolarity, FfsHeader, &FileState)) { if ((FileState == EFI_FILE_HEADER_INVALID) || (FileState == EFI_FILE_HEADER_CONSTRUCTION)) { if (IS_FFS_FILE2 (FfsHeader)) { if (!FvDevice->IsFfs3Fv) { DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &FfsHeader->Name)); } FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2)); } else { FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER)); } continue; } else { // // File system is corrputed // Status = EFI_VOLUME_CORRUPTED; goto Done; } } CacheFfsHeader = FfsHeader; if ((CacheFfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) == FFS_ATTRIB_CHECKSUM) { if (FvDevice->IsMemoryMapped) { // // Memory mapped FV has not been cached. // Here is to cache FFS file to memory buffer for following checksum calculating. // And then, the cached file buffer can be also used for FvReadFile. // WholeFileSize = IS_FFS_FILE2 (CacheFfsHeader) ? FFS_FILE2_SIZE (CacheFfsHeader): FFS_FILE_SIZE (CacheFfsHeader); CacheFfsHeader = AllocateCopyPool (WholeFileSize, CacheFfsHeader); if (CacheFfsHeader == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } FileCached = TRUE; } } if (!IsValidFfsFile (FvDevice->ErasePolarity, CacheFfsHeader)) { // // File system is corrupted // Status = EFI_VOLUME_CORRUPTED; goto Done; } if (IS_FFS_FILE2 (CacheFfsHeader)) { ASSERT (FFS_FILE2_SIZE (CacheFfsHeader) > 0x00FFFFFF); if (!FvDevice->IsFfs3Fv) { DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &CacheFfsHeader->Name)); FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE2_SIZE (CacheFfsHeader)); // // Adjust pointer to the next 8-byte aligned boundry. // FfsHeader = (EFI_FFS_FILE_HEADER *) (((UINTN) FfsHeader + 7) & ~0x07); continue; } } FileState = GetFileState (FvDevice->ErasePolarity, CacheFfsHeader); // // check for non-deleted file // if (FileState != EFI_FILE_DELETED) { // // Create a FFS list entry for each non-deleted file // FfsFileEntry = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY)); if (FfsFileEntry == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } FfsFileEntry->FfsHeader = CacheFfsHeader; FfsFileEntry->FileCached = FileCached; FileCached = FALSE; InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link); } if (IS_FFS_FILE2 (CacheFfsHeader)) { FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE2_SIZE (CacheFfsHeader)); } else { FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE_SIZE (CacheFfsHeader)); } // // Adjust pointer to the next 8-byte aligned boundry. // FfsHeader = (EFI_FFS_FILE_HEADER *)(((UINTN)FfsHeader + 7) & ~0x07); } Done: if (EFI_ERROR (Status)) { if (FileCached) { CoreFreePool (CacheFfsHeader); FileCached = FALSE; } FreeFvDeviceResource (FvDevice); } return Status; }
/** Find core image base. @param[in] BootFirmwareVolumePtr Point to the boot firmware volume. @param[out] SecCoreImageBase The base address of the SEC core image. @param[out] PeiCoreImageBase The base address of the PEI core image. **/ EFI_STATUS EFIAPI FindImageBase ( IN EFI_FIRMWARE_VOLUME_HEADER *BootFirmwareVolumePtr, OUT EFI_PHYSICAL_ADDRESS *SecCoreImageBase, OUT EFI_PHYSICAL_ADDRESS *PeiCoreImageBase ) { 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; *PeiCoreImageBase = 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; if (IS_FFS_FILE2 (File)) { Size = FFS_FILE2_SIZE (File); if (Size <= 0x00FFFFFF) { return EFI_NOT_FOUND; } } else { Size = FFS_FILE_SIZE (File); if (Size < sizeof (EFI_FFS_FILE_HEADER)) { return EFI_NOT_FOUND; } } EndOfFile = CurrentAddress + Size; if (EndOfFile > EndOfFirmwareVolume) { return EFI_NOT_FOUND; } // // Look for SEC Core / PEI Core files // if (File->Type != EFI_FV_FILETYPE_SECURITY_CORE && File->Type != EFI_FV_FILETYPE_PEI_CORE) { continue; } // // Loop through the FFS file sections within the FFS file // if (IS_FFS_FILE2 (File)) { EndOfSection = (EFI_PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) File + sizeof (EFI_FFS_FILE_HEADER2)); } else { EndOfSection = (EFI_PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) File + sizeof (EFI_FFS_FILE_HEADER)); } for (;;) { CurrentAddress = (EndOfSection + 3) & 0xfffffffffffffffcULL; Section = (EFI_COMMON_SECTION_HEADER*)(UINTN) CurrentAddress; if (IS_SECTION2 (Section)) { Size = SECTION2_SIZE (Section); if (Size <= 0x00FFFFFF) { return EFI_NOT_FOUND; } } else { Size = SECTION_SIZE (Section); if (Size < sizeof (EFI_COMMON_SECTION_HEADER)) { 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) { if (IS_SECTION2 (Section)) { *SecCoreImageBase = (PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER2)); } else { *SecCoreImageBase = (PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER)); } } else { if (IS_SECTION2 (Section)) { *PeiCoreImageBase = (PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER2)); } else { *PeiCoreImageBase = (PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER)); } } break; } } // // Both SEC Core and PEI Core images found // if (*SecCoreImageBase != 0 && *PeiCoreImageBase != 0) { return EFI_SUCCESS; } } }
/** Create multiple files within a PAD File area. @param FvDevice Firmware Volume Device. @param PadFileEntry The pad file entry to be written in. @param NumOfFiles Total File number to be written. @param BufferSize The array of buffer size of each FfsBuffer. @param ActualFileSize The array of actual file size. @param PadSize The array of leading pad file size for each FFS File @param FfsBuffer The array of Ffs Buffer pointer. @param FileData The array of EFI_FV_WRITE_FILE_DATA structure, used to get name, attributes, type, etc. @retval EFI_SUCCESS Add the input multiple files into PAD file area. @retval EFI_OUT_OF_RESOURCES No enough memory is allocated. @retval other error Files can't be added into PAD file area. **/ EFI_STATUS FvCreateMultipleFilesInsidePadFile ( IN FV_DEVICE *FvDevice, IN FFS_FILE_LIST_ENTRY *PadFileEntry, IN UINTN NumOfFiles, IN UINTN *BufferSize, IN UINTN *ActualFileSize, IN UINTN *PadSize, IN UINT8 **FfsBuffer, IN EFI_FV_WRITE_FILE_DATA *FileData ) { EFI_STATUS Status; EFI_FFS_FILE_HEADER *OldPadFileHeader; UINTN Index; EFI_FFS_FILE_HEADER *PadFileHeader; EFI_FFS_FILE_HEADER *FileHeader; EFI_FFS_FILE_HEADER *TailPadFileHeader; UINTN TotalSize; UINTN PadAreaLength; LIST_ENTRY NewFileList; FFS_FILE_LIST_ENTRY *NewFileListEntry; UINTN Offset; UINTN NumBytesWritten; UINT8 *StartPos; FFS_FILE_LIST_ENTRY *FfsEntry; FFS_FILE_LIST_ENTRY *NextFfsEntry; InitializeListHead (&NewFileList); NewFileListEntry = NULL; OldPadFileHeader = (EFI_FFS_FILE_HEADER *) PadFileEntry->FfsHeader; if (IS_FFS_FILE2 (OldPadFileHeader)) { PadAreaLength = FFS_FILE2_SIZE (OldPadFileHeader) - sizeof (EFI_FFS_FILE_HEADER2); } else { PadAreaLength = FFS_FILE_SIZE (OldPadFileHeader) - sizeof (EFI_FFS_FILE_HEADER); } Status = UpdateHeaderBit ( FvDevice, OldPadFileHeader, EFI_FILE_MARKED_FOR_UPDATE ); if (EFI_ERROR (Status)) { return Status; } // // Update PAD area // TotalSize = 0; if (IS_FFS_FILE2 (OldPadFileHeader)) { PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER2)); } else { PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER)); } FileHeader = PadFileHeader; for (Index = 0; Index < NumOfFiles; Index++) { if (PadSize[Index] != 0) { FvFillPadFile (PadFileHeader, PadSize[Index]); NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY)); if (NewFileListEntry == NULL) { FreeFileList (&NewFileList); return EFI_OUT_OF_RESOURCES; } NewFileListEntry->FfsHeader = (UINT8 *) PadFileHeader; InsertTailList (&NewFileList, &NewFileListEntry->Link); } FileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) PadFileHeader + PadSize[Index]); Status = FvFillFfsFile ( FileHeader, FfsBuffer[Index], BufferSize[Index], ActualFileSize[Index], FileData[Index].NameGuid, FileData[Index].Type, FileData[Index].FileAttributes ); if (EFI_ERROR (Status)) { FreeFileList (&NewFileList); return Status; } NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY)); if (NewFileListEntry == NULL) { FreeFileList (&NewFileList); return EFI_OUT_OF_RESOURCES; } NewFileListEntry->FfsHeader = (UINT8 *) FileHeader; InsertTailList (&NewFileList, &NewFileListEntry->Link); PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FileHeader + BufferSize[Index]); TotalSize += PadSize[Index]; TotalSize += BufferSize[Index]; } FvDevice->CurrentFfsFile = NewFileListEntry; // // Maybe we need a tail pad file // if (PadAreaLength > TotalSize) { if ((PadAreaLength - TotalSize) >= sizeof (EFI_FFS_FILE_HEADER)) { // // we can insert another PAD file // TailPadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FileHeader + BufferSize[NumOfFiles - 1]); FvFillPadFile (TailPadFileHeader, PadAreaLength - TotalSize); NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY)); if (NewFileListEntry == NULL) { FreeFileList (&NewFileList); FvDevice->CurrentFfsFile = NULL; return EFI_OUT_OF_RESOURCES; } NewFileListEntry->FfsHeader = (UINT8 *) TailPadFileHeader; InsertTailList (&NewFileList, &NewFileListEntry->Link); } else { // // because left size cannot hold another PAD file header, // adjust the writing file size (just in cache) // FvAdjustFfsFile ( FvDevice->ErasePolarity, FileHeader, PadAreaLength - TotalSize ); } } // // Start writing to FV // if (IS_FFS_FILE2 (OldPadFileHeader)) { StartPos = (UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER2); } else { StartPos = (UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER); } Offset = (UINTN) (StartPos - FvDevice->CachedFv); NumBytesWritten = PadAreaLength; Status = FvcWrite ( FvDevice, Offset, &NumBytesWritten, StartPos ); if (EFI_ERROR (Status)) { FreeFileList (&NewFileList); FvDevice->CurrentFfsFile = NULL; return Status; } Status = UpdateHeaderBit ( FvDevice, OldPadFileHeader, EFI_FILE_HEADER_INVALID ); if (EFI_ERROR (Status)) { FreeFileList (&NewFileList); FvDevice->CurrentFfsFile = NULL; return Status; } // // Update File List Link // // // First delete old pad file entry // FfsEntry = (FFS_FILE_LIST_ENTRY *) PadFileEntry->Link.BackLink; NextFfsEntry = (FFS_FILE_LIST_ENTRY *) PadFileEntry->Link.ForwardLink; FreePool (PadFileEntry); FfsEntry->Link.ForwardLink = NewFileList.ForwardLink; (NewFileList.ForwardLink)->BackLink = &FfsEntry->Link; NextFfsEntry->Link.BackLink = NewFileList.BackLink; (NewFileList.BackLink)->ForwardLink = &NextFfsEntry->Link; return EFI_SUCCESS; }
/** Create a new file within a PAD file area. @param FvDevice Firmware Volume Device. @param FfsFileBuffer A buffer that holds an FFS file,(it contains a File Header which is in init state). @param BufferSize The size of FfsFileBuffer. @param ActualFileSize The actual file length, it may not be multiples of 8. @param FileName The FFS File Name. @param FileType The FFS File Type. @param FileAttributes The Attributes of the FFS File to be created. @retval EFI_SUCCESS Successfully create a new file within the found PAD file area. @retval EFI_OUT_OF_RESOURCES No suitable PAD file is found. @retval other errors New file is created failed. **/ EFI_STATUS FvCreateNewFileInsidePadFile ( IN FV_DEVICE *FvDevice, IN UINT8 *FfsFileBuffer, IN UINTN BufferSize, IN UINTN ActualFileSize, IN EFI_GUID *FileName, IN EFI_FV_FILETYPE FileType, IN EFI_FV_FILE_ATTRIBUTES FileAttributes ) { UINTN RequiredAlignment; FFS_FILE_LIST_ENTRY *PadFileEntry; EFI_STATUS Status; UINTN PadAreaLength; UINTN PadSize; EFI_FFS_FILE_HEADER *FileHeader; EFI_FFS_FILE_HEADER *OldPadFileHeader; EFI_FFS_FILE_HEADER *PadFileHeader; EFI_FFS_FILE_HEADER *TailPadFileHeader; UINTN StateOffset; UINTN Offset; UINTN NumBytesWritten; UINT8 *StartPos; LIST_ENTRY NewFileList; FFS_FILE_LIST_ENTRY *NewFileListEntry; FFS_FILE_LIST_ENTRY *FfsEntry; FFS_FILE_LIST_ENTRY *NextFfsEntry; // // First get the required alignment from the File Attributes // RequiredAlignment = GetRequiredAlignment (FileAttributes); // // Find a suitable PAD File // Status = FvLocatePadFile ( FvDevice, BufferSize, RequiredAlignment, &PadSize, &PadFileEntry ); if (EFI_ERROR (Status)) { return EFI_OUT_OF_RESOURCES; } OldPadFileHeader = (EFI_FFS_FILE_HEADER *) PadFileEntry->FfsHeader; // // Step 1: Update Pad File Header // SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldPadFileHeader); StartPos = PadFileEntry->FfsHeader; Offset = (UINTN) (StartPos - FvDevice->CachedFv); StateOffset = Offset + (UINT8 *) &OldPadFileHeader->State - (UINT8 *) OldPadFileHeader; NumBytesWritten = sizeof (EFI_FFS_FILE_STATE); Status = FvcWrite ( FvDevice, StateOffset, &NumBytesWritten, &OldPadFileHeader->State ); if (EFI_ERROR (Status)) { SetFileState (EFI_FILE_HEADER_CONSTRUCTION, OldPadFileHeader); return Status; } // // Step 2: Update Pad area // InitializeListHead (&NewFileList); if (IS_FFS_FILE2 (OldPadFileHeader)) { PadAreaLength = FFS_FILE2_SIZE (OldPadFileHeader) - sizeof (EFI_FFS_FILE_HEADER); PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER2)); } else { PadAreaLength = FFS_FILE_SIZE (OldPadFileHeader) - sizeof (EFI_FFS_FILE_HEADER); PadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER)); } if (PadSize != 0) { // // Insert a PAD file before to achieve required alignment // FvFillPadFile (PadFileHeader, PadSize); NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY)); ASSERT (NewFileListEntry != NULL); NewFileListEntry->FfsHeader = (UINT8 *) PadFileHeader; InsertTailList (&NewFileList, &NewFileListEntry->Link); } FileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) PadFileHeader + PadSize); Status = FvFillFfsFile ( FileHeader, FfsFileBuffer, BufferSize, ActualFileSize, FileName, FileType, FileAttributes ); if (EFI_ERROR (Status)) { FreeFileList (&NewFileList); return Status; } NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY)); ASSERT (NewFileListEntry != NULL); NewFileListEntry->FfsHeader = (UINT8 *) FileHeader; InsertTailList (&NewFileList, &NewFileListEntry->Link); FvDevice->CurrentFfsFile = NewFileListEntry; if (PadAreaLength > (BufferSize + PadSize)) { if ((PadAreaLength - BufferSize - PadSize) >= sizeof (EFI_FFS_FILE_HEADER)) { // // we can insert another PAD file // TailPadFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FileHeader + BufferSize); FvFillPadFile (TailPadFileHeader, PadAreaLength - BufferSize - PadSize); NewFileListEntry = AllocatePool (sizeof (FFS_FILE_LIST_ENTRY)); ASSERT (NewFileListEntry != NULL); NewFileListEntry->FfsHeader = (UINT8 *) TailPadFileHeader; InsertTailList (&NewFileList, &NewFileListEntry->Link); } else { // // because left size cannot hold another PAD file header, // adjust the writing file size (just in cache) // FvAdjustFfsFile ( FvDevice->ErasePolarity, FileHeader, PadAreaLength - BufferSize - PadSize ); } } // // Start writing to FV // if (IS_FFS_FILE2 (OldPadFileHeader)) { StartPos = (UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER2); } else { StartPos = (UINT8 *) OldPadFileHeader + sizeof (EFI_FFS_FILE_HEADER); } Offset = (UINTN) (StartPos - FvDevice->CachedFv); NumBytesWritten = PadAreaLength; Status = FvcWrite ( FvDevice, Offset, &NumBytesWritten, StartPos ); if (EFI_ERROR (Status)) { FreeFileList (&NewFileList); FvDevice->CurrentFfsFile = NULL; return Status; } // // Step 3: Mark Pad file header as EFI_FILE_HEADER_INVALID // SetFileState (EFI_FILE_HEADER_INVALID, OldPadFileHeader); StartPos = PadFileEntry->FfsHeader; Offset = (UINTN) (StartPos - FvDevice->CachedFv); StateOffset = Offset + (UINT8 *) &OldPadFileHeader->State - (UINT8 *) OldPadFileHeader; NumBytesWritten = sizeof (EFI_FFS_FILE_STATE); Status = FvcWrite ( FvDevice, StateOffset, &NumBytesWritten, &OldPadFileHeader->State ); if (EFI_ERROR (Status)) { SetFileState (EFI_FILE_HEADER_INVALID, OldPadFileHeader); FreeFileList (&NewFileList); FvDevice->CurrentFfsFile = NULL; return Status; } // // If all successfully, update FFS_FILE_LIST // // // Delete old pad file entry // FfsEntry = (FFS_FILE_LIST_ENTRY *) PadFileEntry->Link.BackLink; NextFfsEntry = (FFS_FILE_LIST_ENTRY *) PadFileEntry->Link.ForwardLink; FreePool (PadFileEntry); FfsEntry->Link.ForwardLink = NewFileList.ForwardLink; (NewFileList.ForwardLink)->BackLink = &FfsEntry->Link; NextFfsEntry->Link.BackLink = NewFileList.BackLink; (NewFileList.BackLink)->ForwardLink = &NextFfsEntry->Link; return EFI_SUCCESS; }
/** Check if an FV is consistent and allocate cache for it. @param FvDevice A pointer to the FvDevice to be checked. @retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated. @retval EFI_VOLUME_CORRUPTED File system is corrupted. @retval EFI_SUCCESS FV is consistent and cache is allocated. **/ EFI_STATUS FvCheck ( IN FV_DEVICE *FvDevice ) { EFI_STATUS Status; EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; EFI_FVB_ATTRIBUTES_2 FvbAttributes; EFI_FV_BLOCK_MAP_ENTRY *BlockMap; EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExtHeader; UINT8 *FwCache; LBA_ENTRY *LbaEntry; FREE_SPACE_ENTRY *FreeSpaceEntry; FFS_FILE_LIST_ENTRY *FfsFileEntry; UINT8 *LbaStart; UINTN Index; EFI_LBA LbaIndex; UINT8 *Ptr; UINTN Size; UINT8 *FreeStart; UINTN FreeSize; UINT8 ErasePolarity; EFI_FFS_FILE_STATE FileState; UINT8 *TopFvAddress; UINTN TestLength; EFI_PHYSICAL_ADDRESS BaseAddress; Fvb = FvDevice->Fvb; Status = Fvb->GetAttributes (Fvb, &FvbAttributes); if (EFI_ERROR (Status)) { return Status; } InitializeListHead (&FvDevice->LbaHeader); InitializeListHead (&FvDevice->FreeSpaceHeader); InitializeListHead (&FvDevice->FfsFileListHeader); FwVolHeader = NULL; Status = GetFwVolHeader (Fvb, &FwVolHeader); if (EFI_ERROR (Status)) { return Status; } ASSERT (FwVolHeader != NULL); FvDevice->IsFfs3Fv = CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem3Guid); // // Double Check firmware volume header here // if (!VerifyFvHeaderChecksum (FwVolHeader)) { FreePool (FwVolHeader); return EFI_VOLUME_CORRUPTED; } BlockMap = FwVolHeader->BlockMap; // // FwVolHeader->FvLength is the whole FV length including FV header // FwCache = AllocateZeroPool ((UINTN) FwVolHeader->FvLength); if (FwCache == NULL) { FreePool (FwVolHeader); return EFI_OUT_OF_RESOURCES; } FvDevice->CachedFv = (EFI_PHYSICAL_ADDRESS) (UINTN) FwCache; // // Copy to memory // LbaStart = FwCache; LbaIndex = 0; Ptr = NULL; if ((FvbAttributes & EFI_FVB2_MEMORY_MAPPED) != 0) { // // Get volume base address // Status = Fvb->GetPhysicalAddress (Fvb, &BaseAddress); if (EFI_ERROR (Status)) { FreePool (FwVolHeader); return Status; } Ptr = (UINT8 *) ((UINTN) BaseAddress); DEBUG((EFI_D_INFO, "Fv Base Address is 0x%LX\n", BaseAddress)); } // // Copy whole FV into the memory // while ((BlockMap->NumBlocks != 0) || (BlockMap->Length != 0)) { for (Index = 0; Index < BlockMap->NumBlocks; Index++) { LbaEntry = AllocatePool (sizeof (LBA_ENTRY)); if (LbaEntry == NULL) { FreePool (FwVolHeader); FreeFvDeviceResource (FvDevice); return EFI_OUT_OF_RESOURCES; } LbaEntry->LbaIndex = LbaIndex; LbaEntry->StartingAddress = LbaStart; LbaEntry->BlockLength = BlockMap->Length; // // Copy each LBA into memory // if ((FvbAttributes & EFI_FVB2_MEMORY_MAPPED) != 0) { CopyMem (LbaStart, Ptr, BlockMap->Length); Ptr += BlockMap->Length; } else { Size = BlockMap->Length; Status = Fvb->Read ( Fvb, LbaIndex, 0, &Size, LbaStart ); // // Not check EFI_BAD_BUFFER_SIZE, for Size = BlockMap->Length // if (EFI_ERROR (Status)) { FreePool (FwVolHeader); FreeFvDeviceResource (FvDevice); return Status; } } LbaIndex++; LbaStart += BlockMap->Length; InsertTailList (&FvDevice->LbaHeader, &LbaEntry->Link); } BlockMap++; } FvDevice->FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) FwCache; // // it is not used any more, so free FwVolHeader // FreePool (FwVolHeader); // // Scan to check the free space & File list // if ((FvbAttributes & EFI_FVB2_ERASE_POLARITY) != 0) { ErasePolarity = 1; } else { ErasePolarity = 0; } FvDevice->ErasePolarity = ErasePolarity; // // go through the whole FV cache, check the consistence of the FV // if (FvDevice->FwVolHeader->ExtHeaderOffset != 0) { // // Searching for files starts on an 8 byte aligned boundary after the end of the Extended Header if it exists. // FwVolExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *) (UINTN) (FvDevice->CachedFv + FvDevice->FwVolHeader->ExtHeaderOffset); Ptr = (UINT8 *) FwVolExtHeader + FwVolExtHeader->ExtHeaderSize; Ptr = (UINT8 *) ALIGN_POINTER (Ptr, 8); } else { Ptr = (UINT8 *) (UINTN) (FvDevice->CachedFv + FvDevice->FwVolHeader->HeaderLength); } TopFvAddress = (UINT8 *) (UINTN) (FvDevice->CachedFv + FvDevice->FwVolHeader->FvLength); // // Build FFS list & Free Space List here // while (Ptr < TopFvAddress) { TestLength = TopFvAddress - Ptr; if (TestLength > sizeof (EFI_FFS_FILE_HEADER)) { TestLength = sizeof (EFI_FFS_FILE_HEADER); } if (IsBufferErased (ErasePolarity, Ptr, TestLength)) { // // We found free space // FreeStart = Ptr; FreeSize = 0; do { TestLength = TopFvAddress - Ptr; if (TestLength > sizeof (EFI_FFS_FILE_HEADER)) { TestLength = sizeof (EFI_FFS_FILE_HEADER); } if (!IsBufferErased (ErasePolarity, Ptr, TestLength)) { break; } FreeSize += TestLength; Ptr += TestLength; } while (Ptr < TopFvAddress); FreeSpaceEntry = AllocateZeroPool (sizeof (FREE_SPACE_ENTRY)); if (FreeSpaceEntry == NULL) { FreeFvDeviceResource (FvDevice); return EFI_OUT_OF_RESOURCES; } // // Create a Free space entry // FreeSpaceEntry->StartingAddress = FreeStart; FreeSpaceEntry->Length = FreeSize; InsertTailList (&FvDevice->FreeSpaceHeader, &FreeSpaceEntry->Link); continue; } // // double check boundry // if (TestLength < sizeof (EFI_FFS_FILE_HEADER)) { break; } if (!IsValidFFSHeader ( FvDevice->ErasePolarity, (EFI_FFS_FILE_HEADER *) Ptr )) { FileState = GetFileState ( FvDevice->ErasePolarity, (EFI_FFS_FILE_HEADER *) Ptr ); if ((FileState == EFI_FILE_HEADER_INVALID) || (FileState == EFI_FILE_HEADER_CONSTRUCTION)) { if (IS_FFS_FILE2 (Ptr)) { if (!FvDevice->IsFfs3Fv) { DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &((EFI_FFS_FILE_HEADER *) Ptr)->Name)); } Ptr = Ptr + sizeof (EFI_FFS_FILE_HEADER2); } else { Ptr = Ptr + sizeof (EFI_FFS_FILE_HEADER); } continue; } else { // // File system is corrputed, return // FreeFvDeviceResource (FvDevice); return EFI_VOLUME_CORRUPTED; } } if (IS_FFS_FILE2 (Ptr)) { ASSERT (FFS_FILE2_SIZE (Ptr) > 0x00FFFFFF); if (!FvDevice->IsFfs3Fv) { DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &((EFI_FFS_FILE_HEADER *) Ptr)->Name)); Ptr = Ptr + FFS_FILE2_SIZE (Ptr); // // Adjust Ptr to the next 8-byte aligned boundry. // while (((UINTN) Ptr & 0x07) != 0) { Ptr++; } continue; } } if (IsValidFFSFile (FvDevice, (EFI_FFS_FILE_HEADER *) Ptr)) { FileState = GetFileState ( FvDevice->ErasePolarity, (EFI_FFS_FILE_HEADER *) Ptr ); // // check for non-deleted file // if (FileState != EFI_FILE_DELETED) { // // Create a FFS list entry for each non-deleted file // FfsFileEntry = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY)); if (FfsFileEntry == NULL) { FreeFvDeviceResource (FvDevice); return EFI_OUT_OF_RESOURCES; } FfsFileEntry->FfsHeader = Ptr; InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link); } if (IS_FFS_FILE2 (Ptr)) { Ptr = Ptr + FFS_FILE2_SIZE (Ptr); } else { Ptr = Ptr + FFS_FILE_SIZE (Ptr); } // // Adjust Ptr to the next 8-byte aligned boundry. // while (((UINTN) Ptr & 0x07) != 0) { Ptr++; } } else { // // File system is corrupted, return // FreeFvDeviceResource (FvDevice); return EFI_VOLUME_CORRUPTED; } } FvDevice->CurrentFfsFile = NULL; return EFI_SUCCESS; }
/** Get FFS buffer pointer by FileName GUID and FileType. @param[in] FdStart The System Firmware FD image @param[in] FdSize The size of System Firmware FD image @param[in] FileName The FileName GUID of FFS to be found @param[in] Type The FileType of FFS to be found @param[out] OutFfsBuffer The FFS buffer found, including FFS_FILE_HEADER @param[out] OutFfsBufferSize The size of FFS buffer found, including FFS_FILE_HEADER @retval TRUE The FFS buffer is found. @retval FALSE The FFS buffer is not found. **/ BOOLEAN GetFfsByName ( IN VOID *FdStart, IN UINTN FdSize, IN EFI_GUID *FileName, IN EFI_FV_FILETYPE Type, OUT VOID **OutFfsBuffer, OUT UINTN *OutFfsBufferSize ) { UINTN FvSize; EFI_FIRMWARE_VOLUME_HEADER *FvHeader; EFI_FIRMWARE_VOLUME_EXT_HEADER *FvExtHeader; EFI_FFS_FILE_HEADER *FfsHeader; UINT32 FfsSize; UINTN TestLength; BOOLEAN FvFound; DEBUG ((DEBUG_INFO, "GetFfsByName - FV: 0x%08x - 0x%08x\n", (UINTN)FdStart, (UINTN)FdSize)); FvFound = FALSE; FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FdStart; while ((UINTN)FvHeader < (UINTN)FdStart + FdSize - 1) { FvSize = (UINTN)FdStart + FdSize - (UINTN)FvHeader; if (FvHeader->Signature != EFI_FVH_SIGNATURE) { FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN)FvHeader + SIZE_4KB); continue; } DEBUG((DEBUG_ERROR, "checking FV....0x%08x - 0x%x\n", FvHeader, FvHeader->FvLength)); FvFound = TRUE; if (FvHeader->FvLength > FvSize) { DEBUG((DEBUG_ERROR, "GetFfsByName - FvSize: 0x%08x, MaxSize - 0x%08x\n", (UINTN)FvHeader->FvLength, (UINTN)FvSize)); return FALSE; } FvSize = (UINTN)FvHeader->FvLength; // // Find FFS // if (FvHeader->ExtHeaderOffset != 0) { FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)((UINT8 *)FvHeader + FvHeader->ExtHeaderOffset); FfsHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FvExtHeader + FvExtHeader->ExtHeaderSize); } else { FfsHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FvHeader + FvHeader->HeaderLength); } FfsHeader = (EFI_FFS_FILE_HEADER *)((UINTN)FvHeader + ALIGN_VALUE((UINTN)FfsHeader - (UINTN)FvHeader, 8)); while ((UINTN)FfsHeader < (UINTN)FvHeader + FvSize - 1) { DEBUG((DEBUG_INFO, "GetFfsByName - FFS: 0x%08x\n", FfsHeader)); TestLength = (UINTN)((UINTN)FvHeader + FvSize - (UINTN)FfsHeader); if (TestLength > sizeof(EFI_FFS_FILE_HEADER)) { TestLength = sizeof(EFI_FFS_FILE_HEADER); } if (IsBufferErased(1, FfsHeader, TestLength)) { break; } if (IS_FFS_FILE2(FfsHeader)) { FfsSize = FFS_FILE2_SIZE(FfsHeader); } else { FfsSize = FFS_FILE_SIZE(FfsHeader); } if (CompareGuid(FileName, &FfsHeader->Name) && ((Type == EFI_FV_FILETYPE_ALL) || (FfsHeader->Type == Type))) { // // Check section // *OutFfsBuffer = FfsHeader; *OutFfsBufferSize = FfsSize; return TRUE; } else { // // Any other type is not allowed // DEBUG((DEBUG_INFO, "GetFfsByName - other FFS type 0x%x, name %g\n", FfsHeader->Type, &FfsHeader->Name)); } // // Next File // FfsHeader = (EFI_FFS_FILE_HEADER *)((UINTN)FfsHeader + ALIGN_VALUE(FfsSize, 8)); } // // Next FV // FvHeader = (VOID *)(UINTN)((UINTN)FvHeader + FvHeader->FvLength); DEBUG((DEBUG_ERROR, "Next FV....0x%08x - 0x%x\n", FvHeader, FvHeader->FvLength)); } if (!FvFound) { DEBUG((DEBUG_ERROR, "GetFfsByName - NO FV Found\n")); } return FALSE; }
/** Check if it's a valid FFS file. Here we are sure that it has a valid FFS file header since we must call IsValidFfsHeader() first. @param ErasePolarity Erase polarity attribute of the firmware volume @param FfsHeader Points to the FFS file to be checked @retval TRUE Valid FFS file @retval FALSE Invalid FFS file **/ BOOLEAN IsValidFfsFile ( IN UINT8 ErasePolarity, IN EFI_FFS_FILE_HEADER *FfsHeader ) { EFI_FFS_FILE_STATE FileState; UINT8 DataCheckSum; FileState = GetFileState (ErasePolarity, FfsHeader); switch (FileState) { case EFI_FILE_DELETED: case EFI_FILE_DATA_VALID: case EFI_FILE_MARKED_FOR_UPDATE: DataCheckSum = FFS_FIXED_CHECKSUM; if ((FfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) == FFS_ATTRIB_CHECKSUM) { if (IS_FFS_FILE2 (FfsHeader)) { DataCheckSum = CalculateCheckSum8 ((CONST UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2), FFS_FILE2_SIZE (FfsHeader) - sizeof(EFI_FFS_FILE_HEADER2)); } else { DataCheckSum = CalculateCheckSum8 ((CONST UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER), FFS_FILE_SIZE (FfsHeader) - sizeof(EFI_FFS_FILE_HEADER)); } } if (FfsHeader->IntegrityCheck.Checksum.File == DataCheckSum) { return TRUE; } default: return FALSE; } }