/** @file Implements write firmware file. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include "FwVolDriver.h" /** Calculate the checksum for the FFS header. @param FfsHeader FFS File Header which needs to calculate the checksum **/ VOID SetHeaderChecksum ( IN EFI_FFS_FILE_HEADER *FfsHeader ) { EFI_FFS_FILE_STATE State; UINT8 FileChecksum; // // The state and the File checksum are not included // State = FfsHeader->State; FfsHeader->State = 0; FileChecksum = FfsHeader->IntegrityCheck.Checksum.File; FfsHeader->IntegrityCheck.Checksum.File = 0; FfsHeader->IntegrityCheck.Checksum.Header = 0; if (IS_FFS_FILE2 (FfsHeader)) { FfsHeader->IntegrityCheck.Checksum.Header = CalculateCheckSum8 ( (UINT8 *) FfsHeader, sizeof (EFI_FFS_FILE_HEADER2) ); } else { FfsHeader->IntegrityCheck.Checksum.Header = CalculateCheckSum8 ( (UINT8 *) FfsHeader, sizeof (EFI_FFS_FILE_HEADER) ); } FfsHeader->State = State; FfsHeader->IntegrityCheck.Checksum.File = FileChecksum; return ; } /** Calculate the checksum for the FFS File. @param FfsHeader FFS File Header which needs to calculate the checksum @param ActualFileSize The whole Ffs File Length. **/ VOID SetFileChecksum ( IN EFI_FFS_FILE_HEADER *FfsHeader, IN UINTN ActualFileSize ) { if ((FfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) != 0) { FfsHeader->IntegrityCheck.Checksum.File = 0; if (IS_FFS_FILE2 (FfsHeader)) { FfsHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 ( (UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2), ActualFileSize - sizeof (EFI_FFS_FILE_HEADER2) ); } else { FfsHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 ( (UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER), ActualFileSize - sizeof (EFI_FFS_FILE_HEADER) ); } } else { FfsHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; } return ; } /** Get the alignment value from File Attributes. @param FfsAttributes FFS attribute @return Alignment value. **/ UINTN GetRequiredAlignment ( IN EFI_FV_FILE_ATTRIBUTES FfsAttributes ) { UINTN AlignmentValue; AlignmentValue = FfsAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT; if (AlignmentValue <= 3) { return 0x08; } if (AlignmentValue > 16) { // // Anyway, we won't reach this code // return 0x08; } return (UINTN)1 << AlignmentValue; } /** Calculate the leading Pad file size to meet the alignment requirement. @param FvDevice Cached Firmware Volume. @param StartAddress The starting address to write the FFS File. @param BufferSize The FFS File Buffer Size. @param RequiredAlignment FFS File Data alignment requirement. @return The required Pad File Size. **/ UINTN CalculatePadFileSize ( IN FV_DEVICE *FvDevice, IN EFI_PHYSICAL_ADDRESS StartAddress, IN UINTN BufferSize, IN UINTN RequiredAlignment ) { UINTN DataStartPos; UINTN RelativePos; UINTN PadSize; if (BufferSize > 0x00FFFFFF) { DataStartPos = (UINTN) StartAddress + sizeof (EFI_FFS_FILE_HEADER2); } else { DataStartPos = (UINTN) StartAddress + sizeof (EFI_FFS_FILE_HEADER); } RelativePos = DataStartPos - (UINTN) FvDevice->CachedFv; PadSize = 0; while ((RelativePos & (RequiredAlignment - 1)) != 0) { RelativePos++; PadSize++; } // // If padsize is 0, no pad file needed; // If padsize is great than 24, then pad file can be created // if ((PadSize == 0) || (PadSize >= sizeof (EFI_FFS_FILE_HEADER))) { return PadSize; } // // Perhaps following method can save space // RelativePos = DataStartPos - (UINTN) FvDevice->CachedFv + sizeof (EFI_FFS_FILE_HEADER); PadSize = sizeof (EFI_FFS_FILE_HEADER); while ((RelativePos & (RequiredAlignment - 1)) != 0) { RelativePos++; PadSize++; } return PadSize; } /** Convert EFI_FV_FILE_ATTRIBUTES to FFS_FILE_ATTRIBUTES. @param FvFileAttrib The value of EFI_FV_FILE_ATTRIBUTES @param FfsFileAttrib Pointer to the got FFS_FILE_ATTRIBUTES value. **/ VOID FvFileAttrib2FfsFileAttrib ( IN EFI_FV_FILE_ATTRIBUTES FvFileAttrib, OUT UINT8 *FfsFileAttrib ) { UINT8 FvFileAlignment; UINT8 FfsFileAlignment; UINT8 FfsFileAlignment2; FvFileAlignment = (UINT8) (FvFileAttrib & EFI_FV_FILE_ATTRIB_ALIGNMENT); FfsFileAlignment = 0; FfsFileAlignment2 = 0; switch (FvFileAlignment) { case 0: // // fall through // case 1: // // fall through // case 2: // // fall through // case 3: // // fall through // FfsFileAlignment = 0; break; case 4: // // fall through // case 5: // // fall through // case 6: // // fall through // FfsFileAlignment = 1; break; case 7: // // fall through // case 8: // // fall through // FfsFileAlignment = 2; break; case 9: FfsFileAlignment = 3; break; case 10: // // fall through // case 11: // // fall through // FfsFileAlignment = 4; break; case 12: // // fall through // case 13: // // fall through // case 14: // // fall through // FfsFileAlignment = 5; break; case 15: FfsFileAlignment = 6; break; case 16: FfsFileAlignment = 7; break; case 17: FfsFileAlignment = 0; FfsFileAlignment2 = 1; break; case 18: FfsFileAlignment = 1; FfsFileAlignment2 = 1; break; case 19: FfsFileAlignment = 2; FfsFileAlignment2 = 1; break; case 20: FfsFileAlignment = 3; FfsFileAlignment2 = 1; break; case 21: FfsFileAlignment = 4; FfsFileAlignment2 = 1; break; case 22: FfsFileAlignment = 5; FfsFileAlignment2 = 1; break; case 23: FfsFileAlignment = 6; FfsFileAlignment2 = 1; break; case 24: FfsFileAlignment = 7; FfsFileAlignment2 = 1; break; } *FfsFileAttrib = (UINT8) ((FfsFileAlignment << 3) | (FfsFileAlignment2 << 1)); return ; } /** Locate a free space entry that can hold this FFS file. @param FvDevice Cached Firmware Volume. @param Size The FFS file size. @param RequiredAlignment FFS File Data alignment requirement. @param PadSize Pointer to the size of leading Pad File. @param FreeSpaceEntry Pointer to the Free Space Entry that meets the requirement. @retval EFI_SUCCESS The free space entry is found. @retval EFI_NOT_FOUND The free space entry can't be found. **/ EFI_STATUS FvLocateFreeSpaceEntry ( IN FV_DEVICE *FvDevice, IN UINTN Size, IN UINTN RequiredAlignment, OUT UINTN *PadSize, OUT FREE_SPACE_ENTRY **FreeSpaceEntry ) { FREE_SPACE_ENTRY *FreeSpaceListEntry; LIST_ENTRY *Link; UINTN PadFileSize; Link = FvDevice->FreeSpaceHeader.ForwardLink; FreeSpaceListEntry = (FREE_SPACE_ENTRY *) Link; // // Loop the free space entry list to find one that can hold the // required the file size // while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) { PadFileSize = CalculatePadFileSize ( FvDevice, (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceListEntry->StartingAddress, Size, RequiredAlignment ); if (FreeSpaceListEntry->Length >= Size + PadFileSize) { *FreeSpaceEntry = FreeSpaceListEntry; *PadSize = PadFileSize; return EFI_SUCCESS; } FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink; } return EFI_NOT_FOUND; } /** Locate Pad File for writing, this is got from FV Cache. @param FvDevice Cached Firmware Volume. @param Size The required FFS file size. @param RequiredAlignment FFS File Data alignment requirement. @param PadSize Pointer to the size of leading Pad File. @param PadFileEntry Pointer to the Pad File Entry that meets the requirement. @retval EFI_SUCCESS The required pad file is found. @retval EFI_NOT_FOUND The required pad file can't be found. **/ EFI_STATUS FvLocatePadFile ( IN FV_DEVICE *FvDevice, IN UINTN Size, IN UINTN RequiredAlignment, OUT UINTN *PadSize, OUT FFS_FILE_LIST_ENTRY **PadFileEntry ) { FFS_FILE_LIST_ENTRY *FileEntry; EFI_FFS_FILE_STATE FileState; EFI_FFS_FILE_HEADER *FileHeader; UINTN PadAreaLength; UINTN PadFileSize; UINTN HeaderSize; FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink; // // travel through the whole file list to get the pad file entry // while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) { FileHeader = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader; FileState = GetFileState (FvDevice->ErasePolarity, FileHeader); if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) { // // we find one valid pad file, check its free area length // if (IS_FFS_FILE2 (FileHeader)) { HeaderSize = sizeof (EFI_FFS_FILE_HEADER2); PadAreaLength = FFS_FILE2_SIZE (FileHeader) - HeaderSize; } else { HeaderSize = sizeof (EFI_FFS_FILE_HEADER); PadAreaLength = FFS_FILE_SIZE (FileHeader) - HeaderSize; } PadFileSize = CalculatePadFileSize ( FvDevice, (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + HeaderSize, Size, RequiredAlignment ); if (PadAreaLength >= (Size + PadFileSize)) { *PadSize = PadFileSize; *PadFileEntry = FileEntry; return EFI_SUCCESS; } } FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink); } return EFI_NOT_FOUND; } /** Locate a suitable pad file for multiple file writing. @param FvDevice Cached Firmware Volume. @param NumOfFiles The number of Files that needed updating @param BufferSize The array of each file size. @param RequiredAlignment The array of of FFS File Data alignment requirement. @param PadSize The array of size of each leading Pad File. @param TotalSizeNeeded The totalsize that can hold these files. @param PadFileEntry Pointer to the Pad File Entry that meets the requirement. @retval EFI_SUCCESS The required pad file is found. @retval EFI_NOT_FOUND The required pad file can't be found. **/ EFI_STATUS FvSearchSuitablePadFile ( IN FV_DEVICE *FvDevice, IN UINTN NumOfFiles, IN UINTN *BufferSize, IN UINTN *RequiredAlignment, OUT UINTN *PadSize, OUT UINTN *TotalSizeNeeded, OUT FFS_FILE_LIST_ENTRY **PadFileEntry ) { FFS_FILE_LIST_ENTRY *FileEntry; EFI_FFS_FILE_STATE FileState; EFI_FFS_FILE_HEADER *FileHeader; UINTN PadAreaLength; UINTN TotalSize; UINTN Index; UINTN HeaderSize; FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink; // // travel through the whole file list to get the pad file entry // while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) { FileHeader = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader; FileState = GetFileState (FvDevice->ErasePolarity, FileHeader); if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) { // // we find one valid pad file, check its length // if (IS_FFS_FILE2 (FileHeader)) { HeaderSize = sizeof (EFI_FFS_FILE_HEADER2); PadAreaLength = FFS_FILE2_SIZE (FileHeader) - HeaderSize; } else { HeaderSize = sizeof (EFI_FFS_FILE_HEADER); PadAreaLength = FFS_FILE_SIZE (FileHeader) - HeaderSize; } TotalSize = 0; for (Index = 0; Index < NumOfFiles; Index++) { PadSize[Index] = CalculatePadFileSize ( FvDevice, (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + HeaderSize + TotalSize, BufferSize[Index], RequiredAlignment[Index] ); TotalSize += PadSize[Index]; TotalSize += BufferSize[Index]; if (TotalSize > PadAreaLength) { break; } } if (PadAreaLength >= TotalSize) { *PadFileEntry = FileEntry; *TotalSizeNeeded = TotalSize; return EFI_SUCCESS; } } FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink); } return EFI_NOT_FOUND; } /** Locate a Free Space entry which can hold these files, including meeting the alignment requirements. @param FvDevice Cached Firmware Volume. @param NumOfFiles The number of Files that needed updating @param BufferSize The array of each file size. @param RequiredAlignment The array of of FFS File Data alignment requirement. @param PadSize The array of size of each leading Pad File. @param TotalSizeNeeded The got total size that can hold these files. @param FreeSpaceEntry The Free Space Entry that can hold these files. @retval EFI_SUCCESS The free space entry is found. @retval EFI_NOT_FOUND The free space entry can't be found. **/ EFI_STATUS FvSearchSuitableFreeSpace ( IN FV_DEVICE *FvDevice, IN UINTN NumOfFiles, IN UINTN *BufferSize, IN UINTN *RequiredAlignment, OUT UINTN *PadSize, OUT UINTN *TotalSizeNeeded, OUT FREE_SPACE_ENTRY **FreeSpaceEntry ) { FREE_SPACE_ENTRY *FreeSpaceListEntry; LIST_ENTRY *Link; UINTN TotalSize; UINTN Index; UINT8 *StartAddr; Link = FvDevice->FreeSpaceHeader.ForwardLink; FreeSpaceListEntry = (FREE_SPACE_ENTRY *) Link; while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) { TotalSize = 0; StartAddr = FreeSpaceListEntry->StartingAddress; // // Calculate the totalsize we need // for (Index = 0; Index < NumOfFiles; Index++) { // // Perhaps we don't need an EFI_FFS_FILE_HEADER, the first file // have had its leading pad file. // PadSize[Index] = CalculatePadFileSize ( FvDevice, (EFI_PHYSICAL_ADDRESS) (UINTN) StartAddr + TotalSize, BufferSize[Index], RequiredAlignment[Index] ); TotalSize += PadSize[Index]; TotalSize += BufferSize[Index]; if (TotalSize > FreeSpaceListEntry->Length) { break; } } if (FreeSpaceListEntry->Length >= TotalSize) { *FreeSpaceEntry = FreeSpaceListEntry; *TotalSizeNeeded = TotalSize; return EFI_SUCCESS; } FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink; } return EFI_NOT_FOUND; } /** Calculate the length of the remaining space in FV. @param FvDevice Cached Firmware Volume @param Offset Current offset to FV base address. @param Lba LBA number for the current offset. @param LOffset Offset in block for the current offset. @return the length of remaining space. **/ UINTN CalculateRemainingLength ( IN FV_DEVICE *FvDevice, IN UINTN Offset, OUT EFI_LBA *Lba, OUT UINTN *LOffset ) { LIST_ENTRY *Link; LBA_ENTRY *LbaEntry; UINTN Count; Count = 0; *Lba = 0; Link = FvDevice->LbaHeader.ForwardLink; LbaEntry = (LBA_ENTRY *) Link; while (&LbaEntry->Link != &FvDevice->LbaHeader) { if (Count > Offset) { break; } Count += LbaEntry->BlockLength; (*Lba)++; Link = LbaEntry->Link.ForwardLink; LbaEntry = (LBA_ENTRY *) Link; } if (Count <= Offset) { return 0; } Link = LbaEntry->Link.BackLink; LbaEntry = (LBA_ENTRY *) Link; (*Lba)--; *LOffset = (UINTN) (LbaEntry->BlockLength - (Count - Offset)); Count = 0; while (&LbaEntry->Link != &FvDevice->LbaHeader) { Count += LbaEntry->BlockLength; Link = LbaEntry->Link.ForwardLink; LbaEntry = (LBA_ENTRY *) Link; } Count -= *LOffset; return Count; } /** Writes data beginning at Lba:Offset from FV. The write terminates either when *NumBytes of data have been written, or when the firmware end is reached. *NumBytes is updated to reflect the actual number of bytes written. @param FvDevice Cached Firmware Volume @param Offset Offset in the block at which to begin write @param NumBytes At input, indicates the requested write size. At output, indicates the actual number of bytes written. @param Buffer Buffer containing source data for the write. @retval EFI_SUCCESS Data is successfully written into FV. @return error Data is failed written. **/ EFI_STATUS FvcWrite ( IN FV_DEVICE *FvDevice, IN UINTN Offset, IN OUT UINTN *NumBytes, IN UINT8 *Buffer ) { EFI_STATUS Status; EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; EFI_LBA Lba; UINTN LOffset; EFI_FVB_ATTRIBUTES_2 FvbAttributes; UINTN RemainingLength; UINTN WriteLength; UINT8 *TmpBuffer; LOffset = 0; RemainingLength = CalculateRemainingLength (FvDevice, Offset, &Lba, &LOffset); if ((UINTN) (*NumBytes) > RemainingLength) { *NumBytes = (UINTN) RemainingLength; return EFI_INVALID_PARAMETER; } Fvb = FvDevice->Fvb; Status = Fvb->GetAttributes ( Fvb, &FvbAttributes ); if (EFI_ERROR (Status)) { return Status; } if ((FvbAttributes & EFI_FV2_WRITE_STATUS) == 0) { return EFI_ACCESS_DENIED; } RemainingLength = *NumBytes; WriteLength = RemainingLength; TmpBuffer = Buffer; do { Status = Fvb->Write ( Fvb, Lba, LOffset, &WriteLength, TmpBuffer ); if (!EFI_ERROR (Status)) { goto Done; } if (Status == EFI_BAD_BUFFER_SIZE) { Lba++; LOffset = 0; TmpBuffer += WriteLength; RemainingLength -= WriteLength; WriteLength = (UINTN) RemainingLength; continue; } else { return Status; } } while (1); Done: return EFI_SUCCESS; } /** Create a new FFS file into Firmware Volume device. @param FvDevice Cached Firmware Volume. @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 FFS fle is added into FV. @retval EFI_INVALID_PARAMETER File type is not valid. @retval EFI_DEVICE_ERROR FV doesn't set writable attribute. @retval EFI_NOT_FOUND FV has no enough space for the added file. **/ EFI_STATUS FvCreateNewFile ( 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 ) { EFI_STATUS Status; EFI_FFS_FILE_HEADER *FileHeader; EFI_PHYSICAL_ADDRESS BufferPtr; UINTN Offset; UINTN NumBytesWritten; UINTN StateOffset; FREE_SPACE_ENTRY *FreeSpaceEntry; UINTN RequiredAlignment; UINTN PadFileSize; FFS_FILE_LIST_ENTRY *PadFileEntry; EFI_FFS_FILE_ATTRIBUTES TmpFileAttribute; FFS_FILE_LIST_ENTRY *FfsFileEntry; UINTN HeaderSize; // // File Type: 0x0E~0xE0 are reserved // if ((FileType > EFI_FV_FILETYPE_SMM_CORE) && (FileType < 0xE0)) { return EFI_INVALID_PARAMETER; } // // First find a free space that can hold this image. // Check alignment, FFS at least must be aligned at 8-byte boundary // RequiredAlignment = GetRequiredAlignment (FileAttributes); Status = FvLocateFreeSpaceEntry ( FvDevice, BufferSize, RequiredAlignment, &PadFileSize, &FreeSpaceEntry ); if (EFI_ERROR (Status)) { // // Maybe we need to find a PAD file that can hold this image // Status = FvCreateNewFileInsidePadFile ( FvDevice, FfsFileBuffer, BufferSize, ActualFileSize, FileName, FileType, FileAttributes ); return Status; } BufferPtr = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress; // // If we need a leading PAD File, create it first. // if (PadFileSize != 0) { Status = FvCreatePadFileInFreeSpace ( FvDevice, FreeSpaceEntry, PadFileSize - sizeof (EFI_FFS_FILE_HEADER), &PadFileEntry ); if (EFI_ERROR (Status)) { return Status; } } // // Maybe we create a pad file, so re-get the free space starting address // and length // BufferPtr = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress; // // File creation step 1: Allocate File Header, // Mark EFI_FILE_HEADER_CONSTRUCTION bit to TRUE, // Write Name, IntegrityCheck.Header, Type, Attributes, and Size // FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileBuffer; if (ActualFileSize > 0x00FFFFFF) { HeaderSize = sizeof (EFI_FFS_FILE_HEADER2); } else { HeaderSize = sizeof (EFI_FFS_FILE_HEADER); } SetFileState (EFI_FILE_HEADER_CONSTRUCTION, FileHeader); Offset = (UINTN) (BufferPtr - FvDevice->CachedFv); StateOffset = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader; NumBytesWritten = sizeof (EFI_FFS_FILE_STATE); Status = FvcWrite ( FvDevice, StateOffset, &NumBytesWritten, &FileHeader->State ); if (EFI_ERROR (Status)) { return Status; } // // update header 2 cache // CopyMem ( (UINT8 *) (UINTN) BufferPtr, FileHeader, HeaderSize ); // // update Free Space Entry, now need to substract the file header length // FreeSpaceEntry->StartingAddress += HeaderSize; FreeSpaceEntry->Length -= HeaderSize; CopyGuid (&FileHeader->Name, FileName); FileHeader->Type = FileType; // // Convert FvFileAttribute to FfsFileAttributes // FvFileAttrib2FfsFileAttrib (FileAttributes, &TmpFileAttribute); FileHeader->Attributes = TmpFileAttribute; // // File size is including the FFS File Header. // if (ActualFileSize > 0x00FFFFFF) { ((EFI_FFS_FILE_HEADER2 *) FileHeader)->ExtendedSize = (UINT32) ActualFileSize; *(UINT32 *) FileHeader->Size &= 0xFF000000; FileHeader->Attributes |= FFS_ATTRIB_LARGE_FILE; } else { *(UINT32 *) FileHeader->Size &= 0xFF000000; *(UINT32 *) FileHeader->Size |= ActualFileSize; } SetHeaderChecksum (FileHeader); Offset = (UINTN) (BufferPtr - FvDevice->CachedFv); NumBytesWritten = HeaderSize; Status = FvcWrite ( FvDevice, Offset, &NumBytesWritten, (UINT8 *) FileHeader ); if (EFI_ERROR (Status)) { return Status; } // // update header 2 cache // CopyMem ( (UINT8 *) (UINTN) BufferPtr, FileHeader, HeaderSize ); // // end of step 1 // // File creation step 2: // MARK EFI_FILE_HEADER_VALID bit to TRUE, // Write IntegrityCheck.File, File Data // SetFileState (EFI_FILE_HEADER_VALID, FileHeader); Offset = (UINTN) (BufferPtr - FvDevice->CachedFv); StateOffset = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader; NumBytesWritten = sizeof (EFI_FFS_FILE_STATE); Status = FvcWrite ( FvDevice, StateOffset, &NumBytesWritten, &FileHeader->State ); if (EFI_ERROR (Status)) { return Status; } // // update header 2 cache // CopyMem ( (UINT8 *) (UINTN) BufferPtr, FileHeader, HeaderSize ); // // update Free Space Entry, now need to substract the file data length // FreeSpaceEntry->StartingAddress += (BufferSize - HeaderSize); FreeSpaceEntry->Length -= (BufferSize - HeaderSize); // // Calculate File Checksum // SetFileChecksum (FileHeader, ActualFileSize); Offset = (UINTN) (BufferPtr - FvDevice->CachedFv); NumBytesWritten = BufferSize; Status = FvcWrite ( FvDevice, Offset, &NumBytesWritten, FfsFileBuffer ); if (EFI_ERROR (Status)) { return Status; } // // each time write block successfully, write also to cache // CopyMem ( (UINT8 *) (UINTN) BufferPtr, FfsFileBuffer, NumBytesWritten ); // // Step 3: Mark EFI_FILE_DATA_VALID to TRUE // SetFileState (EFI_FILE_DATA_VALID, FileHeader); Offset = (UINTN) (BufferPtr - FvDevice->CachedFv); StateOffset = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader; NumBytesWritten = sizeof (EFI_FFS_FILE_STATE); Status = FvcWrite ( FvDevice, StateOffset, &NumBytesWritten, &FileHeader->State ); if (EFI_ERROR (Status)) { return Status; } // // update header 2 cache // CopyMem ( (UINT8 *) (UINTN) BufferPtr, FileHeader, HeaderSize ); // // If successfully, insert an FfsFileEntry at the end of ffs file list // FfsFileEntry = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY)); ASSERT (FfsFileEntry != NULL); FfsFileEntry->FfsHeader = (UINT8 *) (UINTN) BufferPtr; InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link); // // Set cache file to this file // FvDevice->CurrentFfsFile = FfsFileEntry; return EFI_SUCCESS; } /** Update a File, so after successful update, there are 2 files existing in FV, one is marked for deleted, and another one is valid. @param FvDevice Cached Firmware Volume. @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 NewFileType The FFS File Type. @param NewFileAttributes The Attributes of the FFS File to be created. @retval EFI_SUCCESS FFS fle is updated into FV. @retval EFI_INVALID_PARAMETER File type is not valid. @retval EFI_DEVICE_ERROR FV doesn't set writable attribute. @retval EFI_NOT_FOUND FV has no enough space for the added file. FFS with same file name is not found in FV. **/ EFI_STATUS FvUpdateFile ( IN FV_DEVICE *FvDevice, IN UINT8 *FfsFileBuffer, IN UINTN BufferSize, IN UINTN ActualFileSize, IN EFI_GUID *FileName, IN EFI_FV_FILETYPE NewFileType, IN EFI_FV_FILE_ATTRIBUTES NewFileAttributes ) { EFI_STATUS Status; EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; UINTN NumBytesWritten; EFI_FV_FILETYPE OldFileType; EFI_FV_FILE_ATTRIBUTES OldFileAttributes; UINTN OldFileSize; EFI_FFS_FILE_HEADER *OldFileHeader; UINTN OldOffset; UINTN OldStateOffset; FFS_FILE_LIST_ENTRY *OldFfsFileEntry; UINTN Key; EFI_GUID FileNameGuid; Fv = &FvDevice->Fv; // // Step 1, find old file, // Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE in the older header // // // Check if the file was read last time. // OldFileHeader = NULL; OldFfsFileEntry = FvDevice->CurrentFfsFile; if (OldFfsFileEntry != NULL) { OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader; } if ((OldFfsFileEntry == NULL) || (!CompareGuid (&OldFileHeader->Name, FileName))) { Key = 0; do { OldFileType = 0; Status = Fv->GetNextFile ( Fv, &Key, &OldFileType, &FileNameGuid, &OldFileAttributes, &OldFileSize ); if (EFI_ERROR (Status)) { return Status; } } while (!CompareGuid (&FileNameGuid, FileName)); // // Get FfsFileEntry from the search key // OldFfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key; // // Double check file state before being ready to be removed // OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader; } else { // // Mark the cache file to invalid // FvDevice->CurrentFfsFile = NULL; } // // Update File: Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE // SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader); OldOffset = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv); OldStateOffset = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader; NumBytesWritten = sizeof (EFI_FFS_FILE_STATE); Status = FvcWrite ( FvDevice, OldStateOffset, &NumBytesWritten, &OldFileHeader->State ); if (EFI_ERROR (Status)) { // // if failed, write the bit back in the cache, its XOR operation. // SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader); return Status; } // // Step 2, Create New Files // Status = FvCreateNewFile ( FvDevice, FfsFileBuffer, BufferSize, ActualFileSize, FileName, NewFileType, NewFileAttributes ); if (EFI_ERROR (Status)) { return Status; } // // If successfully, remove this file entry, // although delete file may fail. // (OldFfsFileEntry->Link.BackLink)->ForwardLink = OldFfsFileEntry->Link.ForwardLink; (OldFfsFileEntry->Link.ForwardLink)->BackLink = OldFfsFileEntry->Link.BackLink; FreePool (OldFfsFileEntry); // // Step 3: Delete old files, // by marking EFI_FILE_DELETED to TRUE // SetFileState (EFI_FILE_DELETED, OldFileHeader); OldOffset = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv); OldStateOffset = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader; NumBytesWritten = sizeof (EFI_FFS_FILE_STATE); Status = FvcWrite ( FvDevice, OldStateOffset, &NumBytesWritten, &OldFileHeader->State ); if (EFI_ERROR (Status)) { // // if failed, write the bit back in the cache, its XOR operation. // SetFileState (EFI_FILE_DELETED, OldFileHeader); return Status; } return EFI_SUCCESS; } /** Deleted a given file from FV device. @param FvDevice Cached Firmware Volume. @param NameGuid The FFS File Name. @retval EFI_SUCCESS FFS file with the specified FFS name is removed. @retval EFI_NOT_FOUND FFS file with the specified FFS name is not found. **/ EFI_STATUS FvDeleteFile ( IN FV_DEVICE *FvDevice, IN EFI_GUID *NameGuid ) { EFI_STATUS Status; UINTN Key; EFI_GUID FileNameGuid; EFI_FV_FILETYPE FileType; EFI_FV_FILE_ATTRIBUTES FileAttributes; UINTN FileSize; EFI_FFS_FILE_HEADER *FileHeader; FFS_FILE_LIST_ENTRY *FfsFileEntry; EFI_FFS_FILE_STATE FileState; EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; UINTN Offset; UINTN StateOffset; UINTN NumBytesWritten; Fv = &FvDevice->Fv; // // Check if the file was read last time. // FileHeader = NULL; FfsFileEntry = FvDevice->CurrentFfsFile; if (FfsFileEntry != NULL) { FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader; } if ((FfsFileEntry == NULL) || (!CompareGuid (&FileHeader->Name, NameGuid))) { // // Next search for the file using GetNextFile // Key = 0; do { FileType = 0; Status = Fv->GetNextFile ( Fv, &Key, &FileType, &FileNameGuid, &FileAttributes, &FileSize ); if (EFI_ERROR (Status)) { return Status; } } while (!CompareGuid (&FileNameGuid, NameGuid)); // // Get FfsFileEntry from the search key // FfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key; // // Double check file state before being ready to be removed // FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader; } else { // // Mark the cache file to NULL // FvDevice->CurrentFfsFile = NULL; } FileState = GetFileState (FvDevice->ErasePolarity, FileHeader); if (FileState == EFI_FILE_HEADER_INVALID) { return EFI_NOT_FOUND; } if (FileState == EFI_FILE_DELETED) { return EFI_NOT_FOUND; } // // Delete File: Mark EFI_FILE_DELETED to TRUE // SetFileState (EFI_FILE_DELETED, FileHeader); Offset = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader - FvDevice->CachedFv); StateOffset = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader; NumBytesWritten = sizeof (EFI_FFS_FILE_STATE); Status = FvcWrite ( FvDevice, StateOffset, &NumBytesWritten, &FileHeader->State ); if (EFI_ERROR (Status)) { // // if failed, write the bit back in the cache, its XOR operation. // SetFileState (EFI_FILE_DELETED, FileHeader); return Status; } // // If successfully, remove this file entry // FvDevice->CurrentFfsFile = NULL; (FfsFileEntry->Link.BackLink)->ForwardLink = FfsFileEntry->Link.ForwardLink; (FfsFileEntry->Link.ForwardLink)->BackLink = FfsFileEntry->Link.BackLink; FreePool (FfsFileEntry); return EFI_SUCCESS; } /** Writes one or more files to the firmware volume. @param This Indicates the calling context. @param NumberOfFiles Number of files. @param WritePolicy WritePolicy indicates the level of reliability for the write in the event of a power failure or other system failure during the write operation. @param FileData FileData is an pointer to an array of EFI_FV_WRITE_DATA. Each element of array FileData represents a file to be written. @retval EFI_SUCCESS Files successfully written to firmware volume @retval EFI_OUT_OF_RESOURCES Not enough buffer to be allocated. @retval EFI_DEVICE_ERROR Device error. @retval EFI_WRITE_PROTECTED Write protected. @retval EFI_NOT_FOUND Not found. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_UNSUPPORTED This function not supported. **/ EFI_STATUS EFIAPI FvWriteFile ( IN CONST EFI_FIRMWARE_VOLUME2_PROTOCOL *This, IN UINT32 NumberOfFiles, IN EFI_FV_WRITE_POLICY WritePolicy, IN EFI_FV_WRITE_FILE_DATA *FileData ) { EFI_STATUS Status; UINTN Index1; UINTN Index2; UINT8 *FileBuffer; UINTN BufferSize; UINTN ActualSize; UINT8 ErasePolarity; FV_DEVICE *FvDevice; EFI_FV_FILETYPE FileType; EFI_FV_FILE_ATTRIBUTES FileAttributes; UINTN Size; BOOLEAN CreateNewFile[MAX_FILES]; UINTN NumDelete; EFI_FV_ATTRIBUTES FvAttributes; UINT32 AuthenticationStatus; UINTN HeaderSize; if (NumberOfFiles > MAX_FILES) { return EFI_UNSUPPORTED; } Status = EFI_SUCCESS; SetMem (CreateNewFile, NumberOfFiles, TRUE); FvDevice = FV_DEVICE_FROM_THIS (This); // // First check the volume attributes. // Status = This->GetVolumeAttributes ( This, &FvAttributes ); if (EFI_ERROR (Status)) { return Status; } // // Can we have write right? // if ((FvAttributes & EFI_FV2_WRITE_STATUS) == 0) { return EFI_WRITE_PROTECTED; } ErasePolarity = FvDevice->ErasePolarity; // // Loop for all files // NumDelete = 0; for (Index1 = 0; Index1 < NumberOfFiles; Index1++) { if ((FileData[Index1].BufferSize + sizeof (EFI_FFS_FILE_HEADER) > 0x00FFFFFF) && !FvDevice->IsFfs3Fv) { // // Found a file needs a FFS3 formatted file to store it, but it is in a non-FFS3 formatted FV. // DEBUG ((EFI_D_ERROR, "FFS3 formatted file can't be written in a non-FFS3 formatted FV.\n")); return EFI_INVALID_PARAMETER; } if (FileData[Index1].BufferSize == 0) { // // Here we will delete this file // Status = This->ReadFile ( This, FileData[Index1].NameGuid, NULL, &Size, &FileType, &FileAttributes, &AuthenticationStatus ); if (!EFI_ERROR (Status)) { NumDelete++; } else { return Status; } } if (FileData[Index1].Type == EFI_FV_FILETYPE_FFS_PAD) { // // According to PI spec, on EFI_FV_FILETYPE_FFS_PAD: // "Standard firmware file system services will not return the handle of any pad files, // nor will they permit explicit creation of such files." // return EFI_INVALID_PARAMETER; } } if ((NumDelete != NumberOfFiles) && (NumDelete != 0)) { // // A delete was request with a multiple file write // return EFI_INVALID_PARAMETER; } if (NumDelete == NumberOfFiles) { for (Index1 = 0; Index1 < NumberOfFiles; Index1++) { // // Delete Files // Status = FvDeleteFile (FvDevice, FileData[Index1].NameGuid); if (EFI_ERROR (Status)) { return Status; } } return EFI_SUCCESS; } for (Index1 = 0; Index1 < NumberOfFiles; Index1++) { Status = This->ReadFile ( This, FileData[Index1].NameGuid, NULL, &Size, &FileType, &FileAttributes, &AuthenticationStatus ); if (!EFI_ERROR (Status)) { CreateNewFile[Index1] = FALSE; } else if (Status == EFI_NOT_FOUND) { CreateNewFile[Index1] = TRUE; } else { return Status; } // // Checking alignment // if ((FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT) != 0) { UINT8 FFSAlignmentValue; UINT8 FvAlignmentValue; FFSAlignmentValue = (UINT8) (FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT); FvAlignmentValue = (UINT8) (((UINT32) (FvAttributes & EFI_FV2_ALIGNMENT)) >> 16); if (FFSAlignmentValue > FvAlignmentValue) { return EFI_INVALID_PARAMETER; } } } if ((WritePolicy != EFI_FV_RELIABLE_WRITE) && (WritePolicy != EFI_FV_UNRELIABLE_WRITE)) { return EFI_INVALID_PARAMETER; } // // Checking the reliable write is supported by FV // if ((WritePolicy == EFI_FV_RELIABLE_WRITE) && (NumberOfFiles > 1)) { // // Only for multiple files, reliable write is meaningful // Status = FvCreateMultipleFiles ( FvDevice, NumberOfFiles, FileData, CreateNewFile ); return Status; } for (Index1 = 0; Index1 < NumberOfFiles; Index1++) { // // Making Buffersize QWORD boundary, and add file tail. // HeaderSize = sizeof (EFI_FFS_FILE_HEADER); ActualSize = FileData[Index1].BufferSize + HeaderSize; if (ActualSize > 0x00FFFFFF) { HeaderSize = sizeof (EFI_FFS_FILE_HEADER2); ActualSize = FileData[Index1].BufferSize + HeaderSize; } BufferSize = ActualSize; while ((BufferSize & 0x07) != 0) { BufferSize++; } FileBuffer = AllocateZeroPool (BufferSize); if (FileBuffer == NULL) { return Status; } // // Copy File Data into FileBuffer // CopyMem ( FileBuffer + HeaderSize, FileData[Index1].Buffer, FileData[Index1].BufferSize ); if (ErasePolarity == 1) { // // Fill the file header and padding byte with Erase Byte // for (Index2 = 0; Index2 < HeaderSize; Index2++) { FileBuffer[Index2] = (UINT8)~FileBuffer[Index2]; } for (Index2 = ActualSize; Index2 < BufferSize; Index2++) { FileBuffer[Index2] = (UINT8)~FileBuffer[Index2]; } } if (CreateNewFile[Index1]) { Status = FvCreateNewFile ( FvDevice, FileBuffer, BufferSize, ActualSize, FileData[Index1].NameGuid, FileData[Index1].Type, FileData[Index1].FileAttributes ); } else { Status = FvUpdateFile ( FvDevice, FileBuffer, BufferSize, ActualSize, FileData[Index1].NameGuid, FileData[Index1].Type, FileData[Index1].FileAttributes ); } FreePool (FileBuffer); if (EFI_ERROR (Status)) { return Status; } } return EFI_SUCCESS; }