From 94e0955d3e8a3d949e3f00fe69b2827a637058c3 Mon Sep 17 00:00:00 2001 From: Olivier Martin Date: Thu, 16 Jan 2014 00:06:13 +0000 Subject: [PATCH] ArmPlatformPkg/BootMonFs: Added support for the NorFlash File System of the ARM Development Boards This is the filesystem created by the microcontroller on NOR Flash of the ARM Versatile Express Development Board. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Olivier Martin git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15126 6f19259b-4bc3-4df7-8a09-765794883524 --- ArmPlatformPkg/ArmPlatformPkg.dec | 5 +- .../ArmVExpressPkg/ArmVExpress-CTA15-A7.fdf | 6 +- .../ArmVExpressPkg/ArmVExpress-CTA9x4.fdf | 6 +- .../ArmVExpressPkg/ArmVExpress-RTSM-A15.fdf | 6 +- .../ArmVExpress-RTSM-A15_MPCore.fdf | 6 +- .../ArmVExpressPkg/ArmVExpress-RTSM-A9x4.fdf | 7 +- .../ArmVExpressPkg/ArmVExpress.dsc.inc | 12 +- .../FileSystem/BootMonFs/BootMonFs.inf | 59 ++ .../FileSystem/BootMonFs/BootMonFsApi.h | 216 ++++++ .../FileSystem/BootMonFs/BootMonFsDir.c | 602 +++++++++++++++++ .../BootMonFs/BootMonFsEntryPoint.c | 479 +++++++++++++ .../FileSystem/BootMonFs/BootMonFsHw.h | 53 ++ .../FileSystem/BootMonFs/BootMonFsImages.c | 214 ++++++ .../FileSystem/BootMonFs/BootMonFsInternal.h | 93 +++ .../FileSystem/BootMonFs/BootMonFsOpenClose.c | 628 ++++++++++++++++++ .../FileSystem/BootMonFs/BootMonFsReadWrite.c | 163 +++++ .../BootMonFs/BootMonFsUnsupported.c | 37 ++ 17 files changed, 2582 insertions(+), 10 deletions(-) create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsApi.h create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsEntryPoint.c create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsHw.h create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsImages.c create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsInternal.h create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsReadWrite.c create mode 100644 ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsUnsupported.c diff --git a/ArmPlatformPkg/ArmPlatformPkg.dec b/ArmPlatformPkg/ArmPlatformPkg.dec index b459db21b2..114c5916c3 100644 --- a/ArmPlatformPkg/ArmPlatformPkg.dec +++ b/ArmPlatformPkg/ArmPlatformPkg.dec @@ -1,6 +1,6 @@ #/** @file # -# Copyright (c) 2011-2013, ARM Limited. All rights reserved. +# Copyright (c) 2011-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 @@ -86,6 +86,9 @@ # Size to reserve in the primary core stack for SEC Global Variables gArmPlatformTokenSpaceGuid.PcdSecGlobalVariableSize|0x0|UINT32|0x00000031 + # Boot Monitor FileSystem + gArmPlatformTokenSpaceGuid.PcdBootMonFsSupportedDevicePaths|L""|VOID*|0x0000003A + # # ARM Primecells # diff --git a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA15-A7.fdf b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA15-A7.fdf index 465b88a800..3454e2bcd1 100644 --- a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA15-A7.fdf +++ b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA15-A7.fdf @@ -1,5 +1,5 @@ # -# Copyright (c) 2012, ARM Limited. All rights reserved. +# Copyright (c) 2012-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 @@ -120,6 +120,10 @@ READ_LOCK_STATUS = TRUE INF FatBinPkg/EnhancedFatDxe/Fat.inf INF MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf + # Versatile Express FileSystem + INF ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf + + # ACPI Support INF MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf # diff --git a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA9x4.fdf b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA9x4.fdf index cd44c51645..1b316c7ab5 100644 --- a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA9x4.fdf +++ b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA9x4.fdf @@ -1,6 +1,5 @@ -# FLASH layout file for ARM VE. # -# Copyright (c) 2011, ARM Limited. All rights reserved. +# Copyright (c) 2011-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 @@ -189,6 +188,9 @@ READ_LOCK_STATUS = TRUE INF FatBinPkg/EnhancedFatDxe/Fat.inf INF MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf + # Versatile Express FileSystem + INF ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf + # # Multimedia Card Interface # diff --git a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A15.fdf b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A15.fdf index 133b2d5ecc..c9162f2417 100644 --- a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A15.fdf +++ b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A15.fdf @@ -1,6 +1,5 @@ -# FLASH layout file for ARM VE. # -# Copyright (c) 2011, ARM Limited. All rights reserved. +# Copyright (c) 2011-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 @@ -157,6 +156,9 @@ READ_LOCK_STATUS = TRUE INF FatBinPkg/EnhancedFatDxe/Fat.inf INF MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf + # Versatile Express FileSystem + INF ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf + # # Multimedia Card Interface # diff --git a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A15_MPCore.fdf b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A15_MPCore.fdf index a3feee7003..ec20e33cd1 100644 --- a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A15_MPCore.fdf +++ b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A15_MPCore.fdf @@ -1,6 +1,5 @@ -# FLASH layout file for ARM VE. # -# Copyright (c) 2011, ARM Limited. All rights reserved. +# Copyright (c) 2011-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 @@ -157,6 +156,9 @@ READ_LOCK_STATUS = TRUE INF FatBinPkg/EnhancedFatDxe/Fat.inf INF MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf + # Versatile Express FileSystem + INF ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf + # # Multimedia Card Interface # diff --git a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A9x4.fdf b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A9x4.fdf index f278355f9a..f4ae329ae8 100644 --- a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A9x4.fdf +++ b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-RTSM-A9x4.fdf @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, ARM Limited. All rights reserved. +# Copyright (c) 2011-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 @@ -148,6 +148,11 @@ READ_LOCK_STATUS = TRUE # INF ArmPkg/Filesystem/SemihostFs/SemihostFs.inf + # + # Versatile Express FileSystem + # + INF ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf + # # FAT filesystem + GPT/MBR partitioning # diff --git a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress.dsc.inc b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress.dsc.inc index 7d405379e6..19dd9cc669 100644 --- a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress.dsc.inc +++ b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress.dsc.inc @@ -1,5 +1,5 @@ # -# Copyright (c) 2011-2012, ARM Limited. All rights reserved. +# Copyright (c) 2011-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 @@ -327,6 +327,13 @@ # ARM PrimeCell # + # + # FileSystem + # + + # List of Device Paths that support BootMonFs + gArmPlatformTokenSpaceGuid.PcdBootMonFsSupportedDevicePaths|L"VenHw(1F15DA3C-37FF-4070-B471-BB4AF12A724A)" + # # ARM OS Loader # @@ -342,3 +349,6 @@ gArmPlatformTokenSpaceGuid.PcdDefaultConInPaths|L"VenHw(D3987D4B-971A-435F-8CAF-4967EB627241)/Uart(38400,8,N,1)/VenPcAnsi()" gArmPlatformTokenSpaceGuid.PcdPlatformBootTimeOut|10 +[Components.common] + # Versatile Express FileSystem + ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf new file mode 100644 index 0000000000..d7770f0b96 --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFs.inf @@ -0,0 +1,59 @@ +#/** @file +# Support for ARM Boot Monitor File System +# +# Copyright (c) 2012-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 = BootMonFs + FILE_GUID = 7abbc454-f737-4322-931c-b1bb62a01d6f + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = BootMonFsEntryPoint + +[Sources] + BootMonFsEntryPoint.c + BootMonFsOpenClose.c + BootMonFsDir.c + BootMonFsImages.c + BootMonFsReadWrite.c + BootMonFsUnsupported.c + +[Packages] + ArmPlatformPkg/ArmPlatformPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + DevicePathLib + MemoryAllocationLib + PrintLib + UefiDriverEntryPoint + UefiLib + +[Guids] + gEfiFileSystemInfoGuid + gEfiFileInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + +[Pcd] + gArmPlatformTokenSpaceGuid.PcdBootMonFsSupportedDevicePaths + +[Protocols] + gEfiDiskIoProtocolGuid + gEfiBlockIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiDevicePathProtocolGuid + gEfiDevicePathFromTextProtocolGuid + diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsApi.h b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsApi.h new file mode 100644 index 0000000000..4f0122e32b --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsApi.h @@ -0,0 +1,216 @@ +/** @file +* +* Copyright (c) 2012-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 __BOOTMON_FS_API_H +#define __BOOTMON_FS_API_H + +#include + +EFI_STATUS +BootMonFsDiscoverNextImage ( + IN BOOTMON_FS_INSTANCE *Flash, + IN EFI_LBA *LbaStart, + OUT HW_IMAGE_DESCRIPTION *Image + ); + +EFI_STATUS +BootMonFsInitialize ( + IN BOOTMON_FS_INSTANCE *Instance + ); + +UINT32 +BootMonFsChecksum ( + IN VOID *Data, + IN UINT32 Size + ); + +EFI_STATUS +BootMonFsComputeFooterChecksum ( + IN OUT HW_IMAGE_DESCRIPTION *Footer + ); + +EFIAPI +EFI_STATUS +OpenBootMonFsOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **Root + ); + +UINT32 +BootMonFsGetImageLength ( + IN BOOTMON_FS_FILE *File + ); + +UINTN +BootMonFsGetPhysicalSize ( + IN BOOTMON_FS_FILE* File + ); + +EFI_STATUS +BootMonFsCreateFile ( + IN BOOTMON_FS_INSTANCE *Instance, + OUT BOOTMON_FS_FILE **File + ); + +EFIAPI +EFI_STATUS +BootMonFsGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +EFIAPI +EFI_STATUS +BootMonFsReadDirectory ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +EFIAPI +EFI_STATUS +BootMonFsFlushDirectory ( + IN EFI_FILE_PROTOCOL *This + ); + +EFIAPI +EFI_STATUS +BootMonFsFlushFile ( + IN EFI_FILE_PROTOCOL *This + ); + +EFIAPI +EFI_STATUS +BootMonFsCloseFile ( + IN EFI_FILE_PROTOCOL *This + ); + +EFIAPI +EFI_STATUS +BootMonFsOpenFile ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ); + + +EFIAPI +EFI_STATUS +BootMonFsReadFile ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +EFIAPI +EFI_STATUS +BootMonFsSetDirPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ); + +EFIAPI +EFI_STATUS +BootMonFsGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ); + +EFIAPI +EFI_STATUS +BootMonFsWriteFile ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ); + +EFIAPI +EFI_STATUS +BootMonFsDeleteFail ( + IN EFI_FILE_PROTOCOL *This + ); + +EFIAPI +EFI_STATUS +BootMonFsDelete ( + IN EFI_FILE_PROTOCOL *This + ); + +EFIAPI +EFI_STATUS +BootMonFsSetPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ); + +EFIAPI +EFI_STATUS +BootMonFsGetPosition( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ); + +// +// UNSUPPORTED OPERATIONS +// + +EFIAPI +EFI_STATUS +BootMonFsGetPositionUnsupported ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ); + +EFIAPI +EFI_STATUS +BootMonFsSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +// +// Directory API +// + +EFI_STATUS +BootMonFsOpenDirectory ( + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN BOOTMON_FS_INSTANCE *Volume + ); + +// +// Internal API +// +EFI_STATUS +BootMonGetFileFromAsciiFileName ( + IN BOOTMON_FS_INSTANCE *Instance, + IN CHAR8* AsciiFileName, + OUT BOOTMON_FS_FILE **File + ); + +EFI_STATUS +BootMonGetFileFromPosition ( + IN BOOTMON_FS_INSTANCE *Instance, + IN UINTN Position, + OUT BOOTMON_FS_FILE **File + ); + +#endif diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c new file mode 100644 index 0000000000..773490e37a --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c @@ -0,0 +1,602 @@ +/** @file +* +* Copyright (c) 2012-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 "BootMonFsInternal.h" + +EFIAPI +EFI_STATUS +OpenBootMonFsOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **Root + ) +{ + BOOTMON_FS_INSTANCE *Instance; + + Instance = BOOTMON_FS_FROM_FS_THIS (This); + if (Instance == NULL) { + return EFI_DEVICE_ERROR; + } + + *Root = &Instance->RootFile->File; + + return EFI_SUCCESS; +} + +UINT32 +BootMonFsGetImageLength ( + IN BOOTMON_FS_FILE *File + ) +{ + UINT32 Index; + UINT32 FileSize; + LIST_ENTRY *RegionToFlushLink; + BOOTMON_FS_FILE_REGION *Region; + + FileSize = 0; + + // Look at all Flash areas to determine file size + for (Index = 0; Index < HW_IMAGE_DESCRIPTION_REGION_MAX; Index++) { + FileSize += File->HwDescription.Region[Index].Size; + } + + // Add the regions that have not been flushed yet + for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink); + !IsNull (&File->RegionToFlushLink, RegionToFlushLink); + RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink) + ) + { + Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink; + if (Region->Offset + Region->Size > FileSize) { + FileSize += Region->Offset + Region->Size; + } + } + + return FileSize; +} + +UINTN +BootMonFsGetPhysicalSize ( + IN BOOTMON_FS_FILE* File + ) +{ + // Return 0 for files that haven't yet been flushed to media + if (File->HwDescription.RegionCount == 0) { + return 0; + } + + return ((File->HwDescription.BlockEnd - File->HwDescription.BlockStart) + 1 ) + * File->Instance->Media->BlockSize; +} + +EFIAPI +EFI_STATUS +BootMonFsSetDirPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ) +{ + BOOTMON_FS_FILE *File; + + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File == NULL) { + return EFI_INVALID_PARAMETER; + } + + // UEFI Spec section 12.5: + // "The seek request for nonzero is not valid on open directories." + if (Position != 0) { + return EFI_UNSUPPORTED; + } + File->Position = Position; + + return EFI_SUCCESS; +} + +EFI_STATUS +BootMonFsOpenDirectory ( + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN BOOTMON_FS_INSTANCE *Volume + ) +{ + ASSERT(0); + + return EFI_UNSUPPORTED; +} +EFI_STATUS +GetFileSystemVolumeLabelInfo ( + IN BOOTMON_FS_INSTANCE *Instance, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + UINTN Size; + EFI_FILE_SYSTEM_VOLUME_LABEL *Label; + EFI_STATUS Status; + + Label = Buffer; + + // Value returned by StrSize includes null terminator. + Size = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL + + StrSize (Instance->FsInfo.VolumeLabel); + + if (*BufferSize >= Size) { + CopyMem (&Label->VolumeLabel, &Instance->FsInfo.VolumeLabel, Size); + Status = EFI_SUCCESS; + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + *BufferSize = Size; + return Status; +} + +// Helper function that calculates a rough "free space" by: +// - Taking the media size +// - Subtracting the sum of all file sizes +// - Subtracting the block size times the number of files +// (To account for the blocks containing the HW_IMAGE_INFO +STATIC +UINT64 +ComputeFreeSpace ( + IN BOOTMON_FS_INSTANCE *Instance + ) +{ + LIST_ENTRY *FileLink; + UINT64 FileSizeSum; + UINT64 MediaSize; + UINTN NumFiles; + EFI_BLOCK_IO_MEDIA *Media; + BOOTMON_FS_FILE *File; + + Media = Instance->BlockIo->Media; + MediaSize = Media->BlockSize * (Media->LastBlock + 1); + + NumFiles = 0; + FileSizeSum = 0; + for (FileLink = GetFirstNode (&Instance->RootFile->Link); + !IsNull (&Instance->RootFile->Link, FileLink); + FileLink = GetNextNode (&Instance->RootFile->Link, FileLink) + ) + { + File = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink); + FileSizeSum += BootMonFsGetImageLength (File); + + NumFiles++; + } + + return MediaSize - (FileSizeSum + (Media->BlockSize + NumFiles)); +} + +EFI_STATUS +GetFilesystemInfo ( + IN BOOTMON_FS_INSTANCE *Instance, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + + if (*BufferSize >= Instance->FsInfo.Size) { + Instance->FsInfo.FreeSpace = ComputeFreeSpace (Instance); + CopyMem (Buffer, &Instance->FsInfo, Instance->FsInfo.Size); + Status = EFI_SUCCESS; + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *BufferSize = Instance->FsInfo.Size; + return Status; +} + +EFI_STATUS +GetFileInfo ( + IN BOOTMON_FS_INSTANCE *Instance, + IN BOOTMON_FS_FILE *File, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_FILE_INFO *Info; + UINTN ResultSize; + UINTN NameSize; + UINTN Index; + + if (File == Instance->RootFile) { + NameSize = 0; + ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof (CHAR16); + } else { + NameSize = AsciiStrLen (File->HwDescription.Footer.Filename) + 1; + ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16)); + } + + if (*BufferSize < ResultSize) { + *BufferSize = ResultSize; + return EFI_BUFFER_TOO_SMALL; + } + + Info = Buffer; + + // Zero out the structure + ZeroMem (Info, ResultSize); + + // Fill in the structure + Info->Size = ResultSize; + + if (File == Instance->RootFile) { + Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; + Info->FileName[0] = L'\0'; + } else { + Info->FileSize = BootMonFsGetImageLength (File); + Info->PhysicalSize = BootMonFsGetPhysicalSize (File); + + for (Index = 0; Index < NameSize; Index++) { + Info->FileName[Index] = File->HwDescription.Footer.Filename[Index]; + } + } + + *BufferSize = ResultSize; + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +SetFileName ( + IN BOOTMON_FS_FILE *File, + IN CHAR16 *FileNameUnicode + ) +{ + CHAR8 *FileNameAscii; + UINT16 SavedChar; + UINTN FileNameSize; + BOOTMON_FS_FILE *SameFile; + EFI_STATUS Status; + + // EFI Shell inserts '\' in front of the filename that must be stripped + if (FileNameUnicode[0] == L'\\') { + FileNameUnicode++; + } + // + // Convert Unicode into Ascii + // + SavedChar = L'\0'; + FileNameSize = StrLen (FileNameUnicode) + 1; + FileNameAscii = AllocatePool (FileNameSize * sizeof (CHAR8)); + if (FileNameAscii == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // If Unicode string is too long then truncate it. + if (FileNameSize > MAX_NAME_LENGTH) { + SavedChar = FileNameUnicode[MAX_NAME_LENGTH - 1]; + FileNameUnicode[MAX_NAME_LENGTH - 1] = L'\0'; + } + UnicodeStrToAsciiStr (FileNameUnicode, FileNameAscii); + // If the unicode string was truncated then restore its original content. + if (SavedChar != L'\0') { + FileNameUnicode[MAX_NAME_LENGTH - 1] = SavedChar; + } + + // If we're changing the file name + if (AsciiStrCmp (FileNameAscii, File->HwDescription.Footer.Filename)) { + // Check a file with that filename doesn't already exist + if (BootMonGetFileFromAsciiFileName ( + File->Instance, + File->HwDescription.Footer.Filename, + &SameFile) != EFI_NOT_FOUND) { + Status = EFI_ACCESS_DENIED; + } else { + AsciiStrCpy (FileNameAscii, File->HwDescription.Footer.Filename); + Status = EFI_SUCCESS; + } + } else { + // No change to filename + Status = EFI_SUCCESS; + } + + FreePool (FileNameAscii); + return Status; +} + +// Set the file's size (NB "size", not "physical size"). If the change amounts +// to an increase, simply do a write followed by a flush. +// (This is a helper function for SetFileInfo.) +STATIC +EFI_STATUS +SetFileSize ( + IN BOOTMON_FS_INSTANCE *Instance, + IN BOOTMON_FS_FILE *BootMonFsFile, + IN UINTN Size + ) +{ + UINT64 StoredPosition; + EFI_STATUS Status; + EFI_FILE_PROTOCOL *File; + CHAR8 Buffer; + UINTN BufferSize; + + Buffer = 0; + BufferSize = sizeof (Buffer); + + File = &BootMonFsFile->File; + + if (!(BootMonFsFile->OpenMode & EFI_FILE_MODE_WRITE)) { + return EFI_ACCESS_DENIED; + } + + if (Size <= BootMonFsFile->HwDescription.Region[0].Size) { + BootMonFsFile->HwDescription.Region[0].Size = Size; + } else { + // Increasing a file's size is potentially complicated as it may require + // moving the image description on media. The simplest way to do it is to + // seek past the end of the file (which is valid in UEFI) and perform a + // Write. + + // Save position + Status = File->GetPosition (File, &StoredPosition); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = File->SetPosition (File, Size - 1); + if (EFI_ERROR (Status)) { + return Status; + } + Status = File->Write (File, &BufferSize, &Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // Restore saved position + Status = File->SetPosition (File, Size - 1); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = File->Flush (File); + if (EFI_ERROR (Status)) { + return Status; + } + } + return EFI_SUCCESS; +} + +EFI_STATUS +SetFileInfo ( + IN BOOTMON_FS_INSTANCE *Instance, + IN BOOTMON_FS_FILE *File, + IN UINTN BufferSize, + IN EFI_FILE_INFO *Info + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + UINT8 *DataBuffer; + UINTN BlockSize; + + Status = EFI_SUCCESS; + BlockIo = Instance->BlockIo; + + // Note that a call to this function on a file opened read-only is only + // invalid if it actually changes fields, so we don't immediately fail if the + // OpenMode is wrong. + // Also note that the only fields supported are filename and size, others are + // ignored. + + if (File != Instance->RootFile) { + if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) { + return EFI_ACCESS_DENIED; + } + + SetFileName (File, Info->FileName); + if (EFI_ERROR (Status)) { + return Status; + } + + // Update file size + Status = SetFileSize (Instance, File, Info->FileSize); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the last block + // + BlockSize = BlockIo->Media->BlockSize; + DataBuffer = AllocatePool (BlockSize); + if (DataBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = BlockIo->ReadBlocks (BlockIo, Instance->Media->MediaId, + File->HwDescription.BlockEnd, BlockSize, DataBuffer); + if (EFI_ERROR (Status)) { + FreePool (DataBuffer); + return Status; + } + CopyMem (DataBuffer + BlockSize - sizeof (File->HwDescription), &File->HwDescription, sizeof (File->HwDescription)); + Status = BlockIo->WriteBlocks (BlockIo, Instance->Media->MediaId, + File->HwDescription.BlockEnd, BlockSize, DataBuffer); + FreePool (DataBuffer); + } + return Status; +} + +EFIAPI +EFI_STATUS +BootMonFsGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + BOOTMON_FS_FILE *File; + BOOTMON_FS_INSTANCE *Instance; + + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File == NULL) { + return EFI_DEVICE_ERROR; + } + + Instance = File->Instance; + + // If the instance has not been initialized yet then do it ... + if (!Instance->Initialized) { + Status = BootMonFsInitialize (Instance); + } else { + Status = EFI_SUCCESS; + } + + if (!EFI_ERROR (Status)) { + if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid) + != 0) { + Status = GetFileSystemVolumeLabelInfo (Instance, BufferSize, Buffer); + } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) { + Status = GetFilesystemInfo (Instance, BufferSize, Buffer); + } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) { + Status = GetFileInfo (Instance, File, BufferSize, Buffer); + } else { + Status = EFI_UNSUPPORTED; + } + } + + return Status; +} + +EFIAPI +EFI_STATUS +BootMonFsSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + BOOTMON_FS_FILE *File; + BOOTMON_FS_INSTANCE *Instance; + + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File == NULL) { + return EFI_DEVICE_ERROR; + } + + Instance = File->Instance; + + if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) { + Status = SetFileInfo (Instance, File, BufferSize, (EFI_FILE_INFO *) Buffer); + } else { + // The only writable field in the other two information types + // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the + // filesystem volume label. This can be retrieved with GetInfo, but it is + // hard-coded into this driver, not stored on media. + Status = EFI_UNSUPPORTED; + } + + return Status; +} + +EFIAPI +EFI_STATUS +BootMonFsReadDirectory ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + BOOTMON_FS_INSTANCE *Instance; + BOOTMON_FS_FILE *RootFile; + BOOTMON_FS_FILE *File; + EFI_FILE_INFO *Info; + UINTN NameSize; + UINTN ResultSize; + EFI_STATUS Status; + UINTN Index; + + RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (RootFile == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = RootFile->Instance; + Status = BootMonGetFileFromPosition (Instance, RootFile->Position, &File); + if (EFI_ERROR (Status)) { + // No more file + *BufferSize = 0; + return EFI_SUCCESS; + } + + NameSize = AsciiStrLen (File->HwDescription.Footer.Filename) + 1; + ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16)); + if (*BufferSize < ResultSize) { + *BufferSize = ResultSize; + return EFI_BUFFER_TOO_SMALL; + } + + // Zero out the structure + Info = Buffer; + ZeroMem (Info, ResultSize); + + // Fill in the structure + Info->Size = ResultSize; + Info->FileSize = BootMonFsGetImageLength (File); + Info->PhysicalSize = BootMonFsGetPhysicalSize (File); + for (Index = 0; Index < NameSize; Index++) { + Info->FileName[Index] = File->HwDescription.Footer.Filename[Index]; + } + + *BufferSize = ResultSize; + RootFile->Position++; + + return EFI_SUCCESS; +} + +EFIAPI +EFI_STATUS +BootMonFsFlushDirectory ( + IN EFI_FILE_PROTOCOL *This + ) +{ + BOOTMON_FS_FILE *RootFile; + LIST_ENTRY *ListFiles; + LIST_ENTRY *Link; + BOOTMON_FS_FILE *File; + + RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (RootFile == NULL) { + return EFI_INVALID_PARAMETER; + } + + ListFiles = &RootFile->Link; + + if (IsListEmpty (ListFiles)) { + return EFI_SUCCESS; + } + + // + // Flush all the files that need to be flushed + // + + // Go through all the list of files to flush them + for (Link = GetFirstNode (ListFiles); + !IsNull (ListFiles, Link); + Link = GetNextNode (ListFiles, Link) + ) + { + File = BOOTMON_FS_FILE_FROM_LINK_THIS (Link); + File->File.Flush (&File->File); + } + + return EFI_SUCCESS; +} diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsEntryPoint.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsEntryPoint.c new file mode 100644 index 0000000000..00f7c077ea --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsEntryPoint.c @@ -0,0 +1,479 @@ +/** @file +* +* Copyright (c) 2012-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 "BootMonFsInternal.h" + +EFI_DEVICE_PATH* mBootMonFsSupportedDevicePaths; +EFI_HANDLE mImageHandle; +LIST_ENTRY mInstances; + +EFI_FILE_PROTOCOL mBootMonFsRootTemplate = { + EFI_FILE_PROTOCOL_REVISION, + BootMonFsOpenFile, + BootMonFsCloseFile, + BootMonFsDeleteFail, + BootMonFsReadDirectory, + BootMonFsWriteFile, + BootMonFsGetPositionUnsupported, // UEFI Spec: GetPosition not valid on dirs + BootMonFsSetDirPosition, + BootMonFsGetInfo, + BootMonFsSetInfo, + BootMonFsFlushDirectory +}; + +EFI_FILE_PROTOCOL mBootMonFsFileTemplate = { + EFI_FILE_PROTOCOL_REVISION, + BootMonFsOpenFile, + BootMonFsCloseFile, + BootMonFsDelete, + BootMonFsReadFile, + BootMonFsWriteFile, + BootMonFsGetPosition, + BootMonFsSetPosition, + BootMonFsGetInfo, + BootMonFsSetInfo, + BootMonFsFlushFile +}; + +EFI_STATUS +BootMonGetFileFromAsciiFileName ( + IN BOOTMON_FS_INSTANCE *Instance, + IN CHAR8* AsciiFileName, + OUT BOOTMON_FS_FILE **File + ) +{ + LIST_ENTRY *Entry; + BOOTMON_FS_FILE *FileEntry; + + // Remove the leading '\\' + if (*AsciiFileName == '\\') { + AsciiFileName++; + } + + // Go through all the files in the list and return the file handle + for (Entry = GetFirstNode (&Instance->RootFile->Link); + !IsNull (&Instance->RootFile->Link, Entry); + Entry = GetNextNode (&Instance->RootFile->Link, Entry) + ) + { + FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (Entry); + if (AsciiStrCmp (FileEntry->HwDescription.Footer.Filename, AsciiFileName) == 0) { + *File = FileEntry; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; +} + +EFI_STATUS +BootMonGetFileFromPosition ( + IN BOOTMON_FS_INSTANCE *Instance, + IN UINTN Position, + OUT BOOTMON_FS_FILE **File + ) +{ + LIST_ENTRY *Entry; + BOOTMON_FS_FILE *FileEntry; + + // Go through all the files in the list and return the file handle + for (Entry = GetFirstNode (&Instance->RootFile->Link); + !IsNull (&Instance->RootFile->Link, Entry) && (&Instance->RootFile->Link != Entry); + Entry = GetNextNode (&Instance->RootFile->Link, Entry) + ) + { + if (Position == 0) { + FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (Entry); + *File = FileEntry; + return EFI_SUCCESS; + } + Position--; + } + return EFI_NOT_FOUND; +} + +EFI_STATUS +BootMonFsCreateFile ( + IN BOOTMON_FS_INSTANCE *Instance, + OUT BOOTMON_FS_FILE **File + ) +{ + BOOTMON_FS_FILE *NewFile; + + NewFile = (BOOTMON_FS_FILE*)AllocateZeroPool (sizeof (BOOTMON_FS_FILE)); + if (NewFile == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NewFile->Signature = BOOTMON_FS_FILE_SIGNATURE; + InitializeListHead (&NewFile->Link); + InitializeListHead (&NewFile->RegionToFlushLink); + NewFile->Instance = Instance; + + // If the created file is the root file then create a directory EFI_FILE_PROTOCOL + if (Instance->RootFile == *File) { + CopyMem (&NewFile->File, &mBootMonFsRootTemplate, sizeof (mBootMonFsRootTemplate)); + } else { + CopyMem (&NewFile->File, &mBootMonFsFileTemplate, sizeof (mBootMonFsFileTemplate)); + } + *File = NewFile; + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +SupportedDevicePathsInit ( + VOID + ) +{ + EFI_STATUS Status; + CHAR16* DevicePathListStr; + CHAR16* DevicePathStr; + CHAR16* NextDevicePathStr; + EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol; + EFI_DEVICE_PATH_PROTOCOL *Instance; + + Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol); + ASSERT_EFI_ERROR (Status); + + // Initialize Variable + DevicePathListStr = (CHAR16*)PcdGetPtr (PcdBootMonFsSupportedDevicePaths); + mBootMonFsSupportedDevicePaths = NULL; + + // Extract the Device Path instances from the multi-device path string + while ((DevicePathListStr != NULL) && (DevicePathListStr[0] != L'\0')) { + NextDevicePathStr = StrStr (DevicePathListStr, L";"); + if (NextDevicePathStr == NULL) { + DevicePathStr = DevicePathListStr; + DevicePathListStr = NULL; + } else { + DevicePathStr = (CHAR16*)AllocateCopyPool ((NextDevicePathStr - DevicePathListStr + 1) * sizeof (CHAR16), DevicePathListStr); + if (DevicePathStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + *(DevicePathStr + (NextDevicePathStr - DevicePathListStr)) = L'\0'; + DevicePathListStr = NextDevicePathStr; + if (DevicePathListStr[0] == L';') { + DevicePathListStr++; + } + } + + Instance = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (DevicePathStr); + ASSERT (Instance != NULL); + mBootMonFsSupportedDevicePaths = AppendDevicePathInstance (mBootMonFsSupportedDevicePaths, Instance); + + if (NextDevicePathStr != NULL) { + FreePool (DevicePathStr); + } + FreePool (Instance); + } + + if (mBootMonFsSupportedDevicePaths == NULL) { + return EFI_UNSUPPORTED; + } else { + return EFI_SUCCESS; + } +} + +EFI_STATUS +EFIAPI +BootMonFsDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL + ) +{ + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DEVICE_PATH_PROTOCOL *DevicePathProtocol; + EFI_DEVICE_PATH_PROTOCOL *SupportedDevicePath; + EFI_DEVICE_PATH_PROTOCOL *SupportedDevicePaths; + EFI_STATUS Status; + UINTN Size1; + UINTN Size2; + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + mImageHandle, + 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, + mImageHandle, + ControllerHandle + ); + + // Check that BlockIo protocol instance exists + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + NULL, + mImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // Check if a DevicePath is attached to the handle + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **)&DevicePathProtocol, + mImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // Check if the Device Path is the one which contains the Boot Monitor File System + Size1 = GetDevicePathSize (DevicePathProtocol); + + // Go through the list of Device Path Instances + Status = EFI_UNSUPPORTED; + SupportedDevicePaths = mBootMonFsSupportedDevicePaths; + while (SupportedDevicePaths != NULL) { + SupportedDevicePath = GetNextDevicePathInstance (&SupportedDevicePaths, &Size2); + + if ((Size1 == Size2) && (CompareMem (DevicePathProtocol, SupportedDevicePath, Size1) == 0)) { + // The Device Path is supported + Status = EFI_SUCCESS; + break; + } + } + + gBS->CloseProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid, mImageHandle, ControllerHandle); + return Status; +} + +EFI_STATUS +EFIAPI +BootMonFsDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL + ) +{ + BOOTMON_FS_INSTANCE *Instance; + EFI_STATUS Status; + UINTN VolumeNameSize; + + Instance = AllocateZeroPool (sizeof (BOOTMON_FS_INSTANCE)); + if (Instance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // Initialize the BlockIo of the Instance + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + (VOID **)&(Instance->BlockIo), + mImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + FreePool (Instance); + return Status; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **)&(Instance->DiskIo), + mImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + FreePool (Instance); + return Status; + } + + // + // Initialize the attributes of the Instance + // + Instance->Signature = BOOTMON_FS_SIGNATURE; + Instance->ControllerHandle = ControllerHandle; + Instance->Media = Instance->BlockIo->Media; + Instance->Binding = DriverBinding; + + // Initialize the Simple File System Protocol + Instance->Fs.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION; + Instance->Fs.OpenVolume = OpenBootMonFsOpenVolume; + + // Volume name + L' ' + '2' digit number + VolumeNameSize = StrSize (BOOTMON_FS_VOLUME_LABEL) + (3 * sizeof (CHAR16)); + + // Initialize FileSystem Information + Instance->FsInfo.Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + VolumeNameSize; + Instance->FsInfo.BlockSize = Instance->Media->BlockSize; + Instance->FsInfo.ReadOnly = FALSE; + Instance->FsInfo.VolumeSize = + Instance->Media->BlockSize * (Instance->Media->LastBlock - Instance->Media->LowestAlignedLba); + CopyMem (Instance->FsInfo.VolumeLabel, BOOTMON_FS_VOLUME_LABEL, StrSize (BOOTMON_FS_VOLUME_LABEL)); + + // Initialize the root file + Status = BootMonFsCreateFile (Instance, &Instance->RootFile); + if (EFI_ERROR (Status)) { + FreePool (Instance); + return Status; + } + + // Initialize the DevicePath of the Instance + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **)&(Instance->DevicePath), + mImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + FreePool (Instance); + return Status; + } + + // + // Install the Simple File System Protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, &Instance->Fs, + NULL + ); + + InsertTailList (&mInstances, &Instance->Link); + + return Status; +} + + +EFI_STATUS +EFIAPI +BootMonFsDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + BOOTMON_FS_INSTANCE *Instance; + LIST_ENTRY *Link; + EFI_STATUS Status; + BOOLEAN InstanceFound; + + // Find instance from ControllerHandle. + Instance = NULL; + InstanceFound = FALSE; + // For each instance in mInstances: + for (Link = GetFirstNode (&mInstances); !IsNull (&mInstances, Link); Link = GetNextNode (&mInstances, Link)) { + Instance = BOOTMON_FS_FROM_LINK (Link); + + if (Instance->ControllerHandle == ControllerHandle) { + InstanceFound = TRUE; + break; + } + } + ASSERT (InstanceFound == TRUE); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + DriverBinding->ImageHandle, + ControllerHandle); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + DriverBinding->ImageHandle, + ControllerHandle); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + DriverBinding->ImageHandle, + ControllerHandle); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, &Instance->Fs, + NULL); + + return Status; +} + +// +// Simple Network Protocol Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL mBootMonFsDriverBinding = { + BootMonFsDriverSupported, + BootMonFsDriverStart, + BootMonFsDriverStop, + 0xa, + NULL, + NULL +}; + +EFI_STATUS +EFIAPI +BootMonFsEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + mImageHandle = ImageHandle; + InitializeListHead (&mInstances); + + // Initialize the list of Device Paths that could support BootMonFs + Status = SupportedDevicePathsInit (); + if (!EFI_ERROR (Status)) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiDriverBindingProtocolGuid, &mBootMonFsDriverBinding, + NULL + ); + ASSERT_EFI_ERROR (Status); + } else { + DEBUG((EFI_D_ERROR,"Warning: No Device Paths supporting BootMonFs have been defined in the PCD.\n")); + } + + return Status; +} diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsHw.h b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsHw.h new file mode 100644 index 0000000000..3ebb5ba54a --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsHw.h @@ -0,0 +1,53 @@ +/** @file +* +* Copyright (c) 2012-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 __BOOTMON_FS_HW_H__ +#define __BOOTMON_FS_HW_H__ + +#define MAX_NAME_LENGTH 32 + +#define HW_IMAGE_FOOTER_SIGNATURE_1 0x464C5348 +#define HW_IMAGE_FOOTER_SIGNATURE_2 0x464F4F54 +#define HW_IMAGE_FOOTER_VERSION 1 +#define HW_IMAGE_FOOTER_OFFSET 92 + +typedef struct { + CHAR8 Filename[MAX_NAME_LENGTH]; + UINT32 Offset; + UINT32 Version; + UINT32 FooterSignature1; + UINT32 FooterSignature2; +} HW_IMAGE_FOOTER; + +#define HW_IMAGE_DESCRIPTION_REGION_MAX 4 + +// This structure is located at the end of a block when a file is present +typedef struct { + UINT32 EntryPoint; + UINT32 Attributes; + UINT32 RegionCount; + struct { + UINT32 LoadAddress; + UINT32 Size; + UINT32 Offset; + UINT32 Checksum; + } Region[HW_IMAGE_DESCRIPTION_REGION_MAX]; + UINT32 BlockStart; + UINT32 BlockEnd; + UINT32 FooterChecksum; + + HW_IMAGE_FOOTER Footer; +} HW_IMAGE_DESCRIPTION; + +#endif diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsImages.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsImages.c new file mode 100644 index 0000000000..7d3e901903 --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsImages.c @@ -0,0 +1,214 @@ +/** @file +* +* Copyright (c) 2012-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 "BootMonFsInternal.h" + +UINT32 +BootMonFsChecksum ( + IN VOID *Data, + IN UINT32 Size + ) +{ + UINT32 *Ptr; + UINT32 Word; + UINT32 Checksum; + + ASSERT (Size % 4 == 0); + + Checksum = 0; + Ptr = (UINT32*)Data; + + while (Size > 0) { + Word = *Ptr++; + Size -= 4; + + if (Word > ~Checksum) { + Checksum++; + } + + Checksum += Word; + } + + return ~Checksum; +} + +EFI_STATUS +BootMonFsComputeFooterChecksum ( + IN OUT HW_IMAGE_DESCRIPTION *Footer + ) +{ + HW_IMAGE_DESCRIPTION *Description; + UINT32 Index; + + Footer->Attributes = 1; + + Description = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION)); + if (Description == NULL) { + DEBUG ((DEBUG_ERROR, "BootMonFsComputeFooterChecksum: Unable to allocate memory.\n")); + return EFI_OUT_OF_RESOURCES; + } + + // Copy over to temporary shim + CopyMem (Description, Footer, sizeof (HW_IMAGE_DESCRIPTION)); + + // BootMon doesn't checksum the previous checksum + Description->FooterChecksum = 0; + + // Blank out regions which aren't being used. + for (Index = Footer->RegionCount; Index < HW_IMAGE_DESCRIPTION_REGION_MAX; Index++) { + Description->Region[Index].Checksum = 0; + Description->Region[Index].LoadAddress = 0; + Description->Region[Index].Offset = 0; + Description->Region[Index].Size = 0; + } + + // Compute the checksum + Footer->FooterChecksum = BootMonFsChecksum (Description, sizeof (HW_IMAGE_DESCRIPTION)); + + FreePool (Description); + + return EFI_SUCCESS; +} + +BOOLEAN +BootMonFsImageInThisBlock ( + IN VOID *Buf, + IN UINTN Size, + IN UINT32 Block, + OUT HW_IMAGE_DESCRIPTION *Image + ) +{ + EFI_STATUS Status; + HW_IMAGE_FOOTER *Ptr; + HW_IMAGE_DESCRIPTION *Footer; + UINT32 Checksum; + + // The footer is stored as the last thing in the block + Ptr = (HW_IMAGE_FOOTER *)((UINT8 *)Buf + Size - sizeof (HW_IMAGE_FOOTER)); + + // Check that the verification bytes are present + if ((Ptr->FooterSignature1 != HW_IMAGE_FOOTER_SIGNATURE_1) || (Ptr->FooterSignature2 != HW_IMAGE_FOOTER_SIGNATURE_2)) { + return FALSE; + } + + if (Ptr->Version != HW_IMAGE_FOOTER_VERSION) { + return FALSE; + } + + if (Ptr->Offset != HW_IMAGE_FOOTER_OFFSET) { + return FALSE; + } + + Footer = (HW_IMAGE_DESCRIPTION *)(((UINT8 *)Buf + Size - sizeof (HW_IMAGE_DESCRIPTION))); + Checksum = Footer->FooterChecksum; + Status = BootMonFsComputeFooterChecksum (Footer); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Warning: failed to compute checksum for image '%a'\n", Footer->Footer.Filename)); + } + + if (Footer->FooterChecksum != Checksum) { + DEBUG ((DEBUG_ERROR, "Warning: image '%a' checksum mismatch.\n", Footer->Footer.Filename)); + } + + if ((Footer->BlockEnd != Block) || (Footer->BlockStart > Footer->BlockEnd)) { + return FALSE; + } + + // Copy the image out + CopyMem (Image, Footer, sizeof (HW_IMAGE_DESCRIPTION)); + + return TRUE; +} + +EFI_STATUS +BootMonFsDiscoverNextImage ( + IN BOOTMON_FS_INSTANCE *Instance, + IN EFI_LBA *LbaStart, + OUT HW_IMAGE_DESCRIPTION *Image + ) +{ + EFI_BLOCK_IO_PROTOCOL *Blocks; + EFI_LBA CurrentLba; + VOID *Out; + + Blocks = Instance->BlockIo; + + // Allocate an output buffer + Out = AllocatePool (Instance->Media->BlockSize); + if (Out == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Blocks->Reset (Blocks, FALSE); + CurrentLba = *LbaStart; + + // Look for images in the rest of this block + while (CurrentLba <= Instance->Media->LastBlock) { + // Read in the next block + Blocks->ReadBlocks (Blocks, Instance->Media->MediaId, CurrentLba, Instance->Media->BlockSize, Out); + // Check for an image in the current block + if (BootMonFsImageInThisBlock (Out, Instance->Media->BlockSize, (CurrentLba - Instance->Media->LowestAlignedLba), Image)) { + DEBUG ((EFI_D_ERROR, "Found image: %a in block %d.\n", &(Image->Footer.Filename), (UINTN)(CurrentLba - Instance->Media->LowestAlignedLba))); + FreePool (Out); + *LbaStart = Image->BlockEnd + 1; + return EFI_SUCCESS; + } else { + CurrentLba++; + } + } + + *LbaStart = CurrentLba; + FreePool (Out); + return EFI_NOT_FOUND; +} + +EFI_STATUS +BootMonFsInitialize ( + IN BOOTMON_FS_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + EFI_LBA Lba; + UINT32 ImageCount; + BOOTMON_FS_FILE *NewFile; + + ImageCount = 0; + Lba = 0; + + while (1) { + Status = BootMonFsCreateFile (Instance, &NewFile); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = BootMonFsDiscoverNextImage (Instance, &Lba, &(NewFile->HwDescription)); + if (EFI_ERROR (Status)) { + // Free NewFile allocated by BootMonFsCreateFile () + FreePool (NewFile); + break; + } + InsertTailList (&Instance->RootFile->Link, &NewFile->Link); + ImageCount++; + } + + Instance->Initialized = TRUE; + return EFI_SUCCESS; +} diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsInternal.h b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsInternal.h new file mode 100644 index 0000000000..8bcbac6433 --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsInternal.h @@ -0,0 +1,93 @@ +/** @file +* +* Copyright (c) 2012-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 __BOOTMONFS_INTERNAL_H__ +#define __BOOTMONFS_INTERNAL_H__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "BootMonFsHw.h" + +#define BOOTMON_FS_VOLUME_LABEL L"NOR Flash" + +typedef struct _BOOTMON_FS_INSTANCE BOOTMON_FS_INSTANCE; + +typedef struct { + LIST_ENTRY Link; + VOID* Buffer; + UINTN Size; + UINT64 Offset; // Offset from the start of the file +} BOOTMON_FS_FILE_REGION; + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + BOOTMON_FS_INSTANCE *Instance; + + HW_IMAGE_DESCRIPTION HwDescription; + + EFI_FILE_PROTOCOL File; + + UINT64 Position; + // If the file needs to be flushed then this list contain the memory buffer that creates this file + LIST_ENTRY RegionToFlushLink; + UINT64 OpenMode; +} BOOTMON_FS_FILE; + +#define BOOTMON_FS_FILE_SIGNATURE SIGNATURE_32('b', 'o', 't', 'f') +#define BOOTMON_FS_FILE_FROM_FILE_THIS(a) CR (a, BOOTMON_FS_FILE, File, BOOTMON_FS_FILE_SIGNATURE) +#define BOOTMON_FS_FILE_FROM_LINK_THIS(a) CR (a, BOOTMON_FS_FILE, Link, BOOTMON_FS_FILE_SIGNATURE) + +struct _BOOTMON_FS_INSTANCE { + UINT32 Signature; + EFI_HANDLE ControllerHandle; + + LIST_ENTRY Link; + + EFI_DRIVER_BINDING_PROTOCOL *Binding; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO_MEDIA *Media; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL Fs; + + EFI_FILE_SYSTEM_INFO FsInfo; + CHAR16 Label[20]; + + BOOTMON_FS_FILE *RootFile; // All the other files are linked to this root + BOOLEAN Initialized; +}; + +#define BOOTMON_FS_SIGNATURE SIGNATURE_32('b', 'o', 't', 'm') +#define BOOTMON_FS_FROM_FS_THIS(a) CR (a, BOOTMON_FS_INSTANCE, Fs, BOOTMON_FS_SIGNATURE) +#define BOOTMON_FS_FROM_LINK(a) CR (a, BOOTMON_FS_INSTANCE, Link, BOOTMON_FS_SIGNATURE) + +#include "BootMonFsApi.h" + +#endif + diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c new file mode 100644 index 0000000000..908393fa53 --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c @@ -0,0 +1,628 @@ +/** @file +* +* Copyright (c) 2012-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 "BootMonFsInternal.h" + +// Clear a file's image description on storage media: +// UEFI allows you to seek past the end of a file, a subsequent write will grow +// the file. It does not specify how space between the former end of the file +// and the beginning of the write should be filled. It's therefore possible that +// BootMonFs metadata, that comes after the end of a file, could be left there +// and wrongly detected by BootMonFsImageInBlock. +STATIC +EFI_STATUS +InvalidateImageDescription ( + IN BOOTMON_FS_FILE *File + ) +{ + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + UINT32 MediaId; + UINT32 BlockSize; + VOID *Buffer; + EFI_STATUS Status; + UINT64 DescriptionAddress; + + DiskIo = File->Instance->DiskIo; + BlockIo = File->Instance->BlockIo; + MediaId = BlockIo->Media->MediaId; + BlockSize = BlockIo->Media->BlockSize; + + DescriptionAddress = (File->HwDescription.BlockEnd * BlockSize) + - sizeof (HW_IMAGE_DESCRIPTION); + + Buffer = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION)); + + Status = DiskIo->WriteDisk (DiskIo, + MediaId, + DescriptionAddress, + sizeof (HW_IMAGE_DESCRIPTION), + Buffer + ); + + FreePool(Buffer); + + return Status; +} + +// Flush file data that will extend the file's length. Update and, if necessary, +// move the image description. +// We need to pass the file's starting position on media (FileStart), because +// if the file hasn't been flushed before its Description->BlockStart won't +// have been initialised. +// FileStart must be aligned to the media's block size. +// Note that this function uses DiskIo to flush, so call BlockIo->FlushBlocks() +// after calling it. +STATIC +EFI_STATUS +FlushAppendRegion ( + IN BOOTMON_FS_FILE *File, + IN BOOTMON_FS_FILE_REGION *Region, + IN UINT64 NewFileSize, + IN UINT64 FileStart + ) +{ + EFI_STATUS Status; + EFI_DISK_IO_PROTOCOL *DiskIo; + UINTN BlockSize; + HW_IMAGE_DESCRIPTION *Description; + + DiskIo = File->Instance->DiskIo; + + BlockSize = File->Instance->BlockIo->Media->BlockSize; + + ASSERT (FileStart % BlockSize == 0); + + // Only invalidate the Image Description of files that have already been + // written in Flash + if (File->HwDescription.RegionCount > 0) { + Status = InvalidateImageDescription (File); + ASSERT_EFI_ERROR (Status); + } + + // + // Update File Description + // + Description = &File->HwDescription; + Description->Attributes = 1; + Description->BlockStart = FileStart / BlockSize; + Description->BlockEnd = Description->BlockStart + (NewFileSize / BlockSize); + Description->Footer.FooterSignature1 = HW_IMAGE_FOOTER_SIGNATURE_1; + Description->Footer.FooterSignature2 = HW_IMAGE_FOOTER_SIGNATURE_2; + Description->Footer.Version = HW_IMAGE_FOOTER_VERSION; + Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET; + Description->RegionCount = 1; + Description->Region[0].Checksum = 0; + Description->Region[0].Offset = Description->BlockStart * BlockSize; + Description->Region[0].Size = NewFileSize - sizeof (HW_IMAGE_DESCRIPTION); + + Status = BootMonFsComputeFooterChecksum (Description); + if (EFI_ERROR (Status)) { + return Status; + } + + // Write the new file data + Status = DiskIo->WriteDisk ( + DiskIo, + File->Instance->Media->MediaId, + FileStart + Region->Offset, + Region->Size, + Region->Buffer + ); + ASSERT_EFI_ERROR (Status); + + // Round the file size up to the nearest block size + if ((NewFileSize % BlockSize) > 0) { + NewFileSize += BlockSize - (NewFileSize % BlockSize); + } + // Update the file description on the media + Status = DiskIo->WriteDisk ( + DiskIo, + File->Instance->Media->MediaId, + (FileStart + NewFileSize) - sizeof (HW_IMAGE_DESCRIPTION), + sizeof (HW_IMAGE_DESCRIPTION), + Description + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +BOOLEAN +BootMonFsFileNeedFlush ( + IN BOOTMON_FS_FILE *File + ) +{ + return !IsListEmpty (&File->RegionToFlushLink); +} + +// Find a space on media for a file that has not yet been flushed to disk. +// Just returns the first space that's big enough. +// This function could easily be adapted to: +// - Find space for moving an existing file that has outgrown its space +// (We do not currently move files, just return EFI_VOLUME_FULL) +// - Find space for a fragment of a file that has outgrown its space +// (We do not currently fragment files - it's not clear whether fragmentation +// is actually part of BootMonFs as there is no spec) +// - Be more clever about finding space (choosing the largest or smallest +// suitable space) +// Parameters: +// File - the new (not yet flushed) file for which we need to find space. +// FileStart - the position on media of the file (in bytes). +STATIC +EFI_STATUS +BootMonFsFindSpaceForNewFile ( + IN BOOTMON_FS_FILE *File, + OUT UINT64 *FileStart + ) +{ + LIST_ENTRY *FileLink; + BOOTMON_FS_FILE *RootFile; + BOOTMON_FS_FILE *FileEntry; + UINTN BlockSize; + UINT64 FileSize; + EFI_BLOCK_IO_MEDIA *Media; + + Media = File->Instance->BlockIo->Media; + BlockSize = Media->BlockSize; + RootFile = File->Instance->RootFile; + + if (IsListEmpty (&RootFile->Link)) { + return EFI_SUCCESS; + } + + // This function must only be called for file which has not been flushed into + // Flash yet + ASSERT (File->HwDescription.RegionCount == 0); + + // Find out how big the file will be + FileSize = BootMonFsGetImageLength (File); + // Add the file header to the file + FileSize += sizeof (HW_IMAGE_DESCRIPTION); + + *FileStart = 0; + // Go through all the files in the list + for (FileLink = GetFirstNode (&RootFile->Link); + !IsNull (&RootFile->Link, FileLink); + FileLink = GetNextNode (&RootFile->Link, FileLink) + ) + { + FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink); + // If the free space preceding the file is big enough to contain the new + // file then use it! + if (((FileEntry->HwDescription.BlockStart * BlockSize) - *FileStart) + >= FileSize) { + // The file list must be in disk-order + RemoveEntryList (&File->Link); + File->Link.BackLink = FileLink->BackLink; + File->Link.ForwardLink = FileLink; + FileLink->BackLink->ForwardLink = &File->Link; + FileLink->BackLink = &File->Link; + + return EFI_SUCCESS; + } else { + *FileStart = (FileEntry->HwDescription.BlockEnd + 1) * BlockSize; + } + } + // See if there's space after the last file + if ((((Media->LastBlock + 1) * BlockSize) - *FileStart) >= FileSize) { + return EFI_SUCCESS; + } else { + return EFI_VOLUME_FULL; + } +} + +// Free the resources in the file's Region list. +STATIC +VOID +FreeFileRegions ( + IN BOOTMON_FS_FILE *File + ) +{ + LIST_ENTRY *RegionToFlushLink; + BOOTMON_FS_FILE_REGION *Region; + + RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink); + while (!IsNull (&File->RegionToFlushLink, RegionToFlushLink)) { + // Repeatedly remove the first node from the list and free its resources. + Region = (BOOTMON_FS_FILE_REGION *) RegionToFlushLink; + RemoveEntryList (RegionToFlushLink); + FreePool (Region->Buffer); + FreePool (Region); + + RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink); + } +} + +EFIAPI +EFI_STATUS +BootMonFsFlushFile ( + IN EFI_FILE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + BOOTMON_FS_INSTANCE *Instance; + LIST_ENTRY *RegionToFlushLink; + BOOTMON_FS_FILE *File; + BOOTMON_FS_FILE *NextFile; + BOOTMON_FS_FILE_REGION *Region; + LIST_ENTRY *FileLink; + UINTN CurrentPhysicalSize; + UINTN BlockSize; + UINT64 FileStart; + UINT64 FileEnd; + UINT64 RegionStart; + UINT64 RegionEnd; + UINT64 NewFileSize; + UINT64 EndOfAppendSpace; + BOOLEAN HasSpace; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + Status = EFI_SUCCESS; + FileStart = 0; + + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Check if the file needs to be flushed + if (!BootMonFsFileNeedFlush (File)) { + return Status; + } + + Instance = File->Instance; + BlockIo = Instance->BlockIo; + DiskIo = Instance->DiskIo; + BlockSize = BlockIo->Media->BlockSize; + + // If the file doesn't exist then find a space for it + if (File->HwDescription.RegionCount == 0) { + Status = BootMonFsFindSpaceForNewFile (File, &FileStart); + // FileStart has changed so we need to recompute RegionEnd + if (EFI_ERROR (Status)) { + return Status; + } + } else { + FileStart = File->HwDescription.BlockStart * BlockSize; + } + + // FileEnd is the NOR address of the end of the file's data + FileEnd = FileStart + BootMonFsGetImageLength (File); + + for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink); + !IsNull (&File->RegionToFlushLink, RegionToFlushLink); + RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink) + ) + { + Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink; + + // RegionStart and RegionEnd are the the intended NOR address of the + // start and end of the region + RegionStart = FileStart + Region->Offset; + RegionEnd = RegionStart + Region->Size; + + if (RegionEnd < FileEnd) { + // Handle regions representing edits to existing portions of the file + // Write the region data straight into the file + Status = DiskIo->WriteDisk (DiskIo, + BlockIo->Media->MediaId, + RegionStart, + Region->Size, + Region->Buffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // Handle regions representing appends to the file + // + // Note: Since seeking past the end of the file with SetPosition() is + // valid, it's possible there will be a gap between the current end of + // the file and the beginning of the new region. Since the UEFI spec + // says nothing about this case (except "a subsequent write would grow + // the file"), we just leave garbage in the gap. + + // Check if there is space to append the new region + HasSpace = FALSE; + NewFileSize = (RegionEnd - FileStart) + sizeof (HW_IMAGE_DESCRIPTION); + CurrentPhysicalSize = BootMonFsGetPhysicalSize (File); + if (NewFileSize <= CurrentPhysicalSize) { + HasSpace = TRUE; + } else { + // Get the File Description for the next file + FileLink = GetNextNode (&Instance->RootFile->Link, &File->Link); + if (!IsNull (&Instance->RootFile->Link, FileLink)) { + NextFile = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink); + + // If there is space between the beginning of the current file and the + // beginning of the next file then use it + EndOfAppendSpace = NextFile->HwDescription.BlockStart * BlockSize; + } else { + // We are flushing the last file. + EndOfAppendSpace = (BlockIo->Media->LastBlock + 1) * BlockSize; + } + if (EndOfAppendSpace - FileStart >= NewFileSize) { + HasSpace = TRUE; + } + } + + if (HasSpace == TRUE) { + Status = FlushAppendRegion (File, Region, NewFileSize, FileStart); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // There isn't a space for the file. + // Options here are to move the file or fragment it. However as files + // may represent boot images at fixed positions, these options will + // break booting if the bootloader doesn't use BootMonFs to find the + // image. + + return EFI_VOLUME_FULL; + } + } + } + + FreeFileRegions (File); + + // Flush DiskIo Buffers (see UEFI Spec 12.7 - DiskIo buffers are flushed by + // calling FlushBlocks on the same device's BlockIo). + BlockIo->FlushBlocks (BlockIo); + + return Status; +} + +/** + Closes a file on the Nor Flash FS volume. + + @param This The EFI_FILE_PROTOCOL to close. + + @return Always returns EFI_SUCCESS. + +**/ +EFIAPI +EFI_STATUS +BootMonFsCloseFile ( + IN EFI_FILE_PROTOCOL *This + ) +{ + // Flush the file if needed + This->Flush (This); + return EFI_SUCCESS; +} + +// Create a new instance of BOOTMON_FS_FILE. +// Uses BootMonFsCreateFile to +STATIC +EFI_STATUS +CreateNewFile ( + IN BOOTMON_FS_INSTANCE *Instance, + IN CHAR8* AsciiFileName, + OUT BOOTMON_FS_FILE **NewHandle + ) +{ + EFI_STATUS Status; + BOOTMON_FS_FILE *File; + + Status = BootMonFsCreateFile (Instance, &File); + if (EFI_ERROR (Status)) { + return Status; + } + + // Remove the leading '\\' + if (*AsciiFileName == '\\') { + AsciiFileName++; + } + + // Set the file name + CopyMem (File->HwDescription.Footer.Filename, AsciiFileName, MAX_NAME_LENGTH); + + // Add the file to list of files of the File System + InsertHeadList (&Instance->RootFile->Link, &File->Link); + + *NewHandle = File; + return Status; +} + +/** + Opens a file on the Nor Flash FS volume + + Calls BootMonFsGetFileFromAsciiFilename to search the list of tracked files. + + @param This The EFI_FILE_PROTOCOL parent handle. + @param NewHandle Double-pointer to the newly created protocol. + @param FileName The name of the image/metadata on flash + @param OpenMode Read,write,append etc + @param Attributes ? + + @return EFI_STATUS + OUT_OF_RESOURCES + Run out of space to keep track of the allocated structures + DEVICE_ERROR + Unable to locate the volume associated with the parent file handle + NOT_FOUND + Filename wasn't found on flash + SUCCESS + +**/ +EFIAPI +EFI_STATUS +BootMonFsOpenFile ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + BOOTMON_FS_FILE *Directory; + BOOTMON_FS_FILE *File; + BOOTMON_FS_INSTANCE *Instance; + CHAR8* AsciiFileName; + EFI_STATUS Status; + + if ((FileName == NULL) || (NewHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // The only valid modes are read, read/write, and read/write/create + if (!(OpenMode & EFI_FILE_MODE_READ) || ((OpenMode & EFI_FILE_MODE_CREATE) && !(OpenMode & EFI_FILE_MODE_WRITE))) { + return EFI_INVALID_PARAMETER; + } + + Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (Directory == NULL) { + return EFI_DEVICE_ERROR; + } + + Instance = Directory->Instance; + + // If the instance has not been initialized it yet then do it ... + if (!Instance->Initialized) { + Status = BootMonFsInitialize (Instance); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // BootMonFs interface requires ASCII filenames + AsciiFileName = AllocatePool ((StrLen (FileName) + 1) * sizeof (CHAR8)); + if (AsciiFileName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + UnicodeStrToAsciiStr (FileName, AsciiFileName); + + if ((AsciiStrCmp (AsciiFileName, "\\") == 0) || + (AsciiStrCmp (AsciiFileName, "/") == 0) || + (AsciiStrCmp (AsciiFileName, "") == 0) || + (AsciiStrCmp (AsciiFileName, ".") == 0)) + { + // + // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory + // + + *NewHandle = &Instance->RootFile->File; + Instance->RootFile->Position = 0; + Status = EFI_SUCCESS; + } else { + // + // Open or Create a regular file + // + + // Check if the file already exists + Status = BootMonGetFileFromAsciiFileName (Instance, AsciiFileName, &File); + if (Status == EFI_NOT_FOUND) { + // The file doesn't exist. + if (OpenMode & EFI_FILE_MODE_CREATE) { + // If the file does not exist but is required then create it. + if (Attributes & EFI_FILE_DIRECTORY) { + // BootMonFS doesn't support subdirectories + Status = EFI_UNSUPPORTED; + } else { + // Create a new file + Status = CreateNewFile (Instance, AsciiFileName, &File); + if (!EFI_ERROR (Status)) { + File->OpenMode = OpenMode; + *NewHandle = &File->File; + File->Position = 0; + } + } + } + } else if (Status == EFI_SUCCESS) { + // The file exists + File->OpenMode = OpenMode; + *NewHandle = &File->File; + File->Position = 0; + } + } + + FreePool (AsciiFileName); + + return Status; +} + +// Delete() for the root directory's EFI_FILE_PROTOCOL instance +EFIAPI +EFI_STATUS +BootMonFsDeleteFail ( + IN EFI_FILE_PROTOCOL *This + ) +{ + This->Close(This); + // You can't delete the root directory + return EFI_WARN_DELETE_FAILURE; +} +EFIAPI +EFI_STATUS +BootMonFsDelete ( + IN EFI_FILE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + BOOTMON_FS_FILE *File; + LIST_ENTRY *RegionToFlushLink; + BOOTMON_FS_FILE_REGION *Region; + HW_IMAGE_DESCRIPTION *Description; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + UINT8 *EmptyBuffer; + + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = EFI_SUCCESS; + + if (BootMonFsFileNeedFlush (File)) { + // Free the entries from the Buffer List + RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink); + do { + Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink; + + // Get Next entry + RegionToFlushLink = RemoveEntryList (RegionToFlushLink); + + // Free the buffers + FreePool (Region->Buffer); + FreePool (Region); + } while (!IsListEmpty (&File->RegionToFlushLink)); + } + + // If (RegionCount is greater than 0) then the file already exists + if (File->HwDescription.RegionCount > 0) { + Description = &File->HwDescription; + BlockIo = File->Instance->BlockIo; + + // Create an empty buffer + EmptyBuffer = AllocateZeroPool (BlockIo->Media->BlockSize); + if (EmptyBuffer == NULL) { + FreePool (File); + return EFI_OUT_OF_RESOURCES; + } + + // Invalidate the last Block + Status = BlockIo->WriteBlocks (BlockIo, BlockIo->Media->MediaId, Description->BlockEnd, BlockIo->Media->BlockSize, EmptyBuffer); + ASSERT_EFI_ERROR (Status); + + FreePool (EmptyBuffer); + } + + // Remove the entry from the list + RemoveEntryList (&File->Link); + FreePool (File); + return Status; +} + diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsReadWrite.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsReadWrite.c new file mode 100644 index 0000000000..653b2c507c --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsReadWrite.c @@ -0,0 +1,163 @@ +/** @file +* +* Copyright (c) 2012-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 "BootMonFsInternal.h" + +EFIAPI +EFI_STATUS +BootMonFsReadFile ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + BOOTMON_FS_INSTANCE *Instance; + BOOTMON_FS_FILE *File; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_BLOCK_IO_MEDIA *Media; + UINT64 FileStart; + EFI_STATUS Status; + UINTN RemainingFileSize; + + // Ensure the file has been written in Flash before reading it. + // This keeps the code simple and avoids having to manage a non-flushed file. + BootMonFsFlushFile (This); + + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = File->Instance; + DiskIo = Instance->DiskIo; + Media = Instance->Media; + FileStart = (Media->LowestAlignedLba + File->HwDescription.BlockStart) * Media->BlockSize; + + if (File->Position >= File->HwDescription.Region[0].Size) { + // The entire file has been read + *BufferSize = 0; + return EFI_DEVICE_ERROR; + } + + // This driver assumes that the entire file is in region 0. + RemainingFileSize = File->HwDescription.Region[0].Size - File->Position; + + // If read would go past end of file, truncate the read + if (*BufferSize > RemainingFileSize) { + *BufferSize = RemainingFileSize; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + Media->MediaId, + FileStart + File->Position, + *BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + *BufferSize = 0; + } + + File->Position += *BufferSize; + + return Status; +} + +// Inserts an entry into the write chain +EFIAPI +EFI_STATUS +BootMonFsWriteFile ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + BOOTMON_FS_FILE *File; + BOOTMON_FS_FILE_REGION *Region; + + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + if (File == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) { + return EFI_ACCESS_DENIED; + } + + // Allocate and initialize the memory region + Region = (BOOTMON_FS_FILE_REGION*)AllocateZeroPool (sizeof (BOOTMON_FS_FILE_REGION)); + if (Region == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Region->Buffer = AllocateCopyPool (*BufferSize, Buffer); + if (Region->Buffer == NULL) { + FreePool (Region); + return EFI_OUT_OF_RESOURCES; + } + + Region->Size = *BufferSize; + + Region->Offset = File->Position; + + InsertTailList (&File->RegionToFlushLink, &Region->Link); + + File->Position += *BufferSize; + + return EFI_SUCCESS; +} + +EFIAPI +EFI_STATUS +BootMonFsSetPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ) +{ + BOOTMON_FS_FILE *File; + + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + + // UEFI Spec section 12.5: + // "Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to + // be set to the end of the file." + if (Position == 0xFFFFFFFFFFFFFFFF) { + File->Position = BootMonFsGetImageLength (File); + } else { + // NB: Seeking past the end of the file is valid. + File->Position = Position; + } + + return EFI_SUCCESS; +} + +EFIAPI +EFI_STATUS +BootMonFsGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ) { + BOOTMON_FS_FILE *File; + + File = BOOTMON_FS_FILE_FROM_FILE_THIS (This); + + *Position = File->Position; + return EFI_SUCCESS; +} diff --git a/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsUnsupported.c b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsUnsupported.c new file mode 100644 index 0000000000..4ecc4ea008 --- /dev/null +++ b/ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsUnsupported.c @@ -0,0 +1,37 @@ +/** @file +* +* Copyright (c) 2012-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 "BootMonFsInternal.h" + +EFIAPI +EFI_STATUS +BootMonFsSetPositionUnsupported ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ) +{ + ASSERT(0); + return EFI_UNSUPPORTED; +} + +EFIAPI +EFI_STATUS +BootMonFsGetPositionUnsupported ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ) +{ + ASSERT(0); + return EFI_UNSUPPORTED; +} -- 2.39.2