summaryrefslogtreecommitdiff
path: root/Platform/ARM/Drivers/BootMonFs/BootMonFsOpenClose.c
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2017-11-15 16:20:55 +0000
committerArd Biesheuvel <ard.biesheuvel@linaro.org>2017-12-08 16:17:20 +0000
commit40d0673c2e3db10e47fd0742bf484c3d0aea2efe (patch)
treebf7abfce6aa7143bee3880655ce712c8f9fe9a8b /Platform/ARM/Drivers/BootMonFs/BootMonFsOpenClose.c
parent8ad58788b5c39c3b4cc46aa20c9c557a6b90d42e (diff)
downloadedk2-platforms-40d0673c2e3db10e47fd0742bf484c3d0aea2efe.tar.xz
Platform/ARM: import BootMonFs and ArmShellCmdRunAxf from EDK2
BootMonFs and ArmShellCmdRunAxf are only used on development boards manufactured by ARM itself, so let's keep it under Platform/ARM where it belongs. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
Diffstat (limited to 'Platform/ARM/Drivers/BootMonFs/BootMonFsOpenClose.c')
-rw-r--r--Platform/ARM/Drivers/BootMonFs/BootMonFsOpenClose.c795
1 files changed, 795 insertions, 0 deletions
diff --git a/Platform/ARM/Drivers/BootMonFs/BootMonFsOpenClose.c b/Platform/ARM/Drivers/BootMonFs/BootMonFsOpenClose.c
new file mode 100644
index 0000000000..ae10055255
--- /dev/null
+++ b/Platform/ARM/Drivers/BootMonFs/BootMonFsOpenClose.c
@@ -0,0 +1,795 @@
+/** @file
+*
+* Copyright (c) 2012-2015, ARM Limited. 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 "BootMonFsInternal.h"
+
+// Clear a file's image description on storage media:
+// UEFI allows you to seek past the end of a file, a subsequent write will grow
+// the file. It does not specify how space between the former end of the file
+// and the beginning of the write should be filled. It's therefore possible that
+// BootMonFs metadata, that comes after the end of a file, could be left there
+// and wrongly detected by BootMonFsImageInBlock.
+STATIC
+EFI_STATUS
+InvalidateImageDescription (
+ IN BOOTMON_FS_FILE *File
+ )
+{
+ EFI_DISK_IO_PROTOCOL *DiskIo;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ UINT32 MediaId;
+ VOID *Buffer;
+ EFI_STATUS Status;
+
+ DiskIo = File->Instance->DiskIo;
+ BlockIo = File->Instance->BlockIo;
+ MediaId = BlockIo->Media->MediaId;
+
+ Buffer = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION));
+
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = DiskIo->WriteDisk (DiskIo,
+ MediaId,
+ File->HwDescAddress,
+ sizeof (HW_IMAGE_DESCRIPTION),
+ Buffer
+ );
+
+ FreePool(Buffer);
+
+ return Status;
+}
+
+/**
+ Write the description of a file to storage media.
+
+ This function uses DiskIo to write to the media, so call BlockIo->FlushBlocks()
+ after calling it to ensure the data are written on the media.
+
+ @param[in] File Description of the file whose description on the
+ storage media has to be updated.
+ @param[in] FileName Name of the file. Its length is assumed to be
+ lower than MAX_NAME_LENGTH.
+ @param[in] DataSize Number of data bytes of the file.
+ @param[in] FileStart File's starting position on media. FileStart must
+ be aligned to the media's block size.
+
+ @retval EFI_WRITE_PROTECTED The device cannot be written to.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing
+ the write operation.
+
+**/
+STATIC
+EFI_STATUS
+WriteFileDescription (
+ IN BOOTMON_FS_FILE *File,
+ IN CHAR8 *FileName,
+ IN UINT32 DataSize,
+ IN UINT64 FileStart
+ )
+{
+ EFI_STATUS Status;
+ EFI_DISK_IO_PROTOCOL *DiskIo;
+ UINTN BlockSize;
+ UINT32 FileSize;
+ HW_IMAGE_DESCRIPTION *Description;
+
+ DiskIo = File->Instance->DiskIo;
+ BlockSize = File->Instance->BlockIo->Media->BlockSize;
+ ASSERT (FileStart % BlockSize == 0);
+
+ //
+ // Construct the file description
+ //
+
+ FileSize = DataSize + sizeof (HW_IMAGE_DESCRIPTION);
+ Description = &File->HwDescription;
+ Description->Attributes = 1;
+ Description->BlockStart = FileStart / BlockSize;
+ Description->BlockEnd = Description->BlockStart + (FileSize / BlockSize);
+ AsciiStrCpyS (Description->Footer.Filename,
+ sizeof Description->Footer.Filename, FileName);
+
+#ifdef MDE_CPU_ARM
+ Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET;
+ Description->Footer.Version = HW_IMAGE_FOOTER_VERSION;
+#else
+ Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET2;
+ Description->Footer.Version = HW_IMAGE_FOOTER_VERSION2;
+#endif
+ Description->Footer.FooterSignature1 = HW_IMAGE_FOOTER_SIGNATURE_1;
+ Description->Footer.FooterSignature2 = HW_IMAGE_FOOTER_SIGNATURE_2;
+ Description->RegionCount = 1;
+ Description->Region[0].Checksum = 0;
+ Description->Region[0].Offset = Description->BlockStart * BlockSize;
+ Description->Region[0].Size = DataSize;
+
+ Status = BootMonFsComputeFooterChecksum (Description);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ File->HwDescAddress = ((Description->BlockEnd + 1) * BlockSize) - sizeof (HW_IMAGE_DESCRIPTION);
+
+ // Update the file description on the media
+ Status = DiskIo->WriteDisk (
+ DiskIo,
+ File->Instance->Media->MediaId,
+ File->HwDescAddress,
+ sizeof (HW_IMAGE_DESCRIPTION),
+ Description
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+// Find a space on media for a file that has not yet been flushed to disk.
+// Just returns the first space that's big enough.
+// This function could easily be adapted to:
+// - Find space for moving an existing file that has outgrown its space
+// (We do not currently move files, just return EFI_VOLUME_FULL)
+// - Find space for a fragment of a file that has outgrown its space
+// (We do not currently fragment files - it's not clear whether fragmentation
+// is actually part of BootMonFs as there is no spec)
+// - Be more clever about finding space (choosing the largest or smallest
+// suitable space)
+// Parameters:
+// File - the new (not yet flushed) file for which we need to find space.
+// FileStart - the position on media of the file (in bytes).
+STATIC
+EFI_STATUS
+BootMonFsFindSpaceForNewFile (
+ IN BOOTMON_FS_FILE *File,
+ IN UINT64 FileSize,
+ OUT UINT64 *FileStart
+ )
+{
+ LIST_ENTRY *FileLink;
+ BOOTMON_FS_FILE *RootFile;
+ BOOTMON_FS_FILE *FileEntry;
+ UINTN BlockSize;
+ EFI_BLOCK_IO_MEDIA *Media;
+
+ Media = File->Instance->BlockIo->Media;
+ BlockSize = Media->BlockSize;
+ RootFile = File->Instance->RootFile;
+
+ // This function must only be called for file which has not been flushed into
+ // Flash yet
+ ASSERT (File->HwDescription.RegionCount == 0);
+
+ *FileStart = 0;
+ // Go through all the files in the list
+ for (FileLink = GetFirstNode (&RootFile->Link);
+ !IsNull (&RootFile->Link, FileLink);
+ FileLink = GetNextNode (&RootFile->Link, FileLink)
+ )
+ {
+ FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
+ // Skip files that aren't on disk yet
+ if (FileEntry->HwDescription.RegionCount == 0) {
+ continue;
+ }
+
+ // If the free space preceding the file is big enough to contain the new
+ // file then use it!
+ if (((FileEntry->HwDescription.BlockStart * BlockSize) - *FileStart)
+ >= FileSize) {
+ // The file list must be in disk-order
+ RemoveEntryList (&File->Link);
+ File->Link.BackLink = FileLink->BackLink;
+ File->Link.ForwardLink = FileLink;
+ FileLink->BackLink->ForwardLink = &File->Link;
+ FileLink->BackLink = &File->Link;
+
+ return EFI_SUCCESS;
+ } else {
+ *FileStart = (FileEntry->HwDescription.BlockEnd + 1) * BlockSize;
+ }
+ }
+ // See if there's space after the last file
+ if ((((Media->LastBlock + 1) * BlockSize) - *FileStart) >= FileSize) {
+ return EFI_SUCCESS;
+ } else {
+ return EFI_VOLUME_FULL;
+ }
+}
+
+// Free the resources in the file's Region list.
+STATIC
+VOID
+FreeFileRegions (
+ IN BOOTMON_FS_FILE *File
+ )
+{
+ LIST_ENTRY *RegionToFlushLink;
+ BOOTMON_FS_FILE_REGION *Region;
+
+ RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
+ while (!IsNull (&File->RegionToFlushLink, RegionToFlushLink)) {
+ // Repeatedly remove the first node from the list and free its resources.
+ Region = (BOOTMON_FS_FILE_REGION *) RegionToFlushLink;
+ RemoveEntryList (RegionToFlushLink);
+ FreePool (Region->Buffer);
+ FreePool (Region);
+
+ RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
+ }
+}
+
+/**
+ Flush all modified data associated with a file to a device.
+
+ @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the
+ file handle to flush.
+
+ @retval EFI_SUCCESS The data was flushed.
+ @retval EFI_ACCESS_DENIED The file was opened read-only.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_FULL The volume is full.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to flush the data.
+ @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid.
+
+**/
+EFIAPI
+EFI_STATUS
+BootMonFsFlushFile (
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ EFI_STATUS Status;
+ BOOTMON_FS_INSTANCE *Instance;
+ EFI_FILE_INFO *Info;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ EFI_BLOCK_IO_MEDIA *Media;
+ EFI_DISK_IO_PROTOCOL *DiskIo;
+ UINTN BlockSize;
+ CHAR8 AsciiFileName[MAX_NAME_LENGTH];
+ LIST_ENTRY *RegionToFlushLink;
+ BOOTMON_FS_FILE *File;
+ BOOTMON_FS_FILE *NextFile;
+ BOOTMON_FS_FILE_REGION *Region;
+ LIST_ENTRY *FileLink;
+ UINTN CurrentPhysicalSize;
+ UINT64 FileStart;
+ UINT64 FileEnd;
+ UINT64 RegionStart;
+ UINT64 RegionEnd;
+ UINT64 NewDataSize;
+ UINT64 NewFileSize;
+ UINT64 EndOfAppendSpace;
+ BOOLEAN HasSpace;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
+ if (File->Info == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (File->OpenMode == EFI_FILE_MODE_READ) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ Instance = File->Instance;
+ Info = File->Info;
+ BlockIo = Instance->BlockIo;
+ Media = BlockIo->Media;
+ DiskIo = Instance->DiskIo;
+ BlockSize = Media->BlockSize;
+
+ UnicodeStrToAsciiStrS (Info->FileName, AsciiFileName, MAX_NAME_LENGTH);
+
+ // If the file doesn't exist then find a space for it
+ if (File->HwDescription.RegionCount == 0) {
+ Status = BootMonFsFindSpaceForNewFile (
+ File,
+ Info->FileSize + sizeof (HW_IMAGE_DESCRIPTION),
+ &FileStart
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ FileStart = File->HwDescription.BlockStart * BlockSize;
+ }
+ // FileEnd is the current NOR address of the end of the file's data
+ FileEnd = FileStart + File->HwDescription.Region[0].Size;
+
+ for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
+ !IsNull (&File->RegionToFlushLink, RegionToFlushLink);
+ RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink)
+ )
+ {
+ Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
+ if (Region->Size == 0) {
+ continue;
+ }
+
+ // RegionStart and RegionEnd are the the intended NOR address of the
+ // start and end of the region
+ RegionStart = FileStart + Region->Offset;
+ RegionEnd = RegionStart + Region->Size;
+
+ if (RegionEnd < FileEnd) {
+ // Handle regions representing edits to existing portions of the file
+ // Write the region data straight into the file
+ Status = DiskIo->WriteDisk (DiskIo,
+ Media->MediaId,
+ RegionStart,
+ Region->Size,
+ Region->Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ // Handle regions representing appends to the file
+ //
+ // Note: Since seeking past the end of the file with SetPosition() is
+ // valid, it's possible there will be a gap between the current end of
+ // the file and the beginning of the new region. Since the UEFI spec
+ // says nothing about this case (except "a subsequent write would grow
+ // the file"), we just leave garbage in the gap.
+
+ // Check if there is space to append the new region
+ HasSpace = FALSE;
+ NewDataSize = RegionEnd - FileStart;
+ NewFileSize = NewDataSize + sizeof (HW_IMAGE_DESCRIPTION);
+ CurrentPhysicalSize = BootMonFsGetPhysicalSize (File);
+ if (NewFileSize <= CurrentPhysicalSize) {
+ HasSpace = TRUE;
+ } else {
+ // Get the File Description for the next file
+ FileLink = GetNextNode (&Instance->RootFile->Link, &File->Link);
+ if (!IsNull (&Instance->RootFile->Link, FileLink)) {
+ NextFile = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
+
+ // If there is space between the beginning of the current file and the
+ // beginning of the next file then use it
+ EndOfAppendSpace = NextFile->HwDescription.BlockStart * BlockSize;
+ } else {
+ // We are flushing the last file.
+ EndOfAppendSpace = (Media->LastBlock + 1) * BlockSize;
+ }
+ if (EndOfAppendSpace - FileStart >= NewFileSize) {
+ HasSpace = TRUE;
+ }
+ }
+
+ if (HasSpace == TRUE) {
+ // Invalidate the current image description of the file if any.
+ if (File->HwDescAddress != 0) {
+ Status = InvalidateImageDescription (File);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ // Write the new file data
+ Status = DiskIo->WriteDisk (
+ DiskIo,
+ Media->MediaId,
+ RegionStart,
+ Region->Size,
+ Region->Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = WriteFileDescription (File, AsciiFileName, NewDataSize, FileStart);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ } else {
+ // There isn't a space for the file.
+ // Options here are to move the file or fragment it. However as files
+ // may represent boot images at fixed positions, these options will
+ // break booting if the bootloader doesn't use BootMonFs to find the
+ // image.
+
+ return EFI_VOLUME_FULL;
+ }
+ }
+ }
+
+ FreeFileRegions (File);
+ Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
+
+ if ((AsciiStrCmp (AsciiFileName, File->HwDescription.Footer.Filename) != 0) ||
+ (Info->FileSize != File->HwDescription.Region[0].Size) ) {
+ Status = WriteFileDescription (File, AsciiFileName, Info->FileSize, FileStart);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ // Flush DiskIo Buffers (see UEFI Spec 12.7 - DiskIo buffers are flushed by
+ // calling FlushBlocks on the same device's BlockIo).
+ BlockIo->FlushBlocks (BlockIo);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Close a specified file handle.
+
+ @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
+ handle to close.
+
+ @retval EFI_SUCCESS The file was closed.
+ @retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open
+ file handle.
+
+**/
+EFIAPI
+EFI_STATUS
+BootMonFsCloseFile (
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ BOOTMON_FS_FILE *File;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
+ if (File->Info == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // In the case of a file and not the root directory
+ if (This != &File->Instance->RootFile->File) {
+ This->Flush (This);
+ FreePool (File->Info);
+ File->Info = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Open a file on the boot monitor file system.
+
+ The boot monitor file system does not allow for sub-directories. There is only
+ one directory, the root one. On any attempt to create a directory, the function
+ returns in error with the EFI_WRITE_PROTECTED error code.
+
+ @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is
+ the file handle to source location.
+ @param[out] NewHandle A pointer to the location to return the opened
+ handle for the new file.
+ @param[in] FileName The Null-terminated string of the name of the file
+ to be opened.
+ @param[in] OpenMode The mode to open the file : Read or Read/Write or
+ Read/Write/Create
+ @param[in] Attributes Attributes of the file in case of a file creation
+
+ @retval EFI_SUCCESS The file was open.
+ @retval EFI_NOT_FOUND The specified file could not be found or the specified
+ directory in which to create a file could not be found.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_WRITE_PROTECTED Attempt to create a directory. This is not possible
+ with the Boot Monitor file system.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
+ @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid.
+
+**/
+EFIAPI
+EFI_STATUS
+BootMonFsOpenFile (
+ IN EFI_FILE_PROTOCOL *This,
+ OUT EFI_FILE_PROTOCOL **NewHandle,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes
+ )
+{
+ EFI_STATUS Status;
+ BOOTMON_FS_FILE *Directory;
+ BOOTMON_FS_FILE *File;
+ BOOTMON_FS_INSTANCE *Instance;
+ CHAR8 *Buf;
+ CHAR16 *Path;
+ CHAR16 *Separator;
+ CHAR8 *AsciiFileName;
+ EFI_FILE_INFO *Info;
+ UINTN AsciiFileNameSize;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
+ if (Directory->Info == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((FileName == NULL) || (NewHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The only valid modes are read, read/write, and read/write/create
+ //
+ if ( (OpenMode != EFI_FILE_MODE_READ) &&
+ (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE)) &&
+ (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE)) ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = Directory->Instance;
+
+ //
+ // If the instance has not been initialized yet then do it ...
+ //
+ if (!Instance->Initialized) {
+ Status = BootMonFsInitialize (Instance);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Copy the file path to be able to work on it. We do not want to
+ // modify the input file name string "FileName".
+ //
+ Buf = AllocateCopyPool (StrSize (FileName), FileName);
+ if (Buf == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Path = (CHAR16*)Buf;
+ AsciiFileName = NULL;
+ Info = NULL;
+
+ //
+ // Handle single periods, double periods and convert forward slashes '/'
+ // to backward '\' ones. Does not handle a '.' at the beginning of the
+ // path for the time being.
+ //
+ if (PathCleanUpDirectories (Path) == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Error;
+ }
+
+ //
+ // Detect if the first component of the path refers to a directory.
+ // This is done to return the correct error code when trying to
+ // access or create a directory other than the root directory.
+ //
+
+ //
+ // Search for the '\\' sequence and if found return in error
+ // with the EFI_INVALID_PARAMETER error code. ere in the path.
+ //
+ if (StrStr (Path, L"\\\\") != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Error;
+ }
+ //
+ // Get rid of the leading '\' if any.
+ //
+ Path += (Path[0] == L'\\');
+
+ //
+ // Look for a '\' in the file path. If one is found then
+ // the first component of the path refers to a directory
+ // that is not the root directory.
+ //
+ Separator = StrStr (Path, L"\\");
+ if (Separator != NULL) {
+ //
+ // In the case '<dir name>\' and a creation, return
+ // EFI_WRITE_PROTECTED if this is for a directory
+ // creation, EFI_INVALID_PARAMETER otherwise.
+ //
+ if ((*(Separator + 1) == '\0') && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {
+ if (Attributes & EFI_FILE_DIRECTORY) {
+ Status = EFI_WRITE_PROTECTED;
+ } else {
+ Status = EFI_INVALID_PARAMETER;
+ }
+ } else {
+ //
+ // Attempt to open a file or a directory that is not in the
+ // root directory or to open without creation a directory
+ // located in the root directory, returns EFI_NOT_FOUND.
+ //
+ Status = EFI_NOT_FOUND;
+ }
+ goto Error;
+ }
+
+ //
+ // BootMonFs interface requires ASCII filenames
+ //
+ AsciiFileNameSize = StrLen (Path) + 1;
+ if (AsciiFileNameSize > MAX_NAME_LENGTH) {
+ AsciiFileNameSize = MAX_NAME_LENGTH;
+ }
+ AsciiFileName = AllocatePool (AsciiFileNameSize);
+ if (AsciiFileName == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+ UnicodeStrToAsciiStrS (Path, AsciiFileName, AsciiFileNameSize);
+
+ if ((AsciiFileName[0] == '\0') ||
+ (AsciiFileName[0] == '.' ) ) {
+ //
+ // Opening the root directory
+ //
+
+ *NewHandle = &Instance->RootFile->File;
+ Instance->RootFile->Position = 0;
+ Status = EFI_SUCCESS;
+ } else {
+
+ if ((OpenMode & EFI_FILE_MODE_CREATE) &&
+ (Attributes & EFI_FILE_DIRECTORY) ) {
+ Status = EFI_WRITE_PROTECTED;
+ goto Error;
+ }
+
+ //
+ // Allocate a buffer to store the characteristics of the file while the
+ // file is open. We allocate the maximum size to not have to reallocate
+ // if the file name is changed.
+ //
+ Info = AllocateZeroPool (
+ SIZE_OF_EFI_FILE_INFO + (sizeof (CHAR16) * MAX_NAME_LENGTH));
+ if (Info == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ //
+ // Open or create a file in the root directory.
+ //
+
+ Status = BootMonGetFileFromAsciiFileName (Instance, AsciiFileName, &File);
+ if (Status == EFI_NOT_FOUND) {
+ if ((OpenMode & EFI_FILE_MODE_CREATE) == 0) {
+ goto Error;
+ }
+
+ Status = BootMonFsCreateFile (Instance, &File);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ InsertHeadList (&Instance->RootFile->Link, &File->Link);
+ Info->Attribute = Attributes;
+ } else {
+ //
+ // File already open, not supported yet.
+ //
+ if (File->Info != NULL) {
+ Status = EFI_UNSUPPORTED;
+ goto Error;
+ }
+ }
+
+ Info->FileSize = BootMonFsGetImageLength (File);
+ Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
+ AsciiStrToUnicodeStrS (AsciiFileName, Info->FileName, MAX_NAME_LENGTH);
+
+ File->Info = Info;
+ Info = NULL;
+ File->Position = 0;
+ File->OpenMode = OpenMode;
+
+ *NewHandle = &File->File;
+ }
+
+Error:
+
+ FreePool (Buf);
+ if (AsciiFileName != NULL) {
+ FreePool (AsciiFileName);
+ }
+ if (Info != NULL) {
+ FreePool (Info);
+ }
+
+ return Status;
+}
+
+// Delete() for the root directory's EFI_FILE_PROTOCOL instance
+EFIAPI
+EFI_STATUS
+BootMonFsDeleteFail (
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ This->Close(This);
+ // You can't delete the root directory
+ return EFI_WARN_DELETE_FAILURE;
+}
+
+/**
+ Close and delete a file from the boot monitor file system.
+
+ @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
+ handle to delete.
+
+ @retval EFI_SUCCESS The file was closed and deleted.
+ @retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open
+ file handle.
+ @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted.
+
+**/
+EFIAPI
+EFI_STATUS
+BootMonFsDelete (
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ EFI_STATUS Status;
+ BOOTMON_FS_FILE *File;
+ LIST_ENTRY *RegionToFlushLink;
+ BOOTMON_FS_FILE_REGION *Region;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
+ if (File->Info == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsListEmpty (&File->RegionToFlushLink)) {
+ // Free the entries from the Buffer List
+ RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
+ do {
+ Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
+
+ //
+ // Get next element of the list before deleting the region description
+ // that contain the LIST_ENTRY structure.
+ //
+ RegionToFlushLink = RemoveEntryList (RegionToFlushLink);
+
+ // Free the buffers
+ FreePool (Region->Buffer);
+ FreePool (Region);
+ } while (!IsListEmpty (&File->RegionToFlushLink));
+ }
+
+ // If (RegionCount is greater than 0) then the file already exists
+ if (File->HwDescription.RegionCount > 0) {
+ // Invalidate the last Block
+ Status = InvalidateImageDescription (File);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return EFI_WARN_DELETE_FAILURE;
+ }
+ }
+
+ // Remove the entry from the list
+ RemoveEntryList (&File->Link);
+ FreePool (File->Info);
+ FreePool (File);
+
+ return EFI_SUCCESS;
+}