summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/FatPkg
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/FatPkg')
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/ComponentName.c343
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Data.c40
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Delete.c119
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryCache.c184
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryManage.c1391
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DiskCache.c489
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.c525
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.h2008
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.inf88
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Fat.uni18
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FatExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FatFileSystem.h208
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileName.c499
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileSpace.c744
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Flush.c468
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Hash.c166
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Info.c595
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Init.c387
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Misc.c612
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/Open.c320
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/OpenVolume.c58
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/ReadWrite.c620
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/UnicodeCollation.c275
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Eltorito.c233
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteAccess.c521
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteApi.c675
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteApi.h25
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteFmt.h138
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLiteLib.c370
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatLitePeim.h522
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPei.inf75
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPei.uni16
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/FatPeiExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Gpt.c545
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Mbr.c175
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPei/Part.c104
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.ci.yaml65
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.dec19
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.dsc84
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPkg.uni20
-rw-r--r--src/VBox/Devices/EFI/Firmware/FatPkg/FatPkgExtra.uni14
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"
+
+