From patchwork Mon Mar 24 20:22:59 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Olivier Martin X-Patchwork-Id: 26959 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-qa0-f71.google.com (mail-qa0-f71.google.com [209.85.216.71]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 94BED20143 for ; Mon, 24 Mar 2014 20:23:36 +0000 (UTC) Received: by mail-qa0-f71.google.com with SMTP id j7sf13436612qaq.2 for ; Mon, 24 Mar 2014 13:23:36 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:date:message-id:in-reply-to :references:cc:subject:precedence:reply-to:list-id:list-unsubscribe :list-archive:list-post:list-help:list-subscribe:mime-version :errors-to:x-original-sender:x-original-authentication-results :mailing-list:content-type:content-transfer-encoding; bh=y5CwAeje9ypdsHa7ya0hsIkStB/odD/5qvx1FGTOdPQ=; b=b0Jr/VidScn5ORQjeot4FFwLiYKrokG8QHh9y8z2kDGSiRvN1CIj0JWvfgqQB7ACvB U6FQbLqlwtDT/jRU3rR4/NwxUhOmEGv95vjEpfgG67cfAAjINmk9CqAjZiFWafuNW32U hKdPeT1FW4/RifpJGF6bJVJFigi2/TaUyqFTGiec6+wlrUZP7C8dob41yflB4HOLkMcH olBQ3Yo8IQjvgHbnZRPY3w4+4aSyOiqgI16G355tPut1gmJJZsLejOLnTicTBh4jkU+m Rsgk44eHVH2fXmoKv67DSEqI1KP/DYBx+FSYUSVaX/g9YraW7DkRuDrOYP9gXFsCiVNo 6ihg== X-Gm-Message-State: ALoCoQkLvoAx/gMjTXijLxix9brR8fjYAL5NH9QBMTozjSmnplIMs6Ya/wV2B4M+hJvfDfX6yjt7 X-Received: by 10.58.22.166 with SMTP id e6mr17891090vef.6.1395692616377; Mon, 24 Mar 2014 13:23:36 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.47.239 with SMTP id m102ls1705857qga.8.gmail; Mon, 24 Mar 2014 13:23:36 -0700 (PDT) X-Received: by 10.52.166.18 with SMTP id zc18mr81881vdb.65.1395692616227; Mon, 24 Mar 2014 13:23:36 -0700 (PDT) Received: from mail-vc0-f180.google.com (mail-vc0-f180.google.com [209.85.220.180]) by mx.google.com with ESMTPS id xx6si3220249vcb.79.2014.03.24.13.23.36 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 24 Mar 2014 13:23:36 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.220.180 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.220.180; Received: by mail-vc0-f180.google.com with SMTP id lf12so6285435vcb.39 for ; Mon, 24 Mar 2014 13:23:36 -0700 (PDT) X-Received: by 10.52.137.74 with SMTP id qg10mr84962vdb.61.1395692616109; Mon, 24 Mar 2014 13:23:36 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.220.78.9 with SMTP id i9csp252195vck; Mon, 24 Mar 2014 13:23:35 -0700 (PDT) X-Received: by 10.50.225.134 with SMTP id rk6mr13858994igc.31.1395692614782; Mon, 24 Mar 2014 13:23:34 -0700 (PDT) Received: from lists.sourceforge.net (lists.sourceforge.net. [216.34.181.88]) by mx.google.com with ESMTPS id f9si22088374igo.13.2014.03.24.13.23.34 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Mon, 24 Mar 2014 13:23:34 -0700 (PDT) Received-SPF: pass (google.com: domain of edk2-devel-bounces@lists.sourceforge.net designates 216.34.181.88 as permitted sender) client-ip=216.34.181.88; Received: from localhost ([127.0.0.1] helo=sfs-ml-2.v29.ch3.sourceforge.com) by sfs-ml-2.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1WSBP4-0005zj-A0; Mon, 24 Mar 2014 20:23:26 +0000 Received: from sog-mx-1.v43.ch3.sourceforge.com ([172.29.43.191] helo=mx.sourceforge.net) by sfs-ml-2.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1WSBP0-0005zd-5u for edk2-devel@lists.sourceforge.net; Mon, 24 Mar 2014 20:23:22 +0000 Received-SPF: pass (sog-mx-1.v43.ch3.sourceforge.com: domain of arm.com designates 217.140.96.21 as permitted sender) client-ip=217.140.96.21; envelope-from=olivier.martin@arm.com; helo=cam-smtp0.cambridge.arm.com; Received: from fw-tnat.cambridge.arm.com ([217.140.96.21] helo=cam-smtp0.cambridge.arm.com) by sog-mx-1.v43.ch3.sourceforge.com with esmtps (TLSv1:AES256-SHA:256) (Exim 4.76) id 1WSBOx-0002w0-LD for edk2-devel@lists.sourceforge.net; Mon, 24 Mar 2014 20:23:22 +0000 Received: from e102605-lin.cambridge.arm.com (e102605-lin.cambridge.arm.com [10.1.193.42]) by cam-smtp0.cambridge.arm.com (8.13.8/8.13.8) with ESMTP id s2OKN7l0017891; Mon, 24 Mar 2014 20:23:08 GMT From: Olivier Martin To: feng.tian@intel.com Date: Mon, 24 Mar 2014 20:22:59 +0000 Message-Id: <1395692580-24875-3-git-send-email-olivier.martin@arm.com> X-Mailer: git-send-email 1.8.5 In-Reply-To: <1395692580-24875-1-git-send-email-olivier.martin@arm.com> References: <1395692580-24875-1-git-send-email-olivier.martin@arm.com> X-Spam-Score: -2.0 (--) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -1.5 SPF_CHECK_PASS SPF reports sender host as permitted sender for sender-domain -0.0 SPF_PASS SPF: sender matches SPF record -0.5 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain X-Headers-End: 1WSBOx-0002w0-LD Cc: Brendan Jackman , edk2-devel@lists.sourceforge.net Subject: [edk2] [PATCH 2/3] MdeModulePkg: Implement EFI_FILE_PROTOCOL over Firmware Volumes X-BeenThere: edk2-devel@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list Reply-To: edk2-devel@lists.sourceforge.net List-Id: List-Unsubscribe: , List-Archive: List-Post: , List-Help: , List-Subscribe: , MIME-Version: 1.0 Errors-To: edk2-devel-bounces@lists.sourceforge.net X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: olivier.martin@arm.com X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.180 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 From: Brendan Jackman 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 Reviewed-by: Olivier Martin --- 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 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 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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.
+# +# 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + +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