/** @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; 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) { UnicodeStrToAsciiStr (FileEntry->Info->FileName, OpenFileAsciiFileName); 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; }