diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Devices/EFI/Firmware/FatPkg | |
parent | Initial commit. (diff) | |
download | virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.tar.xz virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/FatPkg')
41 files changed, 13786 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/ComponentName.c new file mode 100644 index 00000000..aa470435 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/ComponentName.c @@ -0,0 +1,343 @@ +/** @file + +Copyright (c) 2005 - 2011, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Fat.h" + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +FatComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +FatComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gFatComponentName = { + FatComponentNameGetDriverName, + FatComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gFatComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) FatComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) FatComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mFatDriverNameTable[] = { + { + "eng;en", + L"FAT File System Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mFatControllerNameTable[] = { + { + "eng;en", + L"FAT File System" + }, + { + NULL, + NULL + } +}; + + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +FatComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mFatDriverNameTable, + DriverName, + (BOOLEAN)(This == &gFatComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +FatComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gFatDriverBinding.DriverBindingHandle, + &gEfiDiskIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mFatControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gFatComponentName) + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Data.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Data.c new file mode 100644 index 00000000..95898bbc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Data.c @@ -0,0 +1,40 @@ +/** @file + Global data in the FAT Filesystem driver. + +Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +// +// Globals +// +// +// FatFsLock - Global lock for synchronizing all requests. +// +EFI_LOCK FatFsLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_CALLBACK); + +EFI_LOCK FatTaskLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY); + +// +// Filesystem interface functions +// +EFI_FILE_PROTOCOL FatFileInterface = { + EFI_FILE_PROTOCOL_REVISION, + FatOpen, + FatClose, + FatDelete, + FatRead, + FatWrite, + FatGetPosition, + FatSetPosition, + FatGetInfo, + FatSetInfo, + FatFlush, + FatOpenEx, + FatReadEx, + FatWriteEx, + FatFlushEx +}; diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Delete.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Delete.c new file mode 100644 index 00000000..2215bb6d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Delete.c @@ -0,0 +1,119 @@ +/** @file + Function that deletes a file. + +Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Fat.h" + +/** + + Deletes the file & Closes the file handle. + + @param FHand - Handle to the file to delete. + + @retval EFI_SUCCESS - Delete the file successfully. + @retval EFI_WARN_DELETE_FAILURE - Fail to delete the file. + +**/ +EFI_STATUS +EFIAPI +FatDelete ( + IN EFI_FILE_PROTOCOL *FHand + ) +{ + FAT_IFILE *IFile; + FAT_OFILE *OFile; + FAT_DIRENT *DirEnt; + EFI_STATUS Status; + UINTN Round; + + IFile = IFILE_FROM_FHAND (FHand); + OFile = IFile->OFile; + + FatWaitNonblockingTask (IFile); + + // + // Lock the volume + // + FatAcquireLock (); + + // + // If the file is read-only, then don't delete it + // + if (IFile->ReadOnly) { + Status = EFI_WRITE_PROTECTED; + goto Done; + } + // + // If the file is the root dir, then don't delete it + // + if (OFile->Parent == NULL) { + Status = EFI_ACCESS_DENIED; + goto Done; + } + // + // If the file has a permanent error, skip the delete + // + Status = OFile->Error; + if (!EFI_ERROR (Status)) { + // + // If this is a directory, make sure it's empty before + // allowing it to be deleted + // + if (OFile->ODir != NULL) { + // + // We do not allow to delete nonempty directory + // + FatResetODirCursor (OFile); + for (Round = 0; Round < 3; Round++) { + Status = FatGetNextDirEnt (OFile, &DirEnt); + if ((EFI_ERROR (Status)) || + ((Round < 2) && (DirEnt == NULL || !FatIsDotDirEnt (DirEnt))) || + ((Round == 2) && (DirEnt != NULL)) + ) { + Status = EFI_ACCESS_DENIED; + goto Done; + } + } + } + // + // Return the file's space by setting its size to 0 + // + FatTruncateOFile (OFile, 0); + // + // Free the directory entry for this file + // + Status = FatRemoveDirEnt (OFile->Parent, OFile->DirEnt); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Set a permanent error for this OFile in case there + // are still opened IFiles attached + // + OFile->Error = EFI_NOT_FOUND; + } else if (OFile->Error == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + } + +Done: + // + // Always close the handle + // + FatIFileClose (IFile); + // + // Done + // + Status = FatCleanupVolume (OFile->Volume, NULL, Status, NULL); + FatReleaseLock (); + + if (EFI_ERROR (Status)) { + Status = EFI_WARN_DELETE_FAILURE; + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryCache.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryCache.c new file mode 100644 index 00000000..318e78f6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryCache.c @@ -0,0 +1,184 @@ +/** @file + Functions for directory cache operation. + +Copyright (c) 2005, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Fat.h" + +/** + + Free the directory structure and release the memory. + + @param ODir - The directory to be freed. + +**/ +STATIC +VOID +FatFreeODir ( + IN FAT_ODIR *ODir + ) +{ + FAT_DIRENT *DirEnt; + + // + // Release Directory Entry Nodes + // + while (!IsListEmpty (&ODir->ChildList)) { + DirEnt = DIRENT_FROM_LINK (ODir->ChildList.ForwardLink); + RemoveEntryList (&DirEnt->Link); + // + // Make sure the OFile has been closed + // + ASSERT (DirEnt->OFile == NULL); + FatFreeDirEnt (DirEnt); + } + + FreePool (ODir); +} + +/** + + Allocate the directory structure. + + @param OFile - The corresponding OFile. + +**/ +STATIC +FAT_ODIR * +FatAllocateODir ( + IN FAT_OFILE *OFile + ) +{ + FAT_ODIR *ODir; + + ODir = AllocateZeroPool (sizeof (FAT_ODIR)); + if (ODir != NULL) { + // + // Initialize the directory entry list + // + ODir->Signature = FAT_ODIR_SIGNATURE; + InitializeListHead (&ODir->ChildList); + ODir->CurrentCursor = &ODir->ChildList; + } + + return ODir; +} + +/** + + Discard the directory structure when an OFile will be freed. + Volume will cache this directory if the OFile does not represent a deleted file. + + @param OFile - The OFile whose directory structure is to be discarded. + +**/ +VOID +FatDiscardODir ( + IN FAT_OFILE *OFile + ) +{ + FAT_ODIR *ODir; + FAT_VOLUME *Volume; + + Volume = OFile->Volume; + ODir = OFile->ODir; + if (!OFile->DirEnt->Invalid) { + // + // If OFile does not represent a deleted file, then we will cache the directory + // We use OFile's first cluster as the directory's tag + // + ODir->DirCacheTag = OFile->FileCluster; + InsertHeadList (&Volume->DirCacheList, &ODir->DirCacheLink); + if (Volume->DirCacheCount == FAT_MAX_DIR_CACHE_COUNT) { + // + // Replace the least recent used directory + // + ODir = ODIR_FROM_DIRCACHELINK (Volume->DirCacheList.BackLink); + RemoveEntryList (&ODir->DirCacheLink); + } else { + // + // No need to find a replace + // + Volume->DirCacheCount++; + ODir = NULL; + } + } + // + // Release ODir Structure + // + if (ODir != NULL) { + FatFreeODir (ODir); + } +} + +/** + + + Request the directory structure when an OFile is newly generated. + If the directory structure is cached by volume, then just return this directory; + Otherwise, allocate a new one for OFile. + + @param OFile - The OFile which requests directory structure. + +**/ +VOID +FatRequestODir ( + IN FAT_OFILE *OFile + ) +{ + UINTN DirCacheTag; + FAT_VOLUME *Volume; + FAT_ODIR *ODir; + FAT_ODIR *CurrentODir; + LIST_ENTRY *CurrentODirLink; + + Volume = OFile->Volume; + ODir = NULL; + DirCacheTag = OFile->FileCluster; + for (CurrentODirLink = Volume->DirCacheList.ForwardLink; + CurrentODirLink != &Volume->DirCacheList; + CurrentODirLink = CurrentODirLink->ForwardLink + ) { + CurrentODir = ODIR_FROM_DIRCACHELINK (CurrentODirLink); + if (CurrentODir->DirCacheTag == DirCacheTag) { + RemoveEntryList (&CurrentODir->DirCacheLink); + Volume->DirCacheCount--; + ODir = CurrentODir; + break; + } + } + + if (ODir == NULL) { + // + // This directory is not cached, then allocate a new one + // + ODir = FatAllocateODir (OFile); + } + + OFile->ODir = ODir; +} + +/** + + Clean up all the cached directory structures when the volume is going to be abandoned. + + @param Volume - FAT file system volume. + +**/ +VOID +FatCleanupODirCache ( + IN FAT_VOLUME *Volume + ) +{ + FAT_ODIR *ODir; + while (Volume->DirCacheCount > 0) { + ODir = ODIR_FROM_DIRCACHELINK (Volume->DirCacheList.BackLink); + RemoveEntryList (&ODir->DirCacheLink); + FatFreeODir (ODir); + Volume->DirCacheCount--; + } +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryManage.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryManage.c new file mode 100644 index 00000000..7d9abbc5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryManage.c @@ -0,0 +1,1391 @@ +/** @file + Functions for performing directory entry io. + +Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +/** + + Get a directory entry from disk for the Ofile. + + @param Parent - The parent of the OFile which need to update. + @param IoMode - Indicate whether to read directory entry or write directory entry. + @param EntryPos - The position of the directory entry to be accessed. + @param Entry - The directory entry read or written. + + @retval EFI_SUCCESS - Access the directory entry successfully. + @return other - An error occurred when reading the directory entry. + +**/ +STATIC +EFI_STATUS +FatAccessEntry ( + IN FAT_OFILE *Parent, + IN IO_MODE IoMode, + IN UINTN EntryPos, + IN OUT VOID *Entry + ) +{ + UINTN Position; + UINTN BufferSize; + + Position = EntryPos * sizeof (FAT_DIRECTORY_ENTRY); + if (Position >= Parent->FileSize) { + // + // End of directory + // + ASSERT (IoMode == ReadData); + ((FAT_DIRECTORY_ENTRY *) Entry)->FileName[0] = EMPTY_ENTRY_MARK; + ((FAT_DIRECTORY_ENTRY *) Entry)->Attributes = 0; + return EFI_SUCCESS; + } + + BufferSize = sizeof (FAT_DIRECTORY_ENTRY); + return FatAccessOFile (Parent, IoMode, Position, &BufferSize, Entry, NULL); +} + +/** + + Save the directory entry to disk. + + @param OFile - The parent OFile which needs to update. + @param DirEnt - The directory entry to be saved. + + @retval EFI_SUCCESS - Store the directory entry successfully. + @return other - An error occurred when writing the directory entry. + +**/ +EFI_STATUS +FatStoreDirEnt ( + IN FAT_OFILE *OFile, + IN FAT_DIRENT *DirEnt + ) +{ + EFI_STATUS Status; + FAT_DIRECTORY_LFN LfnEntry; + UINTN EntryPos; + CHAR16 *LfnBufferPointer; + CHAR16 LfnBuffer[MAX_LFN_ENTRIES * LFN_CHAR_TOTAL + 1]; + UINT8 EntryCount; + UINT8 LfnOrdinal; + + EntryPos = DirEnt->EntryPos; + EntryCount = DirEnt->EntryCount; + // + // Write directory entry + // + Status = FatAccessEntry (OFile, WriteData, EntryPos, &DirEnt->Entry); + if (EFI_ERROR (Status)) { + return Status; + } + + if (--EntryCount > 0) { + // + // Write LFN directory entry + // + SetMem (LfnBuffer, sizeof (CHAR16) * LFN_CHAR_TOTAL * EntryCount, 0xff); + Status = StrCpyS ( + LfnBuffer, + ARRAY_SIZE (LfnBuffer), + DirEnt->FileString + ); + if (EFI_ERROR (Status)) { + return Status; + } + + LfnBufferPointer = LfnBuffer; + LfnEntry.Attributes = FAT_ATTRIBUTE_LFN; + LfnEntry.Type = 0; + LfnEntry.MustBeZero = 0; + LfnEntry.Checksum = FatCheckSum (DirEnt->Entry.FileName); + for (LfnOrdinal = 1; LfnOrdinal <= EntryCount; LfnOrdinal++) { + LfnEntry.Ordinal = LfnOrdinal; + if (LfnOrdinal == EntryCount) { + LfnEntry.Ordinal |= FAT_LFN_LAST; + } + + CopyMem (LfnEntry.Name1, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR1_LEN); + LfnBufferPointer += LFN_CHAR1_LEN; + CopyMem (LfnEntry.Name2, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR2_LEN); + LfnBufferPointer += LFN_CHAR2_LEN; + CopyMem (LfnEntry.Name3, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR3_LEN); + LfnBufferPointer += LFN_CHAR3_LEN; + EntryPos--; + if (DirEnt->Invalid) { + LfnEntry.Ordinal = DELETE_ENTRY_MARK; + } + + Status = FatAccessEntry (OFile, WriteData, EntryPos, &LfnEntry); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + + return EFI_SUCCESS; +} + +/** + + Determine whether the directory entry is "." or ".." entry. + + @param DirEnt - The corresponding directory entry. + + @retval TRUE - The directory entry is "." or ".." directory entry + @retval FALSE - The directory entry is not "." or ".." directory entry + +**/ +BOOLEAN +FatIsDotDirEnt ( + IN FAT_DIRENT *DirEnt + ) +{ + CHAR16 *FileString; + FileString = DirEnt->FileString; + if (StrCmp (FileString, L".") == 0 || StrCmp (FileString, L"..") == 0) { + return TRUE; + } + + return FALSE; +} + +/** + + Set the OFile's cluster info in its directory entry. + + @param OFile - The corresponding OFile. + +**/ +STATIC +VOID +FatSetDirEntCluster ( + IN FAT_OFILE *OFile + ) +{ + UINTN Cluster; + FAT_DIRENT *DirEnt; + + DirEnt = OFile->DirEnt; + Cluster = OFile->FileCluster; + DirEnt->Entry.FileClusterHigh = (UINT16) (Cluster >> 16); + DirEnt->Entry.FileCluster = (UINT16) Cluster; +} + +/** + + Set the OFile's cluster and size info in its directory entry. + + @param OFile - The corresponding OFile. + +**/ +VOID +FatUpdateDirEntClusterSizeInfo ( + IN FAT_OFILE *OFile + ) +{ + ASSERT (OFile->ODir == NULL); + OFile->DirEnt->Entry.FileSize = (UINT32) OFile->FileSize; + FatSetDirEntCluster (OFile); +} + +/** + + Copy all the information of DirEnt2 to DirEnt1 except for 8.3 name. + + @param DirEnt1 - The destination directory entry. + @param DirEnt2 - The source directory entry. + +**/ +VOID +FatCloneDirEnt ( + IN FAT_DIRENT *DirEnt1, + IN FAT_DIRENT *DirEnt2 + ) +{ + UINT8 *Entry1; + UINT8 *Entry2; + Entry1 = (UINT8 *) &DirEnt1->Entry; + Entry2 = (UINT8 *) &DirEnt2->Entry; + CopyMem ( + Entry1 + FAT_ENTRY_INFO_OFFSET, + Entry2 + FAT_ENTRY_INFO_OFFSET, + sizeof (FAT_DIRECTORY_ENTRY) - FAT_ENTRY_INFO_OFFSET + ); +} + +/** + + Get the LFN for the directory entry. + + @param Parent - The parent directory. + @param DirEnt - The directory entry to get LFN. + +**/ +STATIC +VOID +FatLoadLongNameEntry ( + IN FAT_OFILE *Parent, + IN FAT_DIRENT *DirEnt + ) +{ + CHAR16 LfnBuffer[MAX_LFN_ENTRIES * LFN_CHAR_TOTAL + 1]; + CHAR16 *LfnBufferPointer; + CHAR8 *File8Dot3Name; + UINTN EntryPos; + UINT8 LfnOrdinal; + UINT8 LfnChecksum; + FAT_DIRECTORY_LFN LfnEntry; + EFI_STATUS Status; + + EntryPos = DirEnt->EntryPos; + File8Dot3Name = DirEnt->Entry.FileName; + LfnBufferPointer = LfnBuffer; + // + // Computes checksum for LFN + // + LfnChecksum = FatCheckSum (File8Dot3Name); + LfnOrdinal = 1; + do { + if (EntryPos == 0) { + LfnBufferPointer = LfnBuffer; + break; + } + + EntryPos--; + Status = FatAccessEntry (Parent, ReadData, EntryPos, &LfnEntry); + if (EFI_ERROR (Status) || + LfnEntry.Attributes != FAT_ATTRIBUTE_LFN || + LfnEntry.MustBeZero != 0 || + LfnEntry.Checksum != LfnChecksum || + (LfnEntry.Ordinal & (~FAT_LFN_LAST)) != LfnOrdinal || + LfnOrdinal > MAX_LFN_ENTRIES + ) { + // + // The directory entry does not have a long file name or + // some error occurs when loading long file name for a directory entry, + // and then we load the long name from short name + // + LfnBufferPointer = LfnBuffer; + break; + } + + CopyMem (LfnBufferPointer, LfnEntry.Name1, sizeof (CHAR16) * LFN_CHAR1_LEN); + LfnBufferPointer += LFN_CHAR1_LEN; + CopyMem (LfnBufferPointer, LfnEntry.Name2, sizeof (CHAR16) * LFN_CHAR2_LEN); + LfnBufferPointer += LFN_CHAR2_LEN; + CopyMem (LfnBufferPointer, LfnEntry.Name3, sizeof (CHAR16) * LFN_CHAR3_LEN); + LfnBufferPointer += LFN_CHAR3_LEN; + LfnOrdinal++; + } while ((LfnEntry.Ordinal & FAT_LFN_LAST) == 0); + DirEnt->EntryCount = LfnOrdinal; + // + // Terminate current Lfnbuffer + // + *LfnBufferPointer = 0; + if (LfnBufferPointer == LfnBuffer) { + // + // Fail to get the long file name from long file name entry, + // get the file name from short name + // + FatGetFileNameViaCaseFlag ( + DirEnt, + LfnBuffer, + ARRAY_SIZE (LfnBuffer) + ); + } + + DirEnt->FileString = AllocateCopyPool (StrSize (LfnBuffer), LfnBuffer); +} + +/** + + Add this directory entry node to the list of directory entries and hash table. + + @param ODir - The parent OFile which needs to be updated. + @param DirEnt - The directory entry to be added. + +**/ +STATIC +VOID +FatAddDirEnt ( + IN FAT_ODIR *ODir, + IN FAT_DIRENT *DirEnt + ) +{ + if (DirEnt->Link.BackLink == NULL) { + DirEnt->Link.BackLink = &ODir->ChildList; + } + InsertTailList (DirEnt->Link.BackLink, &DirEnt->Link); + FatInsertToHashTable (ODir, DirEnt); +} + +/** + + Load from disk the next directory entry at current end of directory position. + + @param OFile - The parent OFile. + @param PtrDirEnt - The directory entry that is loaded. + + @retval EFI_SUCCESS - Load the directory entry successfully. + @retval EFI_OUT_OF_RESOURCES - Out of resource. + @return other - An error occurred when reading the directory entries. + +**/ +STATIC +EFI_STATUS +FatLoadNextDirEnt ( + IN FAT_OFILE *OFile, + OUT FAT_DIRENT **PtrDirEnt + ) +{ + EFI_STATUS Status; + FAT_DIRENT *DirEnt; + FAT_ODIR *ODir; + FAT_DIRECTORY_ENTRY Entry; + + ODir = OFile->ODir; + // + // Make sure the parent's directory has been opened + // + ASSERT (ODir != NULL); + // + // Assert we have not reached the end of directory + // + ASSERT (!ODir->EndOfDir); + DirEnt = NULL; + + for (;;) { + // + // Read the next directory entry until we find a valid directory entry (excluding lfn entry) + // + Status = FatAccessEntry (OFile, ReadData, ODir->CurrentEndPos, &Entry); + if (EFI_ERROR (Status)) { + return Status; + } + + if (((UINT8) Entry.FileName[0] != DELETE_ENTRY_MARK) && (Entry.Attributes & FAT_ATTRIBUTE_VOLUME_ID) == 0) { + // + // We get a valid directory entry, then handle it + // + break; + } + + ODir->CurrentEndPos++; + } + + if (Entry.FileName[0] != EMPTY_ENTRY_MARK) { + // + // Although FAT spec states this field is always 0 for FAT12 & FAT16, some applications + // might use it for some special usage, it is safer to zero it in memory for FAT12 & FAT16. + // + if (OFile->Volume->FatType != Fat32) { + Entry.FileClusterHigh = 0; + } + + // + // This is a valid directory entry + // + DirEnt = AllocateZeroPool (sizeof (FAT_DIRENT)); + if (DirEnt == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DirEnt->Signature = FAT_DIRENT_SIGNATURE; + // + // Remember the directory's entry position on disk + // + DirEnt->EntryPos = (UINT16) ODir->CurrentEndPos; + CopyMem (&DirEnt->Entry, &Entry, sizeof (FAT_DIRECTORY_ENTRY)); + FatLoadLongNameEntry (OFile, DirEnt); + if (DirEnt->FileString == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + // + // Add this directory entry to directory + // + FatAddDirEnt (ODir, DirEnt); + // + // Point to next directory entry + // + ODir->CurrentEndPos++; + } else { + ODir->EndOfDir = TRUE; + } + + *PtrDirEnt = DirEnt; + return EFI_SUCCESS; + +Done: + FatFreeDirEnt (DirEnt); + return Status; +} + +/** + + Get the directory entry's info into Buffer. + + @param Volume - FAT file system volume. + @param DirEnt - The corresponding directory entry. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing file info. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. + +**/ +EFI_STATUS +FatGetDirEntInfo ( + IN FAT_VOLUME *Volume, + IN FAT_DIRENT *DirEnt, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + UINTN Size; + UINTN NameSize; + UINTN ResultSize; + UINTN Cluster; + EFI_STATUS Status; + EFI_FILE_INFO *Info; + FAT_DIRECTORY_ENTRY *Entry; + FAT_DATE_TIME FatLastAccess; + + ASSERT_VOLUME_LOCKED (Volume); + + Size = SIZE_OF_EFI_FILE_INFO; + NameSize = StrSize (DirEnt->FileString); + ResultSize = Size + NameSize; + + Status = EFI_BUFFER_TOO_SMALL; + if (*BufferSize >= ResultSize) { + Status = EFI_SUCCESS; + Entry = &DirEnt->Entry; + Info = Buffer; + Info->Size = ResultSize; + if ((Entry->Attributes & FAT_ATTRIBUTE_DIRECTORY) != 0) { + Cluster = (Entry->FileClusterHigh << 16) | Entry->FileCluster; + Info->PhysicalSize = FatPhysicalDirSize (Volume, Cluster); + Info->FileSize = Info->PhysicalSize; + } else { + Info->FileSize = Entry->FileSize; + Info->PhysicalSize = FatPhysicalFileSize (Volume, Entry->FileSize); + } + + ZeroMem (&FatLastAccess.Time, sizeof (FatLastAccess.Time)); + CopyMem (&FatLastAccess.Date, &Entry->FileLastAccess, sizeof (FatLastAccess.Date)); + FatFatTimeToEfiTime (&FatLastAccess, &Info->LastAccessTime); + FatFatTimeToEfiTime (&Entry->FileCreateTime, &Info->CreateTime); + FatFatTimeToEfiTime (&Entry->FileModificationTime, &Info->ModificationTime); + Info->Attribute = Entry->Attributes & EFI_FILE_VALID_ATTR; + CopyMem ((CHAR8 *) Buffer + Size, DirEnt->FileString, NameSize); + } + + *BufferSize = ResultSize; + return Status; +} + +/** + + Search the directory for the directory entry whose filename is FileNameString. + + @param OFile - The parent OFile whose directory is to be searched. + @param FileNameString - The filename to be searched. + @param PtrDirEnt - pointer to the directory entry if found. + + @retval EFI_SUCCESS - Find the directory entry or not found. + @return other - An error occurred when reading the directory entries. + +**/ +STATIC +EFI_STATUS +FatSearchODir ( + IN FAT_OFILE *OFile, + IN CHAR16 *FileNameString, + OUT FAT_DIRENT **PtrDirEnt + ) +{ + BOOLEAN PossibleShortName; + CHAR8 File8Dot3Name[FAT_NAME_LEN]; + FAT_ODIR *ODir; + FAT_DIRENT *DirEnt; + EFI_STATUS Status; + + ODir = OFile->ODir; + ASSERT (ODir != NULL); + // + // Check if the file name is a valid short name + // + PossibleShortName = FatCheckIs8Dot3Name (FileNameString, File8Dot3Name); + // + // Search the hash table first + // + DirEnt = *FatLongNameHashSearch (ODir, FileNameString); + if (DirEnt == NULL && PossibleShortName) { + DirEnt = *FatShortNameHashSearch (ODir, File8Dot3Name); + } + if (DirEnt == NULL) { + // + // We fail to get the directory entry from hash table; we then + // search the rest directory + // + while (!ODir->EndOfDir) { + Status = FatLoadNextDirEnt (OFile, &DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + + if (DirEnt != NULL) { + if (FatStriCmp (FileNameString, DirEnt->FileString) == 0) { + break; + } + + if (PossibleShortName && CompareMem (File8Dot3Name, DirEnt->Entry.FileName, FAT_NAME_LEN) == 0) { + break; + } + } + } + } + + *PtrDirEnt = DirEnt; + return EFI_SUCCESS; +} + +/** + + Set the OFile's current directory cursor to the list head. + + @param OFile - The directory OFile whose directory cursor is reset. + +**/ +VOID +FatResetODirCursor ( + IN FAT_OFILE *OFile + ) +{ + FAT_ODIR *ODir; + + ODir = OFile->ODir; + ASSERT (ODir != NULL); + ODir->CurrentCursor = &(ODir->ChildList); + ODir->CurrentPos = 0; +} + +/** + + Set the directory's cursor to the next and get the next directory entry. + + @param OFile - The parent OFile. + @param PtrDirEnt - The next directory entry. + + @retval EFI_SUCCESS - We get the next directory entry successfully. + @return other - An error occurred when get next directory entry. + +**/ +EFI_STATUS +FatGetNextDirEnt ( + IN FAT_OFILE *OFile, + OUT FAT_DIRENT **PtrDirEnt + ) +{ + EFI_STATUS Status; + FAT_DIRENT *DirEnt; + FAT_ODIR *ODir; + + ODir = OFile->ODir; + ASSERT (ODir != NULL); + if (ODir->CurrentCursor->ForwardLink == &ODir->ChildList) { + // + // End of directory, we will try one more time + // + if (!ODir->EndOfDir) { + // + // Read directory from disk + // + Status = FatLoadNextDirEnt (OFile, &DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + + if (ODir->CurrentCursor->ForwardLink == &ODir->ChildList) { + // + // End of directory, return NULL + // + DirEnt = NULL; + ODir->CurrentPos = ODir->CurrentEndPos; + } else { + ODir->CurrentCursor = ODir->CurrentCursor->ForwardLink; + DirEnt = DIRENT_FROM_LINK (ODir->CurrentCursor); + ODir->CurrentPos = DirEnt->EntryPos + 1; + } + + *PtrDirEnt = DirEnt; + return EFI_SUCCESS; +} + +/** + + Set the directory entry count according to the filename. + + @param OFile - The corresponding OFile. + @param DirEnt - The directory entry to be set. + +**/ +STATIC +VOID +FatSetEntryCount ( + IN FAT_OFILE *OFile, + IN FAT_DIRENT *DirEnt + ) +{ + CHAR16 *FileString; + CHAR8 *File8Dot3Name; + + // + // Get new entry count and set the 8.3 name + // + DirEnt->EntryCount = 1; + FileString = DirEnt->FileString; + File8Dot3Name = DirEnt->Entry.FileName; + SetMem (File8Dot3Name, FAT_NAME_LEN, ' '); + if (StrCmp (FileString, L".") == 0) { + // + // "." entry + // + File8Dot3Name[0] = '.'; + FatCloneDirEnt (DirEnt, OFile->DirEnt); + } else if (StrCmp (FileString, L"..") == 0) { + // + // ".." entry + // + File8Dot3Name[0] = '.'; + File8Dot3Name[1] = '.'; + FatCloneDirEnt (DirEnt, OFile->Parent->DirEnt); + } else { + // + // Normal name + // + if (FatCheckIs8Dot3Name (FileString, File8Dot3Name)) { + // + // This file name is a valid 8.3 file name, we need to further check its case flag + // + FatSetCaseFlag (DirEnt); + } else { + // + // The file name is not a valid 8.3 name we need to generate an 8.3 name for it + // + FatCreate8Dot3Name (OFile, DirEnt); + DirEnt->EntryCount = (UINT8)(LFN_ENTRY_NUMBER (StrLen (FileString)) + DirEnt->EntryCount); + } + } +} + +/** + + Append a zero cluster to the current OFile. + + @param OFile - The directory OFile which needs to be updated. + + @retval EFI_SUCCESS - Append a zero cluster to the OFile successfully. + @return other - An error occurred when appending the zero cluster. + +**/ +STATIC +EFI_STATUS +FatExpandODir ( + IN FAT_OFILE *OFile + ) +{ + return FatExpandOFile (OFile, OFile->FileSize + OFile->Volume->ClusterSize); +} + +/** + + Search the Root OFile for the possible volume label. + + @param Root - The Root OFile. + @param DirEnt - The returned directory entry of volume label. + + @retval EFI_SUCCESS - The search process is completed successfully. + @return other - An error occurred when searching volume label. + +**/ +STATIC +EFI_STATUS +FatSeekVolumeId ( + IN FAT_OFILE *Root, + OUT FAT_DIRENT *DirEnt + ) +{ + EFI_STATUS Status; + UINTN EntryPos; + FAT_DIRECTORY_ENTRY *Entry; + + EntryPos = 0; + Entry = &DirEnt->Entry; + DirEnt->Invalid = TRUE; + do { + Status = FatAccessEntry (Root, ReadData, EntryPos, Entry); + if (EFI_ERROR (Status)) { + return Status; + } + + if (((UINT8) Entry->FileName[0] != DELETE_ENTRY_MARK) && (((Entry->Attributes) & (~FAT_ATTRIBUTE_ARCHIVE)) == FAT_ATTRIBUTE_VOLUME_ID)) { + DirEnt->EntryPos = (UINT16) EntryPos; + DirEnt->EntryCount = 1; + DirEnt->Invalid = FALSE; + break; + } + + EntryPos++; + } while (Entry->FileName[0] != EMPTY_ENTRY_MARK); + return EFI_SUCCESS; +} + +/** + + Use First Fit Algorithm to insert directory entry. + Only this function will erase "E5" entries in a directory. + In view of safest recovery, this function will only be triggered + when maximum directory entry number has reached. + + @param OFile - The corresponding OFile. + @param DirEnt - The directory entry to be inserted. + + @retval EFI_SUCCESS - The directory entry has been successfully inserted. + @retval EFI_VOLUME_FULL - The directory can not hold more directory entries. + @return Others - Some error occurred when inserting new directory entries. + +**/ +STATIC +EFI_STATUS +FatFirstFitInsertDirEnt ( + IN FAT_OFILE *OFile, + IN FAT_DIRENT *DirEnt + ) +{ + EFI_STATUS Status; + FAT_ODIR *ODir; + LIST_ENTRY *CurrentEntry; + FAT_DIRENT *CurrentDirEnt; + UINT32 CurrentPos; + UINT32 LabelPos; + UINT32 NewEntryPos; + UINT16 EntryCount; + FAT_DIRENT LabelDirEnt; + + LabelPos = 0; + if (OFile->Parent == NULL) { + Status = FatSeekVolumeId (OFile, &LabelDirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!LabelDirEnt.Invalid) { + LabelPos = LabelDirEnt.EntryPos; + } + } + + EntryCount = DirEnt->EntryCount; + NewEntryPos = EntryCount; + CurrentPos = 0; + ODir = OFile->ODir; + for (CurrentEntry = ODir->ChildList.ForwardLink; + CurrentEntry != &ODir->ChildList; + CurrentEntry = CurrentEntry->ForwardLink + ) { + CurrentDirEnt = DIRENT_FROM_LINK (CurrentEntry); + if (NewEntryPos + CurrentDirEnt->EntryCount <= CurrentDirEnt->EntryPos) { + if (LabelPos > NewEntryPos || LabelPos <= CurrentPos) { + // + // first fit succeeded + // + goto Done; + } + } + + CurrentPos = CurrentDirEnt->EntryPos; + NewEntryPos = CurrentPos + EntryCount; + } + + if (NewEntryPos >= ODir->CurrentEndPos) { + return EFI_VOLUME_FULL; + } + +Done: + DirEnt->EntryPos = (UINT16) NewEntryPos; + DirEnt->Link.BackLink = CurrentEntry; + return EFI_SUCCESS; +} + +/** + + Find the new directory entry position for the directory entry. + + @param OFile - The corresponding OFile. + @param DirEnt - The directory entry whose new position is to be set. + + @retval EFI_SUCCESS - The new directory entry position is successfully found. + @retval EFI_VOLUME_FULL - The directory has reach its maximum capacity. + @return other - An error occurred when reading the directory entry. + +**/ +STATIC +EFI_STATUS +FatNewEntryPos ( + IN FAT_OFILE *OFile, + IN FAT_DIRENT *DirEnt + ) +{ + EFI_STATUS Status; + FAT_ODIR *ODir; + FAT_DIRENT *TempDirEnt; + UINT32 NewEndPos; + + ODir = OFile->ODir; + ASSERT (ODir != NULL); + // + // Make sure the whole directory has been loaded + // + while (!ODir->EndOfDir) { + Status = FatLoadNextDirEnt (OFile, &TempDirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + } + // + // We will append this entry to the end of directory + // + FatGetCurrentFatTime (&DirEnt->Entry.FileCreateTime); + CopyMem (&DirEnt->Entry.FileModificationTime, &DirEnt->Entry.FileCreateTime, sizeof (FAT_DATE_TIME)); + CopyMem (&DirEnt->Entry.FileLastAccess, &DirEnt->Entry.FileCreateTime.Date, sizeof (FAT_DATE)); + NewEndPos = ODir->CurrentEndPos + DirEnt->EntryCount; + if (NewEndPos * sizeof (FAT_DIRECTORY_ENTRY) > OFile->FileSize) { + if (NewEndPos >= (OFile->IsFixedRootDir ? OFile->Volume->RootEntries : FAT_MAX_DIRENTRY_COUNT)) { + // + // We try to use fist fit algorithm to insert this directory entry + // + return FatFirstFitInsertDirEnt (OFile, DirEnt); + } + // + // We should allocate a new cluster for this directory + // + Status = FatExpandODir (OFile); + if (EFI_ERROR (Status)) { + return Status; + } + } + // + // We append our directory entry at the end of directory file + // + ODir->CurrentEndPos = NewEndPos; + DirEnt->EntryPos = (UINT16) (ODir->CurrentEndPos - 1); + return EFI_SUCCESS; +} + +/** + + Get the directory entry for the volume. + + @param Volume - FAT file system volume. + @param Name - The file name of the volume. + + @retval EFI_SUCCESS - Update the volume with the directory entry successfully. + @return others - An error occurred when getting volume label. + +**/ +EFI_STATUS +FatGetVolumeEntry ( + IN FAT_VOLUME *Volume, + IN CHAR16 *Name + ) +{ + EFI_STATUS Status; + FAT_DIRENT LabelDirEnt; + + *Name = 0; + Status = FatSeekVolumeId (Volume->Root, &LabelDirEnt); + if (!EFI_ERROR (Status)) { + if (!LabelDirEnt.Invalid) { + FatNameToStr (LabelDirEnt.Entry.FileName, FAT_NAME_LEN, FALSE, Name); + } + } + + return Status; +} + +/** + + Set the relevant directory entry into disk for the volume. + + @param Volume - FAT file system volume. + @param Name - The new file name of the volume. + + @retval EFI_SUCCESS - Update the Volume successfully. + @retval EFI_UNSUPPORTED - The input label is not a valid volume label. + @return other - An error occurred when setting volume label. + +**/ +EFI_STATUS +FatSetVolumeEntry ( + IN FAT_VOLUME *Volume, + IN CHAR16 *Name + ) +{ + EFI_STATUS Status; + FAT_DIRENT LabelDirEnt; + FAT_OFILE *Root; + + Root = Volume->Root; + Status = FatSeekVolumeId (Volume->Root, &LabelDirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + + if (LabelDirEnt.Invalid) { + // + // If there is not the relevant directory entry, create a new one + // + ZeroMem (&LabelDirEnt, sizeof (FAT_DIRENT)); + LabelDirEnt.EntryCount = 1; + Status = FatNewEntryPos (Root, &LabelDirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + + LabelDirEnt.Entry.Attributes = FAT_ATTRIBUTE_VOLUME_ID; + } + + SetMem (LabelDirEnt.Entry.FileName, FAT_NAME_LEN, ' '); + if (FatStrToFat (Name, FAT_NAME_LEN, LabelDirEnt.Entry.FileName)) { + return EFI_UNSUPPORTED; + } + + FatGetCurrentFatTime (&LabelDirEnt.Entry.FileModificationTime); + return FatStoreDirEnt (Root, &LabelDirEnt); +} + +/** + + Create "." and ".." directory entries in the newly-created parent OFile. + + @param OFile - The parent OFile. + + @retval EFI_SUCCESS - The dot directory entries are successfully created. + @return other - An error occurred when creating the directory entry. + +**/ +EFI_STATUS +FatCreateDotDirEnts ( + IN FAT_OFILE *OFile + ) +{ + EFI_STATUS Status; + FAT_DIRENT *DirEnt; + + Status = FatExpandODir (OFile); + if (EFI_ERROR (Status)) { + return Status; + } + + FatSetDirEntCluster (OFile); + // + // Create "." + // + Status = FatCreateDirEnt (OFile, L".", FAT_ATTRIBUTE_DIRECTORY, &DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Create ".." + // + Status = FatCreateDirEnt (OFile, L"..", FAT_ATTRIBUTE_DIRECTORY, &DirEnt); + return Status; +} + +/** + + Create a directory entry in the parent OFile. + + @param OFile - The parent OFile. + @param FileName - The filename of the newly-created directory entry. + @param Attributes - The attribute of the newly-created directory entry. + @param PtrDirEnt - The pointer to the newly-created directory entry. + + @retval EFI_SUCCESS - The directory entry is successfully created. + @retval EFI_OUT_OF_RESOURCES - Not enough memory to create the directory entry. + @return other - An error occurred when creating the directory entry. + +**/ +EFI_STATUS +FatCreateDirEnt ( + IN FAT_OFILE *OFile, + IN CHAR16 *FileName, + IN UINT8 Attributes, + OUT FAT_DIRENT **PtrDirEnt + ) +{ + FAT_DIRENT *DirEnt; + FAT_ODIR *ODir; + EFI_STATUS Status; + + ASSERT (OFile != NULL); + ODir = OFile->ODir; + ASSERT (ODir != NULL); + DirEnt = AllocateZeroPool (sizeof (FAT_DIRENT)); + if (DirEnt == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DirEnt->Signature = FAT_DIRENT_SIGNATURE; + DirEnt->FileString = AllocateCopyPool (StrSize (FileName), FileName); + if (DirEnt->FileString == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + // + // Determine how many directory entries we need + // + FatSetEntryCount (OFile, DirEnt); + // + // Determine the file's directory entry position + // + Status = FatNewEntryPos (OFile, DirEnt); + if (EFI_ERROR (Status)) { + goto Done; + } + + FatAddDirEnt (ODir, DirEnt); + DirEnt->Entry.Attributes = Attributes; + *PtrDirEnt = DirEnt; + DEBUG ((EFI_D_INFO, "FSOpen: Created new directory entry '%S'\n", DirEnt->FileString)); + return FatStoreDirEnt (OFile, DirEnt); + +Done: + FatFreeDirEnt (DirEnt); + return Status; +} + +/** + + Remove this directory entry node from the list of directory entries and hash table. + + @param OFile - The parent OFile. + @param DirEnt - The directory entry to be removed. + + @retval EFI_SUCCESS - The directory entry is successfully removed. + @return other - An error occurred when removing the directory entry. + +**/ +EFI_STATUS +FatRemoveDirEnt ( + IN FAT_OFILE *OFile, + IN FAT_DIRENT *DirEnt + ) +{ + FAT_ODIR *ODir; + + ODir = OFile->ODir; + if (ODir->CurrentCursor == &DirEnt->Link) { + // + // Move the directory cursor to its previous directory entry + // + ODir->CurrentCursor = ODir->CurrentCursor->BackLink; + } + // + // Remove from directory entry list + // + RemoveEntryList (&DirEnt->Link); + // + // Remove from hash table + // + FatDeleteFromHashTable (ODir, DirEnt); + DirEnt->Entry.FileName[0] = DELETE_ENTRY_MARK; + DirEnt->Invalid = TRUE; + return FatStoreDirEnt (OFile, DirEnt); +} + +/** + + Open the directory entry to get the OFile. + + @param Parent - The parent OFile. + @param DirEnt - The directory entry to be opened. + + @retval EFI_SUCCESS - The directory entry is successfully opened. + @retval EFI_OUT_OF_RESOURCES - not enough memory to allocate a new OFile. + @return other - An error occurred when opening the directory entry. + +**/ +EFI_STATUS +FatOpenDirEnt ( + IN FAT_OFILE *Parent, + IN FAT_DIRENT *DirEnt + ) +{ + FAT_OFILE *OFile; + FAT_VOLUME *Volume; + + if (DirEnt->OFile == NULL) { + // + // Open the directory entry + // + OFile = AllocateZeroPool (sizeof (FAT_OFILE)); + if (OFile == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OFile->Signature = FAT_OFILE_SIGNATURE; + InitializeListHead (&OFile->Opens); + InitializeListHead (&OFile->ChildHead); + OFile->Parent = Parent; + OFile->DirEnt = DirEnt; + if (Parent != NULL) { + // + // The newly created OFile is not root + // + Volume = Parent->Volume; + OFile->FullPathLen = Parent->FullPathLen + 1 + StrLen (DirEnt->FileString); + OFile->FileCluster = ((DirEnt->Entry.FileClusterHigh) << 16) | (DirEnt->Entry.FileCluster); + InsertTailList (&Parent->ChildHead, &OFile->ChildLink); + } else { + // + // The newly created OFile is root + // + Volume = VOLUME_FROM_ROOT_DIRENT (DirEnt); + Volume->Root = OFile; + OFile->FileCluster = Volume->RootCluster; + if (Volume->FatType != Fat32) { + OFile->IsFixedRootDir = TRUE; + } + } + + OFile->FileCurrentCluster = OFile->FileCluster; + OFile->Volume = Volume; + InsertHeadList (&Volume->CheckRef, &OFile->CheckLink); + + OFile->FileSize = DirEnt->Entry.FileSize; + if ((DirEnt->Entry.Attributes & FAT_ATTRIBUTE_DIRECTORY) != 0) { + if (OFile->IsFixedRootDir) { + OFile->FileSize = Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY); + } else { + OFile->FileSize = FatPhysicalDirSize (Volume, OFile->FileCluster); + } + + FatRequestODir (OFile); + if (OFile->ODir == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + DirEnt->OFile = OFile; + } + + return EFI_SUCCESS; +} + +/** + + Close the directory entry and free the OFile. + + @param DirEnt - The directory entry to be closed. + +**/ +VOID +FatCloseDirEnt ( + IN FAT_DIRENT *DirEnt + ) +{ + FAT_OFILE *OFile; + FAT_VOLUME *Volume; + + OFile = DirEnt->OFile; + ASSERT (OFile != NULL); + Volume = OFile->Volume; + + if (OFile->ODir != NULL) { + FatDiscardODir (OFile); + } + + if (OFile->Parent == NULL) { + Volume->Root = NULL; + } else { + RemoveEntryList (&OFile->ChildLink); + } + + FreePool (OFile); + DirEnt->OFile = NULL; + if (DirEnt->Invalid == TRUE) { + // + // Free directory entry itself + // + FatFreeDirEnt (DirEnt); + } +} + +/** + + Traverse filename and open all OFiles that can be opened. + Update filename pointer to the component that can't be opened. + If more than one name component remains, returns an error; + otherwise, return the remaining name component so that the caller might choose to create it. + + @param PtrOFile - As input, the reference OFile; as output, the located OFile. + @param FileName - The file name relevant to the OFile. + @param Attributes - The attribute of the destination OFile. + @param NewFileName - The remaining file name. + + @retval EFI_NOT_FOUND - The file name can't be opened and there is more than one + components within the name left (this means the name can + not be created either). + @retval EFI_INVALID_PARAMETER - The parameter is not valid. + @retval EFI_SUCCESS - Open the file successfully. + @return other - An error occurred when locating the OFile. + +**/ +EFI_STATUS +FatLocateOFile ( + IN OUT FAT_OFILE **PtrOFile, + IN CHAR16 *FileName, + IN UINT8 Attributes, + OUT CHAR16 *NewFileName + ) +{ + EFI_STATUS Status; + FAT_VOLUME *Volume; + CHAR16 ComponentName[EFI_PATH_STRING_LENGTH]; + UINTN FileNameLen; + BOOLEAN DirIntended; + CHAR16 *Next; + FAT_OFILE *OFile; + FAT_DIRENT *DirEnt; + + DirEnt = NULL; + + FileNameLen = StrLen (FileName); + if (FileNameLen == 0) { + return EFI_INVALID_PARAMETER; + } + + OFile = *PtrOFile; + Volume = OFile->Volume; + + DirIntended = FALSE; + if (FileName[FileNameLen - 1] == PATH_NAME_SEPARATOR) { + DirIntended = TRUE; + } + // + // If name starts with path name separator, then move to root OFile + // + if (*FileName == PATH_NAME_SEPARATOR) { + OFile = Volume->Root; + FileName++; + FileNameLen--; + } + // + // Per FAT Spec the file name should meet the following criteria: + // C1. Length (FileLongName) <= 255 + // C2. Length (X:FileFullPath<NUL>) <= 260 + // Here we check C2 first. + // + if (2 + OFile->FullPathLen + 1 + FileNameLen + 1 > EFI_PATH_STRING_LENGTH) { + // + // Full path length can not surpass 256 + // + return EFI_INVALID_PARAMETER; + } + // + // Start at current location + // + Next = FileName; + for (;;) { + // + // Get the next component name + // + FileName = Next; + Next = FatGetNextNameComponent (FileName, ComponentName); + + // + // If end of the file name, we're done + // + if (ComponentName[0] == 0) { + if (DirIntended && OFile->ODir == NULL) { + return EFI_NOT_FOUND; + } + + NewFileName[0] = 0; + break; + } + // + // If "dot", then current + // + if (StrCmp (ComponentName, L".") == 0) { + continue; + } + // + // If "dot dot", then parent + // + if (StrCmp (ComponentName, L"..") == 0) { + if (OFile->Parent == NULL) { + return EFI_INVALID_PARAMETER; + } + OFile = OFile->Parent; + continue; + } + + if (!FatFileNameIsValid (ComponentName, NewFileName)) { + return EFI_INVALID_PARAMETER; + } + // + // We have a component name, try to open it + // + if (OFile->ODir == NULL) { + // + // This file isn't a directory, can't open it + // + return EFI_NOT_FOUND; + } + // + // Search the compName in the directory + // + Status = FatSearchODir (OFile, NewFileName, &DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + + if (DirEnt == NULL) { + // + // component name is not found in the directory + // + if (*Next != 0) { + return EFI_NOT_FOUND; + } + + if (DirIntended && (Attributes & FAT_ATTRIBUTE_DIRECTORY) == 0) { + return EFI_INVALID_PARAMETER; + } + // + // It's the last component name - return with the open + // path and the remaining name + // + break; + } + + Status = FatOpenDirEnt (OFile, DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + + OFile = DirEnt->OFile; + } + + *PtrOFile = OFile; + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DiskCache.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DiskCache.c new file mode 100644 index 00000000..51ab9d3e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DiskCache.c @@ -0,0 +1,489 @@ +/** @file + Cache implementation for EFI FAT File system driver. + +Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +/** + + This function is used by the Data Cache. + + When this function is called by write command, all entries in this range + are older than the contents in disk, so they are invalid; just mark them invalid. + + When this function is called by read command, if any entry in this range + is dirty, it means that the relative info directly read from media is older than + than the info in the cache; So need to update the relative info in the Buffer. + + @param Volume - FAT file system volume. + @param IoMode - This function is called by read command or write command + @param StartPageNo - First PageNo to be checked in the cache. + @param EndPageNo - Last PageNo to be checked in the cache. + @param Buffer - The user buffer need to update. Only when doing the read command + and there is dirty cache in the cache range, this parameter will be used. + +**/ +STATIC +VOID +FatFlushDataCacheRange ( + IN FAT_VOLUME *Volume, + IN IO_MODE IoMode, + IN UINTN StartPageNo, + IN UINTN EndPageNo, + OUT UINT8 *Buffer + ) +{ + UINTN PageNo; + UINTN GroupNo; + UINTN GroupMask; + UINTN PageSize; + UINT8 PageAlignment; + DISK_CACHE *DiskCache; + CACHE_TAG *CacheTag; + UINT8 *BaseAddress; + + DiskCache = &Volume->DiskCache[CacheData]; + BaseAddress = DiskCache->CacheBase; + GroupMask = DiskCache->GroupMask; + PageAlignment = DiskCache->PageAlignment; + PageSize = (UINTN)1 << PageAlignment; + + for (PageNo = StartPageNo; PageNo < EndPageNo; PageNo++) { + GroupNo = PageNo & GroupMask; + CacheTag = &DiskCache->CacheTag[GroupNo]; + if (CacheTag->RealSize > 0 && CacheTag->PageNo == PageNo) { + // + // When reading data form disk directly, if some dirty data + // in cache is in this rang, this data in the Buffer need to + // be updated with the cache's dirty data. + // + if (IoMode == ReadDisk) { + if (CacheTag->Dirty) { + CopyMem ( + Buffer + ((PageNo - StartPageNo) << PageAlignment), + BaseAddress + (GroupNo << PageAlignment), + PageSize + ); + } + } else { + // + // Make all valid entries in this range invalid. + // + CacheTag->RealSize = 0; + } + } + } +} + +/** + + Exchange the cache page with the image on the disk + + @param Volume - FAT file system volume. + @param DataType - Indicate the cache type. + @param IoMode - Indicate whether to load this page from disk or store this page to disk. + @param CacheTag - The Cache Tag for the current cache page. + @param Task point to task instance. + + @retval EFI_SUCCESS - Cache page exchanged successfully. + @return Others - An error occurred when exchanging cache page. + +**/ +STATIC +EFI_STATUS +FatExchangeCachePage ( + IN FAT_VOLUME *Volume, + IN CACHE_DATA_TYPE DataType, + IN IO_MODE IoMode, + IN CACHE_TAG *CacheTag, + IN FAT_TASK *Task + ) +{ + EFI_STATUS Status; + UINTN GroupNo; + UINTN PageNo; + UINTN WriteCount; + UINTN RealSize; + UINT64 EntryPos; + UINT64 MaxSize; + DISK_CACHE *DiskCache; + VOID *PageAddress; + UINT8 PageAlignment; + + DiskCache = &Volume->DiskCache[DataType]; + PageNo = CacheTag->PageNo; + GroupNo = PageNo & DiskCache->GroupMask; + PageAlignment = DiskCache->PageAlignment; + PageAddress = DiskCache->CacheBase + (GroupNo << PageAlignment); + EntryPos = DiskCache->BaseAddress + LShiftU64 (PageNo, PageAlignment); + RealSize = CacheTag->RealSize; + if (IoMode == ReadDisk) { + RealSize = (UINTN)1 << PageAlignment; + MaxSize = DiskCache->LimitAddress - EntryPos; + if (MaxSize < RealSize) { + DEBUG ((EFI_D_INFO, "FatDiskIo: Cache Page OutBound occurred! \n")); + RealSize = (UINTN) MaxSize; + } + } + + WriteCount = 1; + if (DataType == CacheFat && IoMode == WriteDisk) { + WriteCount = Volume->NumFats; + } + + do { + // + // Only fat table writing will execute more than once + // + Status = FatDiskIo (Volume, IoMode, EntryPos, RealSize, PageAddress, Task); + if (EFI_ERROR (Status)) { + return Status; + } + + EntryPos += Volume->FatSize; + } while (--WriteCount > 0); + + CacheTag->Dirty = FALSE; + CacheTag->RealSize = RealSize; + return EFI_SUCCESS; +} + +/** + + Get one cache page by specified PageNo. + + @param Volume - FAT file system volume. + @param CacheDataType - The cache type: CACHE_FAT or CACHE_DATA. + @param PageNo - PageNo to match with the cache. + @param CacheTag - The Cache Tag for the current cache page. + + @retval EFI_SUCCESS - Get the cache page successfully. + @return other - An error occurred when accessing data. + +**/ +STATIC +EFI_STATUS +FatGetCachePage ( + IN FAT_VOLUME *Volume, + IN CACHE_DATA_TYPE CacheDataType, + IN UINTN PageNo, + IN CACHE_TAG *CacheTag + ) +{ + EFI_STATUS Status; + UINTN OldPageNo; + + OldPageNo = CacheTag->PageNo; + if (CacheTag->RealSize > 0 && OldPageNo == PageNo) { + // + // Cache Hit occurred + // + return EFI_SUCCESS; + } + + // + // Write dirty cache page back to disk + // + if (CacheTag->RealSize > 0 && CacheTag->Dirty) { + Status = FatExchangeCachePage (Volume, CacheDataType, WriteDisk, CacheTag, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + } + // + // Load new data from disk; + // + CacheTag->PageNo = PageNo; + Status = FatExchangeCachePage (Volume, CacheDataType, ReadDisk, CacheTag, NULL); + + return Status; +} + +/** + + Read Length bytes from the position of Offset into Buffer, or + write Length bytes from Buffer into the position of Offset. + + @param Volume - FAT file system volume. + @param CacheDataType - The type of cache: CACHE_DATA or CACHE_FAT. + @param IoMode - Indicate the type of disk access. + @param PageNo - The number of unaligned cache page. + @param Offset - The starting byte of cache page. + @param Length - The number of bytes that is read or written + @param Buffer - Buffer containing cache data. + + @retval EFI_SUCCESS - The data was accessed correctly. + @return Others - An error occurred when accessing unaligned cache page. + +**/ +STATIC +EFI_STATUS +FatAccessUnalignedCachePage ( + IN FAT_VOLUME *Volume, + IN CACHE_DATA_TYPE CacheDataType, + IN IO_MODE IoMode, + IN UINTN PageNo, + IN UINTN Offset, + IN UINTN Length, + IN OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + VOID *Source; + VOID *Destination; + DISK_CACHE *DiskCache; + CACHE_TAG *CacheTag; + UINTN GroupNo; + + DiskCache = &Volume->DiskCache[CacheDataType]; + GroupNo = PageNo & DiskCache->GroupMask; + CacheTag = &DiskCache->CacheTag[GroupNo]; + Status = FatGetCachePage (Volume, CacheDataType, PageNo, CacheTag); + if (!EFI_ERROR (Status)) { + Source = DiskCache->CacheBase + (GroupNo << DiskCache->PageAlignment) + Offset; + Destination = Buffer; + if (IoMode != ReadDisk) { + CacheTag->Dirty = TRUE; + DiskCache->Dirty = TRUE; + Destination = Source; + Source = Buffer; + } + + CopyMem (Destination, Source, Length); + } + + return Status; +} + +/** + + Read BufferSize bytes from the position of Offset into Buffer, + or write BufferSize bytes from Buffer into the position of Offset. + + Base on the parameter of CACHE_DATA_TYPE, the data access will be divided into + the access of FAT cache (CACHE_FAT) and the access of Data cache (CACHE_DATA): + + 1. Access of FAT cache (CACHE_FAT): Access the data in the FAT cache, if there is cache + page hit, just return the cache page; else update the related cache page and return + the right cache page. + 2. Access of Data cache (CACHE_DATA): + The access data will be divided into UnderRun data, Aligned data and OverRun data; + The UnderRun data and OverRun data will be accessed by the Data cache, + but the Aligned data will be accessed with disk directly. + + @param Volume - FAT file system volume. + @param CacheDataType - The type of cache: CACHE_DATA or CACHE_FAT. + @param IoMode - Indicate the type of disk access. + @param Offset - The starting byte offset to read from. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing cache data. + @param Task point to task instance. + + @retval EFI_SUCCESS - The data was accessed correctly. + @retval EFI_MEDIA_CHANGED - The MediaId does not match the current device. + @return Others - An error occurred when accessing cache. + +**/ +EFI_STATUS +FatAccessCache ( + IN FAT_VOLUME *Volume, + IN CACHE_DATA_TYPE CacheDataType, + IN IO_MODE IoMode, + IN UINT64 Offset, + IN UINTN BufferSize, + IN OUT UINT8 *Buffer, + IN FAT_TASK *Task + ) +{ + EFI_STATUS Status; + UINTN PageSize; + UINTN UnderRun; + UINTN OverRun; + UINTN AlignedSize; + UINTN Length; + UINTN PageNo; + UINTN AlignedPageCount; + UINTN OverRunPageNo; + DISK_CACHE *DiskCache; + UINT64 EntryPos; + UINT8 PageAlignment; + + ASSERT (Volume->CacheBuffer != NULL); + + Status = EFI_SUCCESS; + DiskCache = &Volume->DiskCache[CacheDataType]; + EntryPos = Offset - DiskCache->BaseAddress; + PageAlignment = DiskCache->PageAlignment; + PageSize = (UINTN)1 << PageAlignment; + PageNo = (UINTN) RShiftU64 (EntryPos, PageAlignment); + UnderRun = ((UINTN) EntryPos) & (PageSize - 1); + + if (UnderRun > 0) { + Length = PageSize - UnderRun; + if (Length > BufferSize) { + Length = BufferSize; + } + + Status = FatAccessUnalignedCachePage (Volume, CacheDataType, IoMode, PageNo, UnderRun, Length, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + Buffer += Length; + BufferSize -= Length; + PageNo++; + } + + AlignedPageCount = BufferSize >> PageAlignment; + OverRunPageNo = PageNo + AlignedPageCount; + // + // The access of the Aligned data + // + if (AlignedPageCount > 0) { + // + // Accessing fat table cannot have alignment data + // + ASSERT (CacheDataType == CacheData); + + EntryPos = Volume->RootPos + LShiftU64 (PageNo, PageAlignment); + AlignedSize = AlignedPageCount << PageAlignment; + Status = FatDiskIo (Volume, IoMode, EntryPos, AlignedSize, Buffer, Task); + if (EFI_ERROR (Status)) { + return Status; + } + // + // If these access data over laps the relative cache range, these cache pages need + // to be updated. + // + FatFlushDataCacheRange (Volume, IoMode, PageNo, OverRunPageNo, Buffer); + Buffer += AlignedSize; + BufferSize -= AlignedSize; + } + // + // The access of the OverRun data + // + OverRun = BufferSize; + if (OverRun > 0) { + // + // Last read is not a complete page + // + Status = FatAccessUnalignedCachePage (Volume, CacheDataType, IoMode, OverRunPageNo, 0, OverRun, Buffer); + } + + return Status; +} + +/** + + Flush all the dirty cache back, include the FAT cache and the Data cache. + + @param Volume - FAT file system volume. + @param Task point to task instance. + + @retval EFI_SUCCESS - Flush all the dirty cache back successfully + @return other - An error occurred when writing the data into the disk + +**/ +EFI_STATUS +FatVolumeFlushCache ( + IN FAT_VOLUME *Volume, + IN FAT_TASK *Task + ) +{ + EFI_STATUS Status; + CACHE_DATA_TYPE CacheDataType; + UINTN GroupIndex; + UINTN GroupMask; + DISK_CACHE *DiskCache; + CACHE_TAG *CacheTag; + + for (CacheDataType = (CACHE_DATA_TYPE) 0; CacheDataType < CacheMaxType; CacheDataType++) { + DiskCache = &Volume->DiskCache[CacheDataType]; + if (DiskCache->Dirty) { + // + // Data cache or fat cache is dirty, write the dirty data back + // + GroupMask = DiskCache->GroupMask; + for (GroupIndex = 0; GroupIndex <= GroupMask; GroupIndex++) { + CacheTag = &DiskCache->CacheTag[GroupIndex]; + if (CacheTag->RealSize > 0 && CacheTag->Dirty) { + // + // Write back all Dirty Data Cache Page to disk + // + Status = FatExchangeCachePage (Volume, CacheDataType, WriteDisk, CacheTag, Task); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + + DiskCache->Dirty = FALSE; + } + } + // + // Flush the block device. + // + Status = Volume->BlockIo->FlushBlocks (Volume->BlockIo); + return Status; +} + +/** + + Initialize the disk cache according to Volume's FatType. + + @param Volume - FAT file system volume. + + @retval EFI_SUCCESS - The disk cache is successfully initialized. + @retval EFI_OUT_OF_RESOURCES - Not enough memory to allocate disk cache. + +**/ +EFI_STATUS +FatInitializeDiskCache ( + IN FAT_VOLUME *Volume + ) +{ + DISK_CACHE *DiskCache; + UINTN FatCacheGroupCount; + UINTN DataCacheSize; + UINTN FatCacheSize; + UINT8 *CacheBuffer; + + DiskCache = Volume->DiskCache; + // + // Configure the parameters of disk cache + // + if (Volume->FatType == Fat12) { + FatCacheGroupCount = FAT_FATCACHE_GROUP_MIN_COUNT; + DiskCache[CacheFat].PageAlignment = FAT_FATCACHE_PAGE_MIN_ALIGNMENT; + DiskCache[CacheData].PageAlignment = FAT_DATACACHE_PAGE_MIN_ALIGNMENT; + } else { + FatCacheGroupCount = FAT_FATCACHE_GROUP_MAX_COUNT; + DiskCache[CacheFat].PageAlignment = FAT_FATCACHE_PAGE_MAX_ALIGNMENT; + DiskCache[CacheData].PageAlignment = FAT_DATACACHE_PAGE_MAX_ALIGNMENT; + } + + DiskCache[CacheData].GroupMask = FAT_DATACACHE_GROUP_COUNT - 1; + DiskCache[CacheData].BaseAddress = Volume->RootPos; + DiskCache[CacheData].LimitAddress = Volume->VolumeSize; + DiskCache[CacheFat].GroupMask = FatCacheGroupCount - 1; + DiskCache[CacheFat].BaseAddress = Volume->FatPos; + DiskCache[CacheFat].LimitAddress = Volume->FatPos + Volume->FatSize; + FatCacheSize = FatCacheGroupCount << DiskCache[CacheFat].PageAlignment; + DataCacheSize = FAT_DATACACHE_GROUP_COUNT << DiskCache[CacheData].PageAlignment; + // + // Allocate the Fat Cache buffer + // + CacheBuffer = AllocateZeroPool (FatCacheSize + DataCacheSize); + if (CacheBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Volume->CacheBuffer = CacheBuffer; + DiskCache[CacheFat].CacheBase = CacheBuffer; + DiskCache[CacheData].CacheBase = CacheBuffer + FatCacheSize; + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.c new file mode 100644 index 00000000..40c5f343 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.c @@ -0,0 +1,525 @@ +/** @file + Fat File System driver routines that support EFI driver model. + +Copyright (c) 2005 - 2014, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +/** + + Register Driver Binding protocol for this driver. + + @param ImageHandle - Handle for the image of this driver. + @param SystemTable - Pointer to the EFI System Table. + + @retval EFI_SUCCESS - Driver loaded. + @return other - Driver not loaded. + +**/ +EFI_STATUS +EFIAPI +FatEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + + Unload function for this image. Uninstall DriverBinding protocol. + + @param ImageHandle - Handle for the image of this driver. + + @retval EFI_SUCCESS - Driver unloaded successfully. + @return other - Driver can not unloaded. + +**/ +EFI_STATUS +EFIAPI +FatUnload ( + IN EFI_HANDLE ImageHandle + ); + +/** + + Test to see if this driver can add a file system to ControllerHandle. + ControllerHandle must support both Disk IO and Block IO protocols. + + @param This - Protocol instance pointer. + @param ControllerHandle - Handle of device to test. + @param RemainingDevicePath - Not used. + + @retval EFI_SUCCESS - This driver supports this device. + @retval EFI_ALREADY_STARTED - This driver is already running on this device. + @return other - This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +FatDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + + Start this driver on ControllerHandle by opening a Block IO and Disk IO + protocol, reading Device Path. Add a Simple File System protocol to + ControllerHandle if the media contains a valid file system. + + @param This - Protocol instance pointer. + @param ControllerHandle - Handle of device to bind driver to. + @param RemainingDevicePath - Not used. + + @retval EFI_SUCCESS - This driver is added to DeviceHandle. + @retval EFI_ALREADY_STARTED - This driver is already running on DeviceHandle. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory. + @return other - This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +FatDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + + Stop this driver on ControllerHandle. + + @param This - Protocol instance pointer. + @param ControllerHandle - Handle of device to stop driver on. + @param NumberOfChildren - Not used. + @param ChildHandleBuffer - Not used. + + @retval EFI_SUCCESS - This driver is removed DeviceHandle. + @return other - This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +FatDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// DriverBinding protocol instance +// +EFI_DRIVER_BINDING_PROTOCOL gFatDriverBinding = { + FatDriverBindingSupported, + FatDriverBindingStart, + FatDriverBindingStop, + 0xa, + NULL, + NULL +}; + +/** + + Register Driver Binding protocol for this driver. + + @param ImageHandle - Handle for the image of this driver. + @param SystemTable - Pointer to the EFI System Table. + + @retval EFI_SUCCESS - Driver loaded. + @return other - Driver not loaded. + +**/ +EFI_STATUS +EFIAPI +FatEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Initialize the EFI Driver Library + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gFatDriverBinding, + ImageHandle, + &gFatComponentName, + &gFatComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + + Unload function for this image. Uninstall DriverBinding protocol. + + @param ImageHandle - Handle for the image of this driver. + + @retval EFI_SUCCESS - Driver unloaded successfully. + @return other - Driver can not unloaded. + +**/ +EFI_STATUS +EFIAPI +FatUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE *DeviceHandleBuffer; + UINTN DeviceHandleCount; + UINTN Index; + VOID *ComponentName; + VOID *ComponentName2; + + Status = gBS->LocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &DeviceHandleCount, + &DeviceHandleBuffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < DeviceHandleCount; Index++) { + Status = EfiTestManagedDevice (DeviceHandleBuffer[Index], ImageHandle, &gEfiDiskIoProtocolGuid); + if (!EFI_ERROR (Status)) { + Status = gBS->DisconnectController ( + DeviceHandleBuffer[Index], + ImageHandle, + NULL + ); + if (EFI_ERROR (Status)) { + break; + } + } + } + + if (Index == DeviceHandleCount) { + // + // Driver is stopped successfully. + // + Status = gBS->HandleProtocol (ImageHandle, &gEfiComponentNameProtocolGuid, &ComponentName); + if (EFI_ERROR (Status)) { + ComponentName = NULL; + } + + Status = gBS->HandleProtocol (ImageHandle, &gEfiComponentName2ProtocolGuid, &ComponentName2); + if (EFI_ERROR (Status)) { + ComponentName2 = NULL; + } + + if (ComponentName == NULL) { + if (ComponentName2 == NULL) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, &gFatDriverBinding, + NULL + ); + } else { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, &gFatDriverBinding, + &gEfiComponentName2ProtocolGuid, ComponentName2, + NULL + ); + } + } else { + if (ComponentName2 == NULL) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, &gFatDriverBinding, + &gEfiComponentNameProtocolGuid, ComponentName, + NULL + ); + } else { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, &gFatDriverBinding, + &gEfiComponentNameProtocolGuid, ComponentName, + &gEfiComponentName2ProtocolGuid, ComponentName2, + NULL + ); + } + } + } + + if (DeviceHandleBuffer != NULL) { + FreePool (DeviceHandleBuffer); + } + + return Status; +} + +/** + + Test to see if this driver can add a file system to ControllerHandle. + ControllerHandle must support both Disk IO and Block IO protocols. + + @param This - Protocol instance pointer. + @param ControllerHandle - Handle of device to test. + @param RemainingDevicePath - Not used. + + @retval EFI_SUCCESS - This driver supports this device. + @retval EFI_ALREADY_STARTED - This driver is already running on this device. + @return other - This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +FatDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DISK_IO_PROTOCOL *DiskIo; + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +/** + + Start this driver on ControllerHandle by opening a Block IO and Disk IO + protocol, reading Device Path. Add a Simple File System protocol to + ControllerHandle if the media contains a valid file system. + + @param This - Protocol instance pointer. + @param ControllerHandle - Handle of device to bind driver to. + @param RemainingDevicePath - Not used. + + @retval EFI_SUCCESS - This driver is added to DeviceHandle. + @retval EFI_ALREADY_STARTED - This driver is already running on DeviceHandle. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory. + @return other - This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +FatDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DISK_IO2_PROTOCOL *DiskIo2; + BOOLEAN LockedByMe; + + LockedByMe = FALSE; + // + // Acquire the lock. + // If caller has already acquired the lock, cannot lock it again. + // + Status = FatAcquireLockOrFail (); + if (!EFI_ERROR (Status)) { + LockedByMe = TRUE; + } + + Status = InitializeUnicodeCollationSupport (This->DriverBindingHandle); + if (EFI_ERROR (Status)) { + goto Exit; + } + // + // Open our required BlockIo and DiskIo + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + (VOID **) &DiskIo2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + DiskIo2 = NULL; + } + + // + // Allocate Volume structure. In FatAllocateVolume(), Resources + // are allocated with protocol installed and cached initialized + // + Status = FatAllocateVolume (ControllerHandle, DiskIo, DiskIo2, BlockIo); + + // + // When the media changes on a device it will Reinstall the BlockIo interface. + // This will cause a call to our Stop(), and a subsequent reentrant call to our + // Start() successfully. We should leave the device open when this happen. + // + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + } + } + +Exit: + // + // Unlock if locked by myself. + // + if (LockedByMe) { + FatReleaseLock (); + } + return Status; +} + +/** + + Stop this driver on ControllerHandle. + + @param This - Protocol instance pointer. + @param ControllerHandle - Handle of device to stop driver on. + @param NumberOfChildren - Not used. + @param ChildHandleBuffer - Not used. + + @retval EFI_SUCCESS - This driver is removed DeviceHandle. + @return other - This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +FatDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem; + FAT_VOLUME *Volume; + EFI_DISK_IO2_PROTOCOL *DiskIo2; + + DiskIo2 = NULL; + // + // Get our context back + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, + (VOID **) &FileSystem, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + Volume = VOLUME_FROM_VOL_INTERFACE (FileSystem); + DiskIo2 = Volume->DiskIo2; + Status = FatAbandonVolume (Volume); + } + + if (!EFI_ERROR (Status)) { + if (DiskIo2 != NULL) { + Status = gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + ASSERT_EFI_ERROR (Status); + } + Status = gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + ASSERT_EFI_ERROR (Status); + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.h b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.h new file mode 100644 index 00000000..a04a5a0f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.h @@ -0,0 +1,2008 @@ +/** @file + Main header file for EFI FAT file system driver. + +Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _FAT_H_ +#define _FAT_H_ + +#include <Uefi.h> + +#include <Guid/FileInfo.h> +#include <Guid/FileSystemInfo.h> +#include <Guid/FileSystemVolumeLabelInfo.h> +#include <Protocol/BlockIo.h> +#include <Protocol/DiskIo.h> +#include <Protocol/DiskIo2.h> +#include <Protocol/SimpleFileSystem.h> +#include <Protocol/UnicodeCollation.h> + +#include <Library/PcdLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> + +#include "FatFileSystem.h" + +// +// The FAT signature +// +#define FAT_VOLUME_SIGNATURE SIGNATURE_32 ('f', 'a', 't', 'v') +#define FAT_IFILE_SIGNATURE SIGNATURE_32 ('f', 'a', 't', 'i') +#define FAT_ODIR_SIGNATURE SIGNATURE_32 ('f', 'a', 't', 'd') +#define FAT_DIRENT_SIGNATURE SIGNATURE_32 ('f', 'a', 't', 'e') +#define FAT_OFILE_SIGNATURE SIGNATURE_32 ('f', 'a', 't', 'o') +#define FAT_TASK_SIGNATURE SIGNATURE_32 ('f', 'a', 't', 'T') +#define FAT_SUBTASK_SIGNATURE SIGNATURE_32 ('f', 'a', 't', 'S') + +#define ASSERT_VOLUME_LOCKED(a) ASSERT_LOCKED (&FatFsLock) + +#define IFILE_FROM_FHAND(a) CR (a, FAT_IFILE, Handle, FAT_IFILE_SIGNATURE) + +#define DIRENT_FROM_LINK(a) CR (a, FAT_DIRENT, Link, FAT_DIRENT_SIGNATURE) + +#define VOLUME_FROM_ROOT_DIRENT(a) CR (a, FAT_VOLUME, RootDirEnt, FAT_VOLUME_SIGNATURE) + +#define VOLUME_FROM_VOL_INTERFACE(a) CR (a, FAT_VOLUME, VolumeInterface, FAT_VOLUME_SIGNATURE); + +#define ODIR_FROM_DIRCACHELINK(a) CR (a, FAT_ODIR, DirCacheLink, FAT_ODIR_SIGNATURE) + +#define OFILE_FROM_CHECKLINK(a) CR (a, FAT_OFILE, CheckLink, FAT_OFILE_SIGNATURE) + +#define OFILE_FROM_CHILDLINK(a) CR (a, FAT_OFILE, ChildLink, FAT_OFILE_SIGNATURE) + +// +// Minimum sector size is 512B, Maximum sector size is 4096B +// Max sectors per cluster is 128 +// +#define MAX_BLOCK_ALIGNMENT 12 +#define MIN_BLOCK_ALIGNMENT 9 +#define MAX_SECTORS_PER_CLUSTER_ALIGNMENT 7 + +// +// Efi Time Definition +// +#define IS_LEAP_YEAR(a) (((a) % 4 == 0) && (((a) % 100 != 0) || ((a) % 400 == 0))) + +// +// Minimum fat page size is 8K, maximum fat page alignment is 32K +// Minimum data page size is 8K, maximum fat page alignment is 64K +// +#define FAT_FATCACHE_PAGE_MIN_ALIGNMENT 13 +#define FAT_FATCACHE_PAGE_MAX_ALIGNMENT 15 +#define FAT_DATACACHE_PAGE_MIN_ALIGNMENT 13 +#define FAT_DATACACHE_PAGE_MAX_ALIGNMENT 16 +#define FAT_DATACACHE_GROUP_COUNT 64 +#define FAT_FATCACHE_GROUP_MIN_COUNT 1 +#define FAT_FATCACHE_GROUP_MAX_COUNT 16 + +// +// Used in 8.3 generation algorithm +// +#define MAX_SPEC_RETRY 4 +#define SPEC_BASE_TAG_LEN 6 +#define HASH_BASE_TAG_LEN 2 +#define HASH_VALUE_TAG_LEN (SPEC_BASE_TAG_LEN - HASH_BASE_TAG_LEN) + +// +// Path name separator is back slash +// +#define PATH_NAME_SEPARATOR L'\\' + + +#define EFI_PATH_STRING_LENGTH 260 +#define EFI_FILE_STRING_LENGTH 255 +#define FAT_MAX_ALLOCATE_SIZE 0xA00000 +#define LC_ISO_639_2_ENTRY_SIZE 3 +#define MAX_LANG_CODE_SIZE 100 + +#define FAT_MAX_DIR_CACHE_COUNT 8 +#define FAT_MAX_DIRENTRY_COUNT 0xFFFF +typedef CHAR8 LC_ISO_639_2; + +// +// The fat types we support +// +typedef enum { + Fat12, + Fat16, + Fat32, + FatUndefined +} FAT_VOLUME_TYPE; + +typedef enum { + CacheFat, + CacheData, + CacheMaxType +} CACHE_DATA_TYPE; + +// +// Used in FatDiskIo +// +typedef enum { + ReadDisk = 0, // raw disk read + WriteDisk = 1, // raw disk write + ReadFat = 2, // read fat cache + WriteFat = 3, // write fat cache + ReadData = 6, // read data cache + WriteData = 7 // write data cache +} IO_MODE; + +#define CACHE_ENABLED(a) ((a) >= 2) +#define RAW_ACCESS(a) ((IO_MODE)((a) & 0x1)) +#define CACHE_TYPE(a) ((CACHE_DATA_TYPE)((a) >> 2)) + +// +// Disk cache tag +// +typedef struct { + UINTN PageNo; + UINTN RealSize; + BOOLEAN Dirty; +} CACHE_TAG; + +typedef struct { + UINT64 BaseAddress; + UINT64 LimitAddress; + UINT8 *CacheBase; + BOOLEAN Dirty; + UINT8 PageAlignment; + UINTN GroupMask; + CACHE_TAG CacheTag[FAT_DATACACHE_GROUP_COUNT]; +} DISK_CACHE; + +// +// Hash table size +// +#define HASH_TABLE_SIZE 0x400 +#define HASH_TABLE_MASK (HASH_TABLE_SIZE - 1) + +// +// The directory entry for opened directory +// + +typedef struct _FAT_DIRENT FAT_DIRENT; +typedef struct _FAT_ODIR FAT_ODIR; +typedef struct _FAT_OFILE FAT_OFILE; +typedef struct _FAT_VOLUME FAT_VOLUME; + +struct _FAT_DIRENT { + UINTN Signature; + UINT16 EntryPos; // The position of this directory entry in the parent directory file + UINT8 EntryCount; // The count of the directory entry in the parent directory file + BOOLEAN Invalid; // Indicate whether this directory entry is valid + CHAR16 *FileString; // The unicode long file name for this directory entry + FAT_OFILE *OFile; // The OFile of the corresponding directory entry + FAT_DIRENT *ShortNameForwardLink; // Hash successor link for short filename + FAT_DIRENT *LongNameForwardLink; // Hash successor link for long filename + LIST_ENTRY Link; // Connection of every directory entry + FAT_DIRECTORY_ENTRY Entry; // The physical directory entry stored in disk +}; + +struct _FAT_ODIR { + UINTN Signature; + UINT32 CurrentEndPos; // Current end position of the directory + UINT32 CurrentPos; // Current position of the directory + LIST_ENTRY *CurrentCursor; // Current directory entry pointer + LIST_ENTRY ChildList; // List of all directory entries + BOOLEAN EndOfDir; // Indicate whether we have reached the end of the directory + LIST_ENTRY DirCacheLink; // Linked in Volume->DirCacheList when discarded + UINTN DirCacheTag; // The identification of the directory when in directory cache + FAT_DIRENT *LongNameHashTable[HASH_TABLE_SIZE]; + FAT_DIRENT *ShortNameHashTable[HASH_TABLE_SIZE]; +}; + +typedef struct { + UINTN Signature; + EFI_FILE_PROTOCOL Handle; + UINT64 Position; + BOOLEAN ReadOnly; + FAT_OFILE *OFile; + LIST_ENTRY Tasks; // List of all FAT_TASKs + LIST_ENTRY Link; // Link to other IFiles +} FAT_IFILE; + +typedef struct { + UINTN Signature; + EFI_FILE_IO_TOKEN *FileIoToken; + FAT_IFILE *IFile; + LIST_ENTRY Subtasks; // List of all FAT_SUBTASKs + LIST_ENTRY Link; // Link to other FAT_TASKs +} FAT_TASK; + +typedef struct { + UINTN Signature; + EFI_DISK_IO2_TOKEN DiskIo2Token; + FAT_TASK *Task; + BOOLEAN Write; + UINT64 Offset; + VOID *Buffer; + UINTN BufferSize; + LIST_ENTRY Link; +} FAT_SUBTASK; + +// +// FAT_OFILE - Each opened file +// +struct _FAT_OFILE { + UINTN Signature; + FAT_VOLUME *Volume; + // + // A permanent error code to return to all accesses to + // this opened file + // + EFI_STATUS Error; + // + // A list of the IFILE instances for this OFile + // + LIST_ENTRY Opens; + + // + // The dynamic information + // + UINTN FileSize; + UINTN FileCluster; + UINTN FileCurrentCluster; + UINTN FileLastCluster; + + // + // Dirty is set if there have been any updates to the + // file + // Archive is set if the archive attribute in the file's + // directory entry needs to be set when performing flush + // PreserveLastMod is set if the last modification of the + // file is specified by SetInfo API + // + BOOLEAN Dirty; + BOOLEAN IsFixedRootDir; + BOOLEAN PreserveLastModification; + BOOLEAN Archive; + // + // Set by an OFile SetPosition + // + UINTN Position; // within file + UINT64 PosDisk; // on the disk + UINTN PosRem; // remaining in this disk run + // + // The opened parent, full path length and currently opened child files + // + FAT_OFILE *Parent; + UINTN FullPathLen; + LIST_ENTRY ChildHead; + LIST_ENTRY ChildLink; + + // + // The opened directory structure for a directory; if this + // OFile represents a file, then ODir = NULL + // + FAT_ODIR *ODir; + // + // The directory entry for the Ofile + // + FAT_DIRENT *DirEnt; + + // + // Link in Volume's reference list + // + LIST_ENTRY CheckLink; +}; + +struct _FAT_VOLUME { + UINTN Signature; + + EFI_HANDLE Handle; + BOOLEAN Valid; + BOOLEAN DiskError; + + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL VolumeInterface; + + // + // If opened, the parent handle and BlockIo interface + // + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DISK_IO2_PROTOCOL *DiskIo2; + UINT32 MediaId; + BOOLEAN ReadOnly; + + // + // Computed values from fat bpb info + // + UINT64 VolumeSize; + UINT64 FatPos; // Disk pos of fat tables + UINT64 RootPos; // Disk pos of root directory + UINT64 FirstClusterPos; // Disk pos of first cluster + UINTN FatSize; // Number of bytes in each fat + UINTN MaxCluster; // Max cluster number + UINTN ClusterSize; // Cluster size of fat partition + UINT8 ClusterAlignment; // Equal to log_2 (clustersize); + FAT_VOLUME_TYPE FatType; + + // + // Current part of fat table that's present + // + UINT64 FatEntryPos; // Location of buffer + UINTN FatEntrySize; // Size of buffer + UINT32 FatEntryBuffer; // The buffer + FAT_INFO_SECTOR FatInfoSector; // Free cluster info + UINTN FreeInfoPos; // Pos with the free cluster info + BOOLEAN FreeInfoValid; // If free cluster info is valid + // + // Unpacked Fat BPB info + // + UINTN NumFats; + UINTN RootEntries; // < FAT32, root dir is fixed size + UINTN RootCluster; // >= FAT32, root cluster chain head + // + // info for marking the volume dirty or not + // + BOOLEAN FatDirty; // If fat-entries have been updated + UINT32 DirtyValue; + UINT32 NotDirtyValue; + + // + // The root directory entry and opened root file + // + FAT_DIRENT RootDirEnt; + // + // File Name of root OFile, it is empty string + // + CHAR16 RootFileString[1]; + FAT_OFILE *Root; + + // + // New OFiles are added to this list so they + // can be cleaned up if they aren't referenced. + // + LIST_ENTRY CheckRef; + + // + // Directory cache List + // + LIST_ENTRY DirCacheList; + UINTN DirCacheCount; + + // + // Disk Cache for this volume + // + VOID *CacheBuffer; + DISK_CACHE DiskCache[CacheMaxType]; +}; + +// +// Function Prototypes +// + +/** + + Implements Open() of Simple File System Protocol. + + @param FHand - File handle of the file serves as a starting reference point. + @param NewHandle - Handle of the file that is newly opened. + @param FileName - File name relative to FHand. + @param OpenMode - Open mode. + @param Attributes - Attributes to set if the file is created. + + + @retval EFI_INVALID_PARAMETER - The FileName is NULL or the file string is empty. + The OpenMode is not supported. + The Attributes is not the valid attributes. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory for file string. + @retval EFI_SUCCESS - Open the file successfully. + @return Others - The status of open file. + +**/ +EFI_STATUS +EFIAPI +FatOpen ( + IN EFI_FILE_PROTOCOL *FHand, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +; + +/** + + Implements OpenEx() of Simple File System Protocol. + + @param FHand - File handle of the file serves as a starting reference point. + @param NewHandle - Handle of the file that is newly opened. + @param FileName - File name relative to FHand. + @param OpenMode - Open mode. + @param Attributes - Attributes to set if the file is created. + @param Token - A pointer to the token associated with the transaction. + + @retval EFI_INVALID_PARAMETER - The FileName is NULL or the file string is empty. + The OpenMode is not supported. + The Attributes is not the valid attributes. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory for file string. + @retval EFI_SUCCESS - Open the file successfully. + @return Others - The status of open file. + +**/ +EFI_STATUS +EFIAPI +FatOpenEx ( + IN EFI_FILE_PROTOCOL *FHand, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes, + IN OUT EFI_FILE_IO_TOKEN *Token + ) +; + +/** + + Get the file's position of the file + + @param FHand - The handle of file. + @param Position - The file's position of the file. + + @retval EFI_SUCCESS - Get the info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_UNSUPPORTED - The open file is not a file. + +**/ +EFI_STATUS +EFIAPI +FatGetPosition ( + IN EFI_FILE_PROTOCOL *FHand, + OUT UINT64 *Position + ) +; + +/** + + Get the some types info of the file into Buffer + + @param FHand - The handle of file. + @param Type - The type of the info. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing volume info. + + @retval EFI_SUCCESS - Get the info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + +**/ +EFI_STATUS +EFIAPI +FatGetInfo ( + IN EFI_FILE_PROTOCOL *FHand, + IN EFI_GUID *Type, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +; + +/** + + Set the some types info of the file into Buffer. + + @param FHand - The handle of file. + @param Type - The type of the info. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing volume info. + + @retval EFI_SUCCESS - Set the info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + +**/ +EFI_STATUS +EFIAPI +FatSetInfo ( + IN EFI_FILE_PROTOCOL *FHand, + IN EFI_GUID *Type, + IN UINTN BufferSize, + IN VOID *Buffer + ) +; + +/** + + Flushes all data associated with the file handle. + + @param FHand - Handle to file to flush + + @retval EFI_SUCCESS - Flushed the file successfully + @retval EFI_WRITE_PROTECTED - The volume is read only + @retval EFI_ACCESS_DENIED - The volume is not read only + but the file is read only + @return Others - Flushing of the file is failed + +**/ +EFI_STATUS +EFIAPI +FatFlush ( + IN EFI_FILE_PROTOCOL *FHand + ) +; + +/** + + Flushes all data associated with the file handle. + + @param FHand - Handle to file to flush. + @param Token - A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS - Flushed the file successfully. + @retval EFI_WRITE_PROTECTED - The volume is read only. + @retval EFI_ACCESS_DENIED - The file is read only. + @return Others - Flushing of the file failed. + +**/ +EFI_STATUS +EFIAPI +FatFlushEx ( + IN EFI_FILE_PROTOCOL *FHand, + IN EFI_FILE_IO_TOKEN *Token + ) +; + +/** + + Flushes & Closes the file handle. + + @param FHand - Handle to the file to delete. + + @retval EFI_SUCCESS - Closed the file successfully. + +**/ +EFI_STATUS +EFIAPI +FatClose ( + IN EFI_FILE_PROTOCOL *FHand + ) +; + +/** + + Deletes the file & Closes the file handle. + + @param FHand - Handle to the file to delete. + + @retval EFI_SUCCESS - Delete the file successfully. + @retval EFI_WARN_DELETE_FAILURE - Fail to delete the file. + +**/ +EFI_STATUS +EFIAPI +FatDelete ( + IN EFI_FILE_PROTOCOL *FHand + ) +; + +/** + + Set the file's position of the file. + + @param FHand - The handle of file + @param Position - The file's position of the file + + @retval EFI_SUCCESS - Set the info successfully + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file + @retval EFI_UNSUPPORTED - Set a directory with a not-zero position + +**/ +EFI_STATUS +EFIAPI +FatSetPosition ( + IN EFI_FILE_PROTOCOL *FHand, + IN UINT64 Position + ) +; + +/** + + Get the file info. + + @param FHand - The handle of the file. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing read data. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +EFIAPI +FatRead ( + IN EFI_FILE_PROTOCOL *FHand, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +; + +/** + + Get the file info. + + @param FHand - The handle of the file. + @param Token - A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +EFIAPI +FatReadEx ( + IN EFI_FILE_PROTOCOL *FHand, + IN OUT EFI_FILE_IO_TOKEN *Token + ) +; + +/** + + Set the file info. + + @param FHand - The handle of the file. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing write data. + + @retval EFI_SUCCESS - Set the file info successfully. + @retval EFI_WRITE_PROTECTED - The disk is write protected. + @retval EFI_ACCESS_DENIED - The file is read-only. + @retval EFI_DEVICE_ERROR - The OFile is not valid. + @retval EFI_UNSUPPORTED - The open file is not a file. + - The writing file size is larger than 4GB. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +EFIAPI +FatWrite ( + IN EFI_FILE_PROTOCOL *FHand, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +; + +/** + + Get the file info. + + @param FHand - The handle of the file. + @param Token - A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +EFIAPI +FatWriteEx ( + IN EFI_FILE_PROTOCOL *FHand, + IN OUT EFI_FILE_IO_TOKEN *Token + ) +; + +// +// DiskCache.c +// +/** + + Initialize the disk cache according to Volume's FatType. + + @param Volume - FAT file system volume. + + @retval EFI_SUCCESS - The disk cache is successfully initialized. + @retval EFI_OUT_OF_RESOURCES - Not enough memory to allocate disk cache. + +**/ +EFI_STATUS +FatInitializeDiskCache ( + IN FAT_VOLUME *Volume + ); + +/** + + Read BufferSize bytes from the position of Offset into Buffer, + or write BufferSize bytes from Buffer into the position of Offset. + + Base on the parameter of CACHE_DATA_TYPE, the data access will be divided into + the access of FAT cache (CACHE_FAT) and the access of Data cache (CACHE_DATA): + + 1. Access of FAT cache (CACHE_FAT): Access the data in the FAT cache, if there is cache + page hit, just return the cache page; else update the related cache page and return + the right cache page. + 2. Access of Data cache (CACHE_DATA): + The access data will be divided into UnderRun data, Aligned data and OverRun data; + The UnderRun data and OverRun data will be accessed by the Data cache, + but the Aligned data will be accessed with disk directly. + + @param Volume - FAT file system volume. + @param CacheDataType - The type of cache: CACHE_DATA or CACHE_FAT. + @param IoMode - Indicate the type of disk access. + @param Offset - The starting byte offset to read from. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing cache data. + @param Task point to task instance. + + @retval EFI_SUCCESS - The data was accessed correctly. + @retval EFI_MEDIA_CHANGED - The MediaId does not match the current device. + @return Others - An error occurred when accessing cache. + +**/ +EFI_STATUS +FatAccessCache ( + IN FAT_VOLUME *Volume, + IN CACHE_DATA_TYPE CacheDataType, + IN IO_MODE IoMode, + IN UINT64 Offset, + IN UINTN BufferSize, + IN OUT UINT8 *Buffer, + IN FAT_TASK *Task + ); + +/** + + Flush all the dirty cache back, include the FAT cache and the Data cache. + + @param Volume - FAT file system volume. + @param Task point to task instance. + + @retval EFI_SUCCESS - Flush all the dirty cache back successfully + @return other - An error occurred when writing the data into the disk + +**/ +EFI_STATUS +FatVolumeFlushCache ( + IN FAT_VOLUME *Volume, + IN FAT_TASK *Task + ); + +// +// Flush.c +// +/** + + Flush the data associated with an open file. + In this implementation, only last Mod/Access time is updated. + + @param OFile - The open file. + + @retval EFI_SUCCESS - The OFile is flushed successfully. + @return Others - An error occurred when flushing this OFile. + +**/ +EFI_STATUS +FatOFileFlush ( + IN FAT_OFILE *OFile + ); + +/** + + Check the references of the OFile. + If the OFile (that is checked) is no longer + referenced, then it is freed. + + @param OFile - The OFile to be checked. + + @retval TRUE - The OFile is not referenced and freed. + @retval FALSE - The OFile is kept. + +**/ +BOOLEAN +FatCheckOFileRef ( + IN FAT_OFILE *OFile + ); + +/** + + Set the OFile and its child OFile with the error Status + + @param OFile - The OFile whose permanent error code is to be set. + @param Status - Error code to be set. + +**/ +VOID +FatSetVolumeError ( + IN FAT_OFILE *OFile, + IN EFI_STATUS Status + ); + +/** + + Close the open file instance. + + @param IFile - Open file instance. + + @retval EFI_SUCCESS - Closed the file successfully. + +**/ +EFI_STATUS +FatIFileClose ( + FAT_IFILE *IFile + ); + +/** + + Set error status for a specific OFile, reference checking the volume. + If volume is already marked as invalid, and all resources are freed + after reference checking, the file system protocol is uninstalled and + the volume structure is freed. + + @param Volume - the Volume that is to be reference checked and unlocked. + @param OFile - the OFile whose permanent error code is to be set. + @param EfiStatus - error code to be set. + @param Task point to task instance. + + @retval EFI_SUCCESS - Clean up the volume successfully. + @return Others - Cleaning up of the volume is failed. + +**/ +EFI_STATUS +FatCleanupVolume ( + IN FAT_VOLUME *Volume, + IN FAT_OFILE *OFile, + IN EFI_STATUS EfiStatus, + IN FAT_TASK *Task + ); + +// +// FileSpace.c +// +/** + + Shrink the end of the open file base on the file size. + + @param OFile - The open file. + + @retval EFI_SUCCESS - Shrinked successfully. + @retval EFI_VOLUME_CORRUPTED - There are errors in the file's clusters. + +**/ +EFI_STATUS +FatShrinkEof ( + IN FAT_OFILE *OFile + ); + +/** + + Grow the end of the open file base on the NewSizeInBytes. + + @param OFile - The open file. + @param NewSizeInBytes - The new size in bytes of the open file. + + @retval EFI_SUCCESS - The file is grown successfully. + @retval EFI_UNSUPPORTED - The file size is larger than 4GB. + @retval EFI_VOLUME_CORRUPTED - There are errors in the files' clusters. + @retval EFI_VOLUME_FULL - The volume is full and can not grow the file. + +**/ +EFI_STATUS +FatGrowEof ( + IN FAT_OFILE *OFile, + IN UINT64 NewSizeInBytes + ); + +/** + + Get the size of directory of the open file. + + @param Volume - The File System Volume. + @param Cluster - The Starting cluster. + + @return The physical size of the file starting at the input cluster, if there is error in the + cluster chain, the return value is 0. + +**/ +UINTN +FatPhysicalDirSize ( + IN FAT_VOLUME *Volume, + IN UINTN Cluster + ); + +/** + + Get the physical size of a file on the disk. + + @param Volume - The file system volume. + @param RealSize - The real size of a file. + + @return The physical size of a file on the disk. + +**/ +UINT64 +FatPhysicalFileSize ( + IN FAT_VOLUME *Volume, + IN UINTN RealSize + ); + +/** + + Seek OFile to requested position, and calculate the number of + consecutive clusters from the position in the file + + @param OFile - The open file. + @param Position - The file's position which will be accessed. + @param PosLimit - The maximum length current reading/writing may access + + @retval EFI_SUCCESS - Set the info successfully. + @retval EFI_VOLUME_CORRUPTED - Cluster chain corrupt. + +**/ +EFI_STATUS +FatOFilePosition ( + IN FAT_OFILE *OFile, + IN UINTN Position, + IN UINTN PosLimit + ); + +/** + + Update the free cluster info of FatInfoSector of the volume. + + @param Volume - FAT file system volume. + +**/ +VOID +FatComputeFreeInfo ( + IN FAT_VOLUME *Volume + ); + +// +// Init.c +// +/** + + Allocates volume structure, detects FAT file system, installs protocol, + and initialize cache. + + @param Handle - The handle of parent device. + @param DiskIo - The DiskIo of parent device. + @param DiskIo2 - The DiskIo2 of parent device. + @param BlockIo - The BlockIo of parent device. + + @retval EFI_SUCCESS - Allocate a new volume successfully. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory. + @return Others - Allocating a new volume failed. + +**/ +EFI_STATUS +FatAllocateVolume ( + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo + ); + +/** + + Detects FAT file system on Disk and set relevant fields of Volume. + + @param Volume - The volume structure. + + @retval EFI_SUCCESS - The Fat File System is detected successfully + @retval EFI_UNSUPPORTED - The volume is not FAT file system. + @retval EFI_VOLUME_CORRUPTED - The volume is corrupted. + +**/ +EFI_STATUS +FatOpenDevice ( + IN OUT FAT_VOLUME *Volume + ); + +/** + + Called by FatDriverBindingStop(), Abandon the volume. + + @param Volume - The volume to be abandoned. + + @retval EFI_SUCCESS - Abandoned the volume successfully. + @return Others - Can not uninstall the protocol interfaces. + +**/ +EFI_STATUS +FatAbandonVolume ( + IN FAT_VOLUME *Volume + ); + +// +// Misc.c +// +/** + + Create the task + + @param IFile - The instance of the open file. + @param Token - A pointer to the token associated with the transaction. + + @return FAT_TASK * - Return the task instance. + +**/ +FAT_TASK * +FatCreateTask ( + FAT_IFILE *IFile, + EFI_FILE_IO_TOKEN *Token + ); + +/** + + Destroy the task. + + @param Task - The task to be destroyed. + +**/ +VOID +FatDestroyTask ( + FAT_TASK *Task + ); + +/** + + Wait all non-blocking requests complete. + + @param IFile - The instance of the open file. + +**/ +VOID +FatWaitNonblockingTask ( + FAT_IFILE *IFile + ); + +/** + + Remove the subtask from subtask list. + + @param Subtask - The subtask to be removed. + + @return LIST_ENTRY * - The next node in the list. + +**/ +LIST_ENTRY * +FatDestroySubtask ( + FAT_SUBTASK *Subtask + ); + +/** + + Execute the task. + + @param IFile - The instance of the open file. + @param Task - The task to be executed. + + @retval EFI_SUCCESS - The task was executed successfully. + @return other - An error occurred when executing the task. + +**/ +EFI_STATUS +FatQueueTask ( + IN FAT_IFILE *IFile, + IN FAT_TASK *Task + ); + +/** + + Set the volume as dirty or not. + + @param Volume - FAT file system volume. + @param IoMode - The access mode. + @param DirtyValue - Set the volume as dirty or not. + + @retval EFI_SUCCESS - Set the new FAT entry value successfully. + @return other - An error occurred when operation the FAT entries. + +**/ +EFI_STATUS +FatAccessVolumeDirty ( + IN FAT_VOLUME *Volume, + IN IO_MODE IoMode, + IN VOID *DirtyValue + ); + +/** + + General disk access function. + + @param Volume - FAT file system volume. + @param IoMode - The access mode (disk read/write or cache access). + @param Offset - The starting byte offset to read from. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing read data. + @param Task point to task instance. + + @retval EFI_SUCCESS - The operation is performed successfully. + @retval EFI_VOLUME_CORRUPTED - The access is + @return Others - The status of read/write the disk + +**/ +EFI_STATUS +FatDiskIo ( + IN FAT_VOLUME *Volume, + IN IO_MODE IoMode, + IN UINT64 Offset, + IN UINTN BufferSize, + IN OUT VOID *Buffer, + IN FAT_TASK *Task + ); + +/** + + Lock the volume. + +**/ +VOID +FatAcquireLock ( + VOID + ); + +/** + + Unlock the volume. + +**/ +VOID +FatReleaseLock ( + VOID + ); + +/** + + Lock the volume. + If the lock is already in the acquired state, then EFI_ACCESS_DENIED is returned. + Otherwise, EFI_SUCCESS is returned. + + @retval EFI_SUCCESS - The volume is locked. + @retval EFI_ACCESS_DENIED - The volume could not be locked because it is already locked. + +**/ +EFI_STATUS +FatAcquireLockOrFail ( + VOID + ); + +/** + + Free directory entry. + + @param DirEnt - The directory entry to be freed. + +**/ +VOID +FatFreeDirEnt ( + IN FAT_DIRENT *DirEnt + ); + +/** + + Free volume structure (including the contents of directory cache and disk cache). + + @param Volume - The volume structure to be freed. + +**/ +VOID +FatFreeVolume ( + IN FAT_VOLUME *Volume + ); + +/** + + Translate EFI time to FAT time. + + @param ETime - The time of EFI_TIME. + @param FTime - The time of FAT_DATE_TIME. + +**/ +VOID +FatEfiTimeToFatTime ( + IN EFI_TIME *ETime, + OUT FAT_DATE_TIME *FTime + ); + +/** + + Translate Fat time to EFI time. + + @param FTime - The time of FAT_DATE_TIME. + @param ETime - The time of EFI_TIME.. + +**/ +VOID +FatFatTimeToEfiTime ( + IN FAT_DATE_TIME *FTime, + OUT EFI_TIME *ETime + ); + +/** + + Get Current FAT time. + + @param FatTime - Current FAT time. + +**/ +VOID +FatGetCurrentFatTime ( + OUT FAT_DATE_TIME *FatTime + ); + +/** + + Check whether a time is valid. + + @param Time - The time of EFI_TIME. + + @retval TRUE - The time is valid. + @retval FALSE - The time is not valid. + +**/ +BOOLEAN +FatIsValidTime ( + IN EFI_TIME *Time + ); + +// +// UnicodeCollation.c +// +/** + Initialize Unicode Collation support. + + It tries to locate Unicode Collation 2 protocol and matches it with current + platform language code. If for any reason the first attempt fails, it then tries to + use Unicode Collation Protocol. + + @param AgentHandle The handle used to open Unicode Collation (2) protocol. + + @retval EFI_SUCCESS The Unicode Collation (2) protocol has been successfully located. + @retval Others The Unicode Collation (2) protocol has not been located. + +**/ +EFI_STATUS +InitializeUnicodeCollationSupport ( + IN EFI_HANDLE AgentHandle + ); + +/** + Convert FAT string to unicode string. + + @param FatSize The size of FAT string. + @param Fat The FAT string. + @param String The unicode string. + + @return None. + +**/ +VOID +FatFatToStr ( + IN UINTN FatSize, + IN CHAR8 *Fat, + OUT CHAR16 *String + ); + +/** + Convert unicode string to Fat string. + + @param String The unicode string. + @param FatSize The size of the FAT string. + @param Fat The FAT string. + + @retval TRUE Convert successfully. + @retval FALSE Convert error. + +**/ +BOOLEAN +FatStrToFat ( + IN CHAR16 *String, + IN UINTN FatSize, + OUT CHAR8 *Fat + ); + +/** + Lowercase a string + + @param Str The string which will be lower-cased. + +**/ +VOID +FatStrLwr ( + IN CHAR16 *Str + ); + +/** + Uppercase a string. + + @param Str The string which will be upper-cased. + +**/ +VOID +FatStrUpr ( + IN CHAR16 *Str + ); + +/** + Performs a case-insensitive comparison of two Null-terminated Unicode strings. + + @param Str1 A pointer to a Null-terminated Unicode string. + @param Str2 A pointer to a Null-terminated Unicode string. + + @retval 0 S1 is equivalent to S2. + @retval >0 S1 is lexically greater than S2. + @retval <0 S1 is lexically less than S2. +**/ +INTN +FatStriCmp ( + IN CHAR16 *Str1, + IN CHAR16 *Str2 + ); + +// +// Open.c +// + +/** + + Open a file for a file name relative to an existing OFile. + The IFile of the newly opened file is passed out. + + @param OFile - The file that serves as a starting reference point. + @param NewIFile - The newly generated IFile instance. + @param FileName - The file name relative to the OFile. + @param OpenMode - Open mode. + @param Attributes - Attributes to set if the file is created. + + + @retval EFI_SUCCESS - Open the file successfully. + @retval EFI_INVALID_PARAMETER - The open mode is conflict with the attributes + or the file name is not valid. + @retval EFI_NOT_FOUND - Conflicts between dir intention and attribute. + @retval EFI_WRITE_PROTECTED - Can't open for write if the volume is read only. + @retval EFI_ACCESS_DENIED - If the file's attribute is read only, and the + open is for read-write fail it. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory. + +**/ +EFI_STATUS +FatOFileOpen ( + IN FAT_OFILE *OFile, + OUT FAT_IFILE **NewIFile, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT8 Attributes + ); + +/** + + Create an Open instance for the existing OFile. + The IFile of the newly opened file is passed out. + + @param OFile - The file that serves as a starting reference point. + @param PtrIFile - The newly generated IFile instance. + + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory for the IFile + @retval EFI_SUCCESS - Create the new IFile for the OFile successfully + +**/ +EFI_STATUS +FatAllocateIFile ( + IN FAT_OFILE *OFile, + OUT FAT_IFILE **PtrIFile + ); + +// +// OpenVolume.c +// +/** + + Implements Simple File System Protocol interface function OpenVolume(). + + @param This - Calling context. + @param File - the Root Directory of the volume. + + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory. + @retval EFI_VOLUME_CORRUPTED - The FAT type is error. + @retval EFI_SUCCESS - Open the volume successfully. + +**/ +EFI_STATUS +EFIAPI +FatOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **File + ); + +// +// ReadWrite.c +// +/** + + This function reads data from a file or writes data to a file. + It uses OFile->PosRem to determine how much data can be accessed in one time. + + @param OFile - The open file. + @param IoMode - Indicate whether the access mode is reading or writing. + @param Position - The position where data will be accessed. + @param DataBufferSize - Size of Buffer. + @param UserBuffer - Buffer containing data. + @param Task point to task instance. + + @retval EFI_SUCCESS - Access the data successfully. + @return other - An error occurred when operating on the disk. + +**/ +EFI_STATUS +FatAccessOFile ( + IN FAT_OFILE *OFile, + IN IO_MODE IoMode, + IN UINTN Position, + IN UINTN *DataBufferSize, + IN UINT8 *UserBuffer, + IN FAT_TASK *Task + ); + +/** + + Expand OFile by appending zero bytes at the end of OFile. + + @param OFile - The open file. + @param ExpandedSize - The number of zero bytes appended at the end of the file. + + @retval EFI_SUCCESS - The file is expanded successfully. + @return other - An error occurred when expanding file. + +**/ +EFI_STATUS +FatExpandOFile ( + IN FAT_OFILE *OFile, + IN UINT64 ExpandedSize + ); + +/** + + Write zero pool from the WritePos to the end of OFile. + + @param OFile - The open file to write zero pool. + @param WritePos - The number of zero bytes written. + + @retval EFI_SUCCESS - Write the zero pool successfully. + @retval EFI_OUT_OF_RESOURCES - Not enough memory to perform the operation. + @return other - An error occurred when writing disk. + +**/ +EFI_STATUS +FatWriteZeroPool ( + IN FAT_OFILE *OFile, + IN UINTN WritePos + ); + +/** + + Truncate the OFile to smaller file size. + + @param OFile - The open file. + @param TruncatedSize - The new file size. + + @retval EFI_SUCCESS - The file is truncated successfully. + @return other - An error occurred when truncating file. + +**/ +EFI_STATUS +FatTruncateOFile ( + IN FAT_OFILE *OFile, + IN UINTN TruncatedSize + ); + +// +// DirectoryManage.c +// +/** + + Set the OFile's current directory cursor to the list head. + + @param OFile - The directory OFile whose directory cursor is reset. + +**/ +VOID +FatResetODirCursor ( + IN FAT_OFILE *OFile + ); + +/** + + Set the directory's cursor to the next and get the next directory entry. + + @param OFile - The parent OFile. + @param PtrDirEnt - The next directory entry. + + @retval EFI_SUCCESS - We get the next directory entry successfully. + @return other - An error occurred when get next directory entry. + +**/ +EFI_STATUS +FatGetNextDirEnt ( + IN FAT_OFILE *OFile, + OUT FAT_DIRENT **PtrDirEnt + ); + +/** + + Remove this directory entry node from the list of directory entries and hash table. + + @param OFile - The parent OFile. + @param DirEnt - The directory entry to be removed. + + @retval EFI_SUCCESS - The directory entry is successfully removed. + @return other - An error occurred when removing the directory entry. + +**/ +EFI_STATUS +FatRemoveDirEnt ( + IN FAT_OFILE *OFile, + IN FAT_DIRENT *DirEnt + ); + +/** + + Save the directory entry to disk. + + @param OFile - The parent OFile which needs to update. + @param DirEnt - The directory entry to be saved. + + @retval EFI_SUCCESS - Store the directory entry successfully. + @return other - An error occurred when writing the directory entry. + +**/ +EFI_STATUS +FatStoreDirEnt ( + IN FAT_OFILE *OFile, + IN FAT_DIRENT *DirEnt + ); + +/** + + Create a directory entry in the parent OFile. + + @param OFile - The parent OFile. + @param FileName - The filename of the newly-created directory entry. + @param Attributes - The attribute of the newly-created directory entry. + @param PtrDirEnt - The pointer to the newly-created directory entry. + + @retval EFI_SUCCESS - The directory entry is successfully created. + @retval EFI_OUT_OF_RESOURCES - Not enough memory to create the directory entry. + @return other - An error occurred when creating the directory entry. + +**/ +EFI_STATUS +FatCreateDirEnt ( + IN FAT_OFILE *OFile, + IN CHAR16 *FileName, + IN UINT8 Attributes, + OUT FAT_DIRENT **PtrDirEnt + ); + +/** + + Determine whether the directory entry is "." or ".." entry. + + @param DirEnt - The corresponding directory entry. + + @retval TRUE - The directory entry is "." or ".." directory entry + @retval FALSE - The directory entry is not "." or ".." directory entry + +**/ +BOOLEAN +FatIsDotDirEnt ( + IN FAT_DIRENT *DirEnt + ); + +/** + + Set the OFile's cluster and size info in its directory entry. + + @param OFile - The corresponding OFile. + +**/ +VOID +FatUpdateDirEntClusterSizeInfo ( + IN FAT_OFILE *OFile + ); + +/** + + Copy all the information of DirEnt2 to DirEnt1 except for 8.3 name. + + @param DirEnt1 - The destination directory entry. + @param DirEnt2 - The source directory entry. + +**/ +VOID +FatCloneDirEnt ( + IN FAT_DIRENT *DirEnt1, + IN FAT_DIRENT *DirEnt2 + ); + +/** + + Get the directory entry's info into Buffer. + + @param Volume - FAT file system volume. + @param DirEnt - The corresponding directory entry. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing file info. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. + +**/ +EFI_STATUS +FatGetDirEntInfo ( + IN FAT_VOLUME *Volume, + IN FAT_DIRENT *DirEnt, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + + Open the directory entry to get the OFile. + + @param Parent - The parent OFile. + @param DirEnt - The directory entry to be opened. + + @retval EFI_SUCCESS - The directory entry is successfully opened. + @retval EFI_OUT_OF_RESOURCES - not enough memory to allocate a new OFile. + @return other - An error occurred when opening the directory entry. + +**/ +EFI_STATUS +FatOpenDirEnt ( + IN FAT_OFILE *OFile, + IN FAT_DIRENT *DirEnt + ); + +/** + + Create "." and ".." directory entries in the newly-created parent OFile. + + @param OFile - The parent OFile. + + @retval EFI_SUCCESS - The dot directory entries are successfully created. + @return other - An error occurred when creating the directory entry. + +**/ +EFI_STATUS +FatCreateDotDirEnts ( + IN FAT_OFILE *OFile + ); + +/** + + Close the directory entry and free the OFile. + + @param DirEnt - The directory entry to be closed. + +**/ +VOID +FatCloseDirEnt ( + IN FAT_DIRENT *DirEnt + ); + +/** + + Traverse filename and open all OFiles that can be opened. + Update filename pointer to the component that can't be opened. + If more than one name component remains, returns an error; + otherwise, return the remaining name component so that the caller might choose to create it. + + @param PtrOFile - As input, the reference OFile; as output, the located OFile. + @param FileName - The file name relevant to the OFile. + @param Attributes - The attribute of the destination OFile. + @param NewFileName - The remaining file name. + + @retval EFI_NOT_FOUND - The file name can't be opened and there is more than one + components within the name left (this means the name can + not be created either). + @retval EFI_INVALID_PARAMETER - The parameter is not valid. + @retval EFI_SUCCESS - Open the file successfully. + @return other - An error occurred when locating the OFile. + +**/ +EFI_STATUS +FatLocateOFile ( + IN OUT FAT_OFILE **PtrOFile, + IN CHAR16 *FileName, + IN UINT8 Attributes, + OUT CHAR16 *NewFileName + ); + +/** + + Get the directory entry for the volume. + + @param Volume - FAT file system volume. + @param Name - The file name of the volume. + + @retval EFI_SUCCESS - Update the volume with the directory entry successfully. + @return others - An error occurred when getting volume label. + +**/ +EFI_STATUS +FatGetVolumeEntry ( + IN FAT_VOLUME *Volume, + IN CHAR16 *Name + ); + +/** + + Set the relevant directory entry into disk for the volume. + + @param Volume - FAT file system volume. + @param Name - The new file name of the volume. + + @retval EFI_SUCCESS - Update the Volume successfully. + @retval EFI_UNSUPPORTED - The input label is not a valid volume label. + @return other - An error occurred when setting volume label. + +**/ +EFI_STATUS +FatSetVolumeEntry ( + IN FAT_VOLUME *Volume, + IN CHAR16 *Name + ); + +// +// Hash.c +// +/** + + Search the long name hash table for the directory entry. + + @param ODir - The directory to be searched. + @param LongNameString - The long name string to search. + + @return The previous long name hash node of the directory entry. + +**/ +FAT_DIRENT ** +FatLongNameHashSearch ( + IN FAT_ODIR *ODir, + IN CHAR16 *LongNameString + ); + +/** + + Search the short name hash table for the directory entry. + + @param ODir - The directory to be searched. + @param ShortNameString - The short name string to search. + + @return The previous short name hash node of the directory entry. + +**/ +FAT_DIRENT ** +FatShortNameHashSearch ( + IN FAT_ODIR *ODir, + IN CHAR8 *ShortNameString + ); + +/** + + Insert directory entry to hash table. + + @param ODir - The parent directory. + @param DirEnt - The directory entry node. + +**/ +VOID +FatInsertToHashTable ( + IN FAT_ODIR *ODir, + IN FAT_DIRENT *DirEnt + ); + +/** + + Delete directory entry from hash table. + + @param ODir - The parent directory. + @param DirEnt - The directory entry node. + +**/ +VOID +FatDeleteFromHashTable ( + IN FAT_ODIR *ODir, + IN FAT_DIRENT *DirEnt + ); + +// +// FileName.c +// +/** + + This function checks whether the input FileName is a valid 8.3 short name. + If the input FileName is a valid 8.3, the output is the 8.3 short name; + otherwise, the output is the base tag of 8.3 short name. + + @param FileName - The input unicode filename. + @param File8Dot3Name - The output ascii 8.3 short name or base tag of 8.3 short name. + + @retval TRUE - The input unicode filename is a valid 8.3 short name. + @retval FALSE - The input unicode filename is not a valid 8.3 short name. + +**/ +BOOLEAN +FatCheckIs8Dot3Name ( + IN CHAR16 *FileName, + OUT CHAR8 *File8Dot3Name + ); + +/** + + This function generates 8Dot3 name from user specified name for a newly created file. + + @param Parent - The parent directory. + @param DirEnt - The directory entry whose 8Dot3Name needs to be generated. + +**/ +VOID +FatCreate8Dot3Name ( + IN FAT_OFILE *Parent, + IN FAT_DIRENT *DirEnt + ); + +/** + + Convert the ascii fat name to the unicode string and strip trailing spaces, + and if necessary, convert the unicode string to lower case. + + @param FatName - The Char8 string needs to be converted. + @param Len - The length of the fat name. + @param LowerCase - Indicate whether to convert the string to lower case. + @param Str - The result of the conversion. + +**/ +VOID +FatNameToStr ( + IN CHAR8 *FatName, + IN UINTN Len, + IN UINTN LowerCase, + IN CHAR16 *Str + ); + +/** + + Set the caseflag value for the directory entry. + + @param DirEnt - The logical directory entry whose caseflag value is to be set. + +**/ +VOID +FatSetCaseFlag ( + IN FAT_DIRENT *DirEnt + ); + +/** + + Convert the 8.3 ASCII fat name to cased Unicode string according to case flag. + + @param DirEnt - The corresponding directory entry. + @param FileString - The output Unicode file name. + @param FileStringMax The max length of FileString. + +**/ +VOID +FatGetFileNameViaCaseFlag ( + IN FAT_DIRENT *DirEnt, + IN OUT CHAR16 *FileString, + IN UINTN FileStringMax + ); + +/** + + Get the Check sum for a short name. + + @param ShortNameString - The short name for a file. + + @retval Sum - UINT8 checksum. + +**/ +UINT8 +FatCheckSum ( + IN CHAR8 *ShortNameString + ); + +/** + + Takes Path as input, returns the next name component + in Name, and returns the position after Name (e.g., the + start of the next name component) + + @param Path - The path of one file. + @param Name - The next name component in Path. + + The position after Name in the Path + +**/ +CHAR16* +FatGetNextNameComponent ( + IN CHAR16 *Path, + OUT CHAR16 *Name + ); + +/** + + Check whether the IFileName is valid long file name. If the IFileName is a valid + long file name, then we trim the possible leading blanks and leading/trailing dots. + the trimmed filename is stored in OutputFileName + + @param InputFileName - The input file name. + @param OutputFileName - The output file name. + + @retval TRUE - The InputFileName is a valid long file name. + @retval FALSE - The InputFileName is not a valid long file name. + +**/ +BOOLEAN +FatFileNameIsValid ( + IN CHAR16 *InputFileName, + OUT CHAR16 *OutputFileName + ); + +// +// DirectoryCache.c +// +/** + + Discard the directory structure when an OFile will be freed. + Volume will cache this directory if the OFile does not represent a deleted file. + + @param OFile - The OFile whose directory structure is to be discarded. + +**/ +VOID +FatDiscardODir ( + IN FAT_OFILE *OFile + ); + +/** + + Request the directory structure when an OFile is newly generated. + If the directory structure is cached by volume, then just return this directory; + Otherwise, allocate a new one for OFile. + + @param OFile - The OFile which requests directory structure. + +**/ +VOID +FatRequestODir ( + IN FAT_OFILE *OFile + ); + +/** + + Clean up all the cached directory structures when the volume is going to be abandoned. + + @param Volume - FAT file system volume. + +**/ +VOID +FatCleanupODirCache ( + IN FAT_VOLUME *Volume + ); + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gFatDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gFatComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gFatComponentName2; +extern EFI_LOCK FatFsLock; +extern EFI_LOCK FatTaskLock; +extern EFI_FILE_PROTOCOL FatFileInterface; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.inf b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.inf new file mode 100644 index 00000000..a5a977a0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.inf @@ -0,0 +1,88 @@ +## @file +# Component Description File for FAT module. +# +# This UEFI driver detects the FAT file system in the disk. +# It also produces the Simple File System protocol for the consumer to +# perform file and directory operations on the disk. +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Fat + MODULE_UNI_FILE = Fat.uni + FILE_GUID = 961578FE-B6B7-44c3-AF35-6BC705CD2B1F + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = FatEntryPoint + UNLOAD_IMAGE = FatUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gFatDriverBinding +# COMPONENT_NAME = gFatComponentName +# COMPONENT_NAME2 = gFatComponentName2 +# + +[Sources] + DirectoryCache.c + DiskCache.c + FileName.c + Hash.c + DirectoryManage.c + ComponentName.c + FatFileSystem.h + Fat.h + ReadWrite.c + OpenVolume.c + Open.c + Misc.c + Init.c + Info.c + FileSpace.c + Flush.c + Fat.c + Delete.c + Data.c + UnicodeCollation.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + +[Guids] + gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED + gEfiFileSystemInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED + gEfiFileSystemVolumeLabelInfoIdGuid ## SOMETIMES_CONSUMES ## UNDEFINED + +[Protocols] + gEfiDiskIoProtocolGuid ## TO_START + gEfiDiskIo2ProtocolGuid ## TO_START + gEfiBlockIoProtocolGuid ## TO_START + gEfiSimpleFileSystemProtocolGuid ## BY_START + gEfiUnicodeCollationProtocolGuid ## TO_START + gEfiUnicodeCollation2ProtocolGuid ## TO_START + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang ## SOMETIMES_CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang ## SOMETIMES_CONSUMES +[UserExtensions.TianoCore."ExtraFiles"] + FatExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.uni b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.uni new file mode 100644 index 00000000..c3d4f318 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.uni @@ -0,0 +1,18 @@ +// /** @file
+// Component description file for FAT module.
+//
+// This UEFI driver detects the FAT file system in the disk.
+// It also produces the Simple File System protocol for the consumer to
+// perform file and directory operations on the disk.
+//
+// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "This UEFI driver detects the FAT file system in the disk."
+
+#string STR_MODULE_DESCRIPTION #language en-US "It also produces the Simple File System protocol for the consumer to perform file and directory operations on the disk."
+
diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FatExtra.uni b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FatExtra.uni new file mode 100644 index 00000000..8f7d366a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FatExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// Fat Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"FAT File System DXE Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FatFileSystem.h b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FatFileSystem.h new file mode 100644 index 00000000..5577c5f6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FatFileSystem.h @@ -0,0 +1,208 @@ +/** @file + Definitions for on-disk FAT structures. + +Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#ifndef _FATFILESYSTEM_H_ +#define _FATFILESYSTEM_H_ + +#pragma pack(1) +// +// FAT info signature +// +#define FAT_INFO_SIGNATURE 0x41615252 +#define FAT_INFO_BEGIN_SIGNATURE 0x61417272 +#define FAT_INFO_END_SIGNATURE 0xAA550000 +// +// FAT entry values +// +#define FAT_CLUSTER_SPECIAL_EXT (MAX_UINTN & (~0xF)) +#define FAT_CLUSTER_SPECIAL ((FAT_CLUSTER_SPECIAL_EXT) | 0x07) +#define FAT_CLUSTER_FREE 0 +#define FAT_CLUSTER_RESERVED (FAT_CLUSTER_SPECIAL) +#define FAT_CLUSTER_BAD (FAT_CLUSTER_SPECIAL) +#define FAT_CLUSTER_LAST (-1) +#define FAT_END_OF_FAT_CHAIN(Cluster) ((Cluster) > (FAT_CLUSTER_SPECIAL)) +#define FAT_MIN_CLUSTER 2 +#define FAT_MAX_FAT12_CLUSTER 0xFF5 +#define FAT_MAX_FAT16_CLUSTER 0xFFF5 +#define FAT_CLUSTER_SPECIAL_FAT12 0xFF7 +#define FAT_CLUSTER_SPECIAL_FAT16 0xFFF7 +#define FAT_CLUSTER_SPECIAL_FAT32 0x0FFFFFF7 +#define FAT_CLUSTER_MASK_FAT12 0xFFF +#define FAT_CLUSTER_UNMASK_FAT12 0xF000 +#define FAT_CLUSTER_MASK_FAT32 0x0FFFFFFF +#define FAT_CLUSTER_UNMASK_FAT32 0xF0000000 +#define FAT_POS_FAT12(a) ((a) * 3 / 2) +#define FAT_POS_FAT16(a) ((a) * 2) +#define FAT_POS_FAT32(a) ((a) * 4) +#define FAT_ODD_CLUSTER_FAT12(a) (((a) & 1) != 0) + + +// +// FAT attribute define +// +#define FAT_ATTRIBUTE_READ_ONLY 0x01 +#define FAT_ATTRIBUTE_HIDDEN 0x02 +#define FAT_ATTRIBUTE_SYSTEM 0x04 +#define FAT_ATTRIBUTE_VOLUME_ID 0x08 +#define FAT_ATTRIBUTE_DIRECTORY 0x10 +#define FAT_ATTRIBUTE_ARCHIVE 0x20 +#define FAT_ATTRIBUTE_DEVICE 0x40 +#define FAT_ATTRIBUTE_LFN 0x0F +// +// Some Long File Name definitions +// +#define FAT_LFN_LAST 0x40 // Ordinal field +#define MAX_LFN_ENTRIES 20 +#define LFN_CHAR1_LEN 5 +#define LFN_CHAR2_LEN 6 +#define LFN_CHAR3_LEN 2 +#define LFN_CHAR_TOTAL (LFN_CHAR1_LEN + LFN_CHAR2_LEN + LFN_CHAR3_LEN) +#define LFN_ENTRY_NUMBER(a) (((a) + LFN_CHAR_TOTAL - 1) / LFN_CHAR_TOTAL) +// +// Some 8.3 File Name definitions +// +#define FAT_MAIN_NAME_LEN 8 +#define FAT_EXTEND_NAME_LEN 3 +#define FAT_NAME_LEN (FAT_MAIN_NAME_LEN + FAT_EXTEND_NAME_LEN) +// +// Some directory entry information +// +#define FAT_ENTRY_INFO_OFFSET 13 +#define DELETE_ENTRY_MARK 0xE5 +#define EMPTY_ENTRY_MARK 0x00 + +// +// Volume dirty Mask +// +#define FAT16_DIRTY_MASK 0x7fff +#define FAT32_DIRTY_MASK 0xf7ffffff +// +// internal flag +// +#define FAT_CASE_MIXED 0x01 +#define FAT_CASE_NAME_LOWER 0x08 +#define FAT_CASE_EXT_LOWER 0x10 + +typedef struct { + UINT8 Ia32Jump[3]; + CHAR8 OemId[8]; + UINT16 SectorSize; + UINT8 SectorsPerCluster; + UINT16 ReservedSectors; + UINT8 NumFats; + UINT16 RootEntries; // < FAT32, root dir is fixed size + UINT16 Sectors; + UINT8 Media; + UINT16 SectorsPerFat; // < FAT32 + UINT16 SectorsPerTrack; // (ignored) + UINT16 Heads; // (ignored) + UINT32 HiddenSectors; // (ignored) + UINT32 LargeSectors; // Used if Sectors==0 +} FAT_BOOT_SECTOR_BASIC; + +typedef struct { + UINT8 PhysicalDriveNumber; // (ignored) + UINT8 CurrentHead; // holds boot_sector_dirty bit + UINT8 Signature; // (ignored) + CHAR8 Id[4]; + CHAR8 FatLabel[11]; + CHAR8 SystemId[8]; +} FAT_BOOT_SECTOR_EXT; + +typedef struct { + UINT32 LargeSectorsPerFat; // FAT32 + UINT16 ExtendedFlags; // FAT32 (ignored) + UINT16 FsVersion; // FAT32 (ignored) + UINT32 RootDirFirstCluster; // FAT32 + UINT16 FsInfoSector; // FAT32 + UINT16 BackupBootSector; // FAT32 + UINT8 Reserved[12]; // FAT32 (ignored) + UINT8 PhysicalDriveNumber; // (ignored) + UINT8 CurrentHead; // holds boot_sector_dirty bit + UINT8 Signature; // (ignored) + CHAR8 Id[4]; + CHAR8 FatLabel[11]; + CHAR8 SystemId[8]; +} FAT32_BOOT_SECTOR_EXT; + +typedef union { + FAT_BOOT_SECTOR_EXT FatBse; + FAT32_BOOT_SECTOR_EXT Fat32Bse; + } FAT_BSE; + +typedef struct { + FAT_BOOT_SECTOR_BASIC FatBsb; + FAT_BSE FatBse; +} FAT_BOOT_SECTOR; + +// +// FAT Info Structure +// +typedef struct { + UINT32 ClusterCount; + UINT32 NextCluster; +} FAT_FREE_INFO; + +typedef struct { + UINT32 Signature; + UINT8 ExtraBootCode[480]; + UINT32 InfoBeginSignature; + FAT_FREE_INFO FreeInfo; + UINT8 Reserved[12]; + UINT32 InfoEndSignature; +} FAT_INFO_SECTOR; + +// +// Directory Entry +// +#define FAT_MAX_YEAR_FROM_1980 0x7f +typedef struct { + UINT16 Day : 5; + UINT16 Month : 4; + UINT16 Year : 7; // From 1980 +} FAT_DATE; + +typedef struct { + UINT16 DoubleSecond : 5; + UINT16 Minute : 6; + UINT16 Hour : 5; +} FAT_TIME; + +typedef struct { + FAT_TIME Time; + FAT_DATE Date; +} FAT_DATE_TIME; + +typedef struct { + CHAR8 FileName[11]; // 8.3 filename + UINT8 Attributes; + UINT8 CaseFlag; + UINT8 CreateMillisecond; // (creation milliseconds - ignored) + FAT_DATE_TIME FileCreateTime; + FAT_DATE FileLastAccess; + UINT16 FileClusterHigh; // >= FAT32 + FAT_DATE_TIME FileModificationTime; + UINT16 FileCluster; + UINT32 FileSize; +} FAT_DIRECTORY_ENTRY; + +typedef struct { + UINT8 Ordinal; + CHAR8 Name1[10]; // (Really 5 chars, but not WCHAR aligned) + UINT8 Attributes; + UINT8 Type; + UINT8 Checksum; + CHAR16 Name2[6]; + UINT16 MustBeZero; + CHAR16 Name3[2]; +} FAT_DIRECTORY_LFN; + +#pragma pack() + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileName.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileName.c new file mode 100644 index 00000000..a23adb09 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileName.c @@ -0,0 +1,499 @@ +/** @file + Functions for manipulating file names. + +Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +/** + + This function checks whether the input FileName is a valid 8.3 short name. + If the input FileName is a valid 8.3, the output is the 8.3 short name; + otherwise, the output is the base tag of 8.3 short name. + + @param FileName - The input unicode filename. + @param File8Dot3Name - The output ascii 8.3 short name or base tag of 8.3 short name. + + @retval TRUE - The input unicode filename is a valid 8.3 short name. + @retval FALSE - The input unicode filename is not a valid 8.3 short name. + +**/ +BOOLEAN +FatCheckIs8Dot3Name ( + IN CHAR16 *FileName, + OUT CHAR8 *File8Dot3Name + ) +{ + BOOLEAN PossibleShortName; + CHAR16 *TempName; + CHAR16 *ExtendName; + CHAR16 *SeparateDot; + UINTN MainNameLen; + UINTN ExtendNameLen; + + PossibleShortName = TRUE; + SeparateDot = NULL; + SetMem (File8Dot3Name, FAT_NAME_LEN, ' '); + for (TempName = FileName; *TempName != '\0'; TempName++) { + if (*TempName == L'.') { + SeparateDot = TempName; + } + } + + if (SeparateDot == NULL) { + // + // Extended filename is not detected + // + MainNameLen = TempName - FileName; + ExtendName = TempName; + ExtendNameLen = 0; + } else { + // + // Extended filename is detected + // + MainNameLen = SeparateDot - FileName; + ExtendName = SeparateDot + 1; + ExtendNameLen = TempName - ExtendName; + } + // + // We scan the filename for the second time + // to check if there exists any extra blanks and dots + // + while (--TempName >= FileName) { + if ((*TempName == L'.' || *TempName == L' ') && (TempName != SeparateDot)) { + // + // There exist extra blanks and dots + // + PossibleShortName = FALSE; + } + } + + if (MainNameLen == 0) { + PossibleShortName = FALSE; + } + + if (MainNameLen > FAT_MAIN_NAME_LEN) { + PossibleShortName = FALSE; + MainNameLen = FAT_MAIN_NAME_LEN; + } + + if (ExtendNameLen > FAT_EXTEND_NAME_LEN) { + PossibleShortName = FALSE; + ExtendNameLen = FAT_EXTEND_NAME_LEN; + } + + if (FatStrToFat (FileName, MainNameLen, File8Dot3Name)) { + PossibleShortName = FALSE; + } + + if (FatStrToFat (ExtendName, ExtendNameLen, File8Dot3Name + FAT_MAIN_NAME_LEN)) { + PossibleShortName = FALSE; + } + + return PossibleShortName; +} + +/** + + Trim the trailing blanks of fat name. + + @param Name - The Char8 string needs to be trimmed. + @param Len - The length of the fat name. + + The real length of the fat name after the trailing blanks are trimmed. + +**/ +STATIC +UINTN +FatTrimAsciiTrailingBlanks ( + IN CHAR8 *Name, + IN UINTN Len + ) +{ + while (Len > 0 && Name[Len - 1] == ' ') { + Len--; + } + + return Len; +} + +/** + + Convert the ascii fat name to the unicode string and strip trailing spaces, + and if necessary, convert the unicode string to lower case. + + @param FatName - The Char8 string needs to be converted. + @param Len - The length of the fat name. + @param LowerCase - Indicate whether to convert the string to lower case. + @param Str - The result of the conversion. + +**/ +VOID +FatNameToStr ( + IN CHAR8 *FatName, + IN UINTN Len, + IN UINTN LowerCase, + OUT CHAR16 *Str + ) +{ + // + // First, trim the trailing blanks + // + Len = FatTrimAsciiTrailingBlanks (FatName, Len); + // + // Convert fat string to unicode string + // + FatFatToStr (Len, FatName, Str); + + // + // If the name is to be lower cased, do it now + // + if (LowerCase != 0) { + FatStrLwr (Str); + } +} + +/** + + This function generates 8Dot3 name from user specified name for a newly created file. + + @param Parent - The parent directory. + @param DirEnt - The directory entry whose 8Dot3Name needs to be generated. + +**/ +VOID +FatCreate8Dot3Name ( + IN FAT_OFILE *Parent, + IN FAT_DIRENT *DirEnt + ) +{ + CHAR8 *ShortName; + CHAR8 *ShortNameChar; + UINTN BaseTagLen; + UINTN Index; + UINTN Retry; + UINT8 Segment; + union { + UINT32 Crc; + struct HEX_DATA { + UINT8 Segment : HASH_VALUE_TAG_LEN; + } Hex[HASH_VALUE_TAG_LEN]; + } HashValue; + // + // Make sure the whole directory has been loaded + // + ASSERT (Parent->ODir->EndOfDir); + ShortName = DirEnt->Entry.FileName; + + // + // Trim trailing blanks of 8.3 name + // + BaseTagLen = FatTrimAsciiTrailingBlanks (ShortName, FAT_MAIN_NAME_LEN); + if (BaseTagLen > SPEC_BASE_TAG_LEN) { + BaseTagLen = SPEC_BASE_TAG_LEN; + } + // + // We first use the algorithm described by spec. + // + ShortNameChar = ShortName + BaseTagLen; + *ShortNameChar++ = '~'; + *ShortNameChar = '1'; + Retry = 0; + while (*FatShortNameHashSearch (Parent->ODir, ShortName) != NULL) { + *ShortNameChar = (CHAR8)(*ShortNameChar + 1); + if (++Retry == MAX_SPEC_RETRY) { + // + // We use new algorithm to generate 8.3 name + // + ASSERT (DirEnt->FileString != NULL); + gBS->CalculateCrc32 (DirEnt->FileString, StrSize (DirEnt->FileString), &HashValue.Crc); + + if (BaseTagLen > HASH_BASE_TAG_LEN) { + BaseTagLen = HASH_BASE_TAG_LEN; + } + + ShortNameChar = ShortName + BaseTagLen; + for (Index = 0; Index < HASH_VALUE_TAG_LEN; Index++) { + Segment = HashValue.Hex[Index].Segment; + if (Segment > 9) { + *ShortNameChar++ = (CHAR8)(Segment - 10 + 'A'); + } else { + *ShortNameChar++ = (CHAR8)(Segment + '0'); + } + } + + *ShortNameChar++ = '~'; + *ShortNameChar = '1'; + } + } +} + +/** + + Check the string is lower case or upper case + and it is used by fatname to dir entry count + + @param Str - The string which needs to be checked. + @param InCaseFlag - The input case flag which is returned when the string is lower case. + + @retval OutCaseFlag - The output case flag. + +**/ +STATIC +UINT8 +FatCheckNameCase ( + IN CHAR16 *Str, + IN UINT8 InCaseFlag + ) +{ + CHAR16 Buffer[FAT_MAIN_NAME_LEN + 1 + FAT_EXTEND_NAME_LEN + 1]; + UINT8 OutCaseFlag; + + // + // Assume the case of input string is mixed + // + OutCaseFlag = FAT_CASE_MIXED; + // + // Lower case a copy of the string, if it matches the + // original then the string is lower case + // + StrCpyS (Buffer, ARRAY_SIZE (Buffer), Str); + FatStrLwr (Buffer); + if (StrCmp (Str, Buffer) == 0) { + OutCaseFlag = InCaseFlag; + } + // + // Upper case a copy of the string, if it matches the + // original then the string is upper case + // + StrCpyS (Buffer, ARRAY_SIZE (Buffer), Str); + FatStrUpr (Buffer); + if (StrCmp (Str, Buffer) == 0) { + OutCaseFlag = 0; + } + + return OutCaseFlag; +} + +/** + + Set the caseflag value for the directory entry. + + @param DirEnt - The logical directory entry whose caseflag value is to be set. + +**/ +VOID +FatSetCaseFlag ( + IN FAT_DIRENT *DirEnt + ) +{ + CHAR16 LfnBuffer[FAT_MAIN_NAME_LEN + 1 + FAT_EXTEND_NAME_LEN + 1]; + CHAR16 *TempCharPtr; + CHAR16 *ExtendName; + CHAR16 *FileNameCharPtr; + UINT8 CaseFlag; + + ExtendName = NULL; + TempCharPtr = LfnBuffer; + FileNameCharPtr = DirEnt->FileString; + ASSERT (StrSize (DirEnt->FileString) <= sizeof (LfnBuffer)); + while ((*TempCharPtr = *FileNameCharPtr) != 0) { + if (*TempCharPtr == L'.') { + ExtendName = TempCharPtr; + } + + TempCharPtr++; + FileNameCharPtr++; + } + + CaseFlag = 0; + if (ExtendName != NULL) { + *ExtendName = 0; + ExtendName++; + CaseFlag = (UINT8)(CaseFlag | FatCheckNameCase (ExtendName, FAT_CASE_EXT_LOWER)); + } + + CaseFlag = (UINT8)(CaseFlag | FatCheckNameCase (LfnBuffer, FAT_CASE_NAME_LOWER)); + if ((CaseFlag & FAT_CASE_MIXED) == 0) { + // + // We just need one directory entry to store this file name entry + // + DirEnt->Entry.CaseFlag = CaseFlag; + } else { + // + // We need one extra directory entry to store the mixed case entry + // + DirEnt->Entry.CaseFlag = 0; + DirEnt->EntryCount++; + } +} + +/** + + Convert the 8.3 ASCII fat name to cased Unicode string according to case flag. + + @param DirEnt - The corresponding directory entry. + @param FileString - The output Unicode file name. + @param FileStringMax The max length of FileString. + +**/ +VOID +FatGetFileNameViaCaseFlag ( + IN FAT_DIRENT *DirEnt, + IN OUT CHAR16 *FileString, + IN UINTN FileStringMax + ) +{ + UINT8 CaseFlag; + CHAR8 *File8Dot3Name; + CHAR16 TempExt[1 + FAT_EXTEND_NAME_LEN + 1]; + // + // Store file extension like ".txt" + // + CaseFlag = DirEnt->Entry.CaseFlag; + File8Dot3Name = DirEnt->Entry.FileName; + + FatNameToStr (File8Dot3Name, FAT_MAIN_NAME_LEN, CaseFlag & FAT_CASE_NAME_LOWER, FileString); + FatNameToStr (File8Dot3Name + FAT_MAIN_NAME_LEN, FAT_EXTEND_NAME_LEN, CaseFlag & FAT_CASE_EXT_LOWER, &TempExt[1]); + if (TempExt[1] != 0) { + TempExt[0] = L'.'; + StrCatS (FileString, FileStringMax, TempExt); + } +} + +/** + + Get the Check sum for a short name. + + @param ShortNameString - The short name for a file. + + @retval Sum - UINT8 checksum. + +**/ +UINT8 +FatCheckSum ( + IN CHAR8 *ShortNameString + ) +{ + UINTN ShortNameLen; + UINT8 Sum; + Sum = 0; + for (ShortNameLen = FAT_NAME_LEN; ShortNameLen != 0; ShortNameLen--) { + Sum = (UINT8)((((Sum & 1) != 0) ? 0x80 : 0) + (Sum >> 1) + *ShortNameString++); + } + + return Sum; +} + +/** + + Takes Path as input, returns the next name component + in Name, and returns the position after Name (e.g., the + start of the next name component) + + @param Path - The path of one file. + @param Name - The next name component in Path. + + The position after Name in the Path + +**/ +CHAR16 * +FatGetNextNameComponent ( + IN CHAR16 *Path, + OUT CHAR16 *Name + ) +{ + while (*Path != 0 && *Path != PATH_NAME_SEPARATOR) { + *Name++ = *Path++; + } + *Name = 0; + // + // Get off of trailing path name separator + // + while (*Path == PATH_NAME_SEPARATOR) { + Path++; + } + + return Path; +} + +/** + + Check whether the IFileName is valid long file name. If the IFileName is a valid + long file name, then we trim the possible leading blanks and leading/trailing dots. + the trimmed filename is stored in OutputFileName + + @param InputFileName - The input file name. + @param OutputFileName - The output file name. + + @retval TRUE - The InputFileName is a valid long file name. + @retval FALSE - The InputFileName is not a valid long file name. + +**/ +BOOLEAN +FatFileNameIsValid ( + IN CHAR16 *InputFileName, + OUT CHAR16 *OutputFileName + ) +{ + CHAR16 *TempNamePointer; + CHAR16 TempChar; + // + // Trim Leading blanks + // + while (*InputFileName == L' ') { + InputFileName++; + } + + TempNamePointer = OutputFileName; + while (*InputFileName != 0) { + *TempNamePointer++ = *InputFileName++; + } + // + // Trim Trailing blanks and dots + // + while (TempNamePointer > OutputFileName) { + TempChar = *(TempNamePointer - 1); + if (TempChar != L' ' && TempChar != L'.') { + break; + } + + TempNamePointer--; + } + + *TempNamePointer = 0; + + // + // Per FAT Spec the file name should meet the following criteria: + // C1. Length (FileLongName) <= 255 + // C2. Length (X:FileFullPath<NUL>) <= 260 + // Here we check C1. + // + if (TempNamePointer - OutputFileName > EFI_FILE_STRING_LENGTH) { + return FALSE; + } + // + // See if there is any illegal characters within the name + // + do { + if (*OutputFileName < 0x20 || + *OutputFileName == '\"' || + *OutputFileName == '*' || + *OutputFileName == '/' || + *OutputFileName == ':' || + *OutputFileName == '<' || + *OutputFileName == '>' || + *OutputFileName == '?' || + *OutputFileName == '\\' || + *OutputFileName == '|' + ) { + return FALSE; + } + + OutputFileName++; + } while (*OutputFileName != 0); + return TRUE; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileSpace.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileSpace.c new file mode 100644 index 00000000..78b26a58 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileSpace.c @@ -0,0 +1,744 @@ +/** @file + Routines dealing with disk spaces and FAT table entries. + +Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + + +**/ + +#include "Fat.h" + + +/** + + Get the FAT entry of the volume, which is identified with the Index. + + @param Volume - FAT file system volume. + @param Index - The index of the FAT entry of the volume. + + @return The buffer of the FAT entry + +**/ +STATIC +VOID * +FatLoadFatEntry ( + IN FAT_VOLUME *Volume, + IN UINTN Index + ) +{ + UINTN Pos; + EFI_STATUS Status; + + if (Index > (Volume->MaxCluster + 1)) { + Volume->FatEntryBuffer = (UINT32) -1; + return &Volume->FatEntryBuffer; + } + // + // Compute buffer position needed + // + switch (Volume->FatType) { + case Fat12: + Pos = FAT_POS_FAT12 (Index); + break; + + case Fat16: + Pos = FAT_POS_FAT16 (Index); + break; + + default: + Pos = FAT_POS_FAT32 (Index); + } + // + // Set the position and read the buffer + // + Volume->FatEntryPos = Volume->FatPos + Pos; + Status = FatDiskIo ( + Volume, + ReadFat, + Volume->FatEntryPos, + Volume->FatEntrySize, + &Volume->FatEntryBuffer, + NULL + ); + if (EFI_ERROR (Status)) { + Volume->FatEntryBuffer = (UINT32) -1; + } + + return &Volume->FatEntryBuffer; +} + +/** + + Get the FAT entry value of the volume, which is identified with the Index. + + @param Volume - FAT file system volume. + @param Index - The index of the FAT entry of the volume. + + @return The value of the FAT entry. + +**/ +STATIC +UINTN +FatGetFatEntry ( + IN FAT_VOLUME *Volume, + IN UINTN Index + ) +{ + VOID *Pos; + UINT8 *En12; + UINT16 *En16; + UINT32 *En32; + UINTN Accum; + + Pos = FatLoadFatEntry (Volume, Index); + + if (Index > (Volume->MaxCluster + 1)) { + return (UINTN) -1; + } + + switch (Volume->FatType) { + case Fat12: + En12 = Pos; + Accum = En12[0] | (En12[1] << 8); + Accum = FAT_ODD_CLUSTER_FAT12 (Index) ? (Accum >> 4) : (Accum & FAT_CLUSTER_MASK_FAT12); + Accum = Accum | ((Accum >= FAT_CLUSTER_SPECIAL_FAT12) ? FAT_CLUSTER_SPECIAL_EXT : 0); + break; + + case Fat16: + En16 = Pos; + Accum = *En16; + Accum = Accum | ((Accum >= FAT_CLUSTER_SPECIAL_FAT16) ? FAT_CLUSTER_SPECIAL_EXT : 0); + break; + + default: + En32 = Pos; + Accum = *En32 & FAT_CLUSTER_MASK_FAT32; + Accum = Accum | ((Accum >= FAT_CLUSTER_SPECIAL_FAT32) ? FAT_CLUSTER_SPECIAL_EXT : 0); + } + + return Accum; +} + +/** + + Set the FAT entry value of the volume, which is identified with the Index. + + @param Volume - FAT file system volume. + @param Index - The index of the FAT entry of the volume. + @param Value - The new value of the FAT entry. + + @retval EFI_SUCCESS - Set the new FAT entry value successfully. + @retval EFI_VOLUME_CORRUPTED - The FAT type of the volume is error. + @return other - An error occurred when operation the FAT entries. + +**/ +STATIC +EFI_STATUS +FatSetFatEntry ( + IN FAT_VOLUME *Volume, + IN UINTN Index, + IN UINTN Value + ) +{ + VOID *Pos; + UINT8 *En12; + UINT16 *En16; + UINT32 *En32; + UINTN Accum; + EFI_STATUS Status; + UINTN OriginalVal; + + if (Index < FAT_MIN_CLUSTER) { + return EFI_VOLUME_CORRUPTED; + } + + OriginalVal = FatGetFatEntry (Volume, Index); + if (Value == FAT_CLUSTER_FREE && OriginalVal != FAT_CLUSTER_FREE) { + Volume->FatInfoSector.FreeInfo.ClusterCount += 1; + if (Index < Volume->FatInfoSector.FreeInfo.NextCluster) { + Volume->FatInfoSector.FreeInfo.NextCluster = (UINT32) Index; + } + } else if (Value != FAT_CLUSTER_FREE && OriginalVal == FAT_CLUSTER_FREE) { + if (Volume->FatInfoSector.FreeInfo.ClusterCount != 0) { + Volume->FatInfoSector.FreeInfo.ClusterCount -= 1; + } + } + // + // Make sure the entry is in memory + // + Pos = FatLoadFatEntry (Volume, Index); + + // + // Update the value + // + switch (Volume->FatType) { + case Fat12: + En12 = Pos; + Accum = En12[0] | (En12[1] << 8); + Value = Value & FAT_CLUSTER_MASK_FAT12; + + if (FAT_ODD_CLUSTER_FAT12 (Index)) { + Accum = (Value << 4) | (Accum & 0xF); + } else { + Accum = Value | (Accum & FAT_CLUSTER_UNMASK_FAT12); + } + + En12[0] = (UINT8) (Accum & 0xFF); + En12[1] = (UINT8) (Accum >> 8); + break; + + case Fat16: + En16 = Pos; + *En16 = (UINT16) Value; + break; + + default: + En32 = Pos; + *En32 = (*En32 & FAT_CLUSTER_UNMASK_FAT32) | (UINT32) (Value & FAT_CLUSTER_MASK_FAT32); + } + // + // If the volume's dirty bit is not set, set it now + // + if (!Volume->FatDirty && Volume->FatType != Fat12) { + Volume->FatDirty = TRUE; + FatAccessVolumeDirty (Volume, WriteFat, &Volume->DirtyValue); + } + // + // Write the updated fat entry value to the volume + // The fat is the first fat, and other fat will be in sync + // when the FAT cache flush back. + // + Status = FatDiskIo ( + Volume, + WriteFat, + Volume->FatEntryPos, + Volume->FatEntrySize, + &Volume->FatEntryBuffer, + NULL + ); + return Status; +} + +/** + + Free the cluster chain. + + @param Volume - FAT file system volume. + @param Cluster - The first cluster of cluster chain. + + @retval EFI_SUCCESS - The cluster chain is freed successfully. + @retval EFI_VOLUME_CORRUPTED - There are errors in the file's clusters. + +**/ +STATIC +EFI_STATUS +FatFreeClusters ( + IN FAT_VOLUME *Volume, + IN UINTN Cluster + ) +{ + UINTN LastCluster; + + while (!FAT_END_OF_FAT_CHAIN (Cluster)) { + if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) { + + DEBUG ((EFI_D_INIT | EFI_D_ERROR, "FatShrinkEof: cluster chain corrupt\n")); + return EFI_VOLUME_CORRUPTED; + } + + LastCluster = Cluster; + Cluster = FatGetFatEntry (Volume, Cluster); + FatSetFatEntry (Volume, LastCluster, FAT_CLUSTER_FREE); + } + + return EFI_SUCCESS; +} + +/** + + Allocate a free cluster and return the cluster index. + + @param Volume - FAT file system volume. + + @return The index of the free cluster + +**/ +STATIC +UINTN +FatAllocateCluster ( + IN FAT_VOLUME *Volume + ) +{ + UINTN Cluster; + + // + // Start looking at FatFreePos for the next unallocated cluster + // + if (Volume->DiskError) { + return (UINTN) FAT_CLUSTER_LAST; + } + + for (;;) { + // + // If the end of the list, return no available cluster + // + if (Volume->FatInfoSector.FreeInfo.NextCluster > (Volume->MaxCluster + 1)) { + if (Volume->FreeInfoValid && 0 < (INT32) (Volume->FatInfoSector.FreeInfo.ClusterCount)) { + Volume->FreeInfoValid = FALSE; + } + + FatComputeFreeInfo (Volume); + if (Volume->FatInfoSector.FreeInfo.NextCluster > (Volume->MaxCluster + 1)) { + return (UINTN) FAT_CLUSTER_LAST; + } + } + + Cluster = FatGetFatEntry (Volume, Volume->FatInfoSector.FreeInfo.NextCluster); + if (Cluster == FAT_CLUSTER_FREE) { + break; + } + // + // Try the next cluster + // + Volume->FatInfoSector.FreeInfo.NextCluster += 1; + } + + Cluster = Volume->FatInfoSector.FreeInfo.NextCluster; + Volume->FatInfoSector.FreeInfo.NextCluster += 1; + return Cluster; +} + +/** + + Count the number of clusters given a size. + + @param Volume - The file system volume. + @param Size - The size in bytes. + + @return The number of the clusters. + +**/ +STATIC +UINTN +FatSizeToClusters ( + IN FAT_VOLUME *Volume, + IN UINTN Size + ) +{ + UINTN Clusters; + + Clusters = Size >> Volume->ClusterAlignment; + if ((Size & (Volume->ClusterSize - 1)) > 0) { + Clusters += 1; + } + + return Clusters; +} + +/** + + Shrink the end of the open file base on the file size. + + @param OFile - The open file. + + @retval EFI_SUCCESS - Shrinked successfully. + @retval EFI_VOLUME_CORRUPTED - There are errors in the file's clusters. + +**/ +EFI_STATUS +FatShrinkEof ( + IN FAT_OFILE *OFile + ) +{ + FAT_VOLUME *Volume; + UINTN NewSize; + UINTN CurSize; + UINTN Cluster; + UINTN LastCluster; + + Volume = OFile->Volume; + ASSERT_VOLUME_LOCKED (Volume); + + NewSize = FatSizeToClusters (Volume, OFile->FileSize); + + // + // Find the address of the last cluster + // + Cluster = OFile->FileCluster; + LastCluster = FAT_CLUSTER_FREE; + + if (NewSize != 0) { + + for (CurSize = 0; CurSize < NewSize; CurSize++) { + if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) { + + DEBUG ((EFI_D_INIT | EFI_D_ERROR, "FatShrinkEof: cluster chain corrupt\n")); + return EFI_VOLUME_CORRUPTED; + } + + LastCluster = Cluster; + Cluster = FatGetFatEntry (Volume, Cluster); + } + + FatSetFatEntry (Volume, LastCluster, (UINTN) FAT_CLUSTER_LAST); + + } else { + // + // Check to see if the file is already completely truncated + // + if (Cluster == FAT_CLUSTER_FREE) { + return EFI_SUCCESS; + } + // + // The file is being completely truncated. + // + OFile->FileCluster = FAT_CLUSTER_FREE; + } + // + // Set CurrentCluster == FileCluster + // to force a recalculation of Position related stuffs + // + OFile->FileCurrentCluster = OFile->FileCluster; + OFile->FileLastCluster = LastCluster; + OFile->Dirty = TRUE; + // + // Free the remaining cluster chain + // + return FatFreeClusters (Volume, Cluster); +} + +/** + + Grow the end of the open file base on the NewSizeInBytes. + + @param OFile - The open file. + @param NewSizeInBytes - The new size in bytes of the open file. + + @retval EFI_SUCCESS - The file is grown successfully. + @retval EFI_UNSUPPORTED - The file size is larger than 4GB. + @retval EFI_VOLUME_CORRUPTED - There are errors in the files' clusters. + @retval EFI_VOLUME_FULL - The volume is full and can not grow the file. + +**/ +EFI_STATUS +FatGrowEof ( + IN FAT_OFILE *OFile, + IN UINT64 NewSizeInBytes + ) +{ + FAT_VOLUME *Volume; + EFI_STATUS Status; + UINTN Cluster; + UINTN CurSize; + UINTN NewSize; + UINTN LastCluster; + UINTN NewCluster; + UINTN ClusterCount; + + // + // For FAT file system, the max file is 4GB. + // + if (NewSizeInBytes > 0x0FFFFFFFFL) { + return EFI_UNSUPPORTED; + } + + Volume = OFile->Volume; + ASSERT_VOLUME_LOCKED (Volume); + // + // If the file is already large enough, do nothing + // + CurSize = FatSizeToClusters (Volume, OFile->FileSize); + NewSize = FatSizeToClusters (Volume, (UINTN) NewSizeInBytes); + + if (CurSize < NewSize) { + // + // If we haven't found the files last cluster do it now + // + if ((OFile->FileCluster != 0) && (OFile->FileLastCluster == 0)) { + Cluster = OFile->FileCluster; + ClusterCount = 0; + + while (!FAT_END_OF_FAT_CHAIN (Cluster)) { + if (Cluster < FAT_MIN_CLUSTER || Cluster > Volume->MaxCluster + 1) { + + DEBUG ( + (EFI_D_INIT | EFI_D_ERROR, + "FatGrowEof: cluster chain corrupt\n") + ); + Status = EFI_VOLUME_CORRUPTED; + goto Done; + } + + ClusterCount++; + OFile->FileLastCluster = Cluster; + Cluster = FatGetFatEntry (Volume, Cluster); + } + + if (ClusterCount != CurSize) { + DEBUG ( + (EFI_D_INIT | EFI_D_ERROR, + "FatGrowEof: cluster chain size does not match file size\n") + ); + Status = EFI_VOLUME_CORRUPTED; + goto Done; + } + + } + // + // Loop until we've allocated enough space + // + LastCluster = OFile->FileLastCluster; + + while (CurSize < NewSize) { + NewCluster = FatAllocateCluster (Volume); + if (FAT_END_OF_FAT_CHAIN (NewCluster)) { + if (LastCluster != FAT_CLUSTER_FREE) { + FatSetFatEntry (Volume, LastCluster, (UINTN) FAT_CLUSTER_LAST); + OFile->FileLastCluster = LastCluster; + } + + Status = EFI_VOLUME_FULL; + goto Done; + } + + if (NewCluster < FAT_MIN_CLUSTER || NewCluster > Volume->MaxCluster + 1) { + Status = EFI_VOLUME_CORRUPTED; + goto Done; + } + + if (LastCluster != 0) { + FatSetFatEntry (Volume, LastCluster, NewCluster); + } else { + OFile->FileCluster = NewCluster; + OFile->FileCurrentCluster = NewCluster; + } + + LastCluster = NewCluster; + CurSize += 1; + + // + // Terminate the cluster list + // + // Note that we must do this EVERY time we allocate a cluster, because + // FatAllocateCluster scans the FAT looking for a free cluster and + // "LastCluster" is no longer free! Usually, FatAllocateCluster will + // start looking with the cluster after "LastCluster"; however, when + // there is only one free cluster left, it will find "LastCluster" + // a second time. There are other, less predictable scenarios + // where this could happen, as well. + // + FatSetFatEntry (Volume, LastCluster, (UINTN) FAT_CLUSTER_LAST); + OFile->FileLastCluster = LastCluster; + } + } + + OFile->FileSize = (UINTN) NewSizeInBytes; + OFile->Dirty = TRUE; + return EFI_SUCCESS; + +Done: + FatShrinkEof (OFile); + return Status; +} + +/** + + Seek OFile to requested position, and calculate the number of + consecutive clusters from the position in the file + + @param OFile - The open file. + @param Position - The file's position which will be accessed. + @param PosLimit - The maximum length current reading/writing may access + + @retval EFI_SUCCESS - Set the info successfully. + @retval EFI_VOLUME_CORRUPTED - Cluster chain corrupt. + +**/ +EFI_STATUS +FatOFilePosition ( + IN FAT_OFILE *OFile, + IN UINTN Position, + IN UINTN PosLimit + ) +{ + FAT_VOLUME *Volume; + UINTN ClusterSize; + UINTN Cluster; + UINTN StartPos; + UINTN Run; + + Volume = OFile->Volume; + ClusterSize = Volume->ClusterSize; + + ASSERT_VOLUME_LOCKED (Volume); + + // + // If this is the fixed root dir, then compute its position + // from its fixed info in the fat bpb + // + if (OFile->IsFixedRootDir) { + OFile->PosDisk = Volume->RootPos + Position; + Run = OFile->FileSize - Position; + } else { + // + // Run the file's cluster chain to find the current position + // If possible, run from the current cluster rather than + // start from beginning + // Assumption: OFile->Position is always consistent with + // OFile->FileCurrentCluster. + // OFile->Position is not modified outside this function; + // OFile->FileCurrentCluster is modified outside this function + // to be the same as OFile->FileCluster + // when OFile->FileCluster is updated, so make a check of this + // and invalidate the original OFile->Position in this case + // + Cluster = OFile->FileCurrentCluster; + StartPos = OFile->Position; + if (Position < StartPos || OFile->FileCluster == Cluster) { + StartPos = 0; + Cluster = OFile->FileCluster; + } + + while (StartPos + ClusterSize <= Position) { + StartPos += ClusterSize; + if (Cluster == FAT_CLUSTER_FREE || (Cluster >= FAT_CLUSTER_SPECIAL)) { + DEBUG ((EFI_D_INIT | EFI_D_ERROR, "FatOFilePosition:"" cluster chain corrupt\n")); + return EFI_VOLUME_CORRUPTED; + } + + Cluster = FatGetFatEntry (Volume, Cluster); + } + + if (Cluster < FAT_MIN_CLUSTER || Cluster > Volume->MaxCluster + 1) { + return EFI_VOLUME_CORRUPTED; + } + + OFile->PosDisk = Volume->FirstClusterPos + + LShiftU64 (Cluster - FAT_MIN_CLUSTER, Volume->ClusterAlignment) + + Position - StartPos; + OFile->FileCurrentCluster = Cluster; + OFile->Position = StartPos; + + // + // Compute the number of consecutive clusters in the file + // + Run = StartPos + ClusterSize - Position; + if (!FAT_END_OF_FAT_CHAIN (Cluster)) { + while ((FatGetFatEntry (Volume, Cluster) == Cluster + 1) && Run < PosLimit) { + Run += ClusterSize; + Cluster += 1; + } + } + } + + OFile->PosRem = Run; + return EFI_SUCCESS; +} + +/** + + Get the size of directory of the open file. + + @param Volume - The File System Volume. + @param Cluster - The Starting cluster. + + @return The physical size of the file starting at the input cluster, if there is error in the + cluster chain, the return value is 0. + +**/ +UINTN +FatPhysicalDirSize ( + IN FAT_VOLUME *Volume, + IN UINTN Cluster + ) +{ + UINTN Size; + ASSERT_VOLUME_LOCKED (Volume); + // + // Run the cluster chain for the OFile + // + Size = 0; + // + // N.B. ".." directories on some media do not contain a starting + // cluster. In the case of "." or ".." we don't need the size anyway. + // + if (Cluster != 0) { + while (!FAT_END_OF_FAT_CHAIN (Cluster)) { + if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) { + DEBUG ( + (EFI_D_INIT | EFI_D_ERROR, + "FATDirSize: cluster chain corrupt\n") + ); + return 0; + } + + Size += Volume->ClusterSize; + Cluster = FatGetFatEntry (Volume, Cluster); + } + } + + return Size; +} + +/** + + Get the physical size of a file on the disk. + + @param Volume - The file system volume. + @param RealSize - The real size of a file. + + @return The physical size of a file on the disk. + +**/ +UINT64 +FatPhysicalFileSize ( + IN FAT_VOLUME *Volume, + IN UINTN RealSize + ) +{ + UINTN ClusterSizeMask; + UINT64 PhysicalSize; + ClusterSizeMask = Volume->ClusterSize - 1; + PhysicalSize = (RealSize + ClusterSizeMask) & (~((UINT64) ClusterSizeMask)); + return PhysicalSize; +} + +/** + + Update the free cluster info of FatInfoSector of the volume. + + @param Volume - FAT file system volume. + +**/ +VOID +FatComputeFreeInfo ( + IN FAT_VOLUME *Volume + ) +{ + UINTN Index; + + // + // If we don't have valid info, compute it now + // + if (!Volume->FreeInfoValid) { + + Volume->FreeInfoValid = TRUE; + Volume->FatInfoSector.FreeInfo.ClusterCount = 0; + for (Index = Volume->MaxCluster + 1; Index >= FAT_MIN_CLUSTER; Index--) { + if (Volume->DiskError) { + break; + } + + if (FatGetFatEntry (Volume, Index) == FAT_CLUSTER_FREE) { + Volume->FatInfoSector.FreeInfo.ClusterCount += 1; + Volume->FatInfoSector.FreeInfo.NextCluster = (UINT32) Index; + } + } + + Volume->FatInfoSector.Signature = FAT_INFO_SIGNATURE; + Volume->FatInfoSector.InfoBeginSignature = FAT_INFO_BEGIN_SIGNATURE; + Volume->FatInfoSector.InfoEndSignature = FAT_INFO_END_SIGNATURE; + } +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Flush.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Flush.c new file mode 100644 index 00000000..54a75bae --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Flush.c @@ -0,0 +1,468 @@ +/** @file + Routines that check references and flush OFiles + +Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Fat.h" + +/** + + Flushes all data associated with the file handle. + + @param FHand - Handle to file to flush. + @param Token - A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS - Flushed the file successfully. + @retval EFI_WRITE_PROTECTED - The volume is read only. + @retval EFI_ACCESS_DENIED - The file is read only. + @return Others - Flushing of the file failed. + +**/ +EFI_STATUS +EFIAPI +FatFlushEx ( + IN EFI_FILE_PROTOCOL *FHand, + IN EFI_FILE_IO_TOKEN *Token + ) +{ + FAT_IFILE *IFile; + FAT_OFILE *OFile; + FAT_VOLUME *Volume; + EFI_STATUS Status; + FAT_TASK *Task; + + IFile = IFILE_FROM_FHAND (FHand); + OFile = IFile->OFile; + Volume = OFile->Volume; + Task = NULL; + + // + // If the file has a permanent error, return it + // + if (EFI_ERROR (OFile->Error)) { + return OFile->Error; + } + + if (Volume->ReadOnly) { + return EFI_WRITE_PROTECTED; + } + // + // If read only, return error + // + if (IFile->ReadOnly) { + return EFI_ACCESS_DENIED; + } + + if (Token == NULL) { + FatWaitNonblockingTask (IFile); + } else { + // + // Caller shouldn't call the non-blocking interfaces if the low layer doesn't support DiskIo2. + // But if it calls, the below check can avoid crash. + // + if (FHand->Revision < EFI_FILE_PROTOCOL_REVISION2) { + return EFI_UNSUPPORTED; + } + Task = FatCreateTask (IFile, Token); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + // + // Flush the OFile + // + FatAcquireLock (); + Status = FatOFileFlush (OFile); + Status = FatCleanupVolume (OFile->Volume, OFile, Status, Task); + FatReleaseLock (); + + if (Token != NULL) { + if (!EFI_ERROR (Status)) { + Status = FatQueueTask (IFile, Task); + } else { + FatDestroyTask (Task); + } + } + + return Status; +} + +/** + + Flushes all data associated with the file handle. + + @param FHand - Handle to file to flush. + + @retval EFI_SUCCESS - Flushed the file successfully. + @retval EFI_WRITE_PROTECTED - The volume is read only. + @retval EFI_ACCESS_DENIED - The file is read only. + @return Others - Flushing of the file failed. + +**/ +EFI_STATUS +EFIAPI +FatFlush ( + IN EFI_FILE_PROTOCOL *FHand + ) +{ + return FatFlushEx (FHand, NULL); +} + +/** + + Flushes & Closes the file handle. + + @param FHand - Handle to the file to delete. + + @retval EFI_SUCCESS - Closed the file successfully. + +**/ +EFI_STATUS +EFIAPI +FatClose ( + IN EFI_FILE_PROTOCOL *FHand + ) +{ + FAT_IFILE *IFile; + FAT_OFILE *OFile; + FAT_VOLUME *Volume; + + IFile = IFILE_FROM_FHAND (FHand); + OFile = IFile->OFile; + Volume = OFile->Volume; + + // + // Lock the volume + // + FatAcquireLock (); + + // + // Close the file instance handle + // + FatIFileClose (IFile); + + // + // Done. Unlock the volume + // + FatCleanupVolume (Volume, OFile, EFI_SUCCESS, NULL); + FatReleaseLock (); + + // + // Close always succeed + // + return EFI_SUCCESS; +} + +/** + + Close the open file instance. + + @param IFile - Open file instance. + + @retval EFI_SUCCESS - Closed the file successfully. + +**/ +EFI_STATUS +FatIFileClose ( + FAT_IFILE *IFile + ) +{ + FAT_OFILE *OFile; + FAT_VOLUME *Volume; + + OFile = IFile->OFile; + Volume = OFile->Volume; + + ASSERT_VOLUME_LOCKED (Volume); + + FatWaitNonblockingTask (IFile); + + // + // Remove the IFile struct + // + RemoveEntryList (&IFile->Link); + + // + // Add the OFile to the check reference list + // + if (OFile->CheckLink.ForwardLink == NULL) { + InsertHeadList (&Volume->CheckRef, &OFile->CheckLink); + } + // + // Done. Free the open instance structure + // + FreePool (IFile); + return EFI_SUCCESS; +} + +/** + + Flush the data associated with an open file. + In this implementation, only last Mod/Access time is updated. + + @param OFile - The open file. + + @retval EFI_SUCCESS - The OFile is flushed successfully. + @return Others - An error occurred when flushing this OFile. + +**/ +EFI_STATUS +FatOFileFlush ( + IN FAT_OFILE *OFile + ) +{ + EFI_STATUS Status; + FAT_OFILE *Parent; + FAT_DIRENT *DirEnt; + FAT_DATE_TIME FatNow; + + // + // Flush each entry up the tree while dirty + // + do { + // + // If the file has a permanent error, then don't write any + // of its data to the device (may be from different media) + // + if (EFI_ERROR (OFile->Error)) { + return OFile->Error; + } + + Parent = OFile->Parent; + DirEnt = OFile->DirEnt; + if (OFile->Dirty) { + // + // Update the last modification time + // + FatGetCurrentFatTime (&FatNow); + CopyMem (&DirEnt->Entry.FileLastAccess, &FatNow.Date, sizeof (FAT_DATE)); + if (!OFile->PreserveLastModification) { + FatGetCurrentFatTime (&DirEnt->Entry.FileModificationTime); + } + + OFile->PreserveLastModification = FALSE; + if (OFile->Archive) { + DirEnt->Entry.Attributes |= FAT_ATTRIBUTE_ARCHIVE; + OFile->Archive = FALSE; + } + // + // Write the directory entry + // + if (Parent != NULL && !DirEnt->Invalid) { + // + // Write the OFile's directory entry + // + Status = FatStoreDirEnt (Parent, DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + } + + OFile->Dirty = FALSE; + } + // + // Check the parent + // + OFile = Parent; + } while (OFile != NULL); + return EFI_SUCCESS; +} + +/** + + Check the references of the OFile. + If the OFile (that is checked) is no longer + referenced, then it is freed. + + @param OFile - The OFile to be checked. + + @retval TRUE - The OFile is not referenced and freed. + @retval FALSE - The OFile is kept. + +**/ +BOOLEAN +FatCheckOFileRef ( + IN FAT_OFILE *OFile + ) +{ + // + // If the OFile is on the check ref list, remove it + // + if (OFile->CheckLink.ForwardLink != NULL) { + RemoveEntryList (&OFile->CheckLink); + OFile->CheckLink.ForwardLink = NULL; + } + + FatOFileFlush (OFile); + // + // Are there any references to this OFile? + // + if (!IsListEmpty (&OFile->Opens) || !IsListEmpty (&OFile->ChildHead)) { + // + // The OFile cannot be freed + // + return FALSE; + } + // + // Free the Ofile + // + FatCloseDirEnt (OFile->DirEnt); + return TRUE; +} + +/** + + Check the references of all open files on the volume. + Any open file (that is checked) that is no longer + referenced, is freed - and its parent open file + is then referenced checked. + + @param Volume - The volume to check the pending open file list. + +**/ +STATIC +VOID +FatCheckVolumeRef ( + IN FAT_VOLUME *Volume + ) +{ + FAT_OFILE *OFile; + FAT_OFILE *Parent; + + // + // Check all files on the pending check list + // + while (!IsListEmpty (&Volume->CheckRef)) { + // + // Start with the first file listed + // + Parent = OFILE_FROM_CHECKLINK (Volume->CheckRef.ForwardLink); + // + // Go up the tree cleaning up any un-referenced OFiles + // + while (Parent != NULL) { + OFile = Parent; + Parent = OFile->Parent; + if (!FatCheckOFileRef (OFile)) { + break; + } + } + } +} + +/** + + Set error status for a specific OFile, reference checking the volume. + If volume is already marked as invalid, and all resources are freed + after reference checking, the file system protocol is uninstalled and + the volume structure is freed. + + @param Volume - the Volume that is to be reference checked and unlocked. + @param OFile - the OFile whose permanent error code is to be set. + @param EfiStatus - error code to be set. + @param Task point to task instance. + + @retval EFI_SUCCESS - Clean up the volume successfully. + @return Others - Cleaning up of the volume is failed. + +**/ +EFI_STATUS +FatCleanupVolume ( + IN FAT_VOLUME *Volume, + IN FAT_OFILE *OFile, + IN EFI_STATUS EfiStatus, + IN FAT_TASK *Task + ) +{ + EFI_STATUS Status; + // + // Flag the OFile + // + if (OFile != NULL) { + FatSetVolumeError (OFile, EfiStatus); + } + // + // Clean up any dangling OFiles that don't have IFiles + // we don't check return status here because we want the + // volume be cleaned up even the volume is invalid. + // + FatCheckVolumeRef (Volume); + if (Volume->Valid) { + // + // Update the free hint info. Volume->FreeInfoPos != 0 + // indicates this a FAT32 volume + // + if (Volume->FreeInfoValid && Volume->FatDirty && Volume->FreeInfoPos) { + Status = FatDiskIo (Volume, WriteDisk, Volume->FreeInfoPos, sizeof (FAT_INFO_SECTOR), &Volume->FatInfoSector, Task); + if (EFI_ERROR (Status)) { + return Status; + } + } + // + // Update that the volume is not dirty + // + if (Volume->FatDirty && Volume->FatType != Fat12) { + Volume->FatDirty = FALSE; + Status = FatAccessVolumeDirty (Volume, WriteFat, &Volume->NotDirtyValue); + if (EFI_ERROR (Status)) { + return Status; + } + } + // + // Flush all dirty cache entries to disk + // + Status = FatVolumeFlushCache (Volume, Task); + if (EFI_ERROR (Status)) { + return Status; + } + } + // + // If the volume is cleared , remove it. + // The only time volume be invalidated is in DriverBindingStop. + // + if (Volume->Root == NULL && !Volume->Valid) { + // + // Free the volume structure + // + FatFreeVolume (Volume); + } + + return EfiStatus; +} + +/** + + Set the OFile and its child OFile with the error Status + + @param OFile - The OFile whose permanent error code is to be set. + @param Status - Error code to be set. + +**/ +VOID +FatSetVolumeError ( + IN FAT_OFILE *OFile, + IN EFI_STATUS Status + ) +{ + LIST_ENTRY *Link; + FAT_OFILE *ChildOFile; + + // + // If this OFile doesn't already have an error, set one + // + if (!EFI_ERROR (OFile->Error)) { + OFile->Error = Status; + } + // + // Set the error on each child OFile + // + for (Link = OFile->ChildHead.ForwardLink; Link != &OFile->ChildHead; Link = Link->ForwardLink) { + ChildOFile = OFILE_FROM_CHILDLINK (Link); + FatSetVolumeError (ChildOFile, Status); + } +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Hash.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Hash.c new file mode 100644 index 00000000..90e33b17 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Hash.c @@ -0,0 +1,166 @@ +/** @file + Hash table operations. + +Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +/** + + Get hash value for long name. + + @param LongNameString - The long name string to be hashed. + + @return HashValue. + +**/ +STATIC +UINT32 +FatHashLongName ( + IN CHAR16 *LongNameString + ) +{ + UINT32 HashValue; + CHAR16 UpCasedLongFileName[EFI_PATH_STRING_LENGTH]; + StrnCpyS ( + UpCasedLongFileName, + ARRAY_SIZE (UpCasedLongFileName), + LongNameString, + ARRAY_SIZE (UpCasedLongFileName) - 1 + ); + FatStrUpr (UpCasedLongFileName); + gBS->CalculateCrc32 (UpCasedLongFileName, StrSize (UpCasedLongFileName), &HashValue); + return (HashValue & HASH_TABLE_MASK); +} + +/** + + Get hash value for short name. + + @param ShortNameString - The short name string to be hashed. + + @return HashValue + +**/ +STATIC +UINT32 +FatHashShortName ( + IN CHAR8 *ShortNameString + ) +{ + UINT32 HashValue; + gBS->CalculateCrc32 (ShortNameString, FAT_NAME_LEN, &HashValue); + return (HashValue & HASH_TABLE_MASK); +} + +/** + + Search the long name hash table for the directory entry. + + @param ODir - The directory to be searched. + @param LongNameString - The long name string to search. + + @return The previous long name hash node of the directory entry. + +**/ +FAT_DIRENT ** +FatLongNameHashSearch ( + IN FAT_ODIR *ODir, + IN CHAR16 *LongNameString + ) +{ + FAT_DIRENT **PreviousHashNode; + for (PreviousHashNode = &ODir->LongNameHashTable[FatHashLongName (LongNameString)]; + *PreviousHashNode != NULL; + PreviousHashNode = &(*PreviousHashNode)->LongNameForwardLink + ) { + if (FatStriCmp (LongNameString, (*PreviousHashNode)->FileString) == 0) { + break; + } + } + + return PreviousHashNode; +} + +/** + + Search the short name hash table for the directory entry. + + @param ODir - The directory to be searched. + @param ShortNameString - The short name string to search. + + @return The previous short name hash node of the directory entry. + +**/ +FAT_DIRENT ** +FatShortNameHashSearch ( + IN FAT_ODIR *ODir, + IN CHAR8 *ShortNameString + ) +{ + FAT_DIRENT **PreviousHashNode; + for (PreviousHashNode = &ODir->ShortNameHashTable[FatHashShortName (ShortNameString)]; + *PreviousHashNode != NULL; + PreviousHashNode = &(*PreviousHashNode)->ShortNameForwardLink + ) { + if (CompareMem (ShortNameString, (*PreviousHashNode)->Entry.FileName, FAT_NAME_LEN) == 0) { + break; + } + } + + return PreviousHashNode; +} + +/** + + Insert directory entry to hash table. + + @param ODir - The parent directory. + @param DirEnt - The directory entry node. + +**/ +VOID +FatInsertToHashTable ( + IN FAT_ODIR *ODir, + IN FAT_DIRENT *DirEnt + ) +{ + FAT_DIRENT **HashTable; + UINT32 HashTableIndex; + + // + // Insert hash table index for short name + // + HashTableIndex = FatHashShortName (DirEnt->Entry.FileName); + HashTable = ODir->ShortNameHashTable; + DirEnt->ShortNameForwardLink = HashTable[HashTableIndex]; + HashTable[HashTableIndex] = DirEnt; + // + // Insert hash table index for long name + // + HashTableIndex = FatHashLongName (DirEnt->FileString); + HashTable = ODir->LongNameHashTable; + DirEnt->LongNameForwardLink = HashTable[HashTableIndex]; + HashTable[HashTableIndex] = DirEnt; +} + +/** + + Delete directory entry from hash table. + + @param ODir - The parent directory. + @param DirEnt - The directory entry node. + +**/ +VOID +FatDeleteFromHashTable ( + IN FAT_ODIR *ODir, + IN FAT_DIRENT *DirEnt + ) +{ + *FatShortNameHashSearch (ODir, DirEnt->Entry.FileName) = DirEnt->ShortNameForwardLink; + *FatLongNameHashSearch (ODir, DirEnt->FileString) = DirEnt->LongNameForwardLink; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Info.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Info.c new file mode 100644 index 00000000..879c2678 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Info.c @@ -0,0 +1,595 @@ +/** @file + Routines dealing with setting/getting file/volume info + +Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + + +**/ + +#include "Fat.h" + +/** + + Get the volume's info into Buffer. + + @param Volume - FAT file system volume. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing volume info. + + @retval EFI_SUCCESS - Get the volume info successfully. + @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. + +**/ +EFI_STATUS +FatGetVolumeInfo ( + IN FAT_VOLUME *Volume, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + + Set the volume's info. + + @param Volume - FAT file system volume. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing the new volume info. + + @retval EFI_SUCCESS - Set the volume info successfully. + @retval EFI_BAD_BUFFER_SIZE - The buffer size is error. + @retval EFI_WRITE_PROTECTED - The volume is read only. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +FatSetVolumeInfo ( + IN FAT_VOLUME *Volume, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + + Set or Get the some types info of the file into Buffer. + + @param IsSet - TRUE:The access is set, else is get + @param FHand - The handle of file + @param Type - The type of the info + @param BufferSize - Size of Buffer + @param Buffer - Buffer containing volume info + + @retval EFI_SUCCESS - Get the info successfully + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file + +**/ +EFI_STATUS +FatSetOrGetInfo ( + IN BOOLEAN IsSet, + IN EFI_FILE_PROTOCOL *FHand, + IN EFI_GUID *Type, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer + ); + +/** + + Get the open file's info into Buffer. + + @param OFile - The open file. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing file info. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. + +**/ +EFI_STATUS +FatGetFileInfo ( + IN FAT_OFILE *OFile, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + return FatGetDirEntInfo (OFile->Volume, OFile->DirEnt, BufferSize, Buffer); +} + +/** + + Get the volume's info into Buffer. + + @param Volume - FAT file system volume. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing volume info. + + @retval EFI_SUCCESS - Get the volume info successfully. + @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. + +**/ +EFI_STATUS +FatGetVolumeInfo ( + IN FAT_VOLUME *Volume, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + UINTN Size; + UINTN NameSize; + UINTN ResultSize; + CHAR16 Name[FAT_NAME_LEN + 1]; + EFI_STATUS Status; + EFI_FILE_SYSTEM_INFO *Info; + UINT8 ClusterAlignment; + + Size = SIZE_OF_EFI_FILE_SYSTEM_INFO; + Status = FatGetVolumeEntry (Volume, Name); + NameSize = StrSize (Name); + ResultSize = Size + NameSize; + ClusterAlignment = Volume->ClusterAlignment; + + // + // If we don't have valid info, compute it now + // + FatComputeFreeInfo (Volume); + + Status = EFI_BUFFER_TOO_SMALL; + if (*BufferSize >= ResultSize) { + Status = EFI_SUCCESS; + + Info = Buffer; + ZeroMem (Info, SIZE_OF_EFI_FILE_SYSTEM_INFO); + + Info->Size = ResultSize; + Info->ReadOnly = Volume->ReadOnly; + Info->BlockSize = (UINT32) Volume->ClusterSize; + Info->VolumeSize = LShiftU64 (Volume->MaxCluster, ClusterAlignment); + Info->FreeSpace = LShiftU64 ( + Volume->FatInfoSector.FreeInfo.ClusterCount, + ClusterAlignment + ); + CopyMem ((CHAR8 *) Buffer + Size, Name, NameSize); + } + + *BufferSize = ResultSize; + return Status; +} + +/** + + Get the volume's label info into Buffer. + + @param Volume - FAT file system volume. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing volume's label info. + + @retval EFI_SUCCESS - Get the volume's label info successfully. + @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. + +**/ +EFI_STATUS +FatGetVolumeLabelInfo ( + IN FAT_VOLUME *Volume, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + UINTN Size; + UINTN NameSize; + UINTN ResultSize; + CHAR16 Name[FAT_NAME_LEN + 1]; + EFI_STATUS Status; + + Size = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL; + Status = FatGetVolumeEntry (Volume, Name); + NameSize = StrSize (Name); + ResultSize = Size + NameSize; + + Status = EFI_BUFFER_TOO_SMALL; + if (*BufferSize >= ResultSize) { + Status = EFI_SUCCESS; + CopyMem ((CHAR8 *) Buffer + Size, Name, NameSize); + } + + *BufferSize = ResultSize; + return Status; +} + +/** + + Set the volume's info. + + @param Volume - FAT file system volume. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing the new volume info. + + @retval EFI_SUCCESS - Set the volume info successfully. + @retval EFI_BAD_BUFFER_SIZE - The buffer size is error. + @retval EFI_WRITE_PROTECTED - The volume is read only. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +FatSetVolumeInfo ( + IN FAT_VOLUME *Volume, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_FILE_SYSTEM_INFO *Info; + + Info = (EFI_FILE_SYSTEM_INFO *) Buffer; + + if (BufferSize < SIZE_OF_EFI_FILE_SYSTEM_INFO + 2 || Info->Size > BufferSize) { + return EFI_BAD_BUFFER_SIZE; + } + + return FatSetVolumeEntry (Volume, Info->VolumeLabel); +} + +/** + + Set the volume's label info. + + @param Volume - FAT file system volume. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing the new volume label info. + + @retval EFI_SUCCESS - Set the volume label info successfully. + @retval EFI_WRITE_PROTECTED - The disk is write protected. + @retval EFI_BAD_BUFFER_SIZE - The buffer size is error. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +FatSetVolumeLabelInfo ( + IN FAT_VOLUME *Volume, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_FILE_SYSTEM_VOLUME_LABEL *Info; + + Info = (EFI_FILE_SYSTEM_VOLUME_LABEL *) Buffer; + + if (BufferSize < SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL + 2) { + return EFI_BAD_BUFFER_SIZE; + } + + return FatSetVolumeEntry (Volume, Info->VolumeLabel); +} + +/** + + Set the file info. + + @param Volume - FAT file system volume. + @param IFile - The instance of the open file. + @param OFile - The open file. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing the new file info. + + @retval EFI_SUCCESS - Set the file info successfully. + @retval EFI_ACCESS_DENIED - It is the root directory + or the directory attribute bit can not change + or try to change a directory size + or something else. + @retval EFI_UNSUPPORTED - The new file size is larger than 4GB. + @retval EFI_WRITE_PROTECTED - The disk is write protected. + @retval EFI_BAD_BUFFER_SIZE - The buffer size is error. + @retval EFI_INVALID_PARAMETER - The time info or attributes info is error. + @retval EFI_OUT_OF_RESOURCES - Can not allocate new memory. + @retval EFI_VOLUME_CORRUPTED - The volume is corrupted. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +FatSetFileInfo ( + IN FAT_VOLUME *Volume, + IN FAT_IFILE *IFile, + IN FAT_OFILE *OFile, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + EFI_FILE_INFO *NewInfo; + FAT_OFILE *DotOFile; + FAT_OFILE *Parent; + CHAR16 NewFileName[EFI_PATH_STRING_LENGTH]; + EFI_TIME ZeroTime; + FAT_DIRENT *DirEnt; + FAT_DIRENT *TempDirEnt; + UINT8 NewAttribute; + BOOLEAN ReadOnly; + + ZeroMem (&ZeroTime, sizeof (EFI_TIME)); + Parent = OFile->Parent; + DirEnt = OFile->DirEnt; + // + // If this is the root directory, we can't make any updates + // + if (Parent == NULL) { + return EFI_ACCESS_DENIED; + } + // + // Make sure there's a valid input buffer + // + NewInfo = Buffer; + if (BufferSize < SIZE_OF_EFI_FILE_INFO + 2 || NewInfo->Size > BufferSize) { + return EFI_BAD_BUFFER_SIZE; + } + + ReadOnly = (BOOLEAN)(IFile->ReadOnly || (DirEnt->Entry.Attributes & EFI_FILE_READ_ONLY)); + // + // if a zero time is specified, then the original time is preserved + // + if (CompareMem (&ZeroTime, &NewInfo->CreateTime, sizeof (EFI_TIME)) != 0) { + if (!FatIsValidTime (&NewInfo->CreateTime)) { + return EFI_INVALID_PARAMETER; + } + + if (!ReadOnly) { + FatEfiTimeToFatTime (&NewInfo->CreateTime, &DirEnt->Entry.FileCreateTime); + } + } + + if (CompareMem (&ZeroTime, &NewInfo->ModificationTime, sizeof (EFI_TIME)) != 0) { + if (!FatIsValidTime (&NewInfo->ModificationTime)) { + return EFI_INVALID_PARAMETER; + } + + if (!ReadOnly) { + FatEfiTimeToFatTime (&NewInfo->ModificationTime, &DirEnt->Entry.FileModificationTime); + } + + OFile->PreserveLastModification = TRUE; + } + + if (NewInfo->Attribute & (~EFI_FILE_VALID_ATTR)) { + return EFI_INVALID_PARAMETER; + } + + NewAttribute = (UINT8) NewInfo->Attribute; + // + // Can not change the directory attribute bit + // + if ((NewAttribute ^ DirEnt->Entry.Attributes) & EFI_FILE_DIRECTORY) { + return EFI_ACCESS_DENIED; + } + // + // Set the current attributes even if the IFile->ReadOnly is TRUE + // + DirEnt->Entry.Attributes = (UINT8) ((DirEnt->Entry.Attributes &~EFI_FILE_VALID_ATTR) | NewAttribute); + // + // Open the filename and see if it refers to an existing file + // + Status = FatLocateOFile (&Parent, NewInfo->FileName, DirEnt->Entry.Attributes, NewFileName); + if (EFI_ERROR (Status)) { + return Status; + } + + if (*NewFileName != 0) { + // + // File was not found. We do not allow rename of the current directory if + // there are open files below the current directory + // + if (!IsListEmpty (&OFile->ChildHead) || Parent == OFile) { + return EFI_ACCESS_DENIED; + } + + if (ReadOnly) { + return EFI_ACCESS_DENIED; + } + + Status = FatRemoveDirEnt (OFile->Parent, DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Create new dirent + // + Status = FatCreateDirEnt (Parent, NewFileName, DirEnt->Entry.Attributes, &TempDirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + + FatCloneDirEnt (TempDirEnt, DirEnt); + FatFreeDirEnt (DirEnt); + DirEnt = TempDirEnt; + DirEnt->OFile = OFile; + OFile->DirEnt = DirEnt; + OFile->Parent = Parent; + RemoveEntryList (&OFile->ChildLink); + InsertHeadList (&Parent->ChildHead, &OFile->ChildLink); + // + // If this is a directory, synchronize its dot directory entry + // + if (OFile->ODir != NULL) { + // + // Synchronize its dot entry + // + FatResetODirCursor (OFile); + ASSERT (OFile->Parent != NULL); + for (DotOFile = OFile; DotOFile != OFile->Parent->Parent; DotOFile = DotOFile->Parent) { + Status = FatGetNextDirEnt (OFile, &DirEnt); + if (EFI_ERROR (Status) || DirEnt == NULL || !FatIsDotDirEnt (DirEnt)) { + return EFI_VOLUME_CORRUPTED; + } + + FatCloneDirEnt (DirEnt, DotOFile->DirEnt); + Status = FatStoreDirEnt (OFile, DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + // + // If the file is renamed, we should append the ARCHIVE attribute + // + OFile->Archive = TRUE; + } else if (Parent != OFile) { + // + // filename is to a different filename that already exists + // + return EFI_ACCESS_DENIED; + } + // + // If the file size has changed, apply it + // + if (NewInfo->FileSize != OFile->FileSize) { + if (OFile->ODir != NULL || ReadOnly) { + // + // If this is a directory or the file is read only, we can't change the file size + // + return EFI_ACCESS_DENIED; + } + + if (NewInfo->FileSize > OFile->FileSize) { + Status = FatExpandOFile (OFile, NewInfo->FileSize); + } else { + Status = FatTruncateOFile (OFile, (UINTN) NewInfo->FileSize); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + FatUpdateDirEntClusterSizeInfo (OFile); + } + + OFile->Dirty = TRUE; + return FatOFileFlush (OFile); +} + +/** + + Set or Get the some types info of the file into Buffer. + + @param IsSet - TRUE:The access is set, else is get + @param FHand - The handle of file + @param Type - The type of the info + @param BufferSize - Size of Buffer + @param Buffer - Buffer containing volume info + + @retval EFI_SUCCESS - Get the info successfully + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file + +**/ +EFI_STATUS +FatSetOrGetInfo ( + IN BOOLEAN IsSet, + IN EFI_FILE_PROTOCOL *FHand, + IN EFI_GUID *Type, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer + ) +{ + FAT_IFILE *IFile; + FAT_OFILE *OFile; + FAT_VOLUME *Volume; + EFI_STATUS Status; + + IFile = IFILE_FROM_FHAND (FHand); + OFile = IFile->OFile; + Volume = OFile->Volume; + + Status = OFile->Error; + if (Status == EFI_NOT_FOUND) { + return EFI_DEVICE_ERROR; + } + + FatWaitNonblockingTask (IFile); + + FatAcquireLock (); + + // + // Verify the file handle isn't in an error state + // + if (!EFI_ERROR (Status)) { + // + // Get the proper information based on the request + // + Status = EFI_UNSUPPORTED; + if (IsSet) { + if (CompareGuid (Type, &gEfiFileInfoGuid)) { + Status = Volume->ReadOnly ? EFI_WRITE_PROTECTED : FatSetFileInfo (Volume, IFile, OFile, *BufferSize, Buffer); + } + + if (CompareGuid (Type, &gEfiFileSystemInfoGuid)) { + Status = Volume->ReadOnly ? EFI_WRITE_PROTECTED : FatSetVolumeInfo (Volume, *BufferSize, Buffer); + } + + if (CompareGuid (Type, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + Status = Volume->ReadOnly ? EFI_WRITE_PROTECTED : FatSetVolumeLabelInfo (Volume, *BufferSize, Buffer); + } + } else { + if (CompareGuid (Type, &gEfiFileInfoGuid)) { + Status = FatGetFileInfo (OFile, BufferSize, Buffer); + } + + if (CompareGuid (Type, &gEfiFileSystemInfoGuid)) { + Status = FatGetVolumeInfo (Volume, BufferSize, Buffer); + } + + if (CompareGuid (Type, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + Status = FatGetVolumeLabelInfo (Volume, BufferSize, Buffer); + } + } + } + + Status = FatCleanupVolume (Volume, NULL, Status, NULL); + + FatReleaseLock (); + return Status; +} + +/** + + Get the some types info of the file into Buffer. + + @param FHand - The handle of file. + @param Type - The type of the info. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing volume info. + + @retval EFI_SUCCESS - Get the info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + +**/ +EFI_STATUS +EFIAPI +FatGetInfo ( + IN EFI_FILE_PROTOCOL *FHand, + IN EFI_GUID *Type, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + return FatSetOrGetInfo (FALSE, FHand, Type, BufferSize, Buffer); +} + +/** + + Set the some types info of the file into Buffer. + + @param FHand - The handle of file. + @param Type - The type of the info. + @param BufferSize - Size of Buffer + @param Buffer - Buffer containing volume info. + + @retval EFI_SUCCESS - Set the info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + +**/ +EFI_STATUS +EFIAPI +FatSetInfo ( + IN EFI_FILE_PROTOCOL *FHand, + IN EFI_GUID *Type, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + return FatSetOrGetInfo (TRUE, FHand, Type, &BufferSize, Buffer); +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Init.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Init.c new file mode 100644 index 00000000..b888e436 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Init.c @@ -0,0 +1,387 @@ +/** @file + Initialization routines. + +Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +/** + + Allocates volume structure, detects FAT file system, installs protocol, + and initialize cache. + + @param Handle - The handle of parent device. + @param DiskIo - The DiskIo of parent device. + @param DiskIo2 - The DiskIo2 of parent device. + @param BlockIo - The BlockIo of parent device. + + @retval EFI_SUCCESS - Allocate a new volume successfully. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory. + @return Others - Allocating a new volume failed. + +**/ +EFI_STATUS +FatAllocateVolume ( + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo + ) +{ + EFI_STATUS Status; + FAT_VOLUME *Volume; + + // + // Allocate a volume structure + // + Volume = AllocateZeroPool (sizeof (FAT_VOLUME)); + if (Volume == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize the structure + // + Volume->Signature = FAT_VOLUME_SIGNATURE; + Volume->Handle = Handle; + Volume->DiskIo = DiskIo; + Volume->DiskIo2 = DiskIo2; + Volume->BlockIo = BlockIo; + Volume->MediaId = BlockIo->Media->MediaId; + Volume->ReadOnly = BlockIo->Media->ReadOnly; + Volume->VolumeInterface.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION; + Volume->VolumeInterface.OpenVolume = FatOpenVolume; + InitializeListHead (&Volume->CheckRef); + InitializeListHead (&Volume->DirCacheList); + // + // Initialize Root Directory entry + // + Volume->RootDirEnt.FileString = Volume->RootFileString; + Volume->RootDirEnt.Entry.Attributes = FAT_ATTRIBUTE_DIRECTORY; + // + // Check to see if there's a file system on the volume + // + Status = FatOpenDevice (Volume); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Initialize cache + // + Status = FatInitializeDiskCache (Volume); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Install our protocol interfaces on the device's handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Volume->Handle, + &gEfiSimpleFileSystemProtocolGuid, + &Volume->VolumeInterface, + NULL + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Volume installed + // + DEBUG ((EFI_D_INIT, "Installed Fat filesystem on %p\n", Handle)); + Volume->Valid = TRUE; + +Done: + if (EFI_ERROR (Status)) { + FatFreeVolume (Volume); + } + + return Status; +} + +/** + + Called by FatDriverBindingStop(), Abandon the volume. + + @param Volume - The volume to be abandoned. + + @retval EFI_SUCCESS - Abandoned the volume successfully. + @return Others - Can not uninstall the protocol interfaces. + +**/ +EFI_STATUS +FatAbandonVolume ( + IN FAT_VOLUME *Volume + ) +{ + EFI_STATUS Status; + BOOLEAN LockedByMe; + + // + // Uninstall the protocol interface. + // + if (Volume->Handle != NULL) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + Volume->Handle, + &gEfiSimpleFileSystemProtocolGuid, + &Volume->VolumeInterface, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + LockedByMe = FALSE; + + // + // Acquire the lock. + // If the caller has already acquired the lock (which + // means we are in the process of some Fat operation), + // we can not acquire again. + // + Status = FatAcquireLockOrFail (); + if (!EFI_ERROR (Status)) { + LockedByMe = TRUE; + } + // + // The volume is still being used. Hence, set error flag for all OFiles still in + // use. In two cases, we could get here. One is EFI_MEDIA_CHANGED, the other is + // EFI_NO_MEDIA. + // + if (Volume->Root != NULL) { + FatSetVolumeError ( + Volume->Root, + Volume->BlockIo->Media->MediaPresent ? EFI_MEDIA_CHANGED : EFI_NO_MEDIA + ); + } + + Volume->Valid = FALSE; + + // + // Release the lock. + // If locked by me, this means DriverBindingStop is NOT + // called within an on-going Fat operation, so we should + // take responsibility to cleanup and free the volume. + // Otherwise, the DriverBindingStop is called within an on-going + // Fat operation, we shouldn't check reference, so just let outer + // FatCleanupVolume do the task. + // + if (LockedByMe) { + FatCleanupVolume (Volume, NULL, EFI_SUCCESS, NULL); + FatReleaseLock (); + } + + return EFI_SUCCESS; +} + +/** + + Detects FAT file system on Disk and set relevant fields of Volume. + + @param Volume - The volume structure. + + @retval EFI_SUCCESS - The Fat File System is detected successfully + @retval EFI_UNSUPPORTED - The volume is not FAT file system. + @retval EFI_VOLUME_CORRUPTED - The volume is corrupted. + +**/ +EFI_STATUS +FatOpenDevice ( + IN OUT FAT_VOLUME *Volume + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + UINT32 DirtyMask; + EFI_DISK_IO_PROTOCOL *DiskIo; + FAT_BOOT_SECTOR FatBs; + FAT_VOLUME_TYPE FatType; + UINTN RootDirSectors; + UINTN FatLba; + UINTN RootLba; + UINTN FirstClusterLba; + UINTN Sectors; + UINTN SectorsPerFat; + UINT8 SectorsPerClusterAlignment; + UINT8 BlockAlignment; + + // + // Read the FAT_BOOT_SECTOR BPB info + // This is the only part of FAT code that uses parent DiskIo, + // Others use FatDiskIo which utilizes a Cache. + // + DiskIo = Volume->DiskIo; + Status = DiskIo->ReadDisk (DiskIo, Volume->MediaId, 0, sizeof (FatBs), &FatBs); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INIT, "FatOpenDevice: read of part_lba failed %r\n", Status)); + return Status; + } + + FatType = FatUndefined; + + // + // Use LargeSectors if Sectors is 0 + // + Sectors = FatBs.FatBsb.Sectors; + if (Sectors == 0) { + Sectors = FatBs.FatBsb.LargeSectors; + } + + SectorsPerFat = FatBs.FatBsb.SectorsPerFat; + if (SectorsPerFat == 0) { + SectorsPerFat = FatBs.FatBse.Fat32Bse.LargeSectorsPerFat; + FatType = Fat32; + } + // + // Is boot sector a fat sector? + // (Note that so far we only know if the sector is FAT32 or not, we don't + // know if the sector is Fat16 or Fat12 until later when we can compute + // the volume size) + // + if (FatBs.FatBsb.ReservedSectors == 0 || FatBs.FatBsb.NumFats == 0 || Sectors == 0) { + return EFI_UNSUPPORTED; + } + + if ((FatBs.FatBsb.SectorSize & (FatBs.FatBsb.SectorSize - 1)) != 0) { + return EFI_UNSUPPORTED; + } + + BlockAlignment = (UINT8) HighBitSet32 (FatBs.FatBsb.SectorSize); + if (BlockAlignment > MAX_BLOCK_ALIGNMENT || BlockAlignment < MIN_BLOCK_ALIGNMENT) { + return EFI_UNSUPPORTED; + } + + if ((FatBs.FatBsb.SectorsPerCluster & (FatBs.FatBsb.SectorsPerCluster - 1)) != 0) { + return EFI_UNSUPPORTED; + } + + SectorsPerClusterAlignment = (UINT8) HighBitSet32 (FatBs.FatBsb.SectorsPerCluster); + if (SectorsPerClusterAlignment > MAX_SECTORS_PER_CLUSTER_ALIGNMENT) { + return EFI_UNSUPPORTED; + } + + if (FatBs.FatBsb.Media <= 0xf7 && + FatBs.FatBsb.Media != 0xf0 && + FatBs.FatBsb.Media != 0x00 && + FatBs.FatBsb.Media != 0x01 + ) { + return EFI_UNSUPPORTED; + } + // + // Initialize fields the volume information for this FatType + // + if (FatType != Fat32) { + if (FatBs.FatBsb.RootEntries == 0) { + return EFI_UNSUPPORTED; + } + // + // Unpack fat12, fat16 info + // + Volume->RootEntries = FatBs.FatBsb.RootEntries; + } else { + // + // If this is fat32, refuse to mount mirror-disabled volumes + // + if ((SectorsPerFat == 0 || FatBs.FatBse.Fat32Bse.FsVersion != 0) || (FatBs.FatBse.Fat32Bse.ExtendedFlags & 0x80)) { + return EFI_UNSUPPORTED; + } + // + // Unpack fat32 info + // + Volume->RootCluster = FatBs.FatBse.Fat32Bse.RootDirFirstCluster; + } + + Volume->NumFats = FatBs.FatBsb.NumFats; + // + // Compute some fat locations + // + BlockSize = FatBs.FatBsb.SectorSize; + RootDirSectors = ((Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) + (BlockSize - 1)) / BlockSize; + + FatLba = FatBs.FatBsb.ReservedSectors; + RootLba = FatBs.FatBsb.NumFats * SectorsPerFat + FatLba; + FirstClusterLba = RootLba + RootDirSectors; + + Volume->FatPos = FatLba * BlockSize; + Volume->FatSize = SectorsPerFat * BlockSize; + + Volume->VolumeSize = LShiftU64 (Sectors, BlockAlignment); + Volume->RootPos = LShiftU64 (RootLba, BlockAlignment); + Volume->FirstClusterPos = LShiftU64 (FirstClusterLba, BlockAlignment); + Volume->MaxCluster = (Sectors - FirstClusterLba) >> SectorsPerClusterAlignment; + Volume->ClusterAlignment = (UINT8)(BlockAlignment + SectorsPerClusterAlignment); + Volume->ClusterSize = (UINTN)1 << (Volume->ClusterAlignment); + + // + // If this is not a fat32, determine if it's a fat16 or fat12 + // + if (FatType != Fat32) { + if (Volume->MaxCluster >= FAT_MAX_FAT16_CLUSTER) { + return EFI_VOLUME_CORRUPTED; + } + + FatType = Volume->MaxCluster < FAT_MAX_FAT12_CLUSTER ? Fat12 : Fat16; + // + // fat12 & fat16 fat-entries are 2 bytes + // + Volume->FatEntrySize = sizeof (UINT16); + DirtyMask = FAT16_DIRTY_MASK; + } else { + if (Volume->MaxCluster < FAT_MAX_FAT16_CLUSTER) { + return EFI_VOLUME_CORRUPTED; + } + // + // fat32 fat-entries are 4 bytes + // + Volume->FatEntrySize = sizeof (UINT32); + DirtyMask = FAT32_DIRTY_MASK; + } + // + // Get the DirtyValue and NotDirtyValue + // We should keep the initial value as the NotDirtyValue + // in case the volume is dirty already + // + if (FatType != Fat12) { + Status = FatAccessVolumeDirty (Volume, ReadDisk, &Volume->NotDirtyValue); + if (EFI_ERROR (Status)) { + return Status; + } + + Volume->DirtyValue = Volume->NotDirtyValue & DirtyMask; + } + // + // If present, read the fat hint info + // + if (FatType == Fat32) { + Volume->FreeInfoPos = FatBs.FatBse.Fat32Bse.FsInfoSector * BlockSize; + if (FatBs.FatBse.Fat32Bse.FsInfoSector != 0) { + FatDiskIo (Volume, ReadDisk, Volume->FreeInfoPos, sizeof (FAT_INFO_SECTOR), &Volume->FatInfoSector, NULL); + if (Volume->FatInfoSector.Signature == FAT_INFO_SIGNATURE && + Volume->FatInfoSector.InfoBeginSignature == FAT_INFO_BEGIN_SIGNATURE && + Volume->FatInfoSector.InfoEndSignature == FAT_INFO_END_SIGNATURE && + Volume->FatInfoSector.FreeInfo.ClusterCount <= Volume->MaxCluster + ) { + Volume->FreeInfoValid = TRUE; + } + } + } + // + // Just make up a FreeInfo.NextCluster for use by allocate cluster + // + if (FAT_MIN_CLUSTER > Volume->FatInfoSector.FreeInfo.NextCluster || + Volume->FatInfoSector.FreeInfo.NextCluster > Volume->MaxCluster + 1 + ) { + Volume->FatInfoSector.FreeInfo.NextCluster = FAT_MIN_CLUSTER; + } + // + // We are now defining FAT Type + // + Volume->FatType = FatType; + ASSERT (FatType != FatUndefined); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Misc.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Misc.c new file mode 100644 index 00000000..d13bd276 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Misc.c @@ -0,0 +1,612 @@ +/** @file + Miscellaneous functions. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Fat.h" +UINT8 mMonthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +/** + + Create the task + + @param IFile - The instance of the open file. + @param Token - A pointer to the token associated with the transaction. + + @return FAT_TASK * - Return the task instance. + +**/ +FAT_TASK * +FatCreateTask ( + FAT_IFILE *IFile, + EFI_FILE_IO_TOKEN *Token + ) +{ + FAT_TASK *Task; + + Task = AllocateZeroPool (sizeof (*Task)); + if (Task != NULL) { + Task->Signature = FAT_TASK_SIGNATURE; + Task->IFile = IFile; + Task->FileIoToken = Token; + InitializeListHead (&Task->Subtasks); + InitializeListHead (&Task->Link); + } + return Task; +} + +/** + + Destroy the task. + + @param Task - The task to be destroyed. + +**/ +VOID +FatDestroyTask ( + FAT_TASK *Task + ) +{ + LIST_ENTRY *Link; + FAT_SUBTASK *Subtask; + + Link = GetFirstNode (&Task->Subtasks); + while (!IsNull (&Task->Subtasks, Link)) { + Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE); + Link = FatDestroySubtask (Subtask); + } + FreePool (Task); +} + +/** + + Wait all non-blocking requests complete. + + @param IFile - The instance of the open file. + +**/ +VOID +FatWaitNonblockingTask ( + FAT_IFILE *IFile + ) +{ + BOOLEAN TaskQueueEmpty; + + do { + EfiAcquireLock (&FatTaskLock); + TaskQueueEmpty = IsListEmpty (&IFile->Tasks); + EfiReleaseLock (&FatTaskLock); + } while (!TaskQueueEmpty); +} + +/** + + Remove the subtask from subtask list. + + @param Subtask - The subtask to be removed. + + @return LIST_ENTRY * - The next node in the list. + +**/ +LIST_ENTRY * +FatDestroySubtask ( + FAT_SUBTASK *Subtask + ) +{ + LIST_ENTRY *Link; + + gBS->CloseEvent (Subtask->DiskIo2Token.Event); + + Link = RemoveEntryList (&Subtask->Link); + FreePool (Subtask); + + return Link; +} + +/** + + Execute the task. + + @param IFile - The instance of the open file. + @param Task - The task to be executed. + + @retval EFI_SUCCESS - The task was executed successfully. + @return other - An error occurred when executing the task. + +**/ +EFI_STATUS +FatQueueTask ( + IN FAT_IFILE *IFile, + IN FAT_TASK *Task + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + FAT_SUBTASK *Subtask; + + // + // Sometimes the Task doesn't contain any subtasks, signal the event directly. + // + if (IsListEmpty (&Task->Subtasks)) { + Task->FileIoToken->Status = EFI_SUCCESS; + gBS->SignalEvent (Task->FileIoToken->Event); + FreePool (Task); + return EFI_SUCCESS; + } + + EfiAcquireLock (&FatTaskLock); + InsertTailList (&IFile->Tasks, &Task->Link); + EfiReleaseLock (&FatTaskLock); + + Status = EFI_SUCCESS; + // + // Use NextLink to store the next link of the list, because Link might be remove from the + // doubly-linked list and get freed in the end of current loop. + // + // Also, list operation APIs like IsNull() and GetNextNode() are avoided during the loop, since + // they may check the validity of doubly-linked lists by traversing them. These APIs cannot + // handle list elements being removed during the traverse. + // + for ( Link = GetFirstNode (&Task->Subtasks), NextLink = GetNextNode (&Task->Subtasks, Link) + ; Link != &Task->Subtasks + ; Link = NextLink, NextLink = Link->ForwardLink + ) { + Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE); + if (Subtask->Write) { + + Status = IFile->OFile->Volume->DiskIo2->WriteDiskEx ( + IFile->OFile->Volume->DiskIo2, + IFile->OFile->Volume->MediaId, + Subtask->Offset, + &Subtask->DiskIo2Token, + Subtask->BufferSize, + Subtask->Buffer + ); + } else { + Status = IFile->OFile->Volume->DiskIo2->ReadDiskEx ( + IFile->OFile->Volume->DiskIo2, + IFile->OFile->Volume->MediaId, + Subtask->Offset, + &Subtask->DiskIo2Token, + Subtask->BufferSize, + Subtask->Buffer + ); + } + if (EFI_ERROR (Status)) { + break; + } + } + + if (EFI_ERROR (Status)) { + EfiAcquireLock (&FatTaskLock); + // + // Remove all the remaining subtasks when failure. + // We shouldn't remove all the tasks because the non-blocking requests have + // been submitted and cannot be canceled. + // + while (!IsNull (&Task->Subtasks, Link)) { + Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE); + Link = FatDestroySubtask (Subtask); + } + + if (IsListEmpty (&Task->Subtasks)) { + RemoveEntryList (&Task->Link); + FreePool (Task); + } else { + // + // If one or more subtasks have been already submitted, set FileIoToken + // to NULL so that the callback won't signal the event. + // + Task->FileIoToken = NULL; + } + + EfiReleaseLock (&FatTaskLock); + } + + return Status; +} + +/** + + Set the volume as dirty or not. + + @param Volume - FAT file system volume. + @param IoMode - The access mode. + @param DirtyValue - Set the volume as dirty or not. + + @retval EFI_SUCCESS - Set the new FAT entry value successfully. + @return other - An error occurred when operation the FAT entries. + +**/ +EFI_STATUS +FatAccessVolumeDirty ( + IN FAT_VOLUME *Volume, + IN IO_MODE IoMode, + IN VOID *DirtyValue + ) +{ + UINTN WriteCount; + + WriteCount = Volume->FatEntrySize; + return FatDiskIo (Volume, IoMode, Volume->FatPos + WriteCount, WriteCount, DirtyValue, NULL); +} + +/** + Invoke a notification event. + + @param Event Event whose notification function is being invoked. + @param Context The pointer to the notification function's context, + which is implementation-dependent. + +**/ +VOID +EFIAPI +FatOnAccessComplete ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + FAT_SUBTASK *Subtask; + FAT_TASK *Task; + + // + // Avoid someone in future breaks the below assumption. + // + ASSERT (EfiGetCurrentTpl () == FatTaskLock.Tpl); + + Subtask = (FAT_SUBTASK *) Context; + Task = Subtask->Task; + Status = Subtask->DiskIo2Token.TransactionStatus; + + ASSERT (Task->Signature == FAT_TASK_SIGNATURE); + ASSERT (Subtask->Signature == FAT_SUBTASK_SIGNATURE); + + // + // Remove the task unconditionally + // + FatDestroySubtask (Subtask); + + // + // Task->FileIoToken is NULL which means the task will be ignored (just recycle the subtask and task memory). + // + if (Task->FileIoToken != NULL) { + if (IsListEmpty (&Task->Subtasks) || EFI_ERROR (Status)) { + Task->FileIoToken->Status = Status; + gBS->SignalEvent (Task->FileIoToken->Event); + // + // Mark Task->FileIoToken to NULL so that the subtasks belonging to the task will be ignored. + // + Task->FileIoToken = NULL; + } + } + + if (IsListEmpty (&Task->Subtasks)) { + RemoveEntryList (&Task->Link); + FreePool (Task); + } +} + +/** + + General disk access function. + + @param Volume - FAT file system volume. + @param IoMode - The access mode (disk read/write or cache access). + @param Offset - The starting byte offset to read from. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing read data. + @param Task point to task instance. + + @retval EFI_SUCCESS - The operation is performed successfully. + @retval EFI_VOLUME_CORRUPTED - The access is + @return Others - The status of read/write the disk + +**/ +EFI_STATUS +FatDiskIo ( + IN FAT_VOLUME *Volume, + IN IO_MODE IoMode, + IN UINT64 Offset, + IN UINTN BufferSize, + IN OUT VOID *Buffer, + IN FAT_TASK *Task + ) +{ + EFI_STATUS Status; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DISK_READ IoFunction; + FAT_SUBTASK *Subtask; + + // + // Verify the IO is in devices range + // + Status = EFI_VOLUME_CORRUPTED; + if (Offset + BufferSize <= Volume->VolumeSize) { + if (CACHE_ENABLED (IoMode)) { + // + // Access cache + // + Status = FatAccessCache (Volume, CACHE_TYPE (IoMode), RAW_ACCESS (IoMode), Offset, BufferSize, Buffer, Task); + } else { + // + // Access disk directly + // + if (Task == NULL) { + // + // Blocking access + // + DiskIo = Volume->DiskIo; + IoFunction = (IoMode == ReadDisk) ? DiskIo->ReadDisk : DiskIo->WriteDisk; + Status = IoFunction (DiskIo, Volume->MediaId, Offset, BufferSize, Buffer); + } else { + // + // Non-blocking access + // + Subtask = AllocateZeroPool (sizeof (*Subtask)); + if (Subtask == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + Subtask->Signature = FAT_SUBTASK_SIGNATURE; + Subtask->Task = Task; + Subtask->Write = (BOOLEAN) (IoMode == WriteDisk); + Subtask->Offset = Offset; + Subtask->Buffer = Buffer; + Subtask->BufferSize = BufferSize; + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + FatOnAccessComplete, + Subtask, + &Subtask->DiskIo2Token.Event + ); + if (!EFI_ERROR (Status)) { + InsertTailList (&Task->Subtasks, &Subtask->Link); + } else { + FreePool (Subtask); + } + } + } + } + } + + if (EFI_ERROR (Status)) { + Volume->DiskError = TRUE; + DEBUG ((EFI_D_ERROR, "FatDiskIo: error %r\n", Status)); + } + + return Status; +} + +/** + + Lock the volume. + +**/ +VOID +FatAcquireLock ( + VOID + ) +{ + EfiAcquireLock (&FatFsLock); +} + +/** + + Lock the volume. + If the lock is already in the acquired state, then EFI_ACCESS_DENIED is returned. + Otherwise, EFI_SUCCESS is returned. + + @retval EFI_SUCCESS - The volume is locked. + @retval EFI_ACCESS_DENIED - The volume could not be locked because it is already locked. + +**/ +EFI_STATUS +FatAcquireLockOrFail ( + VOID + ) +{ + return EfiAcquireLockOrFail (&FatFsLock); +} + +/** + + Unlock the volume. + +**/ +VOID +FatReleaseLock ( + VOID + ) +{ + EfiReleaseLock (&FatFsLock); +} + +/** + + Free directory entry. + + @param DirEnt - The directory entry to be freed. + +**/ +VOID +FatFreeDirEnt ( + IN FAT_DIRENT *DirEnt + ) +{ + if (DirEnt->FileString != NULL) { + FreePool (DirEnt->FileString); + } + + FreePool (DirEnt); +} + +/** + + Free volume structure (including the contents of directory cache and disk cache). + + @param Volume - The volume structure to be freed. + +**/ +VOID +FatFreeVolume ( + IN FAT_VOLUME *Volume + ) +{ + // + // Free disk cache + // + if (Volume->CacheBuffer != NULL) { + FreePool (Volume->CacheBuffer); + } + // + // Free directory cache + // + FatCleanupODirCache (Volume); + FreePool (Volume); +} + +/** + + Translate EFI time to FAT time. + + @param ETime - The time of EFI_TIME. + @param FTime - The time of FAT_DATE_TIME. + +**/ +VOID +FatEfiTimeToFatTime ( + IN EFI_TIME *ETime, + OUT FAT_DATE_TIME *FTime + ) +{ + // + // ignores timezone info in source ETime + // + if (ETime->Year > 1980) { + FTime->Date.Year = (UINT16) (ETime->Year - 1980); + } + + if (ETime->Year >= 1980 + FAT_MAX_YEAR_FROM_1980) { + FTime->Date.Year = FAT_MAX_YEAR_FROM_1980; + } + + FTime->Date.Month = ETime->Month; + FTime->Date.Day = ETime->Day; + FTime->Time.Hour = ETime->Hour; + FTime->Time.Minute = ETime->Minute; + FTime->Time.DoubleSecond = (UINT16) (ETime->Second / 2); +} + +/** + + Translate Fat time to EFI time. + + @param FTime - The time of FAT_DATE_TIME. + @param ETime - The time of EFI_TIME.. + +**/ +VOID +FatFatTimeToEfiTime ( + IN FAT_DATE_TIME *FTime, + OUT EFI_TIME *ETime + ) +{ + ETime->Year = (UINT16) (FTime->Date.Year + 1980); + ETime->Month = (UINT8) FTime->Date.Month; + ETime->Day = (UINT8) FTime->Date.Day; + ETime->Hour = (UINT8) FTime->Time.Hour; + ETime->Minute = (UINT8) FTime->Time.Minute; + ETime->Second = (UINT8) (FTime->Time.DoubleSecond * 2); + ETime->Nanosecond = 0; + ETime->TimeZone = EFI_UNSPECIFIED_TIMEZONE; + ETime->Daylight = 0; +} + +/** + + Get Current FAT time. + + @param FatNow - Current FAT time. + +**/ +VOID +FatGetCurrentFatTime ( + OUT FAT_DATE_TIME *FatNow + ) +{ + EFI_STATUS Status; + EFI_TIME Now; + + Status = gRT->GetTime (&Now, NULL); + if (!EFI_ERROR (Status)) { + FatEfiTimeToFatTime (&Now, FatNow); + } else { + ZeroMem (&Now, sizeof (EFI_TIME)); + Now.Year = 1980; + Now.Month = 1; + Now.Day = 1; + FatEfiTimeToFatTime (&Now, FatNow); + } +} + +/** + + Check whether a time is valid. + + @param Time - The time of EFI_TIME. + + @retval TRUE - The time is valid. + @retval FALSE - The time is not valid. + +**/ +BOOLEAN +FatIsValidTime ( + IN EFI_TIME *Time + ) +{ + UINTN Day; + BOOLEAN ValidTime; + + ValidTime = TRUE; + + // + // Check the fields for range problems + // Fat can only support from 1980 + // + if (Time->Year < 1980 || + Time->Month < 1 || + Time->Month > 12 || + Time->Day < 1 || + Time->Day > 31 || + Time->Hour > 23 || + Time->Minute > 59 || + Time->Second > 59 || + Time->Nanosecond > 999999999 + ) { + + ValidTime = FALSE; + + } else { + // + // Perform a more specific check of the day of the month + // + Day = mMonthDays[Time->Month - 1]; + if (Time->Month == 2 && IS_LEAP_YEAR (Time->Year)) { + Day += 1; + // + // 1 extra day this month + // + } + if (Time->Day > Day) { + ValidTime = FALSE; + } + } + + return ValidTime; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Open.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Open.c new file mode 100644 index 00000000..ac00e899 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Open.c @@ -0,0 +1,320 @@ +/** @file + Routines dealing with file open. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +/** + + Create an Open instance for the existing OFile. + The IFile of the newly opened file is passed out. + + @param OFile - The file that serves as a starting reference point. + @param PtrIFile - The newly generated IFile instance. + + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory for the IFile + @retval EFI_SUCCESS - Create the new IFile for the OFile successfully + +**/ +EFI_STATUS +FatAllocateIFile ( + IN FAT_OFILE *OFile, + OUT FAT_IFILE **PtrIFile + ) +{ + FAT_IFILE *IFile; + + ASSERT_VOLUME_LOCKED (OFile->Volume); + + // + // Allocate a new open instance + // + IFile = AllocateZeroPool (sizeof (FAT_IFILE)); + if (IFile == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IFile->Signature = FAT_IFILE_SIGNATURE; + + CopyMem (&(IFile->Handle), &FatFileInterface, sizeof (EFI_FILE_PROTOCOL)); + + // + // Report the correct revision number based on the DiskIo2 availability + // + if (OFile->Volume->DiskIo2 != NULL) { + IFile->Handle.Revision = EFI_FILE_PROTOCOL_REVISION2; + } else { + IFile->Handle.Revision = EFI_FILE_PROTOCOL_REVISION; + } + + IFile->OFile = OFile; + InsertTailList (&OFile->Opens, &IFile->Link); + InitializeListHead (&IFile->Tasks); + + *PtrIFile = IFile; + return EFI_SUCCESS; +} + +/** + + Open a file for a file name relative to an existing OFile. + The IFile of the newly opened file is passed out. + + @param OFile - The file that serves as a starting reference point. + @param NewIFile - The newly generated IFile instance. + @param FileName - The file name relative to the OFile. + @param OpenMode - Open mode. + @param Attributes - Attributes to set if the file is created. + + + @retval EFI_SUCCESS - Open the file successfully. + @retval EFI_INVALID_PARAMETER - The open mode is conflict with the attributes + or the file name is not valid. + @retval EFI_NOT_FOUND - Conflicts between dir intention and attribute. + @retval EFI_WRITE_PROTECTED - Can't open for write if the volume is read only. + @retval EFI_ACCESS_DENIED - If the file's attribute is read only, and the + open is for read-write fail it. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory. + +**/ +EFI_STATUS +FatOFileOpen ( + IN FAT_OFILE *OFile, + OUT FAT_IFILE **NewIFile, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT8 Attributes + ) +{ + FAT_VOLUME *Volume; + EFI_STATUS Status; + CHAR16 NewFileName[EFI_PATH_STRING_LENGTH]; + FAT_DIRENT *DirEnt; + UINT8 FileAttributes; + BOOLEAN WriteMode; + + DirEnt = NULL; + Volume = OFile->Volume; + ASSERT_VOLUME_LOCKED (Volume); + WriteMode = (BOOLEAN) (OpenMode & EFI_FILE_MODE_WRITE); + if (Volume->ReadOnly && WriteMode) { + return EFI_WRITE_PROTECTED; + } + // + // Verify the source file handle isn't in an error state + // + Status = OFile->Error; + if (EFI_ERROR (Status)) { + return Status; + } + // + // Get new OFile for the file + // + Status = FatLocateOFile (&OFile, FileName, Attributes, NewFileName); + if (EFI_ERROR (Status)) { + return Status; + } + + if (*NewFileName != 0) { + // + // If there's a remaining part of the name, then we had + // better be creating the file in the directory + // + if ((OpenMode & EFI_FILE_MODE_CREATE) == 0) { + return EFI_NOT_FOUND; + } + + Status = FatCreateDirEnt (OFile, NewFileName, Attributes, &DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (DirEnt != NULL); + Status = FatOpenDirEnt (OFile, DirEnt); + if (EFI_ERROR (Status)) { + return Status; + } + + OFile = DirEnt->OFile; + if (OFile->ODir != NULL) { + // + // If we just created a directory, we need to create "." and ".." + // + Status = FatCreateDotDirEnts (OFile); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + // + // If the file's attribute is read only, and the open is for + // read-write, then the access is denied. + // + FileAttributes = OFile->DirEnt->Entry.Attributes; + if ((FileAttributes & EFI_FILE_READ_ONLY) != 0 && (FileAttributes & FAT_ATTRIBUTE_DIRECTORY) == 0 && WriteMode) { + return EFI_ACCESS_DENIED; + } + // + // Create an open instance of the OFile + // + Status = FatAllocateIFile (OFile, NewIFile); + if (EFI_ERROR (Status)) { + return Status; + } + + (*NewIFile)->ReadOnly = (BOOLEAN)!WriteMode; + + DEBUG ((EFI_D_INFO, "FSOpen: Open '%S' %r\n", FileName, Status)); + return FatOFileFlush (OFile); +} + +/** + + Implements OpenEx() of Simple File System Protocol. + + @param FHand - File handle of the file serves as a starting reference point. + @param NewHandle - Handle of the file that is newly opened. + @param FileName - File name relative to FHand. + @param OpenMode - Open mode. + @param Attributes - Attributes to set if the file is created. + @param Token - A pointer to the token associated with the transaction.: + + @retval EFI_INVALID_PARAMETER - The FileName is NULL or the file string is empty. + The OpenMode is not supported. + The Attributes is not the valid attributes. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory for file string. + @retval EFI_SUCCESS - Open the file successfully. + @return Others - The status of open file. + +**/ +EFI_STATUS +EFIAPI +FatOpenEx ( + IN EFI_FILE_PROTOCOL *FHand, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes, + IN OUT EFI_FILE_IO_TOKEN *Token + ) +{ + FAT_IFILE *IFile; + FAT_IFILE *NewIFile; + FAT_OFILE *OFile; + EFI_STATUS Status; + FAT_TASK *Task; + + // + // Perform some parameter checking + // + if (FileName == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Check for a valid mode + // + switch (OpenMode) { + case EFI_FILE_MODE_READ: + case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE: + case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE: + break; + + default: + return EFI_INVALID_PARAMETER; + } + + // + // Check for valid Attributes for file creation case. + // + if (((OpenMode & EFI_FILE_MODE_CREATE) != 0) && (Attributes & (EFI_FILE_READ_ONLY | (~EFI_FILE_VALID_ATTR))) != 0) { + return EFI_INVALID_PARAMETER; + } + + IFile = IFILE_FROM_FHAND (FHand); + OFile = IFile->OFile; + Task = NULL; + + if (Token == NULL) { + FatWaitNonblockingTask (IFile); + } else { + // + // Caller shouldn't call the non-blocking interfaces if the low layer doesn't support DiskIo2. + // But if it calls, the below check can avoid crash. + // + if (FHand->Revision < EFI_FILE_PROTOCOL_REVISION2) { + return EFI_UNSUPPORTED; + } + Task = FatCreateTask (IFile, Token); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + // + // Lock + // + FatAcquireLock (); + + // + // Open the file + // + Status = FatOFileOpen (OFile, &NewIFile, FileName, OpenMode, (UINT8) Attributes); + + // + // If the file was opened, return the handle to the caller + // + if (!EFI_ERROR (Status)) { + *NewHandle = &NewIFile->Handle; + } + // + // Unlock + // + Status = FatCleanupVolume (OFile->Volume, NULL, Status, Task); + FatReleaseLock (); + + if (Token != NULL) { + if (!EFI_ERROR (Status)) { + Status = FatQueueTask (IFile, Task); + } else { + FatDestroyTask (Task); + } + } + + return Status; +} + +/** + + Implements Open() of Simple File System Protocol. + + + @param FHand - File handle of the file serves as a starting reference point. + @param NewHandle - Handle of the file that is newly opened. + @param FileName - File name relative to FHand. + @param OpenMode - Open mode. + @param Attributes - Attributes to set if the file is created. + + @retval EFI_INVALID_PARAMETER - The FileName is NULL or the file string is empty. + The OpenMode is not supported. + The Attributes is not the valid attributes. + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory for file string. + @retval EFI_SUCCESS - Open the file successfully. + @return Others - The status of open file. + +**/ +EFI_STATUS +EFIAPI +FatOpen ( + IN EFI_FILE_PROTOCOL *FHand, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + return FatOpenEx (FHand, NewHandle, FileName, OpenMode, Attributes, NULL); +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/OpenVolume.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/OpenVolume.c new file mode 100644 index 00000000..ecb01df7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/OpenVolume.c @@ -0,0 +1,58 @@ +/** @file + OpenVolume() function of Simple File System Protocol. + +Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +/** + + Implements Simple File System Protocol interface function OpenVolume(). + + @param This - Calling context. + @param File - the Root Directory of the volume. + + @retval EFI_OUT_OF_RESOURCES - Can not allocate the memory. + @retval EFI_VOLUME_CORRUPTED - The FAT type is error. + @retval EFI_SUCCESS - Open the volume successfully. + +**/ +EFI_STATUS +EFIAPI +FatOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **File + ) +{ + EFI_STATUS Status; + FAT_VOLUME *Volume; + FAT_IFILE *IFile; + + Volume = VOLUME_FROM_VOL_INTERFACE (This); + FatAcquireLock (); + + // + // Open Root file + // + Status = FatOpenDirEnt (NULL, &Volume->RootDirEnt); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Open a new instance to the root + // + Status = FatAllocateIFile (Volume->Root, &IFile); + if (!EFI_ERROR (Status)) { + *File = &IFile->Handle; + } + +Done: + + Status = FatCleanupVolume (Volume, Volume->Root, Status, NULL); + FatReleaseLock (); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/ReadWrite.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/ReadWrite.c new file mode 100644 index 00000000..f97a2163 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/ReadWrite.c @@ -0,0 +1,620 @@ +/** @file + Functions that perform file read/write. + +Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Fat.h" + +/** + + Get the file's position of the file. + + + @param FHand - The handle of file. + @param Position - The file's position of the file. + + @retval EFI_SUCCESS - Get the info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_UNSUPPORTED - The open file is not a file. + +**/ +EFI_STATUS +EFIAPI +FatGetPosition ( + IN EFI_FILE_PROTOCOL *FHand, + OUT UINT64 *Position + ) +{ + FAT_IFILE *IFile; + FAT_OFILE *OFile; + + IFile = IFILE_FROM_FHAND (FHand); + OFile = IFile->OFile; + + if (OFile->Error == EFI_NOT_FOUND) { + return EFI_DEVICE_ERROR; + } + + if (OFile->ODir != NULL) { + return EFI_UNSUPPORTED; + } + + *Position = IFile->Position; + return EFI_SUCCESS; +} + +/** + + Set the file's position of the file. + + @param FHand - The handle of file. + @param Position - The file's position of the file. + + @retval EFI_SUCCESS - Set the info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_UNSUPPORTED - Set a directory with a not-zero position. + +**/ +EFI_STATUS +EFIAPI +FatSetPosition ( + IN EFI_FILE_PROTOCOL *FHand, + IN UINT64 Position + ) +{ + FAT_IFILE *IFile; + FAT_OFILE *OFile; + + IFile = IFILE_FROM_FHAND (FHand); + OFile = IFile->OFile; + + if (OFile->Error == EFI_NOT_FOUND) { + return EFI_DEVICE_ERROR; + } + + FatWaitNonblockingTask (IFile); + + // + // If this is a directory, we can only set back to position 0 + // + if (OFile->ODir != NULL) { + if (Position != 0) { + // + // Reset current directory cursor; + // + return EFI_UNSUPPORTED; + } + + FatResetODirCursor (OFile); + } + // + // Set the position + // + if (Position == (UINT64)-1) { + Position = OFile->FileSize; + } + // + // Set the position + // + IFile->Position = Position; + return EFI_SUCCESS; +} + +/** + + Get the file info from the open file of the IFile into Buffer. + + @param IFile - The instance of the open file. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing read data. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval other - An error occurred when operation the disk. + +**/ +EFI_STATUS +FatIFileReadDir ( + IN FAT_IFILE *IFile, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + FAT_OFILE *OFile; + FAT_ODIR *ODir; + FAT_DIRENT *DirEnt; + UINT32 CurrentPos; + + OFile = IFile->OFile; + ODir = OFile->ODir; + CurrentPos = ((UINT32) IFile->Position) / sizeof (FAT_DIRECTORY_ENTRY); + + // + // We need to relocate the directory + // + if (CurrentPos < ODir->CurrentPos) { + // + // The directory cursor has been modified by another IFile, we reset the cursor + // + FatResetODirCursor (OFile); + } + // + // We seek the next directory entry's position + // + do { + Status = FatGetNextDirEnt (OFile, &DirEnt); + if (EFI_ERROR (Status) || DirEnt == NULL) { + // + // Something error occurred or reach the end of directory, + // return 0 buffersize + // + *BufferSize = 0; + goto Done; + } + } while (ODir->CurrentPos <= CurrentPos); + Status = FatGetDirEntInfo (OFile->Volume, DirEnt, BufferSize, Buffer); + +Done: + // + // Update IFile's Position + // + if (!EFI_ERROR (Status)) { + // + // Update IFile->Position, if everything is all right + // + CurrentPos = ODir->CurrentPos; + IFile->Position = CurrentPos * sizeof (FAT_DIRECTORY_ENTRY); + } + + return Status; +} + +/** + + Get the file info from the open file of the IFile into Buffer. + + @param FHand - The file handle to access. + @param IoMode - Indicate whether the access mode is reading or writing. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing read data. + @param Token - A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. + @retval EFI_WRITE_PROTECTED - The disk is write protect. + @retval EFI_ACCESS_DENIED - The file is read-only. + @return other - An error occurred when operating on the disk. + +**/ +EFI_STATUS +FatIFileAccess ( + IN EFI_FILE_PROTOCOL *FHand, + IN IO_MODE IoMode, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer, + IN EFI_FILE_IO_TOKEN *Token + ) +{ + EFI_STATUS Status; + FAT_IFILE *IFile; + FAT_OFILE *OFile; + FAT_VOLUME *Volume; + UINT64 EndPosition; + FAT_TASK *Task; + + IFile = IFILE_FROM_FHAND (FHand); + OFile = IFile->OFile; + Volume = OFile->Volume; + Task = NULL; + + // + // Write to a directory is unsupported + // + if ((OFile->ODir != NULL) && (IoMode == WriteData)) { + return EFI_UNSUPPORTED; + } + + if (OFile->Error == EFI_NOT_FOUND) { + return EFI_DEVICE_ERROR; + } + + if (IoMode == ReadData) { + // + // If position is at EOF, then return device error + // + if (IFile->Position > OFile->FileSize) { + return EFI_DEVICE_ERROR; + } + } else { + // + // Check if the we can write data + // + if (Volume->ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + if (IFile->ReadOnly) { + return EFI_ACCESS_DENIED; + } + } + + if (Token == NULL) { + FatWaitNonblockingTask (IFile); + } else { + // + // Caller shouldn't call the non-blocking interfaces if the low layer doesn't support DiskIo2. + // But if it calls, the below check can avoid crash. + // + if (FHand->Revision < EFI_FILE_PROTOCOL_REVISION2) { + return EFI_UNSUPPORTED; + } + Task = FatCreateTask (IFile, Token); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + FatAcquireLock (); + + Status = OFile->Error; + if (!EFI_ERROR (Status)) { + if (OFile->ODir != NULL) { + // + // Read a directory is supported + // + ASSERT (IoMode == ReadData); + Status = FatIFileReadDir (IFile, BufferSize, Buffer); + OFile = NULL; + } else { + // + // Access a file + // + EndPosition = IFile->Position + *BufferSize; + if (EndPosition > OFile->FileSize) { + // + // The position goes beyond the end of file + // + if (IoMode == ReadData) { + // + // Adjust the actual size read + // + *BufferSize -= (UINTN) EndPosition - OFile->FileSize; + } else { + // + // We expand the file size of OFile + // + Status = FatGrowEof (OFile, EndPosition); + if (EFI_ERROR (Status)) { + // + // Must update the file's info into the file's Directory Entry + // and then flush the dirty cache info into disk. + // + *BufferSize = 0; + FatOFileFlush (OFile); + OFile = NULL; + goto Done; + } + + FatUpdateDirEntClusterSizeInfo (OFile); + } + } + + Status = FatAccessOFile (OFile, IoMode, (UINTN) IFile->Position, BufferSize, Buffer, Task); + IFile->Position += *BufferSize; + } + } + + if (Token != NULL) { + if (!EFI_ERROR (Status)) { + Status = FatQueueTask (IFile, Task); + } else { + FatDestroyTask (Task); + } + } + +Done: + // + // On EFI_SUCCESS case, not calling FatCleanupVolume(): + // 1) The Cache flush operation is avoided to enhance + // performance. Caller is responsible to call Flush() when necessary. + // 2) The volume dirty bit is probably set already, and is expected to be + // cleaned in subsequent Flush() or other operations. + // 3) Write operation doesn't affect OFile/IFile structure, so + // Reference checking is not necessary. + // + if (EFI_ERROR (Status)) { + Status = FatCleanupVolume (Volume, OFile, Status, NULL); + } + + FatReleaseLock (); + return Status; +} + +/** + + Get the file info. + + @param FHand - The handle of the file. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing read data. + + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +EFIAPI +FatRead ( + IN EFI_FILE_PROTOCOL *FHand, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + return FatIFileAccess (FHand, ReadData, BufferSize, Buffer, NULL); +} + +/** + + Get the file info. + + @param FHand - The handle of the file. + @param Token - A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +EFIAPI +FatReadEx ( + IN EFI_FILE_PROTOCOL *FHand, + IN OUT EFI_FILE_IO_TOKEN *Token + ) +{ + return FatIFileAccess (FHand, ReadData, &Token->BufferSize, Token->Buffer, Token); +} + +/** + + Write the content of buffer into files. + + @param FHand - The handle of the file. + @param BufferSize - Size of Buffer. + @param Buffer - Buffer containing write data. + + @retval EFI_SUCCESS - Set the file info successfully. + @retval EFI_WRITE_PROTECTED - The disk is write protect. + @retval EFI_ACCESS_DENIED - The file is read-only. + @retval EFI_DEVICE_ERROR - The OFile is not valid. + @retval EFI_UNSUPPORTED - The open file is not a file. + - The writing file size is larger than 4GB. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +EFIAPI +FatWrite ( + IN EFI_FILE_PROTOCOL *FHand, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + return FatIFileAccess (FHand, WriteData, BufferSize, Buffer, NULL); +} + +/** + + Get the file info. + + @param FHand - The handle of the file. + @param Token - A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS - Get the file info successfully. + @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. + @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. + @return other - An error occurred when operation the disk. + +**/ +EFI_STATUS +EFIAPI +FatWriteEx ( + IN EFI_FILE_PROTOCOL *FHand, + IN OUT EFI_FILE_IO_TOKEN *Token + ) +{ + return FatIFileAccess (FHand, WriteData, &Token->BufferSize, Token->Buffer, Token); +} + +/** + + This function reads data from a file or writes data to a file. + It uses OFile->PosRem to determine how much data can be accessed in one time. + + @param OFile - The open file. + @param IoMode - Indicate whether the access mode is reading or writing. + @param Position - The position where data will be accessed. + @param DataBufferSize - Size of Buffer. + @param UserBuffer - Buffer containing data. + @param Task point to task instance. + + @retval EFI_SUCCESS - Access the data successfully. + @return other - An error occurred when operating on the disk. + +**/ +EFI_STATUS +FatAccessOFile ( + IN FAT_OFILE *OFile, + IN IO_MODE IoMode, + IN UINTN Position, + IN OUT UINTN *DataBufferSize, + IN OUT UINT8 *UserBuffer, + IN FAT_TASK *Task + ) +{ + FAT_VOLUME *Volume; + UINTN Len; + EFI_STATUS Status; + UINTN BufferSize; + + BufferSize = *DataBufferSize; + Volume = OFile->Volume; + ASSERT_VOLUME_LOCKED (Volume); + + Status = EFI_SUCCESS; + while (BufferSize > 0) { + // + // Seek the OFile to the file position + // + Status = FatOFilePosition (OFile, Position, BufferSize); + if (EFI_ERROR (Status)) { + break; + } + // + // Clip length to block run + // + Len = BufferSize > OFile->PosRem ? OFile->PosRem : BufferSize; + + // + // Write the data + // + Status = FatDiskIo (Volume, IoMode, OFile->PosDisk, Len, UserBuffer, Task); + if (EFI_ERROR (Status)) { + break; + } + // + // Data was successfully accessed + // + Position += Len; + UserBuffer += Len; + BufferSize -= Len; + if (IoMode == WriteData) { + OFile->Dirty = TRUE; + OFile->Archive = TRUE; + } + // + // Make sure no outbound occurred + // + ASSERT (Position <= OFile->FileSize); + } + // + // Update the number of bytes accessed + // + *DataBufferSize -= BufferSize; + return Status; +} + +/** + + Expand OFile by appending zero bytes at the end of OFile. + + @param OFile - The open file. + @param ExpandedSize - The number of zero bytes appended at the end of the file. + + @retval EFI_SUCCESS - The file is expanded successfully. + @return other - An error occurred when expanding file. + +**/ +EFI_STATUS +FatExpandOFile ( + IN FAT_OFILE *OFile, + IN UINT64 ExpandedSize + ) +{ + EFI_STATUS Status; + UINTN WritePos; + + WritePos = OFile->FileSize; + Status = FatGrowEof (OFile, ExpandedSize); + if (!EFI_ERROR (Status)) { + Status = FatWriteZeroPool (OFile, WritePos); + } + + return Status; +} + +/** + + Write zero pool from the WritePos to the end of OFile. + + @param OFile - The open file to write zero pool. + @param WritePos - The number of zero bytes written. + + @retval EFI_SUCCESS - Write the zero pool successfully. + @retval EFI_OUT_OF_RESOURCES - Not enough memory to perform the operation. + @return other - An error occurred when writing disk. + +**/ +EFI_STATUS +FatWriteZeroPool ( + IN FAT_OFILE *OFile, + IN UINTN WritePos + ) +{ + EFI_STATUS Status; + VOID *ZeroBuffer; + UINTN AppendedSize; + UINTN BufferSize; + UINTN WriteSize; + + AppendedSize = OFile->FileSize - WritePos; + BufferSize = AppendedSize; + if (AppendedSize > FAT_MAX_ALLOCATE_SIZE) { + // + // If the appended size is larger, maybe we can not allocate the whole + // memory once. So if the growed size is larger than 10M, we just + // allocate 10M memory (one healthy system should have 10M available + // memory), and then write the zerobuffer to the file several times. + // + BufferSize = FAT_MAX_ALLOCATE_SIZE; + } + + ZeroBuffer = AllocateZeroPool (BufferSize); + if (ZeroBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + do { + WriteSize = AppendedSize > BufferSize ? BufferSize : (UINTN) AppendedSize; + AppendedSize -= WriteSize; + Status = FatAccessOFile (OFile, WriteData, WritePos, &WriteSize, ZeroBuffer, NULL); + if (EFI_ERROR (Status)) { + break; + } + + WritePos += WriteSize; + } while (AppendedSize > 0); + + FreePool (ZeroBuffer); + return Status; +} + +/** + + Truncate the OFile to smaller file size. + + @param OFile - The open file. + @param TruncatedSize - The new file size. + + @retval EFI_SUCCESS - The file is truncated successfully. + @return other - An error occurred when truncating file. + +**/ +EFI_STATUS +FatTruncateOFile ( + IN FAT_OFILE *OFile, + IN UINTN TruncatedSize + ) +{ + OFile->FileSize = TruncatedSize; + return FatShrinkEof (OFile); +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/UnicodeCollation.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/UnicodeCollation.c new file mode 100644 index 00000000..48bd203a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/UnicodeCollation.c @@ -0,0 +1,275 @@ +/** @file + Unicode Collation Support component that hides the trivial difference of Unicode Collation + and Unicode collation 2 Protocol. + + Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Fat.h" + +EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollationInterface = NULL; + +/** + Worker function to initialize Unicode Collation support. + + It tries to locate Unicode Collation (2) protocol and matches it with current + platform language code. + + @param AgentHandle The handle used to open Unicode Collation (2) protocol. + @param ProtocolGuid The pointer to Unicode Collation (2) protocol GUID. + @param VariableName The name of the RFC 4646 or ISO 639-2 language variable. + @param DefaultLanguage The default language in case the RFC 4646 or ISO 639-2 language is absent. + + @retval EFI_SUCCESS The Unicode Collation (2) protocol has been successfully located. + @retval Others The Unicode Collation (2) protocol has not been located. + +**/ +EFI_STATUS +InitializeUnicodeCollationSupportWorker ( + IN EFI_HANDLE AgentHandle, + IN EFI_GUID *ProtocolGuid, + IN CONST CHAR16 *VariableName, + IN CONST CHAR8 *DefaultLanguage + ) +{ + EFI_STATUS ReturnStatus; + EFI_STATUS Status; + UINTN NumHandles; + UINTN Index; + EFI_HANDLE *Handles; + EFI_UNICODE_COLLATION_PROTOCOL *Uci; + BOOLEAN Iso639Language; + CHAR8 *Language; + CHAR8 *BestLanguage; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + ProtocolGuid, + NULL, + &NumHandles, + &Handles + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Iso639Language = (BOOLEAN) (ProtocolGuid == &gEfiUnicodeCollationProtocolGuid); + GetEfiGlobalVariable2 (VariableName, (VOID**) &Language, NULL); + + ReturnStatus = EFI_UNSUPPORTED; + for (Index = 0; Index < NumHandles; Index++) { + // + // Open Unicode Collation Protocol + // + Status = gBS->OpenProtocol ( + Handles[Index], + ProtocolGuid, + (VOID **) &Uci, + AgentHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Find the best matching matching language from the supported languages + // of Unicode Collation (2) protocol. + // + BestLanguage = GetBestLanguage ( + Uci->SupportedLanguages, + Iso639Language, + (Language == NULL) ? "" : Language, + DefaultLanguage, + NULL + ); + if (BestLanguage != NULL) { + FreePool (BestLanguage); + mUnicodeCollationInterface = Uci; + ReturnStatus = EFI_SUCCESS; + break; + } + } + + if (Language != NULL) { + FreePool (Language); + } + + FreePool (Handles); + + return ReturnStatus; +} + +/** + Initialize Unicode Collation support. + + It tries to locate Unicode Collation 2 protocol and matches it with current + platform language code. If for any reason the first attempt fails, it then tries to + use Unicode Collation Protocol. + + @param AgentHandle The handle used to open Unicode Collation (2) protocol. + + @retval EFI_SUCCESS The Unicode Collation (2) protocol has been successfully located. + @retval Others The Unicode Collation (2) protocol has not been located. + +**/ +EFI_STATUS +InitializeUnicodeCollationSupport ( + IN EFI_HANDLE AgentHandle + ) +{ + + EFI_STATUS Status; + + Status = EFI_UNSUPPORTED; + + // + // First try to use RFC 4646 Unicode Collation 2 Protocol. + // + Status = InitializeUnicodeCollationSupportWorker ( + AgentHandle, + &gEfiUnicodeCollation2ProtocolGuid, + L"PlatformLang", + (CONST CHAR8 *) PcdGetPtr (PcdUefiVariableDefaultPlatformLang) + ); + // + // If the attempt to use Unicode Collation 2 Protocol fails, then we fall back + // on the ISO 639-2 Unicode Collation Protocol. + // + if (EFI_ERROR (Status)) { + Status = InitializeUnicodeCollationSupportWorker ( + AgentHandle, + &gEfiUnicodeCollationProtocolGuid, + L"Lang", + (CONST CHAR8 *) PcdGetPtr (PcdUefiVariableDefaultLang) + ); + } + + return Status; +} + + +/** + Performs a case-insensitive comparison of two Null-terminated Unicode strings. + + @param S1 A pointer to a Null-terminated Unicode string. + @param S2 A pointer to a Null-terminated Unicode string. + + @retval 0 S1 is equivalent to S2. + @retval >0 S1 is lexically greater than S2. + @retval <0 S1 is lexically less than S2. +**/ +INTN +FatStriCmp ( + IN CHAR16 *S1, + IN CHAR16 *S2 + ) +{ + ASSERT (StrSize (S1) != 0); + ASSERT (StrSize (S2) != 0); + ASSERT (mUnicodeCollationInterface != NULL); + + return mUnicodeCollationInterface->StriColl ( + mUnicodeCollationInterface, + S1, + S2 + ); +} + + +/** + Uppercase a string. + + @param String The string which will be upper-cased. + + +**/ +VOID +FatStrUpr ( + IN OUT CHAR16 *String + ) +{ + ASSERT (StrSize (String) != 0); + ASSERT (mUnicodeCollationInterface != NULL); + + mUnicodeCollationInterface->StrUpr (mUnicodeCollationInterface, String); +} + + +/** + Lowercase a string + + @param String The string which will be lower-cased. + + +**/ +VOID +FatStrLwr ( + IN OUT CHAR16 *String + ) +{ + ASSERT (StrSize (String) != 0); + ASSERT (mUnicodeCollationInterface != NULL); + + mUnicodeCollationInterface->StrLwr (mUnicodeCollationInterface, String); +} + + +/** + Convert FAT string to unicode string. + + @param FatSize The size of FAT string. + @param Fat The FAT string. + @param String The unicode string. + + @return None. + +**/ +VOID +FatFatToStr ( + IN UINTN FatSize, + IN CHAR8 *Fat, + OUT CHAR16 *String + ) +{ + ASSERT (Fat != NULL); + ASSERT (String != NULL); + ASSERT (((UINTN) String & 0x01) == 0); + ASSERT (mUnicodeCollationInterface != NULL); + + mUnicodeCollationInterface->FatToStr (mUnicodeCollationInterface, FatSize, Fat, String); +} + + +/** + Convert unicode string to Fat string. + + @param String The unicode string. + @param FatSize The size of the FAT string. + @param Fat The FAT string. + + @retval TRUE Convert successfully. + @retval FALSE Convert error. + +**/ +BOOLEAN +FatStrToFat ( + IN CHAR16 *String, + IN UINTN FatSize, + OUT CHAR8 *Fat + ) +{ + ASSERT (Fat != NULL); + ASSERT (StrSize (String) != 0); + ASSERT (mUnicodeCollationInterface != NULL); + + return mUnicodeCollationInterface->StrToFat ( + mUnicodeCollationInterface, + String, + FatSize, + Fat + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Eltorito.c b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Eltorito.c new file mode 100644 index 00000000..73ad56a2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Eltorito.c @@ -0,0 +1,233 @@ +/** @file + Routines supporting partition discovery and + logical device reading + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <IndustryStandard/ElTorito.h> +#include "FatLitePeim.h" + +/** + This function finds Eltorito partitions. Main algorithm + is ported from DXE partition driver. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + + @retval TRUE New partitions are detected and logical block devices + are added to block device array + @retval FALSE No new partitions are added + +**/ +BOOLEAN +FatFindEltoritoPartitions ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo + ) +{ + EFI_STATUS Status; + BOOLEAN Found; + PEI_FAT_BLOCK_DEVICE *BlockDev; + PEI_FAT_BLOCK_DEVICE *ParentBlockDev; + UINT32 VolDescriptorLba; + UINT32 Lba; + CDROM_VOLUME_DESCRIPTOR *VolDescriptor; + ELTORITO_CATALOG *Catalog; + UINTN Check; + UINTN Index; + UINTN MaxIndex; + UINT16 *CheckBuffer; + UINT32 SubBlockSize; + UINT32 SectorCount; + UINT32 VolSpaceSize; + + if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) { + return FALSE; + } + + Found = FALSE; + ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]); + VolSpaceSize = 0; + + // + // CD_ROM has the fixed block size as 2048 bytes + // + if (ParentBlockDev->BlockSize != 2048) { + return FALSE; + } + + VolDescriptor = (CDROM_VOLUME_DESCRIPTOR *) PrivateData->BlockData; + Catalog = (ELTORITO_CATALOG *) VolDescriptor; + + // + // the ISO-9660 volume descriptor starts at 32k on the media + // and CD_ROM has the fixed block size as 2048 bytes, so... + // + VolDescriptorLba = 15; + // + // ((16*2048) / Media->BlockSize) - 1; + // + // Loop: handle one volume descriptor per time + // + while (TRUE) { + + VolDescriptorLba += 1; + if (VolDescriptorLba > ParentBlockDev->LastBlock) { + // + // We are pointing past the end of the device so exit + // + break; + } + + Status = FatReadBlock ( + PrivateData, + ParentBlockDevNo, + VolDescriptorLba, + ParentBlockDev->BlockSize, + VolDescriptor + ); + if (EFI_ERROR (Status)) { + break; + } + // + // Check for valid volume descriptor signature + // + if (VolDescriptor->Unknown.Type == CDVOL_TYPE_END || + CompareMem (VolDescriptor->Unknown.Id, CDVOL_ID, sizeof (VolDescriptor->Unknown.Id)) != 0 + ) { + // + // end of Volume descriptor list + // + break; + } + // + // Read the Volume Space Size from Primary Volume Descriptor 81-88 byte + // + if (VolDescriptor->Unknown.Type == CDVOL_TYPE_CODED) { + VolSpaceSize = VolDescriptor->PrimaryVolume.VolSpaceSize[1]; + } + // + // Is it an El Torito volume descriptor? + // + if (CompareMem ( + VolDescriptor->BootRecordVolume.SystemId, + CDVOL_ELTORITO_ID, + sizeof (CDVOL_ELTORITO_ID) - 1 + ) != 0) { + continue; + } + // + // Read in the boot El Torito boot catalog + // + Lba = UNPACK_INT32 (VolDescriptor->BootRecordVolume.EltCatalog); + if (Lba > ParentBlockDev->LastBlock) { + continue; + } + + Status = FatReadBlock ( + PrivateData, + ParentBlockDevNo, + Lba, + ParentBlockDev->BlockSize, + Catalog + ); + if (EFI_ERROR (Status)) { + continue; + } + // + // We don't care too much about the Catalog header's contents, but we do want + // to make sure it looks like a Catalog header + // + if (Catalog->Catalog.Indicator != ELTORITO_ID_CATALOG || Catalog->Catalog.Id55AA != 0xAA55) { + continue; + } + + Check = 0; + CheckBuffer = (UINT16 *) Catalog; + for (Index = 0; Index < sizeof (ELTORITO_CATALOG) / sizeof (UINT16); Index += 1) { + Check += CheckBuffer[Index]; + } + + if ((Check & 0xFFFF) != 0) { + continue; + } + + MaxIndex = ParentBlockDev->BlockSize / sizeof (ELTORITO_CATALOG); + for (Index = 1; Index < MaxIndex; Index += 1) { + // + // Next entry + // + Catalog += 1; + + // + // Check this entry + // + if (Catalog->Boot.Indicator != ELTORITO_ID_SECTION_BOOTABLE || Catalog->Boot.Lba == 0) { + continue; + } + + SubBlockSize = 512; + SectorCount = Catalog->Boot.SectorCount; + + switch (Catalog->Boot.MediaType) { + + case ELTORITO_NO_EMULATION: + SubBlockSize = ParentBlockDev->BlockSize; + SectorCount = Catalog->Boot.SectorCount; + break; + + case ELTORITO_HARD_DISK: + break; + + case ELTORITO_12_DISKETTE: + SectorCount = 0x50 * 0x02 * 0x0F; + break; + + case ELTORITO_14_DISKETTE: + SectorCount = 0x50 * 0x02 * 0x12; + break; + + case ELTORITO_28_DISKETTE: + SectorCount = 0x50 * 0x02 * 0x24; + break; + + default: + SectorCount = 0; + SubBlockSize = ParentBlockDev->BlockSize; + break; + } + + if (SectorCount < 2) { + SectorCount = (VolSpaceSize > ParentBlockDev->LastBlock + 1) ? (UINT32) (ParentBlockDev->LastBlock - Catalog->Boot.Lba + 1) : (UINT32) (VolSpaceSize - Catalog->Boot.Lba); + } + // + // Register this partition + // + if (PrivateData->BlockDeviceCount < PEI_FAT_MAX_BLOCK_DEVICE) { + + Found = TRUE; + + BlockDev = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]); + + BlockDev->BlockSize = SubBlockSize; + BlockDev->LastBlock = SectorCount - 1; + BlockDev->IoAlign = ParentBlockDev->IoAlign; + BlockDev->Logical = TRUE; + BlockDev->PartitionChecked = FALSE; + BlockDev->StartingPos = MultU64x32 (Catalog->Boot.Lba, ParentBlockDev->BlockSize); + BlockDev->ParentDevNo = ParentBlockDevNo; + + PrivateData->BlockDeviceCount++; + } + } + } + + ParentBlockDev->PartitionChecked = TRUE; + + return Found; + +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteAccess.c b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteAccess.c new file mode 100644 index 00000000..99168513 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteAccess.c @@ -0,0 +1,521 @@ +/** @file + FAT file system access routines for FAT recovery PEIM + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FatLitePeim.h" + + +/** + Check if there is a valid FAT in the corresponding Block device + of the volume and if yes, fill in the relevant fields for the + volume structure. Note there should be a valid Block device number + already set. + + @param PrivateData Global memory map for accessing global + variables. + @param Volume On input, the BlockDeviceNumber field of the + Volume should be a valid value. On successful + output, all fields except the VolumeNumber + field is initialized. + + @retval EFI_SUCCESS A FAT is found and the volume structure is + initialized. + @retval EFI_NOT_FOUND There is no FAT on the corresponding device. + @retval EFI_DEVICE_ERROR There is something error while accessing device. + +**/ +EFI_STATUS +FatGetBpbInfo ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN OUT PEI_FAT_VOLUME *Volume + ) +{ + EFI_STATUS Status; + PEI_FAT_BOOT_SECTOR Bpb; + PEI_FAT_BOOT_SECTOR_EX BpbEx; + UINT32 Sectors; + UINT32 SectorsPerFat; + UINT32 RootDirSectors; + UINT64 FatLba; + UINT64 RootLba; + UINT64 FirstClusterLba; + + // + // Read in the BPB + // + Status = FatReadDisk ( + PrivateData, + Volume->BlockDeviceNo, + 0, + sizeof (PEI_FAT_BOOT_SECTOR_EX), + &BpbEx + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem ( + (UINT8 *) (&Bpb), + (UINT8 *) (&BpbEx), + sizeof (PEI_FAT_BOOT_SECTOR) + ); + + Volume->FatType = FatUnknown; + + Sectors = Bpb.Sectors; + if (Sectors == 0) { + Sectors = Bpb.LargeSectors; + } + + SectorsPerFat = Bpb.SectorsPerFat; + if (SectorsPerFat == 0) { + SectorsPerFat = BpbEx.LargeSectorsPerFat; + Volume->FatType = Fat32; + } + // + // Filter out those not a FAT + // + if (Bpb.Ia32Jump[0] != 0xe9 && Bpb.Ia32Jump[0] != 0xeb && Bpb.Ia32Jump[0] != 0x49) { + return EFI_NOT_FOUND; + } + + if (Bpb.ReservedSectors == 0 || Bpb.NoFats == 0 || Sectors == 0) { + return EFI_NOT_FOUND; + } + + if (Bpb.SectorsPerCluster != 1 && + Bpb.SectorsPerCluster != 2 && + Bpb.SectorsPerCluster != 4 && + Bpb.SectorsPerCluster != 8 && + Bpb.SectorsPerCluster != 16 && + Bpb.SectorsPerCluster != 32 && + Bpb.SectorsPerCluster != 64 && + Bpb.SectorsPerCluster != 128 + ) { + return EFI_NOT_FOUND; + } + + if (Volume->FatType == Fat32 && (SectorsPerFat == 0 || BpbEx.FsVersion != 0)) { + return EFI_NOT_FOUND; + } + + if (Bpb.Media != 0xf0 && + Bpb.Media != 0xf8 && + Bpb.Media != 0xf9 && + Bpb.Media != 0xfb && + Bpb.Media != 0xfc && + Bpb.Media != 0xfd && + Bpb.Media != 0xfe && + Bpb.Media != 0xff && + // + // FujitsuFMR + // + Bpb.Media != 0x00 && + Bpb.Media != 0x01 && + Bpb.Media != 0xfa + ) { + return EFI_NOT_FOUND; + } + + if (Volume->FatType != Fat32 && Bpb.RootEntries == 0) { + return EFI_NOT_FOUND; + } + // + // If this is fat32, refuse to mount mirror-disabled volumes + // + if (Volume->FatType == Fat32 && ((BpbEx.ExtendedFlags & 0x80) != 0)) { + return EFI_NOT_FOUND; + } + // + // Fill in the volume structure fields + // (Sectors & SectorsPerFat is computed earlier already) + // + Volume->ClusterSize = Bpb.SectorSize * Bpb.SectorsPerCluster; + Volume->RootEntries = Bpb.RootEntries; + Volume->SectorSize = Bpb.SectorSize; + + RootDirSectors = ((Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) + (Volume->SectorSize - 1)) / Volume->SectorSize; + + FatLba = Bpb.ReservedSectors; + RootLba = Bpb.NoFats * SectorsPerFat + FatLba; + FirstClusterLba = RootLba + RootDirSectors; + + Volume->VolumeSize = MultU64x32 (Sectors, Volume->SectorSize); + Volume->FatPos = MultU64x32 (FatLba, Volume->SectorSize); + Volume->RootDirPos = MultU64x32 (RootLba, Volume->SectorSize); + Volume->FirstClusterPos = MultU64x32 (FirstClusterLba, Volume->SectorSize); + Volume->MaxCluster = (UINT32) (Sectors - FirstClusterLba) / Bpb.SectorsPerCluster; + Volume->RootDirCluster = BpbEx.RootDirFirstCluster; + + // + // If this is not a fat32, determine if it's a fat16 or fat12 + // + if (Volume->FatType != Fat32) { + + if (Volume->MaxCluster >= 65525) { + return EFI_NOT_FOUND; + } + + Volume->FatType = Volume->MaxCluster < 4085 ? Fat12 : Fat16; + } + + return EFI_SUCCESS; +} + + +/** + Gets the next cluster in the cluster chain + + @param PrivateData Global memory map for accessing global variables + @param Volume The volume + @param Cluster The cluster + @param NextCluster The cluster number of the next cluster + + @retval EFI_SUCCESS The address is got + @retval EFI_INVALID_PARAMETER ClusterNo exceeds the MaxCluster of the volume. + @retval EFI_DEVICE_ERROR Read disk error + +**/ +EFI_STATUS +FatGetNextCluster ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN PEI_FAT_VOLUME *Volume, + IN UINT32 Cluster, + OUT UINT32 *NextCluster + ) +{ + EFI_STATUS Status; + UINT64 FatEntryPos; + UINT32 Dummy; + + *NextCluster = 0; + + if (Volume->FatType == Fat32) { + FatEntryPos = Volume->FatPos + MultU64x32 (4, Cluster); + + Status = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 4, NextCluster); + *NextCluster &= 0x0fffffff; + + // + // Pad high bits for our FAT_CLUSTER_... macro definitions to work + // + if ((*NextCluster) >= 0x0ffffff7) { + *NextCluster |= (-1 &~0xf); + } + + } else if (Volume->FatType == Fat16) { + FatEntryPos = Volume->FatPos + MultU64x32 (2, Cluster); + + Status = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster); + + // + // Pad high bits for our FAT_CLUSTER_... macro definitions to work + // + if ((*NextCluster) >= 0xfff7) { + *NextCluster |= (-1 &~0xf); + } + + } else { + FatEntryPos = Volume->FatPos + DivU64x32Remainder (MultU64x32 (3, Cluster), 2, &Dummy); + + Status = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster); + + if ((Cluster & 0x01) != 0) { + *NextCluster = (*NextCluster) >> 4; + } else { + *NextCluster = (*NextCluster) & 0x0fff; + } + // + // Pad high bits for our FAT_CLUSTER_... macro definitions to work + // + if ((*NextCluster) >= 0x0ff7) { + *NextCluster |= (-1 &~0xf); + } + } + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; + +} + + +/** + Set a file's CurrentPos and CurrentCluster, then compute StraightReadAmount. + + @param PrivateData the global memory map + @param File the file + @param Pos the Position which is offset from the file's + CurrentPos + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Pos is beyond file's size. + @retval EFI_DEVICE_ERROR Something error while accessing media. + +**/ +EFI_STATUS +FatSetFilePos ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN PEI_FAT_FILE *File, + IN UINT32 Pos + ) +{ + EFI_STATUS Status; + UINT32 AlignedPos; + UINT32 Offset; + UINT32 Cluster; + UINT32 PrevCluster; + + if (File->IsFixedRootDir) { + + if (Pos >= MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos) { + return EFI_INVALID_PARAMETER; + } + + File->CurrentPos += Pos; + File->StraightReadAmount = (UINT32) (MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos); + + } else { + + DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset); + AlignedPos = (UINT32) File->CurrentPos - (UINT32) Offset; + + while + ( + !FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster) && + AlignedPos + File->Volume->ClusterSize <= File->CurrentPos + Pos + ) { + AlignedPos += File->Volume->ClusterSize; + Status = FatGetNextCluster ( + PrivateData, + File->Volume, + File->CurrentCluster, + &File->CurrentCluster + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + } + + if (FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster)) { + return EFI_INVALID_PARAMETER; + } + + File->CurrentPos += Pos; + // + // Calculate the amount of consecutive cluster occupied by the file. + // FatReadFile() will use it to read these blocks once. + // + File->StraightReadAmount = 0; + Cluster = File->CurrentCluster; + while (!FAT_CLUSTER_FUNCTIONAL (Cluster)) { + File->StraightReadAmount += File->Volume->ClusterSize; + PrevCluster = Cluster; + Status = FatGetNextCluster (PrivateData, File->Volume, Cluster, &Cluster); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if (Cluster != PrevCluster + 1) { + break; + } + } + + DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset); + File->StraightReadAmount -= (UINT32) Offset; + + } + + return EFI_SUCCESS; +} + + +/** + Reads file data. Updates the file's CurrentPos. + + @param PrivateData Global memory map for accessing global variables + @param File The file. + @param Size The amount of data to read. + @param Buffer The buffer storing the data. + + @retval EFI_SUCCESS The data is read. + @retval EFI_INVALID_PARAMETER File is invalid. + @retval EFI_DEVICE_ERROR Something error while accessing media. + +**/ +EFI_STATUS +FatReadFile ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN PEI_FAT_FILE *File, + IN UINTN Size, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + CHAR8 *BufferPtr; + UINT32 Offset; + UINT64 PhysicalAddr; + UINTN Amount; + + BufferPtr = Buffer; + + if (File->IsFixedRootDir) { + // + // This is the fixed root dir in FAT12 and FAT16 + // + if (File->CurrentPos + Size > File->Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) { + return EFI_INVALID_PARAMETER; + } + + Status = FatReadDisk ( + PrivateData, + File->Volume->BlockDeviceNo, + File->Volume->RootDirPos + File->CurrentPos, + Size, + Buffer + ); + File->CurrentPos += (UINT32) Size; + return Status; + + } else { + + if ((File->Attributes & FAT_ATTR_DIRECTORY) == 0) { + Size = Size < (File->FileSize - File->CurrentPos) ? Size : (File->FileSize - File->CurrentPos); + } + // + // This is a normal cluster based file + // + while (Size != 0) { + DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset); + PhysicalAddr = File->Volume->FirstClusterPos + MultU64x32 (File->Volume->ClusterSize, File->CurrentCluster - 2); + + Amount = File->StraightReadAmount; + Amount = Size > Amount ? Amount : Size; + Status = FatReadDisk ( + PrivateData, + File->Volume->BlockDeviceNo, + PhysicalAddr + Offset, + Amount, + BufferPtr + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // Advance the file's current pos and current cluster + // + FatSetFilePos (PrivateData, File, (UINT32) Amount); + + BufferPtr += Amount; + Size -= Amount; + } + + return EFI_SUCCESS; + } +} + + +/** + This function reads the next item in the parent directory and + initializes the output parameter SubFile (CurrentPos is initialized to 0). + The function updates the CurrentPos of the parent dir to after the item read. + If no more items were found, the function returns EFI_NOT_FOUND. + + @param PrivateData Global memory map for accessing global variables + @param ParentDir The parent directory. + @param SubFile The File structure containing the sub file that + is caught. + + @retval EFI_SUCCESS The next sub file is obtained. + @retval EFI_INVALID_PARAMETER The ParentDir is not a directory. + @retval EFI_NOT_FOUND No more sub file exists. + @retval EFI_DEVICE_ERROR Something error while accessing media. + +**/ +EFI_STATUS +FatReadNextDirectoryEntry ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN PEI_FAT_FILE *ParentDir, + OUT PEI_FAT_FILE *SubFile + ) +{ + EFI_STATUS Status; + FAT_DIRECTORY_ENTRY DirEntry; + CHAR16 *Pos; + CHAR16 BaseName[9]; + CHAR16 Ext[4]; + + ZeroMem ((UINT8 *) SubFile, sizeof (PEI_FAT_FILE)); + + // + // Pick a valid directory entry + // + while (1) { + // + // Read one entry + // + Status = FatReadFile (PrivateData, ParentDir, 32, &DirEntry); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // We only search for *FILE* in root directory + // Long file name entry is *NOT* supported + // + if (((DirEntry.Attributes & FAT_ATTR_DIRECTORY) == FAT_ATTR_DIRECTORY) || (DirEntry.Attributes == FAT_ATTR_LFN)) { + continue; + } + // + // if this is a terminator dir entry, just return EFI_NOT_FOUND + // + if (DirEntry.FileName[0] == EMPTY_ENTRY_MARK) { + return EFI_NOT_FOUND; + } + // + // If this not an invalid entry neither an empty entry, this is what we want. + // otherwise we will start a new loop to continue to find something meaningful + // + if ((UINT8) DirEntry.FileName[0] != DELETE_ENTRY_MARK) { + break; + } + } + // + // fill in the output parameter + // + EngFatToStr (8, DirEntry.FileName, BaseName); + EngFatToStr (3, DirEntry.FileName + 8, Ext); + + Pos = (UINT16 *) SubFile->FileName; + SetMem ((UINT8 *) Pos, FAT_MAX_FILE_NAME_LENGTH, 0); + CopyMem ((UINT8 *) Pos, (UINT8 *) BaseName, 2 * (StrLen (BaseName) + 1)); + + if (Ext[0] != 0) { + Pos += StrLen (BaseName); + *Pos = '.'; + Pos++; + CopyMem ((UINT8 *) Pos, (UINT8 *) Ext, 2 * (StrLen (Ext) + 1)); + } + + SubFile->Attributes = DirEntry.Attributes; + SubFile->CurrentCluster = DirEntry.FileCluster; + if (ParentDir->Volume->FatType == Fat32) { + SubFile->CurrentCluster |= DirEntry.FileClusterHigh << 16; + } + + SubFile->CurrentPos = 0; + SubFile->FileSize = DirEntry.FileSize; + SubFile->StartingCluster = SubFile->CurrentCluster; + SubFile->Volume = ParentDir->Volume; + + // + // in Pei phase, time parameters do not need to be filled for minimum use. + // + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteApi.c b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteApi.c new file mode 100644 index 00000000..c2bb062f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteApi.c @@ -0,0 +1,675 @@ +/** @file + FAT recovery PEIM entry point, Ppi Functions and FAT Api functions. + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FatLitePeim.h" + +PEI_FAT_PRIVATE_DATA *mPrivateData = NULL; + +/** + BlockIo installation notification function. Find out all the current BlockIO + PPIs in the system and add them into private data. Assume there is + + @param PeiServices General purpose services available to every + PEIM. + @param NotifyDescriptor The typedef structure of the notification + descriptor. Not used in this function. + @param Ppi The typedef structure of the PPI descriptor. + Not used in this function. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +BlockIoNotifyEntry ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + + +/** + Discover all the block I/O devices to find the FAT volume. + + @param PrivateData Global memory map for accessing global + variables. + @param BlockIo2 Boolean to show whether using BlockIo2 or BlockIo + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +UpdateBlocksAndVolumes ( + IN OUT PEI_FAT_PRIVATE_DATA *PrivateData, + IN BOOLEAN BlockIo2 + ) +{ + EFI_STATUS Status; + EFI_PEI_PPI_DESCRIPTOR *TempPpiDescriptor; + UINTN BlockIoPpiInstance; + EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi; + EFI_PEI_RECOVERY_BLOCK_IO2_PPI *BlockIo2Ppi; + UINTN NumberBlockDevices; + UINTN Index; + EFI_PEI_BLOCK_IO_MEDIA Media; + EFI_PEI_BLOCK_IO2_MEDIA Media2; + PEI_FAT_VOLUME Volume; + EFI_PEI_SERVICES **PeiServices; + + PeiServices = (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (); + BlockIo2Ppi = NULL; + BlockIoPpi = NULL; + // + // Clean up caches + // + for (Index = 0; Index < PEI_FAT_CACHE_SIZE; Index++) { + PrivateData->CacheBuffer[Index].Valid = FALSE; + } + + PrivateData->BlockDeviceCount = 0; + + // + // Find out all Block Io Ppi instances within the system + // Assuming all device Block Io Peims are dispatched already + // + for (BlockIoPpiInstance = 0; BlockIoPpiInstance < PEI_FAT_MAX_BLOCK_IO_PPI; BlockIoPpiInstance++) { + if (BlockIo2) { + Status = PeiServicesLocatePpi ( + &gEfiPeiVirtualBlockIo2PpiGuid, + BlockIoPpiInstance, + &TempPpiDescriptor, + (VOID **) &BlockIo2Ppi + ); + } else { + Status = PeiServicesLocatePpi ( + &gEfiPeiVirtualBlockIoPpiGuid, + BlockIoPpiInstance, + &TempPpiDescriptor, + (VOID **) &BlockIoPpi + ); + } + if (EFI_ERROR (Status)) { + // + // Done with all Block Io Ppis + // + break; + } + + if (BlockIo2) { + Status = BlockIo2Ppi->GetNumberOfBlockDevices ( + PeiServices, + BlockIo2Ppi, + &NumberBlockDevices + ); + } else { + Status = BlockIoPpi->GetNumberOfBlockDevices ( + PeiServices, + BlockIoPpi, + &NumberBlockDevices + ); + } + if (EFI_ERROR (Status)) { + continue; + } + + for (Index = 1; Index <= NumberBlockDevices && PrivateData->BlockDeviceCount < PEI_FAT_MAX_BLOCK_DEVICE; Index++) { + + if (BlockIo2) { + Status = BlockIo2Ppi->GetBlockDeviceMediaInfo ( + PeiServices, + BlockIo2Ppi, + Index, + &Media2 + ); + if (EFI_ERROR (Status) || !Media2.MediaPresent) { + continue; + } + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].BlockIo2 = BlockIo2Ppi; + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].InterfaceType = Media2.InterfaceType; + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].LastBlock = Media2.LastBlock; + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].BlockSize = Media2.BlockSize; + } else { + Status = BlockIoPpi->GetBlockDeviceMediaInfo ( + PeiServices, + BlockIoPpi, + Index, + &Media + ); + if (EFI_ERROR (Status) || !Media.MediaPresent) { + continue; + } + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].BlockIo = BlockIoPpi; + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].DevType = Media.DeviceType; + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].LastBlock = Media.LastBlock; + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].BlockSize = (UINT32) Media.BlockSize; + } + + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].IoAlign = 0; + // + // Not used here + // + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].Logical = FALSE; + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].PartitionChecked = FALSE; + + PrivateData->BlockDevice[PrivateData->BlockDeviceCount].PhysicalDevNo = (UINT8) Index; + PrivateData->BlockDeviceCount++; + } + } + // + // Find out all logical devices + // + FatFindPartitions (PrivateData); + + // + // Build up file system volume array + // + PrivateData->VolumeCount = 0; + for (Index = 0; Index < PrivateData->BlockDeviceCount; Index++) { + Volume.BlockDeviceNo = Index; + Status = FatGetBpbInfo (PrivateData, &Volume); + if (Status == EFI_SUCCESS) { + // + // Add the detected volume to the volume array + // + CopyMem ( + (UINT8 *) &(PrivateData->Volume[PrivateData->VolumeCount]), + (UINT8 *) &Volume, + sizeof (PEI_FAT_VOLUME) + ); + PrivateData->VolumeCount += 1; + if (PrivateData->VolumeCount >= PEI_FAT_MAX_VOLUME) { + break; + } + } + } + + return EFI_SUCCESS; +} + + +/** + BlockIo installation notification function. Find out all the current BlockIO + PPIs in the system and add them into private data. Assume there is + + @param PeiServices General purpose services available to every + PEIM. + @param NotifyDescriptor The typedef structure of the notification + descriptor. Not used in this function. + @param Ppi The typedef structure of the PPI descriptor. + Not used in this function. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +BlockIoNotifyEntry ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + if (CompareGuid (NotifyDescriptor->Guid, &gEfiPeiVirtualBlockIo2PpiGuid)) { + UpdateBlocksAndVolumes (mPrivateData, TRUE); + } else { + UpdateBlocksAndVolumes (mPrivateData, FALSE); + } + return EFI_SUCCESS; +} + + +/** + Installs the Device Recovery Module PPI, Initialize BlockIo Ppi + installation notification + + @param FileHandle Handle of the file being invoked. Type + EFI_PEI_FILE_HANDLE is defined in + FfsFindNextFile(). + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS The entry point was executed successfully. + @retval EFI_OUT_OF_RESOURCES There is no enough memory to complete the + operations. + +**/ +EFI_STATUS +EFIAPI +FatPeimEntry ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Address; + PEI_FAT_PRIVATE_DATA *PrivateData; + + Status = PeiServicesRegisterForShadow (FileHandle); + if (!EFI_ERROR (Status)) { + return Status; + } + + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + (sizeof (PEI_FAT_PRIVATE_DATA) - 1) / PEI_FAT_MEMORY_PAGE_SIZE + 1, + &Address + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + PrivateData = (PEI_FAT_PRIVATE_DATA *) (UINTN) Address; + + // + // Initialize Private Data (to zero, as is required by subsequent operations) + // + ZeroMem ((UINT8 *) PrivateData, sizeof (PEI_FAT_PRIVATE_DATA)); + + PrivateData->Signature = PEI_FAT_PRIVATE_DATA_SIGNATURE; + + // + // Installs Ppi + // + PrivateData->DeviceRecoveryPpi.GetNumberRecoveryCapsules = GetNumberRecoveryCapsules; + PrivateData->DeviceRecoveryPpi.GetRecoveryCapsuleInfo = GetRecoveryCapsuleInfo; + PrivateData->DeviceRecoveryPpi.LoadRecoveryCapsule = LoadRecoveryCapsule; + + PrivateData->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST); + PrivateData->PpiDescriptor.Guid = &gEfiPeiDeviceRecoveryModulePpiGuid; + PrivateData->PpiDescriptor.Ppi = &PrivateData->DeviceRecoveryPpi; + + Status = PeiServicesInstallPpi (&PrivateData->PpiDescriptor); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // Other initializations + // + PrivateData->BlockDeviceCount = 0; + + UpdateBlocksAndVolumes (PrivateData, TRUE); + UpdateBlocksAndVolumes (PrivateData, FALSE); + + // + // PrivateData is allocated now, set it to the module variable + // + mPrivateData = PrivateData; + + // + // Installs Block Io Ppi notification function + // + PrivateData->NotifyDescriptor[0].Flags = + ( + EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK + ); + PrivateData->NotifyDescriptor[0].Guid = &gEfiPeiVirtualBlockIoPpiGuid; + PrivateData->NotifyDescriptor[0].Notify = BlockIoNotifyEntry; + PrivateData->NotifyDescriptor[1].Flags = + ( + EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | + EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST + ); + PrivateData->NotifyDescriptor[1].Guid = &gEfiPeiVirtualBlockIo2PpiGuid; + PrivateData->NotifyDescriptor[1].Notify = BlockIoNotifyEntry; + return PeiServicesNotifyPpi (&PrivateData->NotifyDescriptor[0]); +} + + +/** + Returns the number of DXE capsules residing on the device. + + This function searches for DXE capsules from the associated device and returns + the number and maximum size in bytes of the capsules discovered. Entry 1 is + assumed to be the highest load priority and entry N is assumed to be the lowest + priority. + + @param[in] PeiServices General-purpose services that are available + to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[out] NumberRecoveryCapsules Pointer to a caller-allocated UINTN. On + output, *NumberRecoveryCapsules contains + the number of recovery capsule images + available for retrieval from this PEIM + instance. + + @retval EFI_SUCCESS One or more capsules were discovered. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +GetNumberRecoveryCapsules ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + OUT UINTN *NumberRecoveryCapsules + ) +{ + EFI_STATUS Status; + PEI_FAT_PRIVATE_DATA *PrivateData; + UINTN Index; + UINTN RecoveryCapsuleCount; + PEI_FILE_HANDLE Handle; + + PrivateData = PEI_FAT_PRIVATE_DATA_FROM_THIS (This); + + // + // Search each volume in the root directory for the Recovery capsule + // + RecoveryCapsuleCount = 0; + for (Index = 0; Index < PrivateData->VolumeCount; Index++) { + Status = FindRecoveryFile (PrivateData, Index, (CHAR16 *)PcdGetPtr(PcdRecoveryFileName), &Handle); + if (EFI_ERROR (Status)) { + continue; + } + + RecoveryCapsuleCount++; + } + + *NumberRecoveryCapsules = RecoveryCapsuleCount; + + if (*NumberRecoveryCapsules == 0) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + + +/** + Returns the size and type of the requested recovery capsule. + + This function gets the size and type of the capsule specified by CapsuleInstance. + + @param[in] PeiServices General-purpose services that are available to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[in] CapsuleInstance Specifies for which capsule instance to retrieve + the information. This parameter must be between + one and the value returned by GetNumberRecoveryCapsules() + in NumberRecoveryCapsules. + @param[out] Size A pointer to a caller-allocated UINTN in which + the size of the requested recovery module is + returned. + @param[out] CapsuleType A pointer to a caller-allocated EFI_GUID in which + the type of the requested recovery capsule is + returned. The semantic meaning of the value + returned is defined by the implementation. + + @retval EFI_SUCCESS One or more capsules were discovered. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +GetRecoveryCapsuleInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + IN UINTN CapsuleInstance, + OUT UINTN *Size, + OUT EFI_GUID *CapsuleType + ) +{ + EFI_STATUS Status; + PEI_FAT_PRIVATE_DATA *PrivateData; + UINTN Index; + UINTN BlockDeviceNo; + UINTN RecoveryCapsuleCount; + PEI_FILE_HANDLE Handle; + UINTN NumberRecoveryCapsules; + + Status = GetNumberRecoveryCapsules (PeiServices, This, &NumberRecoveryCapsules); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((CapsuleInstance == 0) || (CapsuleInstance > NumberRecoveryCapsules)) { + return EFI_NOT_FOUND; + } + + PrivateData = PEI_FAT_PRIVATE_DATA_FROM_THIS (This); + + // + // Search each volume in the root directory for the Recovery capsule + // + RecoveryCapsuleCount = 0; + for (Index = 0; Index < PrivateData->VolumeCount; Index++) { + Status = FindRecoveryFile (PrivateData, Index, (CHAR16 *)PcdGetPtr(PcdRecoveryFileName), &Handle); + + if (EFI_ERROR (Status)) { + continue; + } + + if (CapsuleInstance - 1 == RecoveryCapsuleCount) { + // + // Get file size + // + *Size = (UINTN) (((PEI_FAT_FILE *) Handle)->FileSize); + + // + // Find corresponding physical block device + // + BlockDeviceNo = PrivateData->Volume[Index].BlockDeviceNo; + while (PrivateData->BlockDevice[BlockDeviceNo].Logical && BlockDeviceNo < PrivateData->BlockDeviceCount) { + BlockDeviceNo = PrivateData->BlockDevice[BlockDeviceNo].ParentDevNo; + } + // + // Fill in the Capsule Type GUID according to the block device type + // + if (BlockDeviceNo < PrivateData->BlockDeviceCount) { + if (PrivateData->BlockDevice[BlockDeviceNo].BlockIo2 != NULL) { + switch (PrivateData->BlockDevice[BlockDeviceNo].InterfaceType) { + case MSG_ATAPI_DP: + CopyGuid (CapsuleType, &gRecoveryOnFatIdeDiskGuid); + break; + + case MSG_USB_DP: + CopyGuid (CapsuleType, &gRecoveryOnFatUsbDiskGuid); + break; + + case MSG_NVME_NAMESPACE_DP: + CopyGuid (CapsuleType, &gRecoveryOnFatNvmeDiskGuid); + break; + + default: + break; + } + } + if (PrivateData->BlockDevice[BlockDeviceNo].BlockIo != NULL) { + switch (PrivateData->BlockDevice[BlockDeviceNo].DevType) { + case LegacyFloppy: + CopyGuid (CapsuleType, &gRecoveryOnFatFloppyDiskGuid); + break; + + case IdeCDROM: + case IdeLS120: + CopyGuid (CapsuleType, &gRecoveryOnFatIdeDiskGuid); + break; + + case UsbMassStorage: + CopyGuid (CapsuleType, &gRecoveryOnFatUsbDiskGuid); + break; + + default: + break; + } + } + } + + return EFI_SUCCESS; + } + + RecoveryCapsuleCount++; + } + + return EFI_NOT_FOUND; +} + + +/** + Loads a DXE capsule from some media into memory. + + This function, by whatever mechanism, retrieves a DXE capsule from some device + and loads it into memory. Note that the published interface is device neutral. + + @param[in] PeiServices General-purpose services that are available + to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[in] CapsuleInstance Specifies which capsule instance to retrieve. + @param[out] Buffer Specifies a caller-allocated buffer in which + the requested recovery capsule will be returned. + + @retval EFI_SUCCESS The capsule was loaded correctly. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A requested recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +LoadRecoveryCapsule ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + IN UINTN CapsuleInstance, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PEI_FAT_PRIVATE_DATA *PrivateData; + UINTN Index; + UINTN RecoveryCapsuleCount; + PEI_FILE_HANDLE Handle; + UINTN NumberRecoveryCapsules; + + Status = GetNumberRecoveryCapsules (PeiServices, This, &NumberRecoveryCapsules); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((CapsuleInstance == 0) || (CapsuleInstance > NumberRecoveryCapsules)) { + return EFI_NOT_FOUND; + } + + PrivateData = PEI_FAT_PRIVATE_DATA_FROM_THIS (This); + + // + // Search each volume in the root directory for the Recovery capsule + // + RecoveryCapsuleCount = 0; + for (Index = 0; Index < PrivateData->VolumeCount; Index++) { + Status = FindRecoveryFile (PrivateData, Index, (CHAR16 *)PcdGetPtr(PcdRecoveryFileName), &Handle); + if (EFI_ERROR (Status)) { + continue; + } + + if (CapsuleInstance - 1 == RecoveryCapsuleCount) { + + Status = FatReadFile ( + PrivateData, + Handle, + (UINTN) (((PEI_FAT_FILE *) Handle)->FileSize), + Buffer + ); + return Status; + } + + RecoveryCapsuleCount++; + } + + return EFI_NOT_FOUND; +} + + +/** + Finds the recovery file on a FAT volume. + This function finds the recovery file named FileName on a specified FAT volume and returns + its FileHandle pointer. + + @param PrivateData Global memory map for accessing global + variables. + @param VolumeIndex The index of the volume. + @param FileName The recovery file name to find. + @param Handle The output file handle. + + @retval EFI_DEVICE_ERROR Some error occurred when operating the FAT + volume. + @retval EFI_NOT_FOUND The recovery file was not found. + @retval EFI_SUCCESS The recovery file was successfully found on the + FAT volume. + +**/ +EFI_STATUS +FindRecoveryFile ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN VolumeIndex, + IN CHAR16 *FileName, + OUT PEI_FILE_HANDLE *Handle + ) +{ + EFI_STATUS Status; + PEI_FAT_FILE Parent; + PEI_FAT_FILE *File; + + File = &PrivateData->File; + + // + // VolumeIndex must be less than PEI_FAT_MAX_VOLUME because PrivateData->VolumeCount + // cannot be larger than PEI_FAT_MAX_VOLUME when detecting recovery volume. + // + ASSERT (VolumeIndex < PEI_FAT_MAX_VOLUME); + + // + // Construct root directory file + // + ZeroMem (&Parent, sizeof (PEI_FAT_FILE)); + Parent.IsFixedRootDir = (BOOLEAN) ((PrivateData->Volume[VolumeIndex].FatType == Fat32) ? FALSE : TRUE); + Parent.Attributes = FAT_ATTR_DIRECTORY; + Parent.CurrentPos = 0; + Parent.CurrentCluster = Parent.IsFixedRootDir ? 0 : PrivateData->Volume[VolumeIndex].RootDirCluster; + Parent.StartingCluster = Parent.CurrentCluster; + Parent.Volume = &PrivateData->Volume[VolumeIndex]; + + Status = FatSetFilePos (PrivateData, &Parent, 0); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // Search for recovery capsule in root directory + // + Status = FatReadNextDirectoryEntry (PrivateData, &Parent, File); + while (Status == EFI_SUCCESS) { + // + // Compare whether the file name is recovery file name. + // + if (EngStriColl (PrivateData, FileName, File->FileName)) { + break; + } + + Status = FatReadNextDirectoryEntry (PrivateData, &Parent, File); + } + + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Get the recovery file, set its file position to 0. + // + if (File->StartingCluster != 0) { + Status = FatSetFilePos (PrivateData, File, 0); + } + + *Handle = File; + + return EFI_SUCCESS; + +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteApi.h b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteApi.h new file mode 100644 index 00000000..712fa9d3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteApi.h @@ -0,0 +1,25 @@ +/** @file + Definitions for FAT recovery PEIM API functions + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _FAT_API_H_ +#define _FAT_API_H_ + +// +// API data structures +// +typedef VOID *PEI_FILE_HANDLE; + +typedef enum { + Fat12, + Fat16, + Fat32, + FatUnknown +} PEI_FAT_TYPE; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteFmt.h b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteFmt.h new file mode 100644 index 00000000..37ea6404 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteFmt.h @@ -0,0 +1,138 @@ +/** @file + FAT format data structures + +Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _FAT_FMT_H_ +#define _FAT_FMT_H_ + +// +// Definitions +// +#define FAT_ATTR_READ_ONLY 0x01 +#define FAT_ATTR_HIDDEN 0x02 +#define FAT_ATTR_SYSTEM 0x04 +#define FAT_ATTR_VOLUME_ID 0x08 +#define FAT_ATTR_DIRECTORY 0x10 +#define FAT_ATTR_ARCHIVE 0x20 +#define FAT_ATTR_LFN (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID) + +#define FAT_CLUSTER_SPECIAL ((MAX_UINT32 &~0xF) | 0x7) +#define FAT_CLUSTER_FREE 0 +#define FAT_CLUSTER_RESERVED (FAT_CLUSTER_SPECIAL) +#define FAT_CLUSTER_BAD (FAT_CLUSTER_SPECIAL) +#define FAT_CLUSTER_LAST (-1) + +#define DELETE_ENTRY_MARK 0xE5 +#define EMPTY_ENTRY_MARK 0x00 + +#define FAT_CLUSTER_FUNCTIONAL(Cluster) (((Cluster) == 0) || ((Cluster) >= FAT_CLUSTER_SPECIAL)) +#define FAT_CLUSTER_END_OF_CHAIN(Cluster) ((Cluster) > (FAT_CLUSTER_SPECIAL)) + +// +// Directory Entry +// +#pragma pack(1) + +typedef struct { + UINT16 Day : 5; + UINT16 Month : 4; + UINT16 Year : 7; // From 1980 +} FAT_DATE; + +typedef struct { + UINT16 DoubleSecond : 5; + UINT16 Minute : 6; + UINT16 Hour : 5; +} FAT_TIME; + +typedef struct { + FAT_TIME Time; + FAT_DATE Date; +} FAT_DATE_TIME; + +typedef struct { + CHAR8 FileName[11]; // 8.3 filename + UINT8 Attributes; + UINT8 CaseFlag; + UINT8 CreateMillisecond; // (creation milliseconds - ignored) + FAT_DATE_TIME FileCreateTime; + FAT_DATE FileLastAccess; + UINT16 FileClusterHigh; // >= FAT32 + FAT_DATE_TIME FileModificationTime; + UINT16 FileCluster; + UINT32 FileSize; +} FAT_DIRECTORY_ENTRY; + +#pragma pack() +// +// Boot Sector +// +#pragma pack(1) + +typedef struct { + + UINT8 Ia32Jump[3]; + CHAR8 OemId[8]; + + UINT16 SectorSize; + UINT8 SectorsPerCluster; + UINT16 ReservedSectors; + UINT8 NoFats; + UINT16 RootEntries; // < FAT32, root dir is fixed size + UINT16 Sectors; + UINT8 Media; // (ignored) + UINT16 SectorsPerFat; // < FAT32 + UINT16 SectorsPerTrack; // (ignored) + UINT16 Heads; // (ignored) + UINT32 HiddenSectors; // (ignored) + UINT32 LargeSectors; // => FAT32 + UINT8 PhysicalDriveNumber; // (ignored) + UINT8 CurrentHead; // holds boot_sector_dirty bit + UINT8 Signature; // (ignored) + CHAR8 Id[4]; + CHAR8 FatLabel[11]; + CHAR8 SystemId[8]; + +} PEI_FAT_BOOT_SECTOR; + +typedef struct { + + UINT8 Ia32Jump[3]; + CHAR8 OemId[8]; + + UINT16 SectorSize; + UINT8 SectorsPerCluster; + UINT16 ReservedSectors; + UINT8 NoFats; + UINT16 RootEntries; // < FAT32, root dir is fixed size + UINT16 Sectors; + UINT8 Media; // (ignored) + UINT16 SectorsPerFat; // < FAT32 + UINT16 SectorsPerTrack; // (ignored) + UINT16 Heads; // (ignored) + UINT32 HiddenSectors; // (ignored) + UINT32 LargeSectors; // Used if Sectors==0 + UINT32 LargeSectorsPerFat; // FAT32 + UINT16 ExtendedFlags; // FAT32 (ignored) + UINT16 FsVersion; // FAT32 (ignored) + UINT32 RootDirFirstCluster; // FAT32 + UINT16 FsInfoSector; // FAT32 + UINT16 BackupBootSector; // FAT32 + UINT8 Reserved[12]; // FAT32 (ignored) + UINT8 PhysicalDriveNumber; // (ignored) + UINT8 CurrentHead; // holds boot_sector_dirty bit + UINT8 Signature; // (ignored) + CHAR8 Id[4]; + CHAR8 FatLabel[11]; + CHAR8 SystemId[8]; + +} PEI_FAT_BOOT_SECTOR_EX; + +#pragma pack() + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteLib.c b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteLib.c new file mode 100644 index 00000000..2b114391 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteLib.c @@ -0,0 +1,370 @@ +/** @file + General purpose supporting routines for FAT recovery PEIM + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FatLitePeim.h" + + +#define CHAR_FAT_VALID 0x01 + + +/** + Converts a union code character to upper case. + This functions converts a unicode character to upper case. + If the input Letter is not a lower-cased letter, + the original value is returned. + + @param Letter The input unicode character. + + @return The upper cased letter. + +**/ +CHAR16 +ToUpper ( + IN CHAR16 Letter + ) +{ + if ('a' <= Letter && Letter <= 'z') { + Letter = (CHAR16) (Letter - 0x20); + } + + return Letter; +} + + +/** + Reads a block of data from the block device by calling + underlying Block I/O service. + + @param PrivateData Global memory map for accessing global variables + @param BlockDeviceNo The index for the block device number. + @param Lba The logic block address to read data from. + @param BufferSize The size of data in byte to read. + @param Buffer The buffer of the + + @retval EFI_DEVICE_ERROR The specified block device number exceeds the maximum + device number. + @retval EFI_DEVICE_ERROR The maximum address has exceeded the maximum address + of the block device. + +**/ +EFI_STATUS +FatReadBlock ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN BlockDeviceNo, + IN EFI_PEI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PEI_FAT_BLOCK_DEVICE *BlockDev; + + if (BlockDeviceNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) { + return EFI_DEVICE_ERROR; + } + + Status = EFI_SUCCESS; + BlockDev = &(PrivateData->BlockDevice[BlockDeviceNo]); + + if (BufferSize > MultU64x32 (BlockDev->LastBlock - Lba + 1, BlockDev->BlockSize)) { + return EFI_DEVICE_ERROR; + } + + if (!BlockDev->Logical) { + // + // Status = BlockDev->ReadFunc + // (PrivateData->PeiServices, BlockDev->PhysicalDevNo, Lba, BufferSize, Buffer); + // + if (BlockDev->BlockIo2 != NULL) { + Status = BlockDev->BlockIo2->ReadBlocks ( + (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (), + BlockDev->BlockIo2, + BlockDev->PhysicalDevNo, + Lba, + BufferSize, + Buffer + ); + } else { + Status = BlockDev->BlockIo->ReadBlocks ( + (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (), + BlockDev->BlockIo, + BlockDev->PhysicalDevNo, + Lba, + BufferSize, + Buffer + ); + } + + } else { + Status = FatReadDisk ( + PrivateData, + BlockDev->ParentDevNo, + BlockDev->StartingPos + MultU64x32 (Lba, BlockDev->BlockSize), + BufferSize, + Buffer + ); + } + + return Status; +} + + +/** + Find a cache block designated to specific Block device and Lba. + If not found, invalidate an oldest one and use it. (LRU cache) + + @param PrivateData the global memory map. + @param BlockDeviceNo the Block device. + @param Lba the Logical Block Address + @param CachePtr Ptr to the starting address of the memory holding the + data; + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_DEVICE_ERROR Something error while accessing media. + +**/ +EFI_STATUS +FatGetCacheBlock ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN BlockDeviceNo, + IN UINT64 Lba, + OUT CHAR8 **CachePtr + ) +{ + EFI_STATUS Status; + PEI_FAT_CACHE_BUFFER *CacheBuffer; + INTN Index; + STATIC UINT8 Seed; + + Status = EFI_SUCCESS; + CacheBuffer = NULL; + + // + // go through existing cache buffers + // + for (Index = 0; Index < PEI_FAT_CACHE_SIZE; Index++) { + CacheBuffer = &(PrivateData->CacheBuffer[Index]); + if (CacheBuffer->Valid && CacheBuffer->BlockDeviceNo == BlockDeviceNo && CacheBuffer->Lba == Lba) { + break; + } + } + + if (Index < PEI_FAT_CACHE_SIZE) { + *CachePtr = (CHAR8 *) CacheBuffer->Buffer; + return EFI_SUCCESS; + } + // + // We have to find an invalid cache buffer + // + for (Index = 0; Index < PEI_FAT_CACHE_SIZE; Index++) { + if (!PrivateData->CacheBuffer[Index].Valid) { + break; + } + } + // + // Use the cache buffer + // + if (Index == PEI_FAT_CACHE_SIZE) { + Index = (Seed++) % PEI_FAT_CACHE_SIZE; + } + + // + // Current device ID should be less than maximum device ID. + // + if (BlockDeviceNo >= PEI_FAT_MAX_BLOCK_DEVICE) { + return EFI_DEVICE_ERROR; + } + + CacheBuffer = &(PrivateData->CacheBuffer[Index]); + + CacheBuffer->BlockDeviceNo = BlockDeviceNo; + CacheBuffer->Lba = Lba; + CacheBuffer->Size = PrivateData->BlockDevice[BlockDeviceNo].BlockSize; + + // + // Read in the data + // + Status = FatReadBlock ( + PrivateData, + BlockDeviceNo, + Lba, + CacheBuffer->Size, + CacheBuffer->Buffer + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + CacheBuffer->Valid = TRUE; + *CachePtr = (CHAR8 *) CacheBuffer->Buffer; + + return Status; +} + + +/** + Disk reading. + + @param PrivateData the global memory map; + @param BlockDeviceNo the block device to read; + @param StartingAddress the starting address. + @param Size the amount of data to read. + @param Buffer the buffer holding the data + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_DEVICE_ERROR Something error. + +**/ +EFI_STATUS +FatReadDisk ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN BlockDeviceNo, + IN UINT64 StartingAddress, + IN UINTN Size, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + CHAR8 *BufferPtr; + CHAR8 *CachePtr; + UINT32 Offset; + UINT64 Lba; + UINT64 OverRunLba; + UINTN Amount; + + Status = EFI_SUCCESS; + BufferPtr = Buffer; + BlockSize = PrivateData->BlockDevice[BlockDeviceNo].BlockSize; + + // + // Read underrun + // + Lba = DivU64x32Remainder (StartingAddress, BlockSize, &Offset); + Status = FatGetCacheBlock (PrivateData, BlockDeviceNo, Lba, &CachePtr); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Amount = Size < (BlockSize - Offset) ? Size : (BlockSize - Offset); + CopyMem (BufferPtr, CachePtr + Offset, Amount); + + if (Size == Amount) { + return EFI_SUCCESS; + } + + Size -= Amount; + BufferPtr += Amount; + StartingAddress += Amount; + Lba += 1; + + // + // Read aligned parts + // + OverRunLba = Lba + DivU64x32Remainder (Size, BlockSize, &Offset); + + Size -= Offset; + Status = FatReadBlock (PrivateData, BlockDeviceNo, Lba, Size, BufferPtr); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + BufferPtr += Size; + + // + // Read overrun + // + if (Offset != 0) { + Status = FatGetCacheBlock (PrivateData, BlockDeviceNo, OverRunLba, &CachePtr); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + CopyMem (BufferPtr, CachePtr, Offset); + } + + return Status; +} + + +/** + This version is different from the version in Unicode collation + protocol in that this version strips off trailing blanks. + Converts an 8.3 FAT file name using an OEM character set + to a Null-terminated Unicode string. + Here does not expand DBCS FAT chars. + + @param FatSize The size of the string Fat in bytes. + @param Fat A pointer to a Null-terminated string that contains + an 8.3 file name using an OEM character set. + @param Str A pointer to a Null-terminated Unicode string. The + string must be allocated in advance to hold FatSize + Unicode characters + +**/ +VOID +EngFatToStr ( + IN UINTN FatSize, + IN CHAR8 *Fat, + OUT CHAR16 *Str + ) +{ + CHAR16 *String; + + String = Str; + // + // No DBCS issues, just expand and add null terminate to end of string + // + while (*Fat != 0 && FatSize != 0) { + if (*Fat == ' ') { + break; + } + *String = *Fat; + String += 1; + Fat += 1; + FatSize -= 1; + } + + *String = 0; +} + + +/** + Performs a case-insensitive comparison of two Null-terminated Unicode strings. + + @param PrivateData Global memory map for accessing global variables + @param Str1 First string to perform case insensitive comparison. + @param Str2 Second string to perform case insensitive comparison. + +**/ +BOOLEAN +EngStriColl ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN CHAR16 *Str1, + IN CHAR16 *Str2 + ) +{ + CHAR16 UpperS1; + CHAR16 UpperS2; + + UpperS1 = ToUpper (*Str1); + UpperS2 = ToUpper (*Str2); + while (*Str1 != 0) { + if (UpperS1 != UpperS2) { + return FALSE; + } + + Str1++; + Str2++; + UpperS1 = ToUpper (*Str1); + UpperS2 = ToUpper (*Str2); + } + + return (BOOLEAN) ((*Str2 != 0) ? FALSE : TRUE); +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLitePeim.h b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLitePeim.h new file mode 100644 index 00000000..0087123c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLitePeim.h @@ -0,0 +1,522 @@ +/** @file + Data structures for FAT recovery PEIM + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _FAT_PEIM_H_ +#define _FAT_PEIM_H_ + +#include <PiPei.h> + +#include <Guid/RecoveryDevice.h> +#include <Ppi/BlockIo.h> +#include <Ppi/BlockIo2.h> +#include <Ppi/DeviceRecoveryModule.h> + +#include <Library/DebugLib.h> +#include <Library/BaseLib.h> +#include <Library/PeimEntryPoint.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/PcdLib.h> +#include <Library/PeiServicesTablePointerLib.h> +#include <Library/PeiServicesLib.h> + +#include "FatLiteApi.h" +#include "FatLiteFmt.h" + +// +// Definitions +// + +#define PEI_FAT_CACHE_SIZE 4 +#define PEI_FAT_MAX_BLOCK_SIZE 8192 +#define FAT_MAX_FILE_NAME_LENGTH 128 +#define PEI_FAT_MAX_BLOCK_DEVICE 64 +#define PEI_FAT_MAX_BLOCK_IO_PPI 32 +#define PEI_FAT_MAX_VOLUME 64 + +#define PEI_FAT_MEMORY_PAGE_SIZE 0x1000 + +// +// Data Structures +// +// +// The block device +// +typedef struct { + + UINT32 BlockSize; + UINT64 LastBlock; + UINT32 IoAlign; + BOOLEAN Logical; + BOOLEAN PartitionChecked; + + // + // Following fields only valid for logical device + // + CHAR8 PartitionFlag[8]; + UINT64 StartingPos; + UINTN ParentDevNo; + + // + // Following fields only valid for physical device + // + EFI_PEI_BLOCK_DEVICE_TYPE DevType; + UINT8 InterfaceType; + // + // EFI_PEI_READ_BLOCKS ReadFunc; + // + EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIo; + EFI_PEI_RECOVERY_BLOCK_IO2_PPI *BlockIo2; + UINT8 PhysicalDevNo; +} PEI_FAT_BLOCK_DEVICE; + +// +// the Volume structure +// +typedef struct { + + UINTN BlockDeviceNo; + UINTN VolumeNo; + UINT64 VolumeSize; + UINTN MaxCluster; + CHAR16 VolumeLabel[FAT_MAX_FILE_NAME_LENGTH]; + PEI_FAT_TYPE FatType; + UINT64 FatPos; + UINT32 SectorSize; + UINT32 ClusterSize; + UINT64 FirstClusterPos; + UINT64 RootDirPos; + UINT32 RootEntries; + UINT32 RootDirCluster; + +} PEI_FAT_VOLUME; + +// +// File instance +// +typedef struct { + + PEI_FAT_VOLUME *Volume; + CHAR16 FileName[FAT_MAX_FILE_NAME_LENGTH]; + + BOOLEAN IsFixedRootDir; + + UINT32 StartingCluster; + UINT32 CurrentPos; + UINT32 StraightReadAmount; + UINT32 CurrentCluster; + + UINT8 Attributes; + UINT32 FileSize; + +} PEI_FAT_FILE; + +// +// Cache Buffer +// +typedef struct { + + BOOLEAN Valid; + UINTN BlockDeviceNo; + UINT64 Lba; + UINT32 Lru; + UINT64 Buffer[PEI_FAT_MAX_BLOCK_SIZE / 8]; + UINTN Size; + +} PEI_FAT_CACHE_BUFFER; + +// +// Private Data. +// This structure abstracts the whole memory usage in FAT PEIM. +// The entry point routine will get a chunk of memory (by whatever +// means) whose size is sizeof(PEI_FAT_PRIVATE_DATA), which is clean +// in both 32 and 64 bit environment. The boundary of the memory chunk +// should be 64bit aligned. +// +#define PEI_FAT_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('p', 'f', 'a', 't') + +typedef struct { + + UINTN Signature; + EFI_PEI_DEVICE_RECOVERY_MODULE_PPI DeviceRecoveryPpi; + EFI_PEI_PPI_DESCRIPTOR PpiDescriptor; + EFI_PEI_NOTIFY_DESCRIPTOR NotifyDescriptor[2]; + + UINT8 UnicodeCaseMap[0x300]; + CHAR8 *EngUpperMap; + CHAR8 *EngLowerMap; + CHAR8 *EngInfoMap; + + UINT64 BlockData[PEI_FAT_MAX_BLOCK_SIZE / 8]; + UINTN BlockDeviceCount; + PEI_FAT_BLOCK_DEVICE BlockDevice[PEI_FAT_MAX_BLOCK_DEVICE]; + UINTN VolumeCount; + PEI_FAT_VOLUME Volume[PEI_FAT_MAX_VOLUME]; + PEI_FAT_FILE File; + PEI_FAT_CACHE_BUFFER CacheBuffer[PEI_FAT_CACHE_SIZE]; + +} PEI_FAT_PRIVATE_DATA; + +#define PEI_FAT_PRIVATE_DATA_FROM_THIS(a) \ + CR (a, PEI_FAT_PRIVATE_DATA, DeviceRecoveryPpi, PEI_FAT_PRIVATE_DATA_SIGNATURE) + +// +// Extract INT32 from char array +// +#define UNPACK_INT32(a) \ + (INT32) ((((UINT8 *) a)[0] << 0) | (((UINT8 *) a)[1] << 8) | (((UINT8 *) a)[2] << 16) | (((UINT8 *) a)[3] << 24)) + +// +// Extract UINT32 from char array +// +#define UNPACK_UINT32(a) \ + (UINT32) ((((UINT8 *) a)[0] << 0) | (((UINT8 *) a)[1] << 8) | (((UINT8 *) a)[2] << 16) | (((UINT8 *) a)[3] << 24)) + + +// +// API functions +// + +/** + Finds the recovery file on a FAT volume. + This function finds the recovery file named FileName on a specified FAT volume and returns + its FileHandle pointer. + + @param PrivateData Global memory map for accessing global + variables. + @param VolumeIndex The index of the volume. + @param FileName The recovery file name to find. + @param Handle The output file handle. + + @retval EFI_DEVICE_ERROR Some error occurred when operating the FAT + volume. + @retval EFI_NOT_FOUND The recovery file was not found. + @retval EFI_SUCCESS The recovery file was successfully found on the + FAT volume. + +**/ +EFI_STATUS +FindRecoveryFile ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN VolumeIndex, + IN CHAR16 *FileName, + OUT PEI_FILE_HANDLE *Handle + ); + + +/** + Returns the number of DXE capsules residing on the device. + This function, by whatever mechanism, searches for DXE capsules from the associated device and + returns the number and maximum size in bytes of the capsules discovered.Entry 1 is assumed to be + the highest load priority and entry N is assumed to be the lowest priority. + + @param PeiServices General-purpose services that are available to + every PEIM. + @param This Indicates the + EFI_PEI_DEVICE_RECOVERY_MODULE_PPI instance. + @param NumberRecoveryCapsules Pointer to a caller-allocated UINTN.On output, + *NumberRecoveryCapsules contains the number of + recovery capsule images available for retrieval + from this PEIM instance. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +GetNumberRecoveryCapsules ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + OUT UINTN *NumberRecoveryCapsules + ); + + +/** + Returns the size and type of the requested recovery capsule. + This function returns the size and type of the capsule specified by CapsuleInstance. + + @param PeiServices General-purpose services that are available to + every PEIM. + @param This Indicates the + EFI_PEI_DEVICE_RECOVERY_MODULE_PPI instance. + @param CapsuleInstance Specifies for which capsule instance to + retrieve the information.T his parameter must + be between one and the value returned by + GetNumberRecoveryCapsules() in + NumberRecoveryCapsules. + @param Size A pointer to a caller-allocated UINTN in which + the size of the requested recovery module is + returned. + @param CapsuleType A pointer to a caller-allocated EFI_GUID in + which the type of the requested recovery + capsule is returned.T he semantic meaning of + the value returned is defined by the + implementation. + + @retval EFI_SUCCESS The capsule type and size were retrieved. + @retval EFI_INVALID_PARAMETER The input CapsuleInstance does not match any + discovered recovery capsule. + +**/ +EFI_STATUS +EFIAPI +GetRecoveryCapsuleInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + IN UINTN CapsuleInstance, + OUT UINTN *Size, + OUT EFI_GUID *CapsuleType + ); + + +/** + Loads a DXE capsule from some media into memory. + + This function, by whatever mechanism, retrieves a DXE capsule from some device + and loads it into memory. Note that the published interface is device neutral. + + @param[in] PeiServices General-purpose services that are available + to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[in] CapsuleInstance Specifies which capsule instance to retrieve. + @param[out] Buffer Specifies a caller-allocated buffer in which + the requested recovery capsule will be returned. + + @retval EFI_SUCCESS The capsule was loaded correctly. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A requested recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +LoadRecoveryCapsule ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + IN UINTN CapsuleInstance, + OUT VOID *Buffer + ); + + +/** + This version is different from the version in Unicode collation + protocol in that this version strips off trailing blanks. + Converts an 8.3 FAT file name using an OEM character set + to a Null-terminated Unicode string. + Here does not expand DBCS FAT chars. + + @param FatSize The size of the string Fat in bytes. + @param Fat A pointer to a Null-terminated string that contains + an 8.3 file name using an OEM character set. + @param Str A pointer to a Null-terminated Unicode string. The + string must be allocated in advance to hold FatSize + Unicode characters + +**/ +VOID +EngFatToStr ( + IN UINTN FatSize, + IN CHAR8 *Fat, + OUT CHAR16 *Str + ); + + +/** + Performs a case-insensitive comparison of two Null-terminated Unicode strings. + + @param PrivateData Global memory map for accessing global variables + @param Str1 First string to perform case insensitive comparison. + @param Str2 Second string to perform case insensitive comparison. + +**/ +BOOLEAN +EngStriColl ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN CHAR16 *Str1, + IN CHAR16 *Str2 + ); + + +/** + Reads a block of data from the block device by calling + underlying Block I/O service. + + @param PrivateData Global memory map for accessing global variables + @param BlockDeviceNo The index for the block device number. + @param Lba The logic block address to read data from. + @param BufferSize The size of data in byte to read. + @param Buffer The buffer of the + + @retval EFI_DEVICE_ERROR The specified block device number exceeds the maximum + device number. + @retval EFI_DEVICE_ERROR The maximum address has exceeded the maximum address + of the block device. + +**/ +EFI_STATUS +FatReadBlock ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN BlockDeviceNo, + IN EFI_PEI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + + +/** + Check if there is a valid FAT in the corresponding Block device + of the volume and if yes, fill in the relevant fields for the + volume structure. Note there should be a valid Block device number + already set. + + @param PrivateData Global memory map for accessing global + variables. + @param Volume On input, the BlockDeviceNumber field of the + Volume should be a valid value. On successful + output, all fields except the VolumeNumber + field is initialized. + + @retval EFI_SUCCESS A FAT is found and the volume structure is + initialized. + @retval EFI_NOT_FOUND There is no FAT on the corresponding device. + @retval EFI_DEVICE_ERROR There is something error while accessing device. + +**/ +EFI_STATUS +FatGetBpbInfo ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN OUT PEI_FAT_VOLUME *Volume + ); + + +/** + Gets the next cluster in the cluster chain. + + @param PrivateData Global memory map for accessing global variables + @param Volume The volume + @param Cluster The cluster + @param NextCluster The cluster number of the next cluster + + @retval EFI_SUCCESS The address is got + @retval EFI_INVALID_PARAMETER ClusterNo exceeds the MaxCluster of the volume. + @retval EFI_DEVICE_ERROR Read disk error + +**/ +EFI_STATUS +FatGetNextCluster ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN PEI_FAT_VOLUME *Volume, + IN UINT32 Cluster, + OUT UINT32 *NextCluster + ); + + +/** + Disk reading. + + @param PrivateData the global memory map; + @param BlockDeviceNo the block device to read; + @param StartingAddress the starting address. + @param Size the amount of data to read. + @param Buffer the buffer holding the data + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_DEVICE_ERROR Something error. + +**/ +EFI_STATUS +FatReadDisk ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN BlockDeviceNo, + IN UINT64 StartingAddress, + IN UINTN Size, + OUT VOID *Buffer + ); + + +/** + Set a file's CurrentPos and CurrentCluster, then compute StraightReadAmount. + + @param PrivateData the global memory map + @param File the file + @param Pos the Position which is offset from the file's + CurrentPos + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Pos is beyond file's size. + @retval EFI_DEVICE_ERROR Something error while accessing media. + +**/ +EFI_STATUS +FatSetFilePos ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN PEI_FAT_FILE *File, + IN UINT32 Pos + ); + + +/** + Reads file data. Updates the file's CurrentPos. + + @param PrivateData Global memory map for accessing global variables + @param File The file. + @param Size The amount of data to read. + @param Buffer The buffer storing the data. + + @retval EFI_SUCCESS The data is read. + @retval EFI_INVALID_PARAMETER File is invalid. + @retval EFI_DEVICE_ERROR Something error while accessing media. + +**/ +EFI_STATUS +FatReadFile ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN PEI_FAT_FILE *File, + IN UINTN Size, + OUT VOID *Buffer + ); + + +/** + This function reads the next item in the parent directory and + initializes the output parameter SubFile (CurrentPos is initialized to 0). + The function updates the CurrentPos of the parent dir to after the item read. + If no more items were found, the function returns EFI_NOT_FOUND. + + @param PrivateData Global memory map for accessing global variables + @param ParentDir The parent directory. + @param SubFile The File structure containing the sub file that + is caught. + + @retval EFI_SUCCESS The next sub file is obtained. + @retval EFI_INVALID_PARAMETER The ParentDir is not a directory. + @retval EFI_NOT_FOUND No more sub file exists. + @retval EFI_DEVICE_ERROR Something error while accessing media. + +**/ +EFI_STATUS +FatReadNextDirectoryEntry ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN PEI_FAT_FILE *ParentDir, + OUT PEI_FAT_FILE *SubFile + ); + + +/** + This function finds partitions (logical devices) in physical block devices. + + @param PrivateData Global memory map for accessing global variables. + +**/ +VOID +FatFindPartitions ( + IN PEI_FAT_PRIVATE_DATA *PrivateData + ); + +#endif // _FAT_PEIM_H_ diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPei.inf b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPei.inf new file mode 100644 index 00000000..02c5a23f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPei.inf @@ -0,0 +1,75 @@ +## @file +# Lite Fat driver only used in Pei Phase. +# +# Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FatPei + MODULE_UNI_FILE = FatPei.uni + FILE_GUID = 5B60CCFD-1011-4BCF-B7D1-BB99CA96A603 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = FatPeimEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + Mbr.c + Gpt.c + Eltorito.c + Part.c + FatLiteApi.c + FatLiteLib.c + FatLiteAccess.c + FatLiteApi.h + FatLitePeim.h + FatLiteFmt.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + PcdLib + BaseMemoryLib + MemoryAllocationLib + PeimEntryPoint + BaseLib + DebugLib + PeiServicesTablePointerLib + PeiServicesLib + + +[Guids] + gRecoveryOnFatUsbDiskGuid ## SOMETIMES_CONSUMES ## UNDEFINED + gRecoveryOnFatIdeDiskGuid ## SOMETIMES_CONSUMES ## UNDEFINED + gRecoveryOnFatFloppyDiskGuid ## SOMETIMES_CONSUMES ## UNDEFINED + gRecoveryOnFatNvmeDiskGuid ## SOMETIMES_CONSUMES ## UNDEFINED + gEfiPartTypeUnusedGuid ## SOMETIMES_CONSUMES ## UNDEFINED + + +[Ppis] + gEfiPeiVirtualBlockIoPpiGuid ## SOMETIMES_CONSUMES PPI_NOTIFY + gEfiPeiVirtualBlockIo2PpiGuid ## SOMETIMES_CONSUMES PPI_NOTIFY + gEfiPeiDeviceRecoveryModulePpiGuid ## SOMETIMES_PRODUCES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdRecoveryFileName ## CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + FatPeiExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPei.uni b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPei.uni new file mode 100644 index 00000000..8d8fc3d9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPei.uni @@ -0,0 +1,16 @@ +// /** @file
+// Lite Fat driver only used in Pei Phase.
+//
+// Lite Fat driver only used in Pei Phase.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Lite Fat driver only used in Pei Phase."
+
+#string STR_MODULE_DESCRIPTION #language en-US "Lite Fat driver only used in Pei Phase."
+
diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPeiExtra.uni b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPeiExtra.uni new file mode 100644 index 00000000..b3a735e4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// FatPei Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"FAT File System Lite PEI Module"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Gpt.c b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Gpt.c new file mode 100644 index 00000000..64d925d4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Gpt.c @@ -0,0 +1,545 @@ +/** @file + Routines supporting partition discovery and + logical device reading + +Copyright (c) 2019 Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <IndustryStandard/Mbr.h> +#include <Uefi/UefiGpt.h> +#include <Library/BaseLib.h> +#include "FatLitePeim.h" + +// +// Assumption: 'a' and 'blocksize' are all UINT32 or UINT64. +// If 'a' and 'blocksize' are not the same type, should use DivU64xU32 to calculate. +// +#define EFI_SIZE_TO_BLOCKS(a, blocksize) (((a) / (blocksize)) + (((a) % (blocksize)) ? 1 : 0)) + +// +// GPT Partition Entry Status +// +typedef struct { + BOOLEAN OutOfRange; + BOOLEAN Overlap; + BOOLEAN OsSpecific; +} EFI_PARTITION_ENTRY_STATUS; + +/** + Check if the CRC field in the Partition table header is valid. + + @param[in] PartHeader Partition table header structure + + @retval TRUE the CRC is valid + @retval FALSE the CRC is invalid + +**/ +BOOLEAN +PartitionCheckGptHeaderCRC ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + UINT32 GptHdrCrc; + UINT32 Crc; + + GptHdrCrc = PartHeader->Header.CRC32; + + // + // Set CRC field to zero when doing calculation + // + PartHeader->Header.CRC32 = 0; + + Crc = CalculateCrc32 (PartHeader, PartHeader->Header.HeaderSize); + + // + // Restore Header CRC + // + PartHeader->Header.CRC32 = GptHdrCrc; + + return (GptHdrCrc == Crc); +} + + +/** + Check if the CRC field in the Partition table header is valid + for Partition entry array. + + @param[in] PartHeader Partition table header structure + @param[in] PartEntry The partition entry array + + @retval TRUE the CRC is valid + @retval FALSE the CRC is invalid + +**/ +BOOLEAN +PartitionCheckGptEntryArrayCRC ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader, + IN EFI_PARTITION_ENTRY *PartEntry + ) +{ + UINT32 Crc; + UINTN Size; + + Size = (UINTN)MultU64x32(PartHeader->NumberOfPartitionEntries, PartHeader->SizeOfPartitionEntry); + Crc = CalculateCrc32 (PartEntry, Size); + + return (BOOLEAN) (PartHeader->PartitionEntryArrayCRC32 == Crc); +} + +/** + The function is used for valid GPT table. Both for Primary and Backup GPT header. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + @param[in] IsPrimaryHeader Indicate to which header will be checked. + @param[in] PartHdr Stores the partition table that is read + + @retval TRUE The partition table is valid + @retval FALSE The partition table is not valid + +**/ +BOOLEAN +PartitionCheckGptHeader ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo, + IN BOOLEAN IsPrimaryHeader, + IN EFI_PARTITION_TABLE_HEADER *PartHdr + ) +{ + PEI_FAT_BLOCK_DEVICE *ParentBlockDev; + EFI_PEI_LBA Lba; + EFI_PEI_LBA AlternateLba; + EFI_PEI_LBA EntryArrayLastLba; + + UINT64 PartitionEntryArraySize; + UINT64 PartitionEntryBlockNumb; + UINT32 EntryArraySizeRemainder; + + ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]); + + if (IsPrimaryHeader) { + Lba = PRIMARY_PART_HEADER_LBA; + AlternateLba = ParentBlockDev->LastBlock; + } else { + Lba = ParentBlockDev->LastBlock; + AlternateLba = PRIMARY_PART_HEADER_LBA; + } + + if ( (PartHdr->Header.Signature != EFI_PTAB_HEADER_ID) || + (PartHdr->Header.Revision != 0x00010000) || + (PartHdr->Header.HeaderSize < 92) || + (PartHdr->Header.HeaderSize > ParentBlockDev->BlockSize) || + (!PartitionCheckGptHeaderCRC (PartHdr)) || + (PartHdr->Header.Reserved != 0) + ) { + DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n")); + return FALSE; + } + + // + // | Block0 | Block1 |Block2 ~ FirstUsableLBA - 1|FirstUsableLBA, ... ,LastUsableLBA|LastUsableLBA+1 ~ LastBlock-1| LastBlock | + // |Protective MBR|Primary Header|Entry Array(At Least 16384)| Partition | Entry Array(At Least 16384) |BackUp Header| + // + // 1. Protective MBR is fixed at Block 0. + // 2. Primary Header is fixed at Block 1. + // 3. Backup Header is fixed at LastBlock. + // 4. Must be remain 128*128 bytes for primary entry array. + // 5. Must be remain 128*128 bytes for backup entry array. + // 6. SizeOfPartitionEntry must be equals to 128 * 2^n. + // + if ( (PartHdr->MyLBA != Lba) || + (PartHdr->AlternateLBA != AlternateLba) || + (PartHdr->FirstUsableLBA < 2 + EFI_SIZE_TO_BLOCKS (EFI_GPT_PART_ENTRY_MIN_SIZE, ParentBlockDev->BlockSize)) || + (PartHdr->LastUsableLBA > ParentBlockDev->LastBlock - 1 - EFI_SIZE_TO_BLOCKS (EFI_GPT_PART_ENTRY_MIN_SIZE, ParentBlockDev->BlockSize)) || + (PartHdr->FirstUsableLBA > PartHdr->LastUsableLBA) || + (PartHdr->PartitionEntryLBA < 2) || + (PartHdr->PartitionEntryLBA > ParentBlockDev->LastBlock - 1) || + (PartHdr->PartitionEntryLBA >= PartHdr->FirstUsableLBA && PartHdr->PartitionEntryLBA <= PartHdr->LastUsableLBA) || + (PartHdr->SizeOfPartitionEntry%128 != 0) || + (PartHdr->SizeOfPartitionEntry != sizeof (EFI_PARTITION_ENTRY)) + ) { + DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n")); + return FALSE; + } + + // + // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow. + // + if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) { + DEBUG ((DEBUG_ERROR, "Memory overflow in GPT Entry Array\n")); + return FALSE; + } + + PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry); + EntryArraySizeRemainder = 0; + PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder); + if (EntryArraySizeRemainder != 0) { + PartitionEntryBlockNumb++; + } + + if (IsPrimaryHeader) { + EntryArrayLastLba = PartHdr->FirstUsableLBA; + } else { + EntryArrayLastLba = ParentBlockDev->LastBlock; + } + + // + // Make sure partition entry array not overlaps with partition area or the LastBlock. + // + if (PartHdr->PartitionEntryLBA + PartitionEntryBlockNumb > EntryArrayLastLba) { + DEBUG ((DEBUG_ERROR, "GPT Partition Entry Array Error!\n")); + DEBUG ((DEBUG_ERROR, "PartitionEntryArraySize = %lu.\n", PartitionEntryArraySize)); + DEBUG ((DEBUG_ERROR, "PartitionEntryLBA = %lu.\n", PartHdr->PartitionEntryLBA)); + DEBUG ((DEBUG_ERROR, "PartitionEntryBlockNumb = %lu.\n", PartitionEntryBlockNumb)); + DEBUG ((DEBUG_ERROR, "EntryArrayLastLba = %lu.\n", EntryArrayLastLba)); + return FALSE; + } + + return TRUE; +} + +/** + This function is used to verify each partition in block device. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + @param[in] PartHdr Stores the partition table that is read + + @retval TRUE The partition is valid + @retval FALSE The partition is not valid + +**/ +BOOLEAN +PartitionCheckGptEntryArray ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo, + IN EFI_PARTITION_TABLE_HEADER *PartHdr + ) +{ + EFI_STATUS Status; + PEI_FAT_BLOCK_DEVICE *ParentBlockDev; + PEI_FAT_BLOCK_DEVICE *BlockDevPtr; + + UINT64 PartitionEntryArraySize; + UINT64 PartitionEntryBlockNumb; + UINT32 EntryArraySizeRemainder; + + EFI_PARTITION_ENTRY *PartitionEntryBuffer; + EFI_PARTITION_ENTRY_STATUS *PartitionEntryStatus; + + BOOLEAN Found; + EFI_LBA StartingLBA; + EFI_LBA EndingLBA; + UINTN Index; + UINTN Index1; + UINTN Index2; + EFI_PARTITION_ENTRY *Entry; + + PartitionEntryBuffer = NULL; + PartitionEntryStatus = NULL; + + ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]); + Found = FALSE; + + PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry); + EntryArraySizeRemainder = 0; + PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder); + if (EntryArraySizeRemainder != 0) { + PartitionEntryBlockNumb++; + } + PartitionEntryArraySize = MultU64x32 (PartitionEntryBlockNumb, ParentBlockDev->BlockSize); + + PartitionEntryBuffer = (EFI_PARTITION_ENTRY *) AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize)); + if (PartitionEntryBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "Allocate memory error!\n")); + goto EXIT; + } + + PartitionEntryStatus = (EFI_PARTITION_ENTRY_STATUS *) AllocatePages (EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS))); + if (PartitionEntryStatus == NULL) { + DEBUG ((DEBUG_ERROR, "Allocate memory error!\n")); + goto EXIT; + } + ZeroMem (PartitionEntryStatus, PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)); + + Status = FatReadBlock ( + PrivateData, + ParentBlockDevNo, + PartHdr->PartitionEntryLBA, + (UINTN)PartitionEntryArraySize, + PartitionEntryBuffer + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Read partition entry array error!\n")); + goto EXIT; + } + + if (!PartitionCheckGptEntryArrayCRC (PartHdr, PartitionEntryBuffer)) { + DEBUG ((DEBUG_ERROR, "Partition entries CRC check fail\n")); + goto EXIT; + } + + for (Index1 = 0; Index1 < PartHdr->NumberOfPartitionEntries; Index1++) { + Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartitionEntryBuffer + Index1 * PartHdr->SizeOfPartitionEntry); + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + StartingLBA = Entry->StartingLBA; + EndingLBA = Entry->EndingLBA; + if (StartingLBA > EndingLBA || + StartingLBA < PartHdr->FirstUsableLBA || + StartingLBA > PartHdr->LastUsableLBA || + EndingLBA < PartHdr->FirstUsableLBA || + EndingLBA > PartHdr->LastUsableLBA + ) { + PartitionEntryStatus[Index1].OutOfRange = TRUE; + continue; + } + + if ((Entry->Attributes & BIT1) != 0) { + // + // If Bit 1 is set, this indicate that this is an OS specific GUID partition. + // + PartitionEntryStatus[Index1].OsSpecific = TRUE; + } + + for (Index2 = Index1 + 1; Index2 < PartHdr->NumberOfPartitionEntries; Index2++) { + Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartitionEntryBuffer + Index2 * PartHdr->SizeOfPartitionEntry); + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + if (Entry->EndingLBA >= StartingLBA && Entry->StartingLBA <= EndingLBA) { + // + // This region overlaps with the Index1'th region + // + PartitionEntryStatus[Index1].Overlap = TRUE; + PartitionEntryStatus[Index2].Overlap = TRUE; + continue; + } + } + } + + for (Index = 0; Index < PartHdr->NumberOfPartitionEntries; Index++) { + if (CompareGuid (&PartitionEntryBuffer[Index].PartitionTypeGUID, &gEfiPartTypeUnusedGuid)|| + PartitionEntryStatus[Index].OutOfRange || + PartitionEntryStatus[Index].Overlap || + PartitionEntryStatus[Index].OsSpecific) { + // + // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific + // partition Entries + // + continue; + } + + if (PrivateData->BlockDeviceCount >= PEI_FAT_MAX_BLOCK_DEVICE) { + break; + } + + Found = TRUE; + BlockDevPtr = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]); + + BlockDevPtr->BlockSize = ParentBlockDev->BlockSize; + BlockDevPtr->LastBlock = PartitionEntryBuffer[Index].EndingLBA; + BlockDevPtr->IoAlign = ParentBlockDev->IoAlign; + BlockDevPtr->Logical = TRUE; + BlockDevPtr->PartitionChecked = FALSE; + BlockDevPtr->StartingPos = MultU64x32 ( + PartitionEntryBuffer[Index].StartingLBA, + ParentBlockDev->BlockSize + ); + BlockDevPtr->ParentDevNo = ParentBlockDevNo; + + PrivateData->BlockDeviceCount++; + + DEBUG ((DEBUG_INFO, "Find GPT Partition [0x%lx", PartitionEntryBuffer[Index].StartingLBA, BlockDevPtr->LastBlock)); + DEBUG ((DEBUG_INFO, ", 0x%lx]\n", BlockDevPtr->LastBlock)); + DEBUG ((DEBUG_INFO, " BlockSize %x\n", BlockDevPtr->BlockSize)); + } + +EXIT: + if (PartitionEntryBuffer != NULL) { + FreePages (PartitionEntryBuffer, EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize)); + } + + if (PartitionEntryStatus != NULL) { + FreePages (PartitionEntryStatus, EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS))); + } + + return Found; +} + +/** + The function is used to check GPT structure, include GPT header and GPT entry array. + + 1. Check GPT header. + 2. Check partition entry array. + 3. Check each partitions. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + @param[in] IsPrimary Indicate primary or backup to be check + + @retval TRUE Primary or backup GPT structure is valid. + @retval FALSE Both primary and backup are invalid. + +**/ +BOOLEAN +PartitionCheckGptStructure ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo, + IN BOOLEAN IsPrimary + ) +{ + EFI_STATUS Status; + PEI_FAT_BLOCK_DEVICE *ParentBlockDev; + EFI_PARTITION_TABLE_HEADER *PartHdr; + EFI_PEI_LBA GptHeaderLBA; + + ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]); + PartHdr = (EFI_PARTITION_TABLE_HEADER *) PrivateData->BlockData; + + if (IsPrimary) { + GptHeaderLBA = PRIMARY_PART_HEADER_LBA; + } else { + GptHeaderLBA = ParentBlockDev->LastBlock; + } + + Status = FatReadBlock ( + PrivateData, + ParentBlockDevNo, + GptHeaderLBA, + ParentBlockDev->BlockSize, + PartHdr + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if (!PartitionCheckGptHeader (PrivateData, ParentBlockDevNo, IsPrimary, PartHdr)) { + return FALSE; + } + + if (!PartitionCheckGptEntryArray (PrivateData, ParentBlockDevNo, PartHdr)) { + return FALSE; + } + + return TRUE; +} + +/** + This function is used to check protective MBR structure before checking GPT. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + + @retval TRUE Valid protective MBR + @retval FALSE Invalid MBR +**/ +BOOLEAN +PartitionCheckProtectiveMbr ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo + ) +{ + EFI_STATUS Status; + MASTER_BOOT_RECORD *ProtectiveMbr; + MBR_PARTITION_RECORD *MbrPartition; + PEI_FAT_BLOCK_DEVICE *ParentBlockDev; + UINTN Index; + + ProtectiveMbr = (MASTER_BOOT_RECORD *) PrivateData->BlockData; + ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]); + + // + // Read Protective MBR + // + Status = FatReadBlock ( + PrivateData, + ParentBlockDevNo, + 0, + ParentBlockDev->BlockSize, + ProtectiveMbr + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "GPT Error When Read Protective Mbr From Partition!\n")); + return FALSE; + } + + if (ProtectiveMbr->Signature != MBR_SIGNATURE) { + DEBUG ((DEBUG_ERROR, "Protective Mbr Signature is invalid!\n")); + return FALSE; + } + + // + // The partition define in UEFI Spec Table 17. + // Boot Code, Unique MBR Disk Signature, Unknown. + // These parts will not be used by UEFI, so we skip to check them. + // + for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) { + MbrPartition = (MBR_PARTITION_RECORD *)&ProtectiveMbr->Partition[Index]; + if (MbrPartition->BootIndicator == 0x00 && + MbrPartition->StartSector == 0x02 && + MbrPartition->OSIndicator == PMBR_GPT_PARTITION && + UNPACK_UINT32 (MbrPartition->StartingLBA) == 1 + ) { + return TRUE; + } + } + + DEBUG ((DEBUG_ERROR, "Protective Mbr, All Partition Entry Are Empty!\n")); + return FALSE; +} + +/** + This function is used for finding GPT partition on block device. + As follow UEFI spec we should check protective MBR first and then + try to check both primary/backup GPT structures. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + + @retval TRUE New partitions are detected and logical block devices + are added to block device array + @retval FALSE No new partitions are added + +**/ +BOOLEAN +FatFindGptPartitions ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo + ) +{ + BOOLEAN Found; + PEI_FAT_BLOCK_DEVICE *ParentBlockDev; + + if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) { + return FALSE; + } + + ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]); + if (ParentBlockDev->BlockSize > PEI_FAT_MAX_BLOCK_SIZE) { + DEBUG ((DEBUG_ERROR, "Device BlockSize %x exceed FAT_MAX_BLOCK_SIZE\n", ParentBlockDev->BlockSize)); + return FALSE; + } + + if (!PartitionCheckProtectiveMbr (PrivateData, ParentBlockDevNo)) { + return FALSE; + } + + Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, TRUE); + if (!Found) { + DEBUG ((DEBUG_ERROR, "Primary GPT Header Error, Try to Check Backup GPT Header!\n")); + Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, FALSE); + } + + if (Found) { + ParentBlockDev->PartitionChecked = TRUE; + } + + return Found; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Mbr.c b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Mbr.c new file mode 100644 index 00000000..5de24c83 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Mbr.c @@ -0,0 +1,175 @@ +/** @file + Routines supporting partition discovery and + logical device reading + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <IndustryStandard/Mbr.h> +#include "FatLitePeim.h" + +/** + Test to see if the Mbr buffer is a valid MBR + + @param[in] Mbr Parent Handle + @param[in] LastLba Last Lba address on the device. + + @retval TRUE Mbr is a Valid MBR + @retval FALSE Mbr is not a Valid MBR + +**/ +BOOLEAN +PartitionValidMbr ( + IN MASTER_BOOT_RECORD *Mbr, + IN EFI_PEI_LBA LastLba + ) +{ + UINT32 StartingLBA; + UINT32 EndingLBA; + UINT32 NewEndingLBA; + INTN Index1; + INTN Index2; + BOOLEAN MbrValid; + + if (Mbr->Signature != MBR_SIGNATURE) { + return FALSE; + } + // + // The BPB also has this signature, so it can not be used alone. + // + MbrValid = FALSE; + for (Index1 = 0; Index1 < MAX_MBR_PARTITIONS; Index1++) { + if (Mbr->Partition[Index1].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) == 0) { + continue; + } + + MbrValid = TRUE; + StartingLBA = UNPACK_UINT32 (Mbr->Partition[Index1].StartingLBA); + EndingLBA = StartingLBA + UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) - 1; + if (EndingLBA > LastLba) { + // + // Compatibility Errata: + // Some systems try to hide drive space with their INT 13h driver + // This does not hide space from the OS driver. This means the MBR + // that gets created from DOS is smaller than the MBR created from + // a real OS (NT & Win98). This leads to BlockIo->LastBlock being + // wrong on some systems FDISKed by the OS. + // + // return FALSE Because no block devices on a system are implemented + // with INT 13h + // + return FALSE; + } + + for (Index2 = Index1 + 1; Index2 < MAX_MBR_PARTITIONS; Index2++) { + if (Mbr->Partition[Index2].OSIndicator == 0x00 || UNPACK_INT32 (Mbr->Partition[Index2].SizeInLBA) == 0) { + continue; + } + + NewEndingLBA = UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) + UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) - 1; + if (NewEndingLBA >= StartingLBA && UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) <= EndingLBA) { + // + // This region overlaps with the Index1'th region + // + return FALSE; + } + } + } + // + // Non of the regions overlapped so MBR is O.K. + // + return MbrValid; +} + +/** + This function finds Mbr partitions. Main algorithm + is ported from DXE partition driver. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + + @retval TRUE New partitions are detected and logical block devices + are added to block device array + @retval FALSE No new partitions are added + +**/ +BOOLEAN +FatFindMbrPartitions ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo + ) +{ + EFI_STATUS Status; + MASTER_BOOT_RECORD *Mbr; + UINTN Index; + BOOLEAN Found; + PEI_FAT_BLOCK_DEVICE *ParentBlockDev; + PEI_FAT_BLOCK_DEVICE *BlockDev; + + if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) { + return FALSE; + } + + ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]); + + if (ParentBlockDev->BlockSize > PEI_FAT_MAX_BLOCK_SIZE) { + DEBUG((DEBUG_ERROR, "Device BlockSize %x exceeds FAT_MAX_BLOCK_SIZE\n", ParentBlockDev->BlockSize)); + return FALSE; + } + + Found = FALSE; + Mbr = (MASTER_BOOT_RECORD *) PrivateData->BlockData; + + Status = FatReadBlock ( + PrivateData, + ParentBlockDevNo, + 0, + ParentBlockDev->BlockSize, + Mbr + ); + + if (EFI_ERROR (Status) || !PartitionValidMbr (Mbr, ParentBlockDev->LastBlock)) { + goto Done; + } + // + // We have a valid mbr - add each partition + // + for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) { + if (Mbr->Partition[Index].OSIndicator == 0x00 || UNPACK_INT32 (Mbr->Partition[Index].SizeInLBA) == 0) { + // + // Don't use null MBR entries + // + continue; + } + // + // Register this partition + // + if (PrivateData->BlockDeviceCount < PEI_FAT_MAX_BLOCK_DEVICE) { + + Found = TRUE; + + BlockDev = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]); + + BlockDev->BlockSize = MBR_SIZE; + BlockDev->LastBlock = UNPACK_INT32 (Mbr->Partition[Index].SizeInLBA) - 1; + BlockDev->IoAlign = ParentBlockDev->IoAlign; + BlockDev->Logical = TRUE; + BlockDev->PartitionChecked = FALSE; + BlockDev->StartingPos = MultU64x32 ( + UNPACK_INT32 (Mbr->Partition[Index].StartingLBA), + ParentBlockDev->BlockSize + ); + BlockDev->ParentDevNo = ParentBlockDevNo; + + PrivateData->BlockDeviceCount++; + } + } + +Done: + + ParentBlockDev->PartitionChecked = TRUE; + return Found; +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Part.c b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Part.c new file mode 100644 index 00000000..d0334660 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Part.c @@ -0,0 +1,104 @@ +/** @file + Routines supporting partition discovery and + logical device reading + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FatLitePeim.h" + +/** + This function finds Eltorito partitions. Main algorithm + is ported from DXE partition driver. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + + @retval TRUE New partitions are detected and logical block devices + are added to block device array + @retval FALSE No new partitions are added + +**/ +BOOLEAN +FatFindEltoritoPartitions ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo + ); + +/** + This function finds Mbr partitions. Main algorithm + is ported from DXE partition driver. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + + @retval TRUE New partitions are detected and logical block devices + are added to block device array + @retval FALSE No new partitions are added + +**/ +BOOLEAN +FatFindMbrPartitions ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo + ); + +/** + This function is used for finding GPT partition on block device. + As follow UEFI spec we should check protective MBR first and then + try to check both primary/backup GPT structures. + + @param[in] PrivateData The global memory map + @param[in] ParentBlockDevNo The parent block device + + @retval TRUE New partitions are detected and logical block devices + are added to block device array + @retval FALSE No new partitions are added + +**/ +BOOLEAN +FatFindGptPartitions ( + IN PEI_FAT_PRIVATE_DATA *PrivateData, + IN UINTN ParentBlockDevNo + ); + +/** + This function finds partitions (logical devices) in physical block devices. + + @param PrivateData Global memory map for accessing global variables. + +**/ +VOID +FatFindPartitions ( + IN PEI_FAT_PRIVATE_DATA *PrivateData + ) +{ + BOOLEAN Found; + UINTN Index; + + do { + Found = FALSE; + + for (Index = 0; Index < PrivateData->BlockDeviceCount; Index++) { + if (!PrivateData->BlockDevice[Index].PartitionChecked) { + if (FatFindGptPartitions (PrivateData, Index)) { + Found = TRUE; + continue; + } + + if (FatFindMbrPartitions (PrivateData, Index)) { + Found = TRUE; + continue; + } + + if (FatFindEltoritoPartitions (PrivateData, Index)) { + Found = TRUE; + continue; + } + } + } + } while (Found && PrivateData->BlockDeviceCount <= PEI_FAT_MAX_BLOCK_DEVICE); +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.ci.yaml b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.ci.yaml new file mode 100644 index 00000000..7dc1829b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.ci.yaml @@ -0,0 +1,65 @@ +## @file +# CI configuration for FatPkg +# +# Copyright (c) Microsoft Corporation +# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +## +{ + "LicenseCheck": { + "IgnoreFiles": [] + }, + "EccCheck": { + ## Exception sample looks like below: + ## "ExceptionList": [ + ## "<ErrorID>", "<KeyWord>" + ## ] + "ExceptionList": [ + ], + ## Both file path and directory path are accepted. + "IgnoreFiles": [ + ] + }, + "CompilerPlugin": { + "DscPath": "FatPkg.dsc" + }, + "CharEncodingCheck": { + "IgnoreFiles": [] + }, + "DependencyCheck": { + "AcceptableDependencies": [ + "MdePkg/MdePkg.dec", + "MdeModulePkg/MdeModulePkg.dec", + ], + # For host based unit tests + "AcceptableDependencies-HOST_APPLICATION":[], + # For UEFI shell based apps + "AcceptableDependencies-UEFI_APPLICATION":[], + "IgnoreInf": [] + }, + "DscCompleteCheck": { + "IgnoreInf": [], + "DscPath": "FatPkg.dsc" + }, + "GuidCheck": { + "IgnoreGuidName": [], + "IgnoreGuidValue": [], + "IgnoreFoldersAndFiles": [] + }, + "LibraryClassCheck": { + "IgnoreHeaderFile": [] + }, + "SpellCheck": { + "ExtendWords": [ + "ELTORITO", + "FHAND", + "IFILE", + "OFILE", + "FDISKed", + "Lfnbuffer", + "FFFFFFFFL", + "CDVOL", + "DMDEPKG" + ] + } +} diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.dec b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.dec new file mode 100644 index 00000000..0d0cc194 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.dec @@ -0,0 +1,19 @@ +## @file +# FAT Package +# +# FAT 32 Driver +# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + DEC_SPECIFICATION = 0x00010005 + PACKAGE_NAME = FatPkg + PACKAGE_UNI_FILE = FatPkg.uni + PACKAGE_GUID = 8EA68A2C-99CB-4332-85C6-DD5864EAA674 + PACKAGE_VERSION = 0.3 + +[UserExtensions.TianoCore."ExtraFiles"] + FatPkgExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.dsc b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.dsc new file mode 100644 index 00000000..5cdfdaab --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.dsc @@ -0,0 +1,84 @@ +## @file +# Build Binary Enhanced Fat Driver Modules. +# +# This Platform file is used to generate the Binary Fat Drivers +# for EDK II Prime release. +# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + PLATFORM_NAME = Fat + PLATFORM_GUID = 25b55dbc-9d0b-4a32-80da-46e1273d622c + PLATFORM_VERSION = 0.3 + DSC_SPECIFICATION = 0x00010005 + SUPPORTED_ARCHITECTURES = IA32|X64|EBC|ARM|AARCH64|RISCV64 + OUTPUT_DIRECTORY = Build/Fat + BUILD_TARGETS = DEBUG|RELEASE|NOOPT + SKUID_IDENTIFIER = DEFAULT + +[BuildOptions] + GCC:RELEASE_*_*_CC_FLAGS = -DMDEPKG_NDEBUG + INTEL:RELEASE_*_*_CC_FLAGS = /D MDEPKG_NDEBUG + MSFT:RELEASE_*_*_CC_FLAGS = /D MDEPKG_NDEBUG + RVCT:RELEASE_*_*_CC_FLAGS = -DMDEPKG_NDEBUG + *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES + +!include MdePkg/MdeLibs.dsc.inc + +[LibraryClasses] + # + # Entry Point Libraries + # + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + # + # Common Libraries + # + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + +[LibraryClasses.common.PEIM] + PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf + PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf + PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf + HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf + MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf + +[LibraryClasses.ARM, LibraryClasses.AARCH64] + NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf + NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf + +################################################################################################### +# +# Components Section - list of the modules and components that will be processed by compilation +# tools and the EDK II tools to generate PE32/PE32+/Coff image files. +# +# Note: The EDK II DSC file is not used to specify how compiled binary images get placed +# into firmware volume images. This section is just a list of modules to compile from +# source into UEFI-compliant binaries. +# It is the FDF file that contains information on combining binary files into firmware +# volume images, whose concept is beyond UEFI and is described in PI specification. +# Binary modules do not need to be listed in this section, as they should be +# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi), +# Logo (Logo.bmp), and etc. +# There may also be modules listed in this section that are not required in the FDF file, +# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be +# generated for it, but the binary will not be put into any firmware volume. +# +################################################################################################### + +[Components] + FatPkg/FatPei/FatPei.inf + FatPkg/EnhancedFatDxe/Fat.inf diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.uni b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.uni new file mode 100644 index 00000000..659b8b32 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.uni @@ -0,0 +1,20 @@ +// /** @file
+// Module implementations for FAT file system, FAT 32 UEFI Driver and FAT PEI Module
+//
+// FAT Package
+//
+// FAT 32 Driver
+//
+// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_PACKAGE_ABSTRACT #language en-US "Module implementations for FAT file system, FAT 32 UEFI Driver and FAT PEI Module"
+
+#string STR_PACKAGE_DESCRIPTION #language en-US "This Package contains module implementation about FAT file system, FAT 32 UEFI Driver and FAT PEI Module."
+
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkgExtra.uni b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkgExtra.uni new file mode 100644 index 00000000..d9c0c9e4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/FatPkgExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// Fat Package Localized Strings and Content.
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_PACKAGE_NAME
+#language en-US
+"Fat package"
+
+
|