blob: 3d71760fef9913aad15a1b8ec5f9e2649d16cffd [file] [log] [blame]
/** @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) {
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;
}