[edk2,2/3] MdeModulePkg: Implement EFI_FILE_PROTOCOL over Firmware Volumes

Message ID 1395692580-24875-3-git-send-email-olivier.martin@arm.com
State New
Headers show

Commit Message

Olivier Martin March 24, 2014, 8:22 p.m.
From: Brendan Jackman <Brendan.Jackman@arm.com>

This module implements Simple FileSystem protocol over Firmware Volume (FV).
EFI Modules included into a FV can be listed and launched from
the EFI Shell or any other EFI applications.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Brendan Jackman <Brendan.Jackman@arm.com>
Reviewed-by: Olivier Martin <olivier.martin@arm.com>
---
 MdeModulePkg/MdeModulePkg.dsc                      |   2 +
 .../FvSimpleFilesystemDxe/FvSimpleFilesystem.c     | 536 +++++++++++++++++++++
 .../FvSimpleFilesystemDxe.inf                      |  52 ++
 .../FvSimpleFilesystemEntryPoint.c                 | 405 ++++++++++++++++
 .../FvSimpleFilesystemInternal.h                   |  99 ++++
 5 files changed, 1094 insertions(+)
 create mode 100644 MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystem.c
 create mode 100644 MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemDxe.inf
 create mode 100644 MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemEntryPoint.c
 create mode 100644 MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemInternal.h

Patch

diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index f473325..363e6c9 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -322,6 +322,8 @@ 
   MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.inf
   MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
+  MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemDxe.inf
+
 [Components.IA32, Components.X64, Components.IPF]
   MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf
   MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf
