+/** @file\r
+*\r
+* Copyright (c) 2012-2014, ARM Limited. All rights reserved.\r
+*\r
+* This program and the accompanying materials\r
+* are licensed and made available under the terms and conditions of the BSD License\r
+* which accompanies this distribution. The full text of the license may be found at\r
+* http://opensource.org/licenses/bsd-license.php\r
+*\r
+* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+*\r
+**/\r
+\r
+#include "BootMonFsInternal.h"\r
+\r
+// Clear a file's image description on storage media:\r
+// UEFI allows you to seek past the end of a file, a subsequent write will grow\r
+// the file. It does not specify how space between the former end of the file\r
+// and the beginning of the write should be filled. It's therefore possible that\r
+// BootMonFs metadata, that comes after the end of a file, could be left there\r
+// and wrongly detected by BootMonFsImageInBlock.\r
+STATIC\r
+EFI_STATUS\r
+InvalidateImageDescription (\r
+ IN BOOTMON_FS_FILE *File\r
+ )\r
+{\r
+ EFI_DISK_IO_PROTOCOL *DiskIo;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ UINT32 MediaId;\r
+ UINT32 BlockSize;\r
+ VOID *Buffer;\r
+ EFI_STATUS Status;\r
+ UINT64 DescriptionAddress;\r
+\r
+ DiskIo = File->Instance->DiskIo;\r
+ BlockIo = File->Instance->BlockIo;\r
+ MediaId = BlockIo->Media->MediaId;\r
+ BlockSize = BlockIo->Media->BlockSize;\r
+\r
+ DescriptionAddress = (File->HwDescription.BlockEnd * BlockSize)\r
+ - sizeof (HW_IMAGE_DESCRIPTION);\r
+\r
+ Buffer = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION));\r
+\r
+ Status = DiskIo->WriteDisk (DiskIo,\r
+ MediaId,\r
+ DescriptionAddress,\r
+ sizeof (HW_IMAGE_DESCRIPTION),\r
+ Buffer\r
+ );\r
+\r
+ FreePool(Buffer);\r
+\r
+ return Status;\r
+}\r
+\r
+// Flush file data that will extend the file's length. Update and, if necessary,\r
+// move the image description.\r
+// We need to pass the file's starting position on media (FileStart), because\r
+// if the file hasn't been flushed before its Description->BlockStart won't\r
+// have been initialised.\r
+// FileStart must be aligned to the media's block size.\r
+// Note that this function uses DiskIo to flush, so call BlockIo->FlushBlocks()\r
+// after calling it.\r
+STATIC\r
+EFI_STATUS\r
+FlushAppendRegion (\r
+ IN BOOTMON_FS_FILE *File,\r
+ IN BOOTMON_FS_FILE_REGION *Region,\r
+ IN UINT64 NewFileSize,\r
+ IN UINT64 FileStart\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DISK_IO_PROTOCOL *DiskIo;\r
+ UINTN BlockSize;\r
+ HW_IMAGE_DESCRIPTION *Description;\r
+\r
+ DiskIo = File->Instance->DiskIo;\r
+\r
+ BlockSize = File->Instance->BlockIo->Media->BlockSize;\r
+\r
+ ASSERT (FileStart % BlockSize == 0);\r
+\r
+ // Only invalidate the Image Description of files that have already been\r
+ // written in Flash\r
+ if (File->HwDescription.RegionCount > 0) {\r
+ Status = InvalidateImageDescription (File);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ //\r
+ // Update File Description\r
+ //\r
+ Description = &File->HwDescription;\r
+ Description->Attributes = 1;\r
+ Description->BlockStart = FileStart / BlockSize;\r
+ Description->BlockEnd = Description->BlockStart + (NewFileSize / BlockSize);\r
+ Description->Footer.FooterSignature1 = HW_IMAGE_FOOTER_SIGNATURE_1;\r
+ Description->Footer.FooterSignature2 = HW_IMAGE_FOOTER_SIGNATURE_2;\r
+ Description->Footer.Version = HW_IMAGE_FOOTER_VERSION;\r
+ Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET;\r
+ Description->RegionCount = 1;\r
+ Description->Region[0].Checksum = 0;\r
+ Description->Region[0].Offset = Description->BlockStart * BlockSize;\r
+ Description->Region[0].Size = NewFileSize - sizeof (HW_IMAGE_DESCRIPTION);\r
+\r
+ Status = BootMonFsComputeFooterChecksum (Description);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Write the new file data\r
+ Status = DiskIo->WriteDisk (\r
+ DiskIo,\r
+ File->Instance->Media->MediaId,\r
+ FileStart + Region->Offset,\r
+ Region->Size,\r
+ Region->Buffer\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ // Round the file size up to the nearest block size\r
+ if ((NewFileSize % BlockSize) > 0) {\r
+ NewFileSize += BlockSize - (NewFileSize % BlockSize);\r
+ }\r
+ // Update the file description on the media\r
+ Status = DiskIo->WriteDisk (\r
+ DiskIo,\r
+ File->Instance->Media->MediaId,\r
+ (FileStart + NewFileSize) - sizeof (HW_IMAGE_DESCRIPTION),\r
+ sizeof (HW_IMAGE_DESCRIPTION),\r
+ Description\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r
+\r
+BOOLEAN\r
+BootMonFsFileNeedFlush (\r
+ IN BOOTMON_FS_FILE *File\r
+ )\r
+{\r
+ return !IsListEmpty (&File->RegionToFlushLink);\r
+}\r
+\r
+// Find a space on media for a file that has not yet been flushed to disk.\r
+// Just returns the first space that's big enough.\r
+// This function could easily be adapted to:\r
+// - Find space for moving an existing file that has outgrown its space\r
+// (We do not currently move files, just return EFI_VOLUME_FULL)\r
+// - Find space for a fragment of a file that has outgrown its space\r
+// (We do not currently fragment files - it's not clear whether fragmentation\r
+// is actually part of BootMonFs as there is no spec)\r
+// - Be more clever about finding space (choosing the largest or smallest\r
+// suitable space)\r
+// Parameters:\r
+// File - the new (not yet flushed) file for which we need to find space.\r
+// FileStart - the position on media of the file (in bytes).\r
+STATIC\r
+EFI_STATUS\r
+BootMonFsFindSpaceForNewFile (\r
+ IN BOOTMON_FS_FILE *File,\r
+ OUT UINT64 *FileStart\r
+ )\r
+{\r
+ LIST_ENTRY *FileLink;\r
+ BOOTMON_FS_FILE *RootFile;\r
+ BOOTMON_FS_FILE *FileEntry;\r
+ UINTN BlockSize;\r
+ UINT64 FileSize;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+\r
+ Media = File->Instance->BlockIo->Media;\r
+ BlockSize = Media->BlockSize;\r
+ RootFile = File->Instance->RootFile;\r
+\r
+ if (IsListEmpty (&RootFile->Link)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // This function must only be called for file which has not been flushed into\r
+ // Flash yet\r
+ ASSERT (File->HwDescription.RegionCount == 0);\r
+\r
+ // Find out how big the file will be\r
+ FileSize = BootMonFsGetImageLength (File);\r
+ // Add the file header to the file\r
+ FileSize += sizeof (HW_IMAGE_DESCRIPTION);\r
+\r
+ *FileStart = 0;\r
+ // Go through all the files in the list\r
+ for (FileLink = GetFirstNode (&RootFile->Link);\r
+ !IsNull (&RootFile->Link, FileLink);\r
+ FileLink = GetNextNode (&RootFile->Link, FileLink)\r
+ )\r
+ {\r
+ FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);\r
+ // If the free space preceding the file is big enough to contain the new\r
+ // file then use it!\r
+ if (((FileEntry->HwDescription.BlockStart * BlockSize) - *FileStart)\r
+ >= FileSize) {\r
+ // The file list must be in disk-order\r
+ RemoveEntryList (&File->Link);\r
+ File->Link.BackLink = FileLink->BackLink;\r
+ File->Link.ForwardLink = FileLink;\r
+ FileLink->BackLink->ForwardLink = &File->Link;\r
+ FileLink->BackLink = &File->Link;\r
+\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ *FileStart = (FileEntry->HwDescription.BlockEnd + 1) * BlockSize;\r
+ }\r
+ }\r
+ // See if there's space after the last file\r
+ if ((((Media->LastBlock + 1) * BlockSize) - *FileStart) >= FileSize) {\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ return EFI_VOLUME_FULL;\r
+ }\r
+}\r
+\r
+// Free the resources in the file's Region list.\r
+STATIC\r
+VOID\r
+FreeFileRegions (\r
+ IN BOOTMON_FS_FILE *File\r
+ )\r
+{\r
+ LIST_ENTRY *RegionToFlushLink;\r
+ BOOTMON_FS_FILE_REGION *Region;\r
+\r
+ RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
+ while (!IsNull (&File->RegionToFlushLink, RegionToFlushLink)) {\r
+ // Repeatedly remove the first node from the list and free its resources.\r
+ Region = (BOOTMON_FS_FILE_REGION *) RegionToFlushLink;\r
+ RemoveEntryList (RegionToFlushLink);\r
+ FreePool (Region->Buffer);\r
+ FreePool (Region);\r
+\r
+ RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
+ }\r
+}\r
+\r
+EFIAPI\r
+EFI_STATUS\r
+BootMonFsFlushFile (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOTMON_FS_INSTANCE *Instance;\r
+ LIST_ENTRY *RegionToFlushLink;\r
+ BOOTMON_FS_FILE *File;\r
+ BOOTMON_FS_FILE *NextFile;\r
+ BOOTMON_FS_FILE_REGION *Region;\r
+ LIST_ENTRY *FileLink;\r
+ UINTN CurrentPhysicalSize;\r
+ UINTN BlockSize;\r
+ UINT64 FileStart;\r
+ UINT64 FileEnd;\r
+ UINT64 RegionStart;\r
+ UINT64 RegionEnd;\r
+ UINT64 NewFileSize;\r
+ UINT64 EndOfAppendSpace;\r
+ BOOLEAN HasSpace;\r
+ EFI_DISK_IO_PROTOCOL *DiskIo;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+\r
+ Status = EFI_SUCCESS;\r
+ FileStart = 0;\r
+\r
+ File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
+ if (File == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Check if the file needs to be flushed\r
+ if (!BootMonFsFileNeedFlush (File)) {\r
+ return Status;\r
+ }\r
+\r
+ Instance = File->Instance;\r
+ BlockIo = Instance->BlockIo;\r
+ DiskIo = Instance->DiskIo;\r
+ BlockSize = BlockIo->Media->BlockSize;\r
+\r
+ // If the file doesn't exist then find a space for it\r
+ if (File->HwDescription.RegionCount == 0) {\r
+ Status = BootMonFsFindSpaceForNewFile (File, &FileStart);\r
+ // FileStart has changed so we need to recompute RegionEnd\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } else {\r
+ FileStart = File->HwDescription.BlockStart * BlockSize;\r
+ }\r
+\r
+ // FileEnd is the NOR address of the end of the file's data\r
+ FileEnd = FileStart + BootMonFsGetImageLength (File);\r
+\r
+ for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
+ !IsNull (&File->RegionToFlushLink, RegionToFlushLink);\r
+ RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink)\r
+ )\r
+ {\r
+ Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;\r
+\r
+ // RegionStart and RegionEnd are the the intended NOR address of the\r
+ // start and end of the region\r
+ RegionStart = FileStart + Region->Offset;\r
+ RegionEnd = RegionStart + Region->Size;\r
+\r
+ if (RegionEnd < FileEnd) {\r
+ // Handle regions representing edits to existing portions of the file\r
+ // Write the region data straight into the file\r
+ Status = DiskIo->WriteDisk (DiskIo,\r
+ BlockIo->Media->MediaId,\r
+ RegionStart,\r
+ Region->Size,\r
+ Region->Buffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } else {\r
+ // Handle regions representing appends to the file\r
+ //\r
+ // Note: Since seeking past the end of the file with SetPosition() is\r
+ // valid, it's possible there will be a gap between the current end of\r
+ // the file and the beginning of the new region. Since the UEFI spec\r
+ // says nothing about this case (except "a subsequent write would grow\r
+ // the file"), we just leave garbage in the gap.\r
+\r
+ // Check if there is space to append the new region\r
+ HasSpace = FALSE;\r
+ NewFileSize = (RegionEnd - FileStart) + sizeof (HW_IMAGE_DESCRIPTION);\r
+ CurrentPhysicalSize = BootMonFsGetPhysicalSize (File);\r
+ if (NewFileSize <= CurrentPhysicalSize) {\r
+ HasSpace = TRUE;\r
+ } else {\r
+ // Get the File Description for the next file\r
+ FileLink = GetNextNode (&Instance->RootFile->Link, &File->Link);\r
+ if (!IsNull (&Instance->RootFile->Link, FileLink)) {\r
+ NextFile = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);\r
+\r
+ // If there is space between the beginning of the current file and the\r
+ // beginning of the next file then use it\r
+ EndOfAppendSpace = NextFile->HwDescription.BlockStart * BlockSize;\r
+ } else {\r
+ // We are flushing the last file.\r
+ EndOfAppendSpace = (BlockIo->Media->LastBlock + 1) * BlockSize;\r
+ }\r
+ if (EndOfAppendSpace - FileStart >= NewFileSize) {\r
+ HasSpace = TRUE;\r
+ }\r
+ }\r
+\r
+ if (HasSpace == TRUE) {\r
+ Status = FlushAppendRegion (File, Region, NewFileSize, FileStart);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } else {\r
+ // There isn't a space for the file.\r
+ // Options here are to move the file or fragment it. However as files\r
+ // may represent boot images at fixed positions, these options will\r
+ // break booting if the bootloader doesn't use BootMonFs to find the\r
+ // image.\r
+\r
+ return EFI_VOLUME_FULL;\r
+ }\r
+ }\r
+ }\r
+\r
+ FreeFileRegions (File);\r
+\r
+ // Flush DiskIo Buffers (see UEFI Spec 12.7 - DiskIo buffers are flushed by\r
+ // calling FlushBlocks on the same device's BlockIo).\r
+ BlockIo->FlushBlocks (BlockIo);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Closes a file on the Nor Flash FS volume.\r
+\r
+ @param This The EFI_FILE_PROTOCOL to close.\r
+\r
+ @return Always returns EFI_SUCCESS.\r
+\r
+**/\r
+EFIAPI\r
+EFI_STATUS\r
+BootMonFsCloseFile (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ )\r
+{\r
+ // Flush the file if needed\r
+ This->Flush (This);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+// Create a new instance of BOOTMON_FS_FILE.\r
+// Uses BootMonFsCreateFile to\r
+STATIC\r
+EFI_STATUS\r
+CreateNewFile (\r
+ IN BOOTMON_FS_INSTANCE *Instance,\r
+ IN CHAR8* AsciiFileName,\r
+ OUT BOOTMON_FS_FILE **NewHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOTMON_FS_FILE *File;\r
+\r
+ Status = BootMonFsCreateFile (Instance, &File);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Remove the leading '\\'\r
+ if (*AsciiFileName == '\\') {\r
+ AsciiFileName++;\r
+ }\r
+\r
+ // Set the file name\r
+ CopyMem (File->HwDescription.Footer.Filename, AsciiFileName, MAX_NAME_LENGTH);\r
+\r
+ // Add the file to list of files of the File System\r
+ InsertHeadList (&Instance->RootFile->Link, &File->Link);\r
+\r
+ *NewHandle = File;\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Opens a file on the Nor Flash FS volume\r
+\r
+ Calls BootMonFsGetFileFromAsciiFilename to search the list of tracked files.\r
+\r
+ @param This The EFI_FILE_PROTOCOL parent handle.\r
+ @param NewHandle Double-pointer to the newly created protocol.\r
+ @param FileName The name of the image/metadata on flash\r
+ @param OpenMode Read,write,append etc\r
+ @param Attributes ?\r
+\r
+ @return EFI_STATUS\r
+ OUT_OF_RESOURCES\r
+ Run out of space to keep track of the allocated structures\r
+ DEVICE_ERROR\r
+ Unable to locate the volume associated with the parent file handle\r
+ NOT_FOUND\r
+ Filename wasn't found on flash\r
+ SUCCESS\r
+\r
+**/\r
+EFIAPI\r
+EFI_STATUS\r
+BootMonFsOpenFile (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ OUT EFI_FILE_PROTOCOL **NewHandle,\r
+ IN CHAR16 *FileName,\r
+ IN UINT64 OpenMode,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ BOOTMON_FS_FILE *Directory;\r
+ BOOTMON_FS_FILE *File;\r
+ BOOTMON_FS_INSTANCE *Instance;\r
+ CHAR8* AsciiFileName;\r
+ EFI_STATUS Status;\r
+\r
+ if ((FileName == NULL) || (NewHandle == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // The only valid modes are read, read/write, and read/write/create\r
+ if (!(OpenMode & EFI_FILE_MODE_READ) || ((OpenMode & EFI_FILE_MODE_CREATE) && !(OpenMode & EFI_FILE_MODE_WRITE))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
+ if (Directory == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Instance = Directory->Instance;\r
+\r
+ // If the instance has not been initialized it yet then do it ...\r
+ if (!Instance->Initialized) {\r
+ Status = BootMonFsInitialize (Instance);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ // BootMonFs interface requires ASCII filenames\r
+ AsciiFileName = AllocatePool ((StrLen (FileName) + 1) * sizeof (CHAR8));\r
+ if (AsciiFileName == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ UnicodeStrToAsciiStr (FileName, AsciiFileName);\r
+\r
+ if ((AsciiStrCmp (AsciiFileName, "\\") == 0) ||\r
+ (AsciiStrCmp (AsciiFileName, "/") == 0) ||\r
+ (AsciiStrCmp (AsciiFileName, "") == 0) ||\r
+ (AsciiStrCmp (AsciiFileName, ".") == 0))\r
+ {\r
+ //\r
+ // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory\r
+ //\r
+\r
+ *NewHandle = &Instance->RootFile->File;\r
+ Instance->RootFile->Position = 0;\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ //\r
+ // Open or Create a regular file\r
+ //\r
+\r
+ // Check if the file already exists\r
+ Status = BootMonGetFileFromAsciiFileName (Instance, AsciiFileName, &File);\r
+ if (Status == EFI_NOT_FOUND) {\r
+ // The file doesn't exist.\r
+ if (OpenMode & EFI_FILE_MODE_CREATE) {\r
+ // If the file does not exist but is required then create it.\r
+ if (Attributes & EFI_FILE_DIRECTORY) {\r
+ // BootMonFS doesn't support subdirectories\r
+ Status = EFI_UNSUPPORTED;\r
+ } else {\r
+ // Create a new file\r
+ Status = CreateNewFile (Instance, AsciiFileName, &File);\r
+ if (!EFI_ERROR (Status)) {\r
+ File->OpenMode = OpenMode;\r
+ *NewHandle = &File->File;\r
+ File->Position = 0;\r
+ }\r
+ }\r
+ }\r
+ } else if (Status == EFI_SUCCESS) {\r
+ // The file exists\r
+ File->OpenMode = OpenMode;\r
+ *NewHandle = &File->File;\r
+ File->Position = 0;\r
+ }\r
+ }\r
+\r
+ FreePool (AsciiFileName);\r
+\r
+ return Status;\r
+}\r
+\r
+// Delete() for the root directory's EFI_FILE_PROTOCOL instance\r
+EFIAPI\r
+EFI_STATUS\r
+BootMonFsDeleteFail (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ )\r
+{\r
+ This->Close(This);\r
+ // You can't delete the root directory\r
+ return EFI_WARN_DELETE_FAILURE;\r
+}\r
+EFIAPI\r
+EFI_STATUS\r
+BootMonFsDelete (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOTMON_FS_FILE *File;\r
+ LIST_ENTRY *RegionToFlushLink;\r
+ BOOTMON_FS_FILE_REGION *Region;\r
+ HW_IMAGE_DESCRIPTION *Description;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ UINT8 *EmptyBuffer;\r
+\r
+ File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
+ if (File == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (BootMonFsFileNeedFlush (File)) {\r
+ // Free the entries from the Buffer List\r
+ RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
+ do {\r
+ Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;\r
+\r
+ // Get Next entry\r
+ RegionToFlushLink = RemoveEntryList (RegionToFlushLink);\r
+\r
+ // Free the buffers\r
+ FreePool (Region->Buffer);\r
+ FreePool (Region);\r
+ } while (!IsListEmpty (&File->RegionToFlushLink));\r
+ }\r
+\r
+ // If (RegionCount is greater than 0) then the file already exists\r
+ if (File->HwDescription.RegionCount > 0) {\r
+ Description = &File->HwDescription;\r
+ BlockIo = File->Instance->BlockIo;\r
+\r
+ // Create an empty buffer\r
+ EmptyBuffer = AllocateZeroPool (BlockIo->Media->BlockSize);\r
+ if (EmptyBuffer == NULL) {\r
+ FreePool (File);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ // Invalidate the last Block\r
+ Status = BlockIo->WriteBlocks (BlockIo, BlockIo->Media->MediaId, Description->BlockEnd, BlockIo->Media->BlockSize, EmptyBuffer);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ FreePool (EmptyBuffer);\r
+ }\r
+\r
+ // Remove the entry from the list\r
+ RemoveEntryList (&File->Link);\r
+ FreePool (File);\r
+ return Status;\r
+}\r
+\r