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; +}