diff --git a/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystem.c b/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystem.c
new file mode 100644
index 0000000..197526c
--- /dev/null
+++ b/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystem.c
@@ -0,0 +1,536 @@ 
+/** @file
+*
+*  Copyright (c) 2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+/*
+  A driver using the EFI_FIRMWARE_VOLUME2_PROTOCOL to expose files in firmware
+  volumes via the the EFI_SIMPLE_FILESYSTEM_PROTOCOL and EFI_FILE_PROTOCOL.
+
+  Its primary intended use is to be able to start EFI applications embedded
+  in FVs from the UEFI shell. For this reason, it is not fully compliant as a
+  filesystem driver: it is entirely read-only, and does not support partially
+  reading files.
+
+  It will expose a single directory, containing one file for each file in the
+  firmware volume. If a file has a UI section, its contents will be used as
+  a filename. Otherwise, a string representation of the GUID will be used.
+  Files of an executable type (That is PEIM, DRIVER, COMBINED_PEIM_DRIVER
+  and APPLICATION) will have ".efi" added to their filename.
+
+  The data returned by Read() depends on the type of the underlying FV file:
+  - For executable types, the first section found that contains executable code
+    is returned.
+  - For files of type FREEFORM, the driver attempts to return the first section
+    of type RAW. If none is found, the entire contents of the FV file are
+    returned.
+  - On all other files the entire contents of the FV file is returned, as by
+    EFI_FIRMWARE_VOLUME2_PROTOCOL.ReadFile.
+
+  See the EFI Firmware Volume specification (a separate document from the main
+  UEFI specification) for more information about firmware volumes.
+*/
+
+#include <PiDxe.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PathLib.h>
+
+#include <Protocol/DriverBinding.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/UnicodeCollation.h>
+
+#include <Guid/FileSystemInfo.h>
+#include <Guid/FileInfo.h>
+
+#include "FvSimpleFilesystemInternal.h"
+
+/*
+  Find and call ReadSection on the first section found of an executable type.
+*/
+STATIC
+EFI_STATUS
+FvFsFindExecutableSection (
+  IN  FV_FILESYSTEM_FILE *File,
+  OUT UINTN              *BufferSize,
+  OUT VOID              **Buffer
+  )
+{
+  EFI_SECTION_TYPE               SectionType;
+  UINT32                         AuthenticationStatus;
+  EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
+  EFI_STATUS                     Status;
+
+  FvProtocol = File->Instance->FvProtocol;
+
+  for (SectionType = EFI_SECTION_PE32; SectionType <= EFI_SECTION_TE; SectionType++) {
+    Status = FvProtocol->ReadSection (
+                           FvProtocol,
+                           &File->NameGuid,
+                           SectionType,
+                           0,
+                           Buffer,
+                           BufferSize,
+                           &AuthenticationStatus
+                           );
+    if (Status != EFI_NOT_FOUND) {
+      return Status;
+    }
+  }
+  return EFI_NOT_FOUND;
+}
+
+/*
+  Get the size of the buffer that will be returned by FvFsReadFile.
+*/
+EFI_STATUS
+FvFsGetFileSize (
+  IN  FV_FILESYSTEM_FILE *File,
+  OUT UINTN              *Size
+  )
+{
+  EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
+  UINT32                         AuthenticationStatus;
+  EFI_FV_FILETYPE                FoundType;
+  EFI_FV_FILE_ATTRIBUTES         Attributes;
+  EFI_STATUS                     Status;
+  UINT8                          IgnoredByte;
+  VOID                          *IgnoredPtr;
+
+  FvProtocol = File->Instance->FvProtocol;
+
+  // To get the size of a section, we pass 0 for BufferSize. But we can't pass
+  // NULL for Buffer, as that will cause a return of INVALID_PARAMETER, and we
+  // can't pass NULL for *Buffer, as that will cause the callee to allocate
+  // a buffer of the sections size.
+  IgnoredPtr = &IgnoredByte;
+  *Size = 0;
+
+  if (FV_FILETYPE_IS_EXECUTABLE (File->Type)) {
+    // Get the size of the first executable section out of the file.
+    Status = FvFsFindExecutableSection (File, Size, &IgnoredPtr);
+    if (Status == EFI_WARN_BUFFER_TOO_SMALL) {
+      return EFI_SUCCESS;
+    }
+  } else if (File->Type == EFI_FV_FILETYPE_FREEFORM) {
+    // Try to get the size of a raw section out of the file
+    Status = FvProtocol->ReadSection (
+                           FvProtocol,
+                           &File->NameGuid,
+                           EFI_SECTION_RAW,
+                           0,
+                           &IgnoredPtr,
+                           Size,
+                           &AuthenticationStatus
+                           );
+    if (Status == EFI_WARN_BUFFER_TOO_SMALL) {
+      return EFI_SUCCESS;
+    }
+    if (EFI_ERROR (Status)) {
+      // Didn't find a raw section, just return the whole file's size.
+      return FvProtocol->ReadFile (
+                           FvProtocol,
+                           &File->NameGuid,
+                           NULL,
+                           Size,
+                           &FoundType,
+                           &Attributes,
+                           &AuthenticationStatus
+                           );
+    }
+  } else {
+    // Get the size of the entire file
+    return FvProtocol->ReadFile (
+                         FvProtocol,
+                         &File->NameGuid,
+                         NULL,
+                         Size,
+                         &FoundType,
+                         &Attributes,
+                         &AuthenticationStatus
+                         );
+  }
+
+  return Status;
+}
+
+/*
+  Helper function to read a file. See comment at the top of this file for
+  information on behaviour.
+*/
+EFI_STATUS
+FvFsReadFile (
+  FV_FILESYSTEM_FILE *File,
+  UINTN              *BufferSize,
+  VOID              **Buffer
+  )
+{
+  EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
+  UINT32                         AuthenticationStatus;
+  EFI_FV_FILETYPE                FoundType;
+  EFI_FV_FILE_ATTRIBUTES         Attributes;
+  EFI_STATUS                     Status;
+
+  FvProtocol = File->Instance->FvProtocol;
+
+  if (FV_FILETYPE_IS_EXECUTABLE (File->Type)) {
+    // Read the first executable section out of the file.
+    Status = FvFsFindExecutableSection (File, BufferSize, Buffer);
+  } else if (File->Type == EFI_FV_FILETYPE_FREEFORM) {
+    // Try to read a raw section out of the file
+    Status = FvProtocol->ReadSection (
+                           FvProtocol,
+                           &File->NameGuid,
+                           EFI_SECTION_RAW,
+                           0,
+                           Buffer,
+                           BufferSize,
+                           &AuthenticationStatus
+                           );
+    if (EFI_ERROR (Status)) {
+      // Didn't find a raw section, just return the whole file.
+      Status = FvProtocol->ReadFile (
+                             FvProtocol,
+                             &File->NameGuid,
+                             Buffer,
+                             BufferSize,
+                             &FoundType,
+                             &Attributes,
+                             &AuthenticationStatus
+                             );
+    }
+  } else {
+    // Read the entire file
+    Status = FvProtocol->ReadFile (
+                           FvProtocol,
+                           &File->NameGuid,
+                           Buffer,
+                           BufferSize,
+                           &FoundType,
+                           &Attributes,
+                           &AuthenticationStatus
+                           );
+  }
+
+  return Status;
+}
+
+/*
+  Helper function for populating an EFI_FILE_INFO for a file.
+*/
+STATIC
+EFI_STATUS
+FvFsGetFileInfo (
+  IN     FV_FILESYSTEM_FILE *File,
+  IN OUT UINTN              *BufferSize,
+     OUT EFI_FILE_INFO      *FileInfo
+  )
+{
+  UINTN                          InfoSize;
+
+  InfoSize = sizeof (EFI_FILE_INFO) + StrSize (File->Name) - sizeof (CHAR16);
+  if (*BufferSize < InfoSize) {
+    *BufferSize = InfoSize;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  // Initialize FileInfo
+  ZeroMem (FileInfo, InfoSize);
+  FileInfo->Size = InfoSize;
+  FileInfo->Attribute = EFI_FILE_READ_ONLY;
+
+  // File is a directory if it is root.
+  if (File == File->Instance->Root) {
+    FileInfo->Attribute |= EFI_FILE_DIRECTORY;
+  }
+
+  FileInfo->FileSize = File->Size;
+  FileInfo->PhysicalSize = File->Size;
+
+  StrCpy (FileInfo->FileName, File->Name);
+
+  *BufferSize = InfoSize;
+  return EFI_SUCCESS;
+}
+
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemOpen (
+  IN EFI_FILE_PROTOCOL  *This,
+  OUT EFI_FILE_PROTOCOL **NewHandle,
+  IN CHAR16             *FileName,
+  IN UINT64             OpenMode,
+  IN UINT64             Attributes
+  )
+{
+  FV_FILESYSTEM_INSTANCE      *Instance;
+  FV_FILESYSTEM_FILE          *File;
+  LIST_ENTRY                  *FileLink;
+
+  File = FVFS_FILE_FROM_FILE_THIS (This);
+  Instance = File->Instance;
+
+  FileName = PathCleanUpDirectories (FileName);
+
+  if (FileName[0] == L'\\') {
+    FileName++;
+  }
+
+  // Check for opening root
+  if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"") == 0) {
+    *NewHandle = &Instance->Root->FileProtocol;
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Do a linear search for a file in the FV with a matching filename
+  //
+  for (FileLink = GetFirstNode (&Instance->Files);
+       !IsNull (&Instance->Files, FileLink);
+       FileLink = GetNextNode (&Instance->Files, FileLink)) {
+
+    File = FVFS_FILE_FROM_LINK (FileLink);
+    if (mUnicodeCollation->StriColl (mUnicodeCollation, File->Name, FileName) == 0) {
+      *NewHandle = &File->FileProtocol;
+      return EFI_SUCCESS;
+    }
+  }
+  return EFI_NOT_FOUND;
+}
+
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemClose (
+  IN EFI_FILE_PROTOCOL  *This
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/*
+  Implementation of EFI_FILE_PROTOCOL.Read.
+
+  This implementation is not compliant with the UEFI specification. As this
+  driver's only intended use case is for loading and executing EFI images,
+  it does not support partial reads. If *BufferSize is less than the size of the
+  image being read, it will return EFI_UNSUPPORTED.
+*/
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemRead (
+  IN EFI_FILE_PROTOCOL  *This,
+  IN OUT UINTN          *BufferSize,
+  OUT VOID              *Buffer
+  )
+{
+  FV_FILESYSTEM_INSTANCE        *Instance;
+  FV_FILESYSTEM_FILE            *File;
+  EFI_STATUS                     Status;
+  LIST_ENTRY                    *FileLink;
+
+  File = FVFS_FILE_FROM_FILE_THIS (This);
+  Instance = File->Instance;
+
+  if (File == Instance->Root) {
+    if (File->DirReadNext) {
+      //
+      // Directory read: populate Buffer with an EFI_FILE_INFO
+      //
+      Status = FvFsGetFileInfo (File->DirReadNext, BufferSize, Buffer);
+      if (!EFI_ERROR (Status)) {
+        //
+        // Successfully read a directory entry, now update the pointer to the
+        // next file, which will be read on the next call to this function
+        //
+
+        FileLink = GetNextNode (&Instance->Files, &File->DirReadNext->Link);
+        if (IsNull (&Instance->Files, FileLink)) {
+          // No more files left
+          File->DirReadNext = NULL;
+        } else {
+          File->DirReadNext = FVFS_FILE_FROM_LINK (FileLink);
+        }
+      }
+      return Status;
+    } else {
+      //
+      // Directory read. All entries have been read, so return a zero-size
+      // buffer.
+      //
+      *BufferSize = 0;
+      return EFI_SUCCESS;
+    }
+  } else {
+    if (*BufferSize < File->Size) {
+      DEBUG ((EFI_D_ERROR, "FV Filesystem does not support partial file reads\n", *BufferSize, File->Size));
+      return EFI_UNSUPPORTED;
+    }
+    return FvFsReadFile (File, BufferSize, &Buffer);
+  }
+}
+
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemWrite (
+  IN EFI_FILE_PROTOCOL  *This,
+  IN OUT UINTN          *BufferSize,
+  IN VOID               *Buffer
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemGetPosition (
+  IN EFI_FILE_PROTOCOL  *This,
+  OUT UINT64            *Position
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/*
+  This implementation of EFI_FILE_PROTOCOL.SetPosition is not compliant with
+  the UEFI specification. We do not support partial file reads (see comment on
+  FvSimpleFilesystemRead), therefore we only support seeking to position 0
+*/
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemSetPosition (
+  IN EFI_FILE_PROTOCOL  *This,
+  IN UINT64             Position
+  )
+{
+  FV_FILESYSTEM_INSTANCE      *Instance;
+  FV_FILESYSTEM_FILE          *File;
+
+  File = FVFS_FILE_FROM_FILE_THIS (This);
+  Instance = File->Instance;
+
+  if (File == Instance->Root) {
+    if (Position != 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+    // Reset directory position to first entry
+    File->DirReadNext = FVFS_GET_FIRST_FILE (Instance);
+  } else if (Position != 0) {
+    // We don't support partial file reads, so we don't support seeking either.
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemFlush (
+  IN EFI_FILE_PROTOCOL  *This
+  )
+{
+  return EFI_SUCCESS;
+}
+
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemDelete (
+  IN EFI_FILE_PROTOCOL *This
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC EFI_FILE_SYSTEM_INFO mFsInfoTemplate = {
+  0,    // Populate at runtime
+  TRUE, // Read-only
+  0,    // Don't know volume size
+  0,    // No free space
+  0,    // Don't know block size
+  L""   // Populate at runtime
+};
+
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemGetInfo (
+  IN EFI_FILE_PROTOCOL  *This,
+  IN EFI_GUID           *InformationType,
+  IN OUT UINTN          *BufferSize,
+  OUT VOID              *Buffer
+  )
+{
+  FV_FILESYSTEM_FILE          *File;
+  EFI_FILE_SYSTEM_INFO        *FsInfoOut;
+  FV_FILESYSTEM_INSTANCE      *Instance;
+  UINTN                        InfoSize;
+
+  File = FVFS_FILE_FROM_FILE_THIS (This);
+
+  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
+    //
+    // Return filesystem info
+    //
+    Instance = File->Instance;
+
+    InfoSize = sizeof (EFI_FILE_SYSTEM_INFO) + StrSize (Instance->VolumeLabel)
+               - sizeof (CHAR16);
+
+    if (*BufferSize < InfoSize) {
+      *BufferSize = InfoSize;
+      return EFI_BUFFER_TOO_SMALL;
+    }
+
+    // Cast output buffer for convenience
+    FsInfoOut = (EFI_FILE_SYSTEM_INFO *) Buffer;
+
+    CopyMem (FsInfoOut, &mFsInfoTemplate, mFsInfoTemplate.Size);
+    StrCpy (FsInfoOut->VolumeLabel, Instance->VolumeLabel);
+    return EFI_SUCCESS;
+  } else if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
+    //
+    // Return file info
+    //
+
+    return FvFsGetFileInfo (File, BufferSize, (EFI_FILE_INFO *) Buffer);
+  } else {
+    return EFI_UNSUPPORTED;
+  }
+}
+
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemSetInfo (
+  IN EFI_FILE_PROTOCOL *This,
+  IN EFI_GUID *InformationType,
+  IN UINTN BufferSize,
+  IN VOID *Buffer
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+EFI_FILE_PROTOCOL mFilesystemTemplate = {
+  EFI_FILE_PROTOCOL_REVISION,
+  FvSimpleFilesystemOpen,
+  FvSimpleFilesystemClose,
+  FvSimpleFilesystemDelete,
+  FvSimpleFilesystemRead,
+  FvSimpleFilesystemWrite,
+  FvSimpleFilesystemGetPosition,
+  FvSimpleFilesystemSetPosition,
+  FvSimpleFilesystemGetInfo,
+  FvSimpleFilesystemSetInfo,
+  FvSimpleFilesystemFlush
+};
diff --git a/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemDxe.inf b/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemDxe.inf
new file mode 100644
index 0000000..9d42004
--- /dev/null
+++ b/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemDxe.inf
@@ -0,0 +1,52 @@ 
+#/** @file
+#  Support for Simple File System over Firmware Volume
+#
+#  Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
+#
+#  This program and the accompanying materials
+#  are licensed and made available under the terms and conditions of the BSD License
+#  which accompanies this distribution.  The full text of the license may be found at
+#  http://opensource.org/licenses/bsd-license.php
+#
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = FvSimpleFilesystem
+  FILE_GUID                      = 907125c0-a5f1-11e3-a3fe-a3198b49350c
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = FvSimpleFilesystemEntryPoint
+
+[Sources]
+  FvSimpleFilesystem.c
+  FvSimpleFilesystemEntryPoint.c
+  FvSimpleFilesystemInternal.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DevicePathLib
+  MemoryAllocationLib
+  PathLib
+  PrintLib
+  UefiDriverEntryPoint
+
+[Guids]
+  gEfiFileInfoGuid
+  gEfiFileSystemInfoGuid
+  gEfiFileSystemVolumeLabelInfoIdGuid
+
+[Protocols]
+  gEfiDevicePathFromTextProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDriverBindingProtocolGuid
+  gEfiFirmwareVolume2ProtocolGuid
+  gEfiSimpleFileSystemProtocolGuid
+  gEfiUnicodeCollationProtocolGuid
diff --git a/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemEntryPoint.c b/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemEntryPoint.c
new file mode 100644
index 0000000..0b286db
--- /dev/null
+++ b/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemEntryPoint.c
@@ -0,0 +1,405 @@ 
+/** @file
+*
+*  Copyright (c) 2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include <PiDxe.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Protocol/DriverBinding.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/UnicodeCollation.h>
+
+#include "FvSimpleFilesystemInternal.h"
+
+EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollation = NULL;
+
+// A Guid string is 32 hex characters with 4 hyphens: 36 characters total
+#define GUID_STRING_SIZE (36 * sizeof (CHAR16))
+
+#define FVFS_VOLUME_LABEL_PREFIX    L"Firmware Volume: "
+#define FVFS_VOLUME_LABEL_SIZE      (sizeof (FVFS_VOLUME_LABEL_PREFIX) + GUID_STRING_SIZE)
+#define FVFS_FALLBACK_VOLUME_LABEL  L"Firmware Volume"
+
+EFI_STATUS
+EFIAPI
+FvSimpleFilesystemOpenVolume (
+  IN  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
+  OUT EFI_FILE_PROTOCOL              **RootFile
+  )
+{
+  EFI_STATUS                      Status;
+  FV_FILESYSTEM_FILE             *Root;
+  CHAR16                         *UiSection;
+  EFI_GUID                        NameGuid;
+  EFI_FV_FILE_ATTRIBUTES          Attributes;
+  UINT32                          Authentication;
+  VOID                           *Key;
+  EFI_FV_FILETYPE                 FileType;
+  UINTN                           Size;
+  FV_FILESYSTEM_INSTANCE         *Instance;
+  FV_FILESYSTEM_FILE             *File;
+  EFI_FIRMWARE_VOLUME2_PROTOCOL  *FvProtocol;
+  CHAR16                         *Name;
+  UINTN                           NumChars;
+
+  Instance = FVFS_INSTANCE_FROM_SIMPLE_FS_THIS (This);
+  Status = EFI_SUCCESS;
+
+  if (Instance->Root == NULL) {
+    //
+    // Allocate file structure for root file
+    //
+    Root = AllocatePool (sizeof (FV_FILESYSTEM_FILE));
+    if (Root == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    Instance->Root = Root;
+
+    Root->Instance = Instance;
+    Root->Signature = FVFS_FILE_SIGNATURE;
+    Root->Name = L"";
+    Root->Size = 0;
+    CopyMem (&Root->FileProtocol, &mFilesystemTemplate, sizeof (mFilesystemTemplate));
+
+    //
+    // Populate the instance's list of files. We consider anything a file that
+    // has a UI_SECTION, which we consider to be its filename.
+    //
+
+    FvProtocol = Instance->FvProtocol;
+
+    // Allocate Key
+    Key = AllocatePool (FvProtocol->KeySize);
+    ASSERT (Key != NULL);
+    ZeroMem (Key, FvProtocol->KeySize);
+
+    do {
+      FileType = EFI_FV_FILETYPE_ALL;
+
+      Status = FvProtocol->GetNextFile (
+                             FvProtocol,
+                             Key,
+                             &FileType,
+                             &NameGuid,
+                             &Attributes,
+                             &Size
+                             );
+      if (EFI_ERROR (Status)) {
+        ASSERT (Status == EFI_NOT_FOUND);
+        break;
+      }
+
+      //
+      // Found a file.
+      // Allocate a file structure and populate it.
+      //
+      File = AllocatePool (sizeof (FV_FILESYSTEM_FILE));
+      if (File == NULL) {
+        return EFI_OUT_OF_RESOURCES;
+      }
+
+      //
+      // Get a file's name: If it has a UI section, use that, otherwise use
+      // its NameGuid.
+      //
+
+      UiSection = NULL;
+      Status = FvProtocol->ReadSection (
+                                 FvProtocol,
+                                 &NameGuid,
+                                 EFI_SECTION_USER_INTERFACE,
+                                 0,
+                                 (VOID **)&UiSection,
+                                 &Size,
+                                 &Authentication
+                                 );
+      if (!EFI_ERROR (Status)) {
+        Name = UiSection;
+      } else {
+        Name = AllocatePool (GUID_STRING_SIZE);
+        if (Name == NULL) {
+          return EFI_OUT_OF_RESOURCES;
+        }
+        NumChars = UnicodeSPrint (Name, GUID_STRING_SIZE, L"%g", &NameGuid);
+        ASSERT (NumChars == GUID_STRING_SIZE);
+      }
+
+      // Add ".efi" to filenames of drivers and applications.
+      if (FV_FILETYPE_IS_EXECUTABLE (FileType)) {
+        File->Name = AllocateCopyPool (StrSize (Name) + 8, Name);
+        StrCat (File->Name, L".efi");
+        FreePool (Name);
+      } else {
+        File->Name = Name;
+      }
+
+      File->Type = FileType;
+      File->Signature = FVFS_FILE_SIGNATURE;
+      CopyGuid (&File->NameGuid, &NameGuid);
+      File->Instance = Instance;
+      CopyMem (&File->FileProtocol, &mFilesystemTemplate, sizeof (mFilesystemTemplate));
+      InsertHeadList (&Instance->Files, &File->Link);
+
+      // Call FvFsReadFile to get the file's size
+      File->Size = 0;
+      Status = FvFsGetFileSize (File, &File->Size);
+      ASSERT_EFI_ERROR (Status);
+    } while (TRUE);
+
+    FreePool (Key);
+
+    if (Status == EFI_NOT_FOUND) {
+      Status = EFI_SUCCESS;
+    }
+  }
+
+  Instance->Root->DirReadNext = FVFS_GET_FIRST_FILE (Instance);
+  *RootFile = &Instance->Root->FileProtocol;
+  return Status;
+}
+
+STATIC EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mSimpleFsTemplate = {
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
+  FvSimpleFilesystemOpenVolume
+};
+
+EFI_STATUS
+EFIAPI
+FvSimpleFilesystemDriverSupported (
+  IN        EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
+  IN        EFI_HANDLE                   ControllerHandle,
+  IN        EFI_DEVICE_PATH_PROTOCOL    *DevicePath OPTIONAL
+  )
+{
+  return gBS->OpenProtocol (
+                ControllerHandle,
+                &gEfiFirmwareVolume2ProtocolGuid,
+                NULL,
+                gImageHandle,
+                ControllerHandle,
+                EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+                );
+}
+
+EFI_STATUS
+EFIAPI
+FvSimpleFilesystemDriverStart (
+  IN        EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
+  IN        EFI_HANDLE                   ControllerHandle,
+  IN        EFI_DEVICE_PATH_PROTOCOL    *DevicePath OPTIONAL
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_FIRMWARE_VOLUME2_PROTOCOL  *FvProtocol;
+  FV_FILESYSTEM_INSTANCE         *Instance;
+  EFI_DEVICE_PATH_PROTOCOL       *FvDevicePath;
+  EFI_GUID                       *FvGuid;
+  UINTN                           NumChars;
+
+  Status = gBS->LocateProtocol (
+                  &gEfiUnicodeCollationProtocolGuid,
+                  NULL,
+                  (VOID **) &mUnicodeCollation
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Open FV protocol
+  //
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiFirmwareVolume2ProtocolGuid,
+                  (VOID **) &FvProtocol,
+                  gImageHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Create an instance
+  //
+  Instance = AllocatePool (sizeof (FV_FILESYSTEM_INSTANCE));
+  if (Instance == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Instance->Root = NULL;
+  Instance->FvProtocol = FvProtocol;
+  Instance->Signature = FVFS_INSTANCE_SIGNATURE;
+  InitializeListHead (&Instance->Files);
+  CopyMem (&Instance->SimpleFs, &mSimpleFsTemplate, sizeof (mSimpleFsTemplate));
+
+  Status = gBS->InstallProtocolInterface(
+                  &ControllerHandle,
+                  &gEfiSimpleFileSystemProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &Instance->SimpleFs
+                  );
+
+  //
+  // Decide on a filesystem volume label, which will include the FV's guid.
+  //
+
+  // Get the device path to find the FV's GUID
+  Instance->VolumeLabel = NULL;
+  Status =  gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &FvDevicePath,
+                  gImageHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (!EFI_ERROR (Status)) {
+    // Iterate over device path until we find a firmware volume node
+    while (!IsDevicePathEndType (FvDevicePath)) {
+      if (DevicePathType (FvDevicePath) == MEDIA_DEVICE_PATH &&
+          DevicePathSubType (FvDevicePath) == MEDIA_PIWG_FW_VOL_DP) {
+        // Allocate the volume label
+        Instance->VolumeLabel = AllocatePool (FVFS_VOLUME_LABEL_SIZE);
+        // Check the allocation was successful
+        if (Instance->VolumeLabel != NULL) {
+          // Extract the FV's guid
+          FvGuid = &((MEDIA_FW_VOL_DEVICE_PATH *) FvDevicePath)->FvName;
+          // Build the volume label string
+          NumChars = UnicodeSPrint (
+                       Instance->VolumeLabel,
+                       FVFS_VOLUME_LABEL_SIZE,
+                       FVFS_VOLUME_LABEL_PREFIX L"%g",
+                       FvGuid
+            );
+          ASSERT (NumChars == FVFS_VOLUME_LABEL_SIZE);
+        }
+        break;
+      }
+      FvDevicePath = NextDevicePathNode (FvDevicePath);
+    }
+  }
+  // If we didn't decide on a volume label, set a fallback one
+  if (Instance->VolumeLabel == NULL) {
+    Instance->VolumeLabel = AllocateCopyPool (
+                              sizeof (FVFS_FALLBACK_VOLUME_LABEL),
+                              FVFS_FALLBACK_VOLUME_LABEL
+                              );
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+FvSimpleFilesystemDriverStop (
+  IN        EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
+  IN        EFI_HANDLE                   ControllerHandle,
+  IN        UINTN                        NumberOfChildren,
+  IN        EFI_HANDLE                  *ChildHandleBuffer OPTIONAL
+  )
+{
+  EFI_STATUS              Status;
+  FV_FILESYSTEM_INSTANCE *Instance;
+  FV_FILESYSTEM_FILE     *File;
+  LIST_ENTRY             *FileLink;
+
+  Instance = FVFS_INSTANCE_FROM_BINDING_THIS (DriverBinding);
+
+  //
+  // Close and uninstall protocols.
+  //
+
+  Status = gBS->CloseProtocol (
+                   ControllerHandle,
+                   &gEfiFirmwareVolume2ProtocolGuid,
+                   gImageHandle,
+                   ControllerHandle
+                   );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->UninstallProtocolInterface (
+                  ControllerHandle,
+                  &gEfiSimpleFileSystemProtocolGuid,
+                  &Instance->SimpleFs
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Free file structures
+  //
+
+  if (Instance->Root != NULL) {
+    for (FileLink = GetFirstNode (&Instance->Files);
+         !IsNull (&Instance->Files, FileLink);
+         FileLink = GetNextNode (&Instance->Files, FileLink)) {
+      File = FVFS_FILE_FROM_LINK (FileLink);
+
+      FreePool (File->Name);
+      FreePool (File);
+    }
+    // Root->Name is statically allocated, no need to free.
+    FreePool (Instance->Root);
+  }
+
+  //
+  // Free Instance
+  //
+
+  if (Instance->VolumeLabel != NULL) {
+    FreePool (Instance->VolumeLabel);
+  }
+  FreePool (Instance);
+
+  return EFI_SUCCESS;
+}
+
+EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {
+  FvSimpleFilesystemDriverSupported,
+  FvSimpleFilesystemDriverStart,
+  FvSimpleFilesystemDriverStop,
+  0,
+  NULL,
+  NULL
+};
+
+EFIAPI
+EFI_STATUS
+FvSimpleFilesystemEntryPoint (
+  IN EFI_HANDLE          ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS Status;
+
+  Status = gBS->InstallProtocolInterface (
+                  &ImageHandle,
+                  &gEfiDriverBindingProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &mDriverBinding
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemInternal.h b/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemInternal.h
new file mode 100644
index 0000000..ecf5461
--- /dev/null
+++ b/MdeModulePkg/Universal/FvSimpleFilesystemDxe/FvSimpleFilesystemInternal.h
@@ -0,0 +1,99 @@ 
+/** @file
+*
+*  Copyright (c) 2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#ifndef __FVFS_INTERNAL_H__
+#define __FVFS_INTERNAL_H__
+
+#include <Library/BaseLib.h>
+
+typedef struct _FV_FILESYSTEM_FILE FV_FILESYSTEM_FILE;
+
+// Struct representing an instance of the "filesystem". There will be one of
+// these structs per FV.
+typedef struct _FV_FILESYSTEM_INSTANCE {
+  UINT32                           Signature;
+  LIST_ENTRY                       Link;
+  LIST_ENTRY                       Files;
+  EFI_DRIVER_BINDING_PROTOCOL     *DriverBinding;
+  EFI_FIRMWARE_VOLUME2_PROTOCOL   *FvProtocol;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  SimpleFs;
+  FV_FILESYSTEM_FILE              *Root;
+  CHAR16                          *VolumeLabel;
+} FV_FILESYSTEM_INSTANCE;
+
+// Struct representing a file. There will be one of these for each file on each
+// FV, plus one for each FV representing the "root directory".
+struct _FV_FILESYSTEM_FILE {
+  UINT32                           Signature;
+  LIST_ENTRY                       Link;
+  CHAR16                          *Name;
+  FV_FILESYSTEM_FILE              *DirReadNext;
+  EFI_GUID                         NameGuid;
+  FV_FILESYSTEM_INSTANCE          *Instance;
+  EFI_FILE_PROTOCOL                FileProtocol;
+  UINTN                            Size;
+  EFI_FV_FILETYPE                  Type;
+};
+
+#define FVFS_FILE_SIGNATURE      SIGNATURE_32 ('f', 'v', 'f', 'l')
+#define FVFS_INSTANCE_SIGNATURE  SIGNATURE_32 ('f', 'v', 'f', 's')
+
+#define FVFS_INSTANCE_FROM_BINDING_THIS(This) CR (    \
+          This,                                       \
+          FV_FILESYSTEM_INSTANCE,                     \
+          DriverBinding,                              \
+          FVFS_INSTANCE_SIGNATURE                     \
+          )
+
+#define FVFS_INSTANCE_FROM_SIMPLE_FS_THIS(This) CR (  \
+          This,                                       \
+          FV_FILESYSTEM_INSTANCE,                     \
+          SimpleFs,                                   \
+          FVFS_INSTANCE_SIGNATURE                     \
+          )
+
+#define FVFS_FILE_FROM_FILE_THIS(This) CR (           \
+          This,                                       \
+          FV_FILESYSTEM_FILE,                         \
+          FileProtocol,                               \
+          FVFS_FILE_SIGNATURE                         \
+          )
+
+#define FVFS_FILE_FROM_LINK(FileLink) CR (FileLink, FV_FILESYSTEM_FILE, Link, FVFS_FILE_SIGNATURE)
+
+#define FVFS_GET_FIRST_FILE(Instance) FVFS_FILE_FROM_LINK (GetFirstNode (&Instance->Files))
+
+#define FV_FILETYPE_IS_EXECUTABLE(Type) ((Type) == EFI_FV_FILETYPE_PEIM                  || \
+                                         (Type) == EFI_FV_FILETYPE_DRIVER                || \
+                                         (Type) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER  || \
+                                         (Type) == EFI_FV_FILETYPE_APPLICATION)
+
+EFI_STATUS
+FvFsReadFile (
+  FV_FILESYSTEM_FILE *File,
+  UINTN              *BufferSize,
+  VOID              **Buffer
+  );
+
+EFI_STATUS
+FvFsGetFileSize (
+  IN  FV_FILESYSTEM_FILE *File,
+  OUT UINTN              *Size
+  );
+
+extern EFI_FILE_PROTOCOL mFilesystemTemplate;
+
+extern EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollation;
+
+#endif