| /** @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 <Library/BaseMemoryLib.h> | |
| #include <Library/DevicePathLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/PrintLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Protocol/DevicePathFromText.h> | |
| #include <Protocol/DriverBinding.h> | |
| #include "BootMonFsInternal.h" | |
| EFI_DEVICE_PATH* mBootMonFsSupportedDevicePaths; | |
| 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 | |
| }; | |
| /** | |
| Search for a file given its name coded in Ascii. | |
| When searching through the files of the volume, if a file is currently not | |
| open, its name was written on the media and is kept in RAM in the | |
| "HwDescription.Footer.Filename[]" field of the file's description. | |
| If a file is currently open, its name might not have been written on the | |
| media yet, and as the "HwDescription" is a mirror in RAM of what is on the | |
| media the "HwDescription.Footer.Filename[]" might be outdated. In that case, | |
| the up to date name of the file is stored in the "Info" field of the file's | |
| description. | |
| @param[in] Instance Pointer to the description of the volume in which | |
| the file has to be search for. | |
| @param[in] AsciiFileName Name of the file. | |
| @param[out] File Pointer to the description of the file if the | |
| file was found. | |
| @retval EFI_SUCCESS The file was found. | |
| @retval EFI_NOT_FOUND The file was not found. | |
| **/ | |
| EFI_STATUS | |
| BootMonGetFileFromAsciiFileName ( | |
| IN BOOTMON_FS_INSTANCE *Instance, | |
| IN CHAR8* AsciiFileName, | |
| OUT BOOTMON_FS_FILE **File | |
| ) | |
| { | |
| LIST_ENTRY *Entry; | |
| BOOTMON_FS_FILE *FileEntry; | |
| CHAR8 OpenFileAsciiFileName[MAX_NAME_LENGTH]; | |
| CHAR8 *AsciiFileNameToCompare; | |
| // 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 (FileEntry->Info != NULL) { | |
| UnicodeStrToAsciiStrS (FileEntry->Info->FileName, OpenFileAsciiFileName, | |
| MAX_NAME_LENGTH); | |
| AsciiFileNameToCompare = OpenFileAsciiFileName; | |
| } else { | |
| AsciiFileNameToCompare = FileEntry->HwDescription.Footer.Filename; | |
| } | |
| if (AsciiStrCmp (AsciiFileNameToCompare, 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, | |
| gImageHandle, | |
| 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, | |
| gImageHandle, | |
| ControllerHandle | |
| ); | |
| // Check that BlockIo protocol instance exists | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiBlockIoProtocolGuid, | |
| NULL, | |
| gImageHandle, | |
| 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, | |
| gImageHandle, | |
| 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, gImageHandle, 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; | |
| EFI_FILE_INFO *Info; | |
| 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), | |
| gImageHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto Error; | |
| } | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiDiskIoProtocolGuid, | |
| (VOID **)&(Instance->DiskIo), | |
| gImageHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto Error; | |
| } | |
| // | |
| // 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)) { | |
| goto Error; | |
| } | |
| Info = AllocateZeroPool (sizeof (EFI_FILE_INFO)); | |
| if (Info == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Error; | |
| } | |
| Instance->RootFile->Info = Info; | |
| // Initialize the DevicePath of the Instance | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID **)&(Instance->DevicePath), | |
| gImageHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto Error; | |
| } | |
| // | |
| // Install the Simple File System Protocol | |
| // | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &ControllerHandle, | |
| &gEfiSimpleFileSystemProtocolGuid, &Instance->Fs, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto Error; | |
| } | |
| InsertTailList (&mInstances, &Instance->Link); | |
| return EFI_SUCCESS; | |
| Error: | |
| if (Instance->RootFile != NULL) { | |
| if (Instance->RootFile->Info != NULL) { | |
| FreePool (Instance->RootFile->Info); | |
| } | |
| FreePool (Instance->RootFile); | |
| } | |
| FreePool (Instance); | |
| 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); | |
| FreePool (Instance->RootFile->Info); | |
| FreePool (Instance->RootFile); | |
| FreePool (Instance); | |
| 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; | |
| 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; | |
| } |