diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsApi.h b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsApi.h index affc510931..d690520a0f 100644 --- a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsApi.h +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsApi.h @@ -79,12 +79,37 @@ BootMonFsFlushDirectory ( IN EFI_FILE_PROTOCOL *This ); +/** + 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 ); +/** + 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 ( @@ -94,7 +119,12 @@ BootMonFsCloseFile ( /** Open a file on the boot monitor file system. - @param[in] This The EFI_FILE_PROTOCOL parent handle. + 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 @@ -108,7 +138,7 @@ BootMonFsCloseFile ( 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 BootMon file system. + 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. @@ -139,6 +169,7 @@ BootMonFsOpenFile ( reported an error while performing the read operation. @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + **/ EFIAPI EFI_STATUS @@ -162,6 +193,26 @@ BootMonFsGetPosition ( OUT UINT64 *Position ); +/** + Write data to an open file. + + The data is not written to the flash yet. It will be written when the file + will be either read, closed or flushed. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to write data to. + @param[in out] BufferSize On input, the size of the Buffer. On output, the + size of the data actually written. In both cases, + the size is measured in bytes. + @param[in] Buffer The buffer of data to write. + + @retval EFI_SUCCESS The data was written. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_OUT_OF_RESOURCES Unable to allocate the buffer to store the + data to write. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ EFIAPI EFI_STATUS BootMonFsWriteFile ( @@ -176,12 +227,35 @@ BootMonFsDeleteFail ( IN EFI_FILE_PROTOCOL *This ); +/** + 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 ); +/** + Set a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to set the requested position on. + @param[in] Position The byte position from the start of the file to set. + + @retval EFI_SUCCESS The position was set. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ EFIAPI EFI_STATUS BootMonFsSetPosition ( @@ -189,6 +263,17 @@ BootMonFsSetPosition ( IN UINT64 Position ); +/** + Return a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to get the current position on. + @param[out] Position The address to return the file's current position value. + + @retval EFI_SUCCESS The position was returned. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ EFIAPI EFI_STATUS BootMonFsGetPosition( @@ -207,6 +292,37 @@ BootMonFsGetPositionUnsupported ( OUT UINT64 *Position ); +/** + Set information about a file or a volume. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle the information is for. + @param[in] InformationType The type identifier for the information being set : + EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or + EFI_FILE_SYSTEM_VOLUME_LABEL_ID + @param[in] BufferSize The size, in bytes, of Buffer. + @param[in] Buffer A pointer to the data buffer to write. The type of the + data inside the buffer is indicated by InformationType. + + @retval EFI_SUCCESS The information was set. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + @retval EFI_ACCESS_DENIED An attempt is being made to change the + EFI_FILE_DIRECTORY Attribute. + @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and + the file was opened in read-only mode and an + attempt is being made to modify a field other + than Attribute. + @retval EFI_WRITE_PROTECTED An attempt is being made to modify a read-only + attribute. + @retval EFI_BAD_BUFFER_SIZE The size of the buffer is lower than that indicated by + the data inside the buffer. + @retval EFI_OUT_OF_RESOURCES A allocation needed to process the request failed. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ EFIAPI EFI_STATUS BootMonFsSetInfo ( @@ -230,6 +346,31 @@ BootMonFsOpenDirectory ( // // Internal API // + +/** + Search for a file given its name coded in Ascii. + + When searching through the files of the volume, if a file is currently not + open, its name was written on the media and is kept in RAM in the + "HwDescription.Footer.Filename[]" field of the file's description. + + If a file is currently open, its name might not have been written on the + media yet, and as the "HwDescription" is a mirror in RAM of what is on the + media the "HwDescription.Footer.Filename[]" might be outdated. In that case, + the up to date name of the file is stored in the "Info" field of the file's + description. + + @param[in] Instance Pointer to the description of the volume in which + the file has to be search for. + @param[in] AsciiFileName Name of the file. + + @param[out] File Pointer to the description of the file if the + file was found. + + @retval EFI_SUCCESS The file was found. + @retval EFI_NOT_FOUND The file was not found. + +**/ EFI_STATUS BootMonGetFileFromAsciiFileName ( IN BOOTMON_FS_INSTANCE *Instance, diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c index 835cdae18a..450a707f18 100644 --- a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c @@ -28,6 +28,8 @@ OpenBootMonFsOpenVolume ( return EFI_DEVICE_ERROR; } + Instance->RootFile->Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; + *Root = &Instance->RootFile->File; return EFI_SUCCESS; @@ -114,6 +116,8 @@ BootMonFsOpenDirectory ( return EFI_UNSUPPORTED; } + +STATIC EFI_STATUS GetFileSystemVolumeLabelInfo ( IN BOOTMON_FS_INSTANCE *Instance, @@ -178,6 +182,7 @@ ComputeFreeSpace ( return MediaSize - (FileSizeSum + (Media->BlockSize + NumFiles)); } +STATIC EFI_STATUS GetFilesystemInfo ( IN BOOTMON_FS_INSTANCE *Instance, @@ -199,26 +204,19 @@ GetFilesystemInfo ( return Status; } +STATIC EFI_STATUS GetFileInfo ( - IN BOOTMON_FS_INSTANCE *Instance, - IN BOOTMON_FS_FILE *File, - IN OUT UINTN *BufferSize, - OUT VOID *Buffer + IN BOOTMON_FS_INSTANCE *Instance, + IN BOOTMON_FS_FILE *File, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer ) { - EFI_FILE_INFO *Info; - UINTN ResultSize; - UINTN NameSize; - UINTN Index; + EFI_FILE_INFO *Info; + UINTN ResultSize; - if (File == Instance->RootFile) { - NameSize = 0; - ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof (CHAR16); - } else { - NameSize = AsciiStrLen (File->HwDescription.Footer.Filename) + 1; - ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16)); - } + ResultSize = SIZE_OF_EFI_FILE_INFO + StrSize (File->Info->FileName); if (*BufferSize < ResultSize) { *BufferSize = ResultSize; @@ -227,24 +225,10 @@ GetFileInfo ( Info = Buffer; - // Zero out the structure - ZeroMem (Info, ResultSize); - - // Fill in the structure + CopyMem (Info, File->Info, ResultSize); + // Size of the information Info->Size = ResultSize; - if (File == Instance->RootFile) { - Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; - Info->FileName[0] = L'\0'; - } else { - Info->FileSize = BootMonFsGetImageLength (File); - Info->PhysicalSize = BootMonFsGetPhysicalSize (File); - - for (Index = 0; Index < NameSize; Index++) { - Info->FileName[Index] = File->HwDescription.Footer.Filename[Index]; - } - } - *BufferSize = ResultSize; return EFI_SUCCESS; @@ -297,171 +281,253 @@ GetBootMonFsFileInfo ( return Status; } +/** + Set the name of a file. + + This is a helper function for SetFileInfo(). + + @param[in] Instance A pointer to the description of the volume + the file belongs to. + @param[in] File A pointer to the description of the file. + @param[in] FileName A pointer to the new name of the file. + + @retval EFI_SUCCESS The name was set. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + +**/ STATIC EFI_STATUS SetFileName ( - IN BOOTMON_FS_FILE *File, - IN CHAR16 *FileNameUnicode + IN BOOTMON_FS_INSTANCE *Instance, + IN BOOTMON_FS_FILE *File, + IN CONST CHAR16 *FileName ) { - CHAR8 *FileNameAscii; - UINT16 SavedChar; - UINTN FileNameSize; - BOOTMON_FS_FILE *SameFile; - EFI_STATUS Status; + CHAR16 TruncFileName[MAX_NAME_LENGTH]; + CHAR8 AsciiFileName[MAX_NAME_LENGTH]; + BOOTMON_FS_FILE *SameFile; - // EFI Shell inserts '\' in front of the filename that must be stripped - if (FileNameUnicode[0] == L'\\') { - FileNameUnicode++; - } - // - // Convert Unicode into Ascii - // - SavedChar = L'\0'; - FileNameSize = StrLen (FileNameUnicode) + 1; - FileNameAscii = AllocatePool (FileNameSize * sizeof (CHAR8)); - if (FileNameAscii == NULL) { - return EFI_OUT_OF_RESOURCES; - } - // If Unicode string is too long then truncate it. - if (FileNameSize > MAX_NAME_LENGTH) { - SavedChar = FileNameUnicode[MAX_NAME_LENGTH - 1]; - FileNameUnicode[MAX_NAME_LENGTH - 1] = L'\0'; - } - UnicodeStrToAsciiStr (FileNameUnicode, FileNameAscii); - // If the unicode string was truncated then restore its original content. - if (SavedChar != L'\0') { - FileNameUnicode[MAX_NAME_LENGTH - 1] = SavedChar; + // If the file path start with a \ strip it. The EFI Shell may + // insert a \ in front of the file name. + if (FileName[0] == L'\\') { + FileName++; } - // If we're changing the file name - if (AsciiStrCmp (FileNameAscii, File->HwDescription.Footer.Filename) == 0) { - // No change to filename. - Status = EFI_SUCCESS; - } else if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) { - // You can only change the filename if you open the file for write. - Status = EFI_ACCESS_DENIED; - } else if (BootMonGetFileFromAsciiFileName ( - File->Instance, - File->HwDescription.Footer.Filename, - &SameFile) != EFI_NOT_FOUND) { + StrnCpy (TruncFileName, FileName, MAX_NAME_LENGTH - 1); + TruncFileName[MAX_NAME_LENGTH - 1] = 0; + UnicodeStrToAsciiStr (TruncFileName, AsciiFileName); + + if (BootMonGetFileFromAsciiFileName ( + File->Instance, + AsciiFileName, + &SameFile + ) != EFI_NOT_FOUND) { // A file with that name already exists. - Status = EFI_ACCESS_DENIED; + return EFI_ACCESS_DENIED; } else { // OK, change the filename. - AsciiStrCpy (FileNameAscii, File->HwDescription.Footer.Filename); - Status = EFI_SUCCESS; + AsciiStrToUnicodeStr (AsciiFileName, File->Info->FileName); + return EFI_SUCCESS; } - - FreePool (FileNameAscii); - return Status; } -// Set the file's size (NB "size", not "physical size"). If the change amounts -// to an increase, simply do a write followed by a flush. -// (This is a helper function for SetFileInfo.) +/** + Set the size of a file. + + This is a helper function for SetFileInfo(). + + @param[in] Instance A pointer to the description of the volume + the file belongs to. + @param[in] File A pointer to the description of the file. + @param[in] NewSize The requested new size for the file. + + @retval EFI_SUCCESS The size was set. + @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request failed. + +**/ STATIC EFI_STATUS SetFileSize ( - IN BOOTMON_FS_INSTANCE *Instance, - IN BOOTMON_FS_FILE *BootMonFsFile, - IN UINTN NewSize + IN BOOTMON_FS_INSTANCE *Instance, + IN BOOTMON_FS_FILE *BootMonFsFile, + IN UINTN NewSize ) { - UINT64 StoredPosition; - EFI_STATUS Status; - EFI_FILE_PROTOCOL *File; - CHAR8 Buffer; - UINTN BufferSize; - UINT32 OldSize; + EFI_STATUS Status; + UINT32 OldSize; + LIST_ENTRY *RegionToFlushLink; + LIST_ENTRY *NextRegionToFlushLink; + BOOTMON_FS_FILE_REGION *Region; + EFI_FILE_PROTOCOL *File; + CHAR8 *Buffer; + UINTN BufferSize; + UINT64 StoredPosition; - OldSize = BootMonFsFile->HwDescription.Region[0].Size; + OldSize = BootMonFsFile->Info->FileSize; - if (OldSize == NewSize) { - return EFI_SUCCESS; - } + // + // In case of file truncation, force the regions waiting for writing to + // not overflow the new size of the file. + // + if (NewSize < OldSize) { + for (RegionToFlushLink = GetFirstNode (&BootMonFsFile->RegionToFlushLink); + !IsNull (&BootMonFsFile->RegionToFlushLink, RegionToFlushLink); + ) + { + NextRegionToFlushLink = GetNextNode (&BootMonFsFile->RegionToFlushLink, RegionToFlushLink); + Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink; + if (Region->Offset > NewSize) { + RemoveEntryList (RegionToFlushLink); + FreePool (Region->Buffer); + FreePool (Region); + } else { + Region->Size = MIN (Region->Size, NewSize - Region->Offset); + } + RegionToFlushLink = NextRegionToFlushLink; + } - Buffer = 0; - BufferSize = sizeof (Buffer); - - File = &BootMonFsFile->File; - - if (!(BootMonFsFile->OpenMode & EFI_FILE_MODE_WRITE)) { - return EFI_ACCESS_DENIED; - } - - if (NewSize <= OldSize) { - OldSize = NewSize; - } else { + } else if (NewSize > OldSize) { // Increasing a file's size is potentially complicated as it may require // moving the image description on media. The simplest way to do it is to // seek past the end of the file (which is valid in UEFI) and perform a // Write. + File = &BootMonFsFile->File; // Save position Status = File->GetPosition (File, &StoredPosition); if (EFI_ERROR (Status)) { return Status; } - - Status = File->SetPosition (File, NewSize - 1); + // Set position at the end of the file + Status = File->SetPosition (File, OldSize); if (EFI_ERROR (Status)) { return Status; } - Status = File->Write (File, &BufferSize, &Buffer); + + BufferSize = NewSize - OldSize; + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = File->Write (File, &BufferSize, Buffer); + FreePool (Buffer); if (EFI_ERROR (Status)) { return Status; } // Restore saved position - Status = File->SetPosition (File, NewSize - 1); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = File->Flush (File); + Status = File->SetPosition (File, StoredPosition); if (EFI_ERROR (Status)) { return Status; } } + + BootMonFsFile->Info->FileSize = NewSize; + return EFI_SUCCESS; } +/** + Set information about a file. + + @param[in] Instance A pointer to the description of the volume + the file belongs to. + @param[in] File A pointer to the description of the file. + @param[in] Info A pointer to the file information to write. + + @retval EFI_SUCCESS The information was set. + @retval EFI_ACCESS_DENIED An attempt is being made to change the + EFI_FILE_DIRECTORY Attribute. + @retval EFI_ACCESS_DENIED The file was opened in read-only mode and an + attempt is being made to modify a field other + than Attribute. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + @retval EFI_WRITE_PROTECTED An attempt is being made to modify a read-only + attribute. + @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request + failed. + +**/ +STATIC EFI_STATUS SetFileInfo ( - IN BOOTMON_FS_INSTANCE *Instance, - IN BOOTMON_FS_FILE *File, - IN UINTN BufferSize, - IN EFI_FILE_INFO *Info + IN BOOTMON_FS_INSTANCE *Instance, + IN BOOTMON_FS_FILE *File, + IN EFI_FILE_INFO *Info ) { - EFI_STATUS Status; + EFI_STATUS Status; + BOOLEAN FileSizeIsDifferent; + BOOLEAN FileNameIsDifferent; + BOOLEAN TimeIsDifferent; - Status = EFI_SUCCESS; + // + // A directory can not be changed to a file and a file can + // not be changed to a directory. + // + if ((Info->Attribute & EFI_FILE_DIRECTORY) != + (File->Info->Attribute & EFI_FILE_DIRECTORY) ) { + return EFI_ACCESS_DENIED; + } - // Note that a call to this function on a file opened read-only is only - // invalid if it actually changes fields, so we don't immediately fail if the - // OpenMode is wrong. - // Also note that the only fields supported are filename and size, others are - // ignored. + FileSizeIsDifferent = (Info->FileSize != File->Info->FileSize); + FileNameIsDifferent = (StrnCmp ( + Info->FileName, + File->Info->FileName, + MAX_NAME_LENGTH - 1 + ) != 0); + // + // Check if the CreateTime, LastAccess or ModificationTime + // have been changed. The file system does not support file + // timestamps thus the three times in "File->Info" are + // always equal to zero. The following comparison actually + // checks if all three times are still equal to 0 or not. + // + TimeIsDifferent = CompareMem ( + &Info->CreateTime, + &File->Info->CreateTime, + 3 * sizeof (EFI_TIME) + ) != 0; - if (File != Instance->RootFile) { - if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) { + // + // For a file opened in read-only mode, only the Attribute field can be + // modified. The root directory open mode is forced to read-only at opening + // thus the following test protects the root directory to be somehow modified. + // + if (File->OpenMode == EFI_FILE_MODE_READ) { + if (FileSizeIsDifferent || FileNameIsDifferent || TimeIsDifferent) { return EFI_ACCESS_DENIED; } + } - Status = SetFileName (File, Info->FileName); - if (EFI_ERROR (Status)) { - return Status; - } + if (TimeIsDifferent) { + return EFI_WRITE_PROTECTED; + } - // Update file size + if (FileSizeIsDifferent) { Status = SetFileSize (Instance, File, Info->FileSize); if (EFI_ERROR (Status)) { return Status; } } - return Status; + + // + // Note down in RAM the Attribute field but we can not + // ask to store it in flash for the time being. + // + File->Info->Attribute = Info->Attribute; + + if (FileNameIsDifferent) { + Status = SetFileName (Instance, File, Info->FileName); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; } EFIAPI @@ -477,11 +543,17 @@ BootMonFsGetInfo ( BOOTMON_FS_FILE *File; BOOTMON_FS_INSTANCE *Instance; - File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); - if (File == NULL) { - return EFI_DEVICE_ERROR; + if ((This == NULL) || + (InformationType == NULL) || + (BufferSize == NULL) || + ((Buffer == NULL) && (*BufferSize > 0)) ) { + return EFI_INVALID_PARAMETER; } + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File->Info == NULL) { + return EFI_INVALID_PARAMETER; + } Instance = File->Instance; // If the instance has not been initialized yet then do it ... @@ -509,6 +581,37 @@ BootMonFsGetInfo ( return Status; } +/** + Set information about a file or a volume. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle the information is for. + @param[in] InformationType The type identifier for the information being set : + EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or + EFI_FILE_SYSTEM_VOLUME_LABEL_ID + @param[in] BufferSize The size, in bytes, of Buffer. + @param[in] Buffer A pointer to the data buffer to write. The type of the + data inside the buffer is indicated by InformationType. + + @retval EFI_SUCCESS The information was set. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + @retval EFI_ACCESS_DENIED An attempt is being made to change the + EFI_FILE_DIRECTORY Attribute. + @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and + the file was opened in read-only mode and an + attempt is being made to modify a field other + than Attribute. + @retval EFI_WRITE_PROTECTED An attempt is being made to modify a read-only + attribute. + @retval EFI_BAD_BUFFER_SIZE The size of the buffer is lower than that indicated by + the data inside the buffer. + @retval EFI_OUT_OF_RESOURCES A allocation needed to process the request failed. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ EFIAPI EFI_STATUS BootMonFsSetInfo ( @@ -518,28 +621,56 @@ BootMonFsSetInfo ( IN VOID *Buffer ) { - EFI_STATUS Status; - BOOTMON_FS_FILE *File; - BOOTMON_FS_INSTANCE *Instance; + BOOTMON_FS_FILE *File; + EFI_FILE_INFO *Info; + EFI_FILE_SYSTEM_INFO *SystemInfo; + + if ((This == NULL) || + (InformationType == NULL) || + (Buffer == NULL) ) { + return EFI_INVALID_PARAMETER; + } File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); - if (File == NULL) { - return EFI_DEVICE_ERROR; + if (File->Info == NULL) { + return EFI_INVALID_PARAMETER; } - Instance = File->Instance; - - if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) { - Status = SetFileInfo (Instance, File, BufferSize, (EFI_FILE_INFO *) Buffer); - } else { - // The only writable field in the other two information types - // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the - // filesystem volume label. This can be retrieved with GetInfo, but it is - // hard-coded into this driver, not stored on media. - Status = EFI_UNSUPPORTED; + if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + Info = Buffer; + if (Info->Size < (SIZE_OF_EFI_FILE_INFO + StrSize (Info->FileName))) { + return EFI_INVALID_PARAMETER; + } + if (BufferSize < Info->Size) { + return EFI_BAD_BUFFER_SIZE; + } + return (SetFileInfo (File->Instance, File, Info)); } - return Status; + // + // The only writable field in the other two information types + // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the + // filesystem volume label. This can be retrieved with GetInfo, but it is + // hard-coded into this driver, not stored on media. + // + + if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + SystemInfo = Buffer; + if (SystemInfo->Size < + (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (SystemInfo->VolumeLabel))) { + return EFI_INVALID_PARAMETER; + } + if (BufferSize < SystemInfo->Size) { + return EFI_BAD_BUFFER_SIZE; + } + return EFI_WRITE_PROTECTED; + } + + if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + return EFI_WRITE_PROTECTED; + } + + return EFI_UNSUPPORTED; } EFIAPI diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsEntryPoint.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsEntryPoint.c index 5eb7afca7c..3d71760fef 100644 --- a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsEntryPoint.c +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsEntryPoint.c @@ -54,6 +54,30 @@ EFI_FILE_PROTOCOL mBootMonFsFileTemplate = { BootMonFsFlushFile }; +/** + Search for a file given its name coded in Ascii. + + When searching through the files of the volume, if a file is currently not + open, its name was written on the media and is kept in RAM in the + "HwDescription.Footer.Filename[]" field of the file's description. + + If a file is currently open, its name might not have been written on the + media yet, and as the "HwDescription" is a mirror in RAM of what is on the + media the "HwDescription.Footer.Filename[]" might be outdated. In that case, + the up to date name of the file is stored in the "Info" field of the file's + description. + + @param[in] Instance Pointer to the description of the volume in which + the file has to be search for. + @param[in] AsciiFileName Name of the file. + + @param[out] File Pointer to the description of the file if the + file was found. + + @retval EFI_SUCCESS The file was found. + @retval EFI_NOT_FOUND The file was not found. + +**/ EFI_STATUS BootMonGetFileFromAsciiFileName ( IN BOOTMON_FS_INSTANCE *Instance, @@ -61,22 +85,26 @@ BootMonGetFileFromAsciiFileName ( OUT BOOTMON_FS_FILE **File ) { - LIST_ENTRY *Entry; - BOOTMON_FS_FILE *FileEntry; - - // Remove the leading '\\' - if (*AsciiFileName == '\\') { - AsciiFileName++; - } + LIST_ENTRY *Entry; + BOOTMON_FS_FILE *FileEntry; + CHAR8 OpenFileAsciiFileName[MAX_NAME_LENGTH]; + CHAR8 *AsciiFileNameToCompare; // Go through all the files in the list and return the file handle for (Entry = GetFirstNode (&Instance->RootFile->Link); - !IsNull (&Instance->RootFile->Link, Entry); - Entry = GetNextNode (&Instance->RootFile->Link, Entry) - ) + !IsNull (&Instance->RootFile->Link, Entry); + Entry = GetNextNode (&Instance->RootFile->Link, Entry) + ) { FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (Entry); - if (AsciiStrCmp (FileEntry->HwDescription.Footer.Filename, AsciiFileName) == 0) { + if (FileEntry->Info != NULL) { + UnicodeStrToAsciiStr (FileEntry->Info->FileName, OpenFileAsciiFileName); + AsciiFileNameToCompare = OpenFileAsciiFileName; + } else { + AsciiFileNameToCompare = FileEntry->HwDescription.Footer.Filename; + } + + if (AsciiStrCmp (AsciiFileNameToCompare, AsciiFileName) == 0) { *File = FileEntry; return EFI_SUCCESS; } @@ -291,6 +319,7 @@ BootMonFsDriverStart ( BOOTMON_FS_INSTANCE *Instance; EFI_STATUS Status; UINTN VolumeNameSize; + EFI_FILE_INFO *Info; Instance = AllocateZeroPool (sizeof (BOOTMON_FS_INSTANCE)); if (Instance == NULL) { @@ -307,8 +336,7 @@ BootMonFsDriverStart ( EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { - FreePool (Instance); - return Status; + goto Error; } Status = gBS->OpenProtocol ( @@ -320,8 +348,7 @@ BootMonFsDriverStart ( EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { - FreePool (Instance); - return Status; + goto Error; } // @@ -350,10 +377,16 @@ BootMonFsDriverStart ( // Initialize the root file Status = BootMonFsCreateFile (Instance, &Instance->RootFile); if (EFI_ERROR (Status)) { - FreePool (Instance); - return Status; + goto Error; } + Info = AllocateZeroPool (sizeof (EFI_FILE_INFO)); + if (Info == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + Instance->RootFile->Info = Info; + // Initialize the DevicePath of the Instance Status = gBS->OpenProtocol ( ControllerHandle, @@ -364,8 +397,7 @@ BootMonFsDriverStart ( EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { - FreePool (Instance); - return Status; + goto Error; } // @@ -376,9 +408,24 @@ BootMonFsDriverStart ( &gEfiSimpleFileSystemProtocolGuid, &Instance->Fs, NULL ); + if (EFI_ERROR (Status)) { + goto Error; + } InsertTailList (&mInstances, &Instance->Link); + return EFI_SUCCESS; + +Error: + + if (Instance->RootFile != NULL) { + if (Instance->RootFile->Info != NULL) { + FreePool (Instance->RootFile->Info); + } + FreePool (Instance->RootFile); + } + FreePool (Instance); + return Status; } @@ -434,6 +481,10 @@ BootMonFsDriverStop ( &gEfiSimpleFileSystemProtocolGuid, &Instance->Fs, NULL); + FreePool (Instance->RootFile->Info); + FreePool (Instance->RootFile); + FreePool (Instance); + return Status; } diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsInternal.h b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsInternal.h index 57343618ab..c0c6599cb7 100644 --- a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsInternal.h +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsInternal.h @@ -54,8 +54,14 @@ typedef struct { EFI_FILE_PROTOCOL File; + // + // The following fields are relevant only if the file is open. + // + + EFI_FILE_INFO *Info; UINT64 Position; - // If the file needs to be flushed then this list contain the memory buffer that creates this file + // If the file needs to be flushed then this list contain the memory + // buffer that creates this file LIST_ENTRY RegionToFlushLink; UINT64 OpenMode; } BOOTMON_FS_FILE; diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c index 45ac89026f..dc83b3882b 100644 --- a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c @@ -39,6 +39,10 @@ InvalidateImageDescription ( Buffer = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION)); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = DiskIo->WriteDisk (DiskIo, MediaId, File->HwDescAddress, @@ -51,83 +55,75 @@ InvalidateImageDescription ( return Status; } -// Flush file data that will extend the file's length. Update and, if necessary, -// move the image description. -// We need to pass the file's starting position on media (FileStart), because -// if the file hasn't been flushed before its Description->BlockStart won't -// have been initialised. -// FileStart must be aligned to the media's block size. -// Note that this function uses DiskIo to flush, so call BlockIo->FlushBlocks() -// after calling it. +/** + 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 -FlushAppendRegion ( - IN BOOTMON_FS_FILE *File, - IN BOOTMON_FS_FILE_REGION *Region, - IN UINT64 NewFileSize, - IN UINT64 FileStart +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; - HW_IMAGE_DESCRIPTION *Description; - - DiskIo = File->Instance->DiskIo; + 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); - // Only invalidate the Image Description of files that have already been - // written in Flash - if (File->HwDescAddress != 0) { - Status = InvalidateImageDescription (File); - ASSERT_EFI_ERROR (Status); - } + // + // Construct the file description + // - // - // Update File Description - // + FileSize = DataSize + sizeof (HW_IMAGE_DESCRIPTION); Description = &File->HwDescription; Description->Attributes = 1; Description->BlockStart = FileStart / BlockSize; - Description->BlockEnd = Description->BlockStart + (NewFileSize / BlockSize); + Description->BlockEnd = Description->BlockStart + (FileSize / BlockSize); + AsciiStrCpy (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; -#ifdef MDE_CPU_ARM - Description->Footer.Version = HW_IMAGE_FOOTER_VERSION; - Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET; -#else - Description->Footer.Version = HW_IMAGE_FOOTER_VERSION2; - Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET2; -#endif Description->RegionCount = 1; Description->Region[0].Checksum = 0; Description->Region[0].Offset = Description->BlockStart * BlockSize; - Description->Region[0].Size = NewFileSize - sizeof (HW_IMAGE_DESCRIPTION); + Description->Region[0].Size = DataSize; Status = BootMonFsComputeFooterChecksum (Description); if (EFI_ERROR (Status)) { return Status; } - // Write the new file data - Status = DiskIo->WriteDisk ( - DiskIo, - File->Instance->Media->MediaId, - FileStart + Region->Offset, - Region->Size, - Region->Buffer - ); - ASSERT_EFI_ERROR (Status); - - // Round the file size up to the nearest block size - if ((NewFileSize % BlockSize) > 0) { - NewFileSize += BlockSize - (NewFileSize % BlockSize); - } - - File->HwDescAddress = (FileStart + NewFileSize) - sizeof (HW_IMAGE_DESCRIPTION); + File->HwDescAddress = ((Description->BlockEnd + 1) * BlockSize) - sizeof (HW_IMAGE_DESCRIPTION); // Update the file description on the media Status = DiskIo->WriteDisk ( @@ -142,14 +138,6 @@ FlushAppendRegion ( return Status; } -BOOLEAN -BootMonFsFileNeedFlush ( - IN BOOTMON_FS_FILE *File - ) -{ - return !IsListEmpty (&File->RegionToFlushLink); -} - // 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: @@ -167,6 +155,7 @@ STATIC EFI_STATUS BootMonFsFindSpaceForNewFile ( IN BOOTMON_FS_FILE *File, + IN UINT64 FileSize, OUT UINT64 *FileStart ) { @@ -174,26 +163,16 @@ BootMonFsFindSpaceForNewFile ( BOOTMON_FS_FILE *RootFile; BOOTMON_FS_FILE *FileEntry; UINTN BlockSize; - UINT64 FileSize; EFI_BLOCK_IO_MEDIA *Media; Media = File->Instance->BlockIo->Media; BlockSize = Media->BlockSize; RootFile = File->Instance->RootFile; - if (IsListEmpty (&RootFile->Link)) { - return EFI_SUCCESS; - } - // This function must only be called for file which has not been flushed into // Flash yet ASSERT (File->HwDescription.RegionCount == 0); - // Find out how big the file will be - FileSize = BootMonFsGetImageLength (File); - // Add the file header to the file - FileSize += sizeof (HW_IMAGE_DESCRIPTION); - *FileStart = 0; // Go through all the files in the list for (FileLink = GetFirstNode (&RootFile->Link); @@ -253,6 +232,20 @@ FreeFileRegions ( } } +/** + 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 ( @@ -261,52 +254,62 @@ BootMonFsFlushFile ( { 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; - UINTN BlockSize; UINT64 FileStart; UINT64 FileEnd; UINT64 RegionStart; UINT64 RegionEnd; + UINT64 NewDataSize; UINT64 NewFileSize; UINT64 EndOfAppendSpace; BOOLEAN HasSpace; - EFI_DISK_IO_PROTOCOL *DiskIo; - EFI_BLOCK_IO_PROTOCOL *BlockIo; - Status = EFI_SUCCESS; - FileStart = 0; - - File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); - if (File == NULL) { + if (This == NULL) { return EFI_INVALID_PARAMETER; } - // Check if the file needs to be flushed - if (!BootMonFsFileNeedFlush (File)) { - return Status; + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File->Info == NULL) { + return EFI_INVALID_PARAMETER; } - Instance = File->Instance; - BlockIo = Instance->BlockIo; - DiskIo = Instance->DiskIo; - BlockSize = BlockIo->Media->BlockSize; + 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; + + UnicodeStrToAsciiStr (Info->FileName, AsciiFileName); // If the file doesn't exist then find a space for it if (File->HwDescription.RegionCount == 0) { - Status = BootMonFsFindSpaceForNewFile (File, &FileStart); - // FileStart has changed so we need to recompute RegionEnd + 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; @@ -316,17 +319,20 @@ BootMonFsFlushFile ( ) { 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; + 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, - BlockIo->Media->MediaId, + Media->MediaId, RegionStart, Region->Size, Region->Buffer @@ -345,7 +351,8 @@ BootMonFsFlushFile ( // Check if there is space to append the new region HasSpace = FALSE; - NewFileSize = (RegionEnd - FileStart) + sizeof (HW_IMAGE_DESCRIPTION); + NewDataSize = RegionEnd - FileStart; + NewFileSize = NewDataSize + sizeof (HW_IMAGE_DESCRIPTION); CurrentPhysicalSize = BootMonFsGetPhysicalSize (File); if (NewFileSize <= CurrentPhysicalSize) { HasSpace = TRUE; @@ -360,7 +367,7 @@ BootMonFsFlushFile ( EndOfAppendSpace = NextFile->HwDescription.BlockStart * BlockSize; } else { // We are flushing the last file. - EndOfAppendSpace = (BlockIo->Media->LastBlock + 1) * BlockSize; + EndOfAppendSpace = (Media->LastBlock + 1) * BlockSize; } if (EndOfAppendSpace - FileStart >= NewFileSize) { HasSpace = TRUE; @@ -368,10 +375,31 @@ BootMonFsFlushFile ( } if (HasSpace == TRUE) { - Status = FlushAppendRegion (File, Region, NewFileSize, FileStart); + // 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 @@ -385,20 +413,32 @@ BootMonFsFlushFile ( } 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 Status; + return EFI_SUCCESS; } /** - Closes a file on the Nor Flash FS volume. + Close a specified file handle. - @param This The EFI_FILE_PROTOCOL to close. + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to close. - @return Always returns EFI_SUCCESS. + @retval EFI_SUCCESS The file was closed. + @retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open + file handle. **/ EFIAPI @@ -407,44 +447,27 @@ BootMonFsCloseFile ( IN EFI_FILE_PROTOCOL *This ) { - // Flush the file if needed - This->Flush (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; } -// Create a new instance of BOOTMON_FS_FILE. -// Uses BootMonFsCreateFile to -STATIC -EFI_STATUS -CreateNewFile ( - IN BOOTMON_FS_INSTANCE *Instance, - IN CHAR8* AsciiFileName, - OUT BOOTMON_FS_FILE **NewHandle - ) -{ - EFI_STATUS Status; - BOOTMON_FS_FILE *File; - - Status = BootMonFsCreateFile (Instance, &File); - if (EFI_ERROR (Status)) { - return Status; - } - - // Remove the leading '\\' - if (*AsciiFileName == '\\') { - AsciiFileName++; - } - - // Set the file name - CopyMem (File->HwDescription.Footer.Filename, AsciiFileName, MAX_NAME_LENGTH); - - // Add the file to list of files of the File System - InsertHeadList (&Instance->RootFile->Link, &File->Link); - - *NewHandle = File; - return Status; -} - /** Open a file on the boot monitor file system. @@ -490,11 +513,17 @@ BootMonFsOpenFile ( CHAR16 *Path; CHAR16 *Separator; CHAR8 *AsciiFileName; + EFI_FILE_INFO *Info; 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; } @@ -508,11 +537,6 @@ BootMonFsOpenFile ( return EFI_INVALID_PARAMETER; } - Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This); - if (Directory == NULL) { - return EFI_INVALID_PARAMETER; - } - Instance = Directory->Instance; // @@ -535,6 +559,7 @@ BootMonFsOpenFile ( } Path = (CHAR16*)Buf; AsciiFileName = NULL; + Info = NULL; // // Handle single periods, double periods and convert forward slashes '/' @@ -624,6 +649,18 @@ BootMonFsOpenFile ( 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. // @@ -634,20 +671,32 @@ BootMonFsOpenFile ( goto Error; } - Status = CreateNewFile (Instance, AsciiFileName, &File); - if (!EFI_ERROR (Status)) { - File->OpenMode = OpenMode; - *NewHandle = &File->File; - File->Position = 0; + Status = BootMonFsCreateFile (Instance, &File); + if (EFI_ERROR (Status)) { + goto Error; } + InsertHeadList (&Instance->RootFile->Link, &File->Link); + Info->Attribute = Attributes; } else { // - // The file already exists. + // File already open, not supported yet. // - File->OpenMode = OpenMode; - *NewHandle = &File->File; - File->Position = 0; + if (File->Info != NULL) { + Status = EFI_UNSUPPORTED; + goto Error; + } } + + Info->FileSize = BootMonFsGetImageLength (File); + Info->PhysicalSize = BootMonFsGetPhysicalSize (File); + AsciiStrToUnicodeStr (AsciiFileName, Info->FileName); + + File->Info = Info; + Info = NULL; + File->Position = 0; + File->OpenMode = OpenMode; + + *NewHandle = &File->File; } Error: @@ -656,6 +705,9 @@ Error: if (AsciiFileName != NULL) { FreePool (AsciiFileName); } + if (Info != NULL) { + FreePool (Info); + } return Status; } @@ -671,6 +723,19 @@ BootMonFsDeleteFail ( // 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 ( @@ -681,23 +746,26 @@ BootMonFsDelete ( BOOTMON_FS_FILE *File; LIST_ENTRY *RegionToFlushLink; BOOTMON_FS_FILE_REGION *Region; - EFI_BLOCK_IO_PROTOCOL *BlockIo; - UINT8 *EmptyBuffer; - File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); - if (File == NULL) { - return EFI_DEVICE_ERROR; + if (This == NULL) { + return EFI_INVALID_PARAMETER; } - Status = EFI_SUCCESS; + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File->Info == NULL) { + return EFI_INVALID_PARAMETER; + } - if (BootMonFsFileNeedFlush (File)) { + if (!IsListEmpty (&File->RegionToFlushLink)) { // Free the entries from the Buffer List RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink); do { Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink; - // Get Next entry + // + // Get next element of the list before deleting the region description + // that contain the LIST_ENTRY structure. + // RegionToFlushLink = RemoveEntryList (RegionToFlushLink); // Free the buffers @@ -708,25 +776,18 @@ BootMonFsDelete ( // If (RegionCount is greater than 0) then the file already exists if (File->HwDescription.RegionCount > 0) { - BlockIo = File->Instance->BlockIo; - - // Create an empty buffer - EmptyBuffer = AllocateZeroPool (BlockIo->Media->BlockSize); - if (EmptyBuffer == NULL) { - FreePool (File); - return EFI_OUT_OF_RESOURCES; - } - // Invalidate the last Block Status = InvalidateImageDescription (File); ASSERT_EFI_ERROR (Status); - - FreePool (EmptyBuffer); + if (EFI_ERROR (Status)) { + return EFI_WARN_DELETE_FAILURE; + } } // Remove the entry from the list RemoveEntryList (&File->Link); + FreePool (File->Info); FreePool (File); - return Status; -} + return EFI_SUCCESS; +} diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsReadWrite.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsReadWrite.c index 358332d658..f8124e95ac 100644 --- a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsReadWrite.c +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsReadWrite.c @@ -36,6 +36,7 @@ reported an error while performing the read operation. @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + **/ EFIAPI EFI_STATUS @@ -53,25 +54,30 @@ BootMonFsReadFile ( EFI_STATUS Status; UINTN RemainingFileSize; + if ((This == NULL) || + (BufferSize == NULL) || + (Buffer == NULL) ) { + return EFI_INVALID_PARAMETER; + } + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File->Info == NULL) { + return EFI_INVALID_PARAMETER; + } + // Ensure the file has been written in Flash before reading it. // This keeps the code simple and avoids having to manage a non-flushed file. BootMonFsFlushFile (This); - File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); - if (File == NULL) { - return EFI_INVALID_PARAMETER; - } - - Instance = File->Instance; - DiskIo = Instance->DiskIo; - Media = Instance->Media; + Instance = File->Instance; + DiskIo = Instance->DiskIo; + Media = Instance->Media; FileStart = (Media->LowestAlignedLba + File->HwDescription.BlockStart) * Media->BlockSize; - if (File->Position >= File->HwDescription.Region[0].Size) { + if (File->Position >= File->Info->FileSize) { // The entire file has been read or the position has been // set past the end of the file. *BufferSize = 0; - if (File->Position > File->HwDescription.Region[0].Size) { + if (File->Position > File->Info->FileSize) { return EFI_DEVICE_ERROR; } else { return EFI_SUCCESS; @@ -79,7 +85,7 @@ BootMonFsReadFile ( } // This driver assumes that the entire file is in region 0. - RemainingFileSize = File->HwDescription.Region[0].Size - File->Position; + RemainingFileSize = File->Info->FileSize - File->Position; // If read would go past end of file, truncate the read if (*BufferSize > RemainingFileSize) { @@ -102,7 +108,26 @@ BootMonFsReadFile ( return Status; } -// Inserts an entry into the write chain +/** + Write data to an open file. + + The data is not written to the flash yet. It will be written when the file + will be either read, closed or flushed. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to write data to. + @param[in out] BufferSize On input, the size of the Buffer. On output, the + size of the data actually written. In both cases, + the size is measured in bytes. + @param[in] Buffer The buffer of data to write. + + @retval EFI_SUCCESS The data was written. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_OUT_OF_RESOURCES Unable to allocate the buffer to store the + data to write. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ EFIAPI EFI_STATUS BootMonFsWriteFile ( @@ -114,38 +139,57 @@ BootMonFsWriteFile ( BOOTMON_FS_FILE *File; BOOTMON_FS_FILE_REGION *Region; + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); - if (File == NULL) { + if (File->Info == NULL) { return EFI_INVALID_PARAMETER; } - if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) { + if (File->OpenMode == EFI_FILE_MODE_READ) { return EFI_ACCESS_DENIED; } // Allocate and initialize the memory region Region = (BOOTMON_FS_FILE_REGION*)AllocateZeroPool (sizeof (BOOTMON_FS_FILE_REGION)); if (Region == NULL) { + *BufferSize = 0; return EFI_OUT_OF_RESOURCES; } - Region->Buffer = AllocateCopyPool (*BufferSize, Buffer); + Region->Buffer = AllocateCopyPool (*BufferSize, Buffer); if (Region->Buffer == NULL) { + *BufferSize = 0; FreePool (Region); return EFI_OUT_OF_RESOURCES; } - Region->Size = *BufferSize; - + Region->Size = *BufferSize; Region->Offset = File->Position; InsertTailList (&File->RegionToFlushLink, &Region->Link); File->Position += *BufferSize; + if (File->Position > File->Info->FileSize) { + File->Info->FileSize = File->Position; + } + return EFI_SUCCESS; } +/** + Set a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to set the requested position on. + @param[in] Position The byte position from the start of the file to set. + + @retval EFI_SUCCESS The position was set. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ EFIAPI EFI_STATUS BootMonFsSetPosition ( @@ -153,33 +197,63 @@ BootMonFsSetPosition ( IN UINT64 Position ) { - BOOTMON_FS_FILE *File; + 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; + } + // // UEFI Spec section 12.5: // "Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to - // be set to the end of the file." + // be set to the end of the file." + // if (Position == 0xFFFFFFFFFFFFFFFF) { - File->Position = BootMonFsGetImageLength (File); - } else { - // NB: Seeking past the end of the file is valid. - File->Position = Position; + Position = File->Info->FileSize; } + File->Position = Position; + return EFI_SUCCESS; } +/** + Return a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to get the current position on. + @param[out] Position The address to return the file's current position value. + + @retval EFI_SUCCESS The position was returned. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ EFIAPI EFI_STATUS BootMonFsGetPosition ( IN EFI_FILE_PROTOCOL *This, OUT UINT64 *Position - ) { + ) +{ 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; + } + + if (Position == NULL) { + return EFI_INVALID_PARAMETER; + } *Position = File->Position; + return EFI_SUCCESS; }