-/** @file
- File IO routines inspired by Streams with an EFI flavor
-
- Copyright (c) 2007, Intel Corporation<BR>
- Portions copyright (c) 2008-2009, Apple Inc. All rights reserved.
-
- 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.
-
- Basic support for opening files on different device types. The device string
- is in the form of DevType:Path. Current DevType is required as there is no
- current mounted device concept of current working directory concept implement
- by this library.
-
- Device names are case insensative and only check the leading characters for
- unique matches. Thus the following are all the same:
- LoadFile0:
- l0:
- L0:
- Lo0:
-
- Supported Device Names:
- A0x1234:0x12 - A memory buffer starting at address 0x1234 for 0x12 bytes
- l1: - EFI LoadFile device one.
- B0: - EFI BlockIo zero.
- fs3: - EFI Simple File System device 3
- Fv2: - EFI Firmware VOlume device 2
- 10.0.1.102: - TFTP service IP followed by the file name
-**/
-
-#include <PiDxe.h>
-#include <Protocol/BlockIo.h>
-#include <Protocol/DiskIo.h>
-#include <Protocol/SimpleFileSystem.h>
-#include <Protocol/FirmwareVolume2.h>
-#include <Protocol/LoadFile.h>
-#include <Protocol/FirmwareVolumeBlock.h>
-#include <Guid/FileInfo.h>
-#include <Library/BaseLib.h>
-#include <Library/MemoryAllocationLib.h>
-#include <Library/DevicePathLib.h>
-#include <Library/PrintLib.h>
-#include <Library/BaseMemoryLib.h>
-#include <Library/UefiLib.h>
-#include <Library/UefiBootServicesTableLib.h>
-#include <Library/UefiRuntimeServicesTableLib.h>
-#include <Library/DebugLib.h>
-#include <Library/EfiFileLib.h>
-#include <Library/PcdLib.h>
-#include <Library/EblNetworkLib.h>
-
-
-#define EFI_OPEN_FILE_GUARD_HEADER 0x4B4D4641
-#define EFI_OPEN_FILE_GUARD_FOOTER 0x444D5A56
-
-// Need to defend against this overflowing
-#define MAX_CMD_LINE 0x200
-
-typedef struct {
- UINT32 Header;
- EFI_OPEN_FILE File;
- UINT32 Footer;
-} EFI_OPEN_FILE_GUARD;
-
-
-// globals to store current open device info
-EFI_HANDLE *mBlkIo = NULL;
-UINTN mBlkIoCount = 0;
-
-EFI_HANDLE *mFs = NULL;
-UINTN mFsCount = 0;
-// mFsInfo[] array entries must match mFs[] handles
-EFI_FILE_SYSTEM_INFO **mFsInfo = NULL;
-
-EFI_HANDLE *mFv = NULL;
-UINTN mFvCount = 0;
-EFI_HANDLE *mLoadFile = NULL;
-UINTN mLoadFileCount = 0;
-
-
-
-/**
- Internal worker function to validate a File handle.
-
- @param File Open File Handle
-
- @return TRUE File is valid
- @return FALSE File is not valid
-
-
-**/
-BOOLEAN
-FileHandleValid (
- IN EFI_OPEN_FILE *File
- )
-{
- EFI_OPEN_FILE_GUARD *GuardFile;
-
- // Look right before and after file structure for the correct signatures
- GuardFile = BASE_CR (File, EFI_OPEN_FILE_GUARD, File);
- if ((GuardFile->Header != EFI_OPEN_FILE_GUARD_HEADER) ||
- (GuardFile->Footer != EFI_OPEN_FILE_GUARD_FOOTER) ) {
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- Internal worker function. If Buffer is not NULL free it.
-
- @param Buffer Buffer to FreePool()
-
-**/
-VOID
-EblFreePool (
- IN VOID *Buffer
- )
-{
- if (Buffer != NULL) {
- FreePool (Buffer);
- }
-}
-
-/**
- Update Device List Global Variables
-
-**/
-VOID
-EblUpdateDeviceLists (
- VOID
- )
-{
- EFI_STATUS Status;
- UINTN Size;
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
- EFI_FILE_HANDLE Root;
- UINTN Index;
-
- if (mBlkIo != NULL) {
- FreePool (mBlkIo);
- }
- gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &mBlkIoCount, &mBlkIo);
-
- if (mFv != NULL) {
- FreePool (mFv);
- }
- gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &mFvCount, &mFv);
-
- if (mLoadFile != NULL) {
- FreePool (mLoadFile);
- }
- gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &mLoadFileCount, &mLoadFile);
-
- if (mFs != NULL) {
- FreePool (mFs);
- }
-
- if (&mFsInfo[0] != NULL) {
- // Need to Free the mFsInfo prior to reclaculating mFsCount so don't move this code
- for (Index = 0; Index < mFsCount; Index++) {
- if (mFsInfo[Index] != NULL) {
- FreePool (mFsInfo[Index]);
- }
- }
- FreePool (mFsInfo);
- }
-
- gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &mFsCount, &mFs);
-
-
- mFsInfo = AllocateZeroPool (mFsCount * sizeof (EFI_FILE_SYSTEM_INFO *));
- if (mFsInfo == NULL) {
- // If we can't do this then we can't support file system entries
- mFsCount = 0;
- } else {
- // Loop through all the file system structures and cache the file system info data
- for (Index =0; Index < mFsCount; Index++) {
- Status = gBS->HandleProtocol (mFs[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
- if (!EFI_ERROR (Status)) {
- Status = Fs->OpenVolume (Fs, &Root);
- if (!EFI_ERROR (Status)) {
- // Get information about the volume
- Size = 0;
- Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- mFsInfo[Index] = AllocatePool (Size);
- Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
- }
-
- Root->Close (Root);
- }
- }
- }
- }
-}
-
-
-/**
- PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.
- Return TRUE if the <devce name> prefix of PathName matches a file system
- Volume Name. MatchIndex is the array index in mFsInfo[] of the match,
- and it can be used with mFs[] to find the handle that needs to be opened
-
- @param PathName PathName to check
- @param FileStart Index of the first character of the <path>
- @param MatchIndex Index in mFsInfo[] that matches
-
- @return TRUE PathName matches a Volume Label and MatchIndex is valid
- @return FALSE PathName does not match a Volume Label MatchIndex undefined
-
-**/
-BOOLEAN
-EblMatchVolumeName (
- IN CHAR8 *PathName,
- IN UINTN FileStart,
- OUT UINTN *MatchIndex
- )
-{
- UINTN Index;
- UINTN Compare;
- UINTN VolStrLen;
- BOOLEAN Match;
-
- for (Index =0; Index < mFsCount; Index++) {
- if (mFsInfo[Index] == NULL) {
- // FsInfo is not valid so skip it
- continue;
- }
- VolStrLen = StrLen (mFsInfo[Index]->VolumeLabel);
- for (Compare = 0, Match = TRUE; Compare < (FileStart - 1); Compare++) {
- if (Compare > VolStrLen) {
- Match = FALSE;
- break;
- }
- if (PathName[Compare] != (CHAR8)mFsInfo[Index]->VolumeLabel[Compare]) {
- // If the VolumeLabel has a space allow a _ to match with it in addition to ' '
- if (!((PathName[Compare] == '_') && (mFsInfo[Index]->VolumeLabel[Compare] == L' '))) {
- Match = FALSE;
- break;
- }
- }
- }
- if (Match) {
- *MatchIndex = Index;
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-
-/**
- Return the number of devices of the current type active in the system
-
- @param Type Device type to check
-
- @return 0 Invalid type
-
-**/
-UINTN
-EfiGetDeviceCounts (
- IN EFI_OPEN_FILE_TYPE DeviceType
- )
-{
- switch (DeviceType) {
- case EfiOpenLoadFile:
- return mLoadFileCount;
- case EfiOpenFirmwareVolume:
- return mFvCount;
- case EfiOpenFileSystem:
- return mFsCount;
- case EfiOpenBlockIo:
- return mBlkIoCount;
- default:
- return 0;
- }
-}
-
-EFI_STATUS
-ConvertIpStringToEfiIp (
- IN CHAR8 *PathName,
- OUT EFI_IP_ADDRESS *ServerIp
- )
-{
- CHAR8 *Str;
-
- Str = PathName;
- ServerIp->v4.Addr[0] = (UINT8)AsciiStrDecimalToUintn (Str);
-
- Str = AsciiStrStr (Str, ".");
- if (Str == NULL) {
- return EFI_DEVICE_ERROR;
- }
-
- ServerIp->v4.Addr[1] = (UINT8)AsciiStrDecimalToUintn (++Str);
-
- Str = AsciiStrStr (Str, ".");
- if (Str == NULL) {
- return EFI_DEVICE_ERROR;
- }
-
- ServerIp->v4.Addr[2] = (UINT8)AsciiStrDecimalToUintn (++Str);
-
- Str = AsciiStrStr (Str, ".");
- if (Str == NULL) {
- return EFI_DEVICE_ERROR;
- }
-
- ServerIp->v4.Addr[3] = (UINT8)AsciiStrDecimalToUintn (++Str);
-
- return EFI_SUCCESS;
-}
-
-
-/**
- Internal work function to extract a device number from a string skipping
- text. Easy way to extract numbers from strings like blk7:.
-
- @param Str String to extract device number form
-
- @return -1 Device string is not valid
- @return Device #
-
-**/
-UINTN
-EblConvertDevStringToNumber (
- IN CHAR8 *Str
- )
-{
- UINTN Max;
- UINTN Index;
-
-
- // Find the first digit
- Max = AsciiStrLen (Str);
- for (Index = 0; !((*Str >= '0') && (*Str <= '9')) && (Index < Max); Index++) {
- Str++;
- }
- if (Index == Max) {
- return (UINTN)-1;
- }
-
- return AsciiStrDecimalToUintn (Str);
-}
-
-
-/**
- Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo
-
- @param File Open file handle
- @param FileName Name of file after device stripped off
-
-
-**/
-EFI_STATUS
-EblFileDevicePath (
- IN OUT EFI_OPEN_FILE *File,
- IN CHAR8 *FileName,
- IN CONST UINT64 OpenMode
- )
-{
- EFI_STATUS Status;
- UINTN Size;
- FILEPATH_DEVICE_PATH *FilePath;
- EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
- CHAR16 UnicodeFileName[MAX_PATHNAME];
- EFI_BLOCK_IO_PROTOCOL *BlkIo;
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
- EFI_FILE_HANDLE Root;
-
-
- if ( *FileName != 0 ) {
- AsciiStrToUnicodeStr (FileName, UnicodeFileName);
- } else {
- AsciiStrToUnicodeStr ("\\", UnicodeFileName);
- }
-
- Size = StrSize (UnicodeFileName);
- FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL));
- if (FileDevicePath != NULL) {
- FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;
- FilePath->Header.Type = MEDIA_DEVICE_PATH;
- FilePath->Header.SubType = MEDIA_FILEPATH_DP;
- CopyMem (&FilePath->PathName, UnicodeFileName, Size);
- SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
- SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
-
- if (File->EfiHandle != NULL) {
- File->DevicePath = DevicePathFromHandle (File->EfiHandle);
- }
-
- File->DevicePath = AppendDevicePath (File->DevicePath, FileDevicePath);
- FreePool (FileDevicePath);
- }
-
- Status = gBS->HandleProtocol (File->EfiHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo);
- if (!EFI_ERROR (Status)) {
- CopyMem (&File->FsBlockIoMedia, BlkIo->Media, sizeof (EFI_BLOCK_IO_MEDIA));
-
- // If we are not opening the device this will get over written with file info
- File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize);
- }
-
- if (File->Type == EfiOpenFileSystem) {
- Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
- if (!EFI_ERROR (Status)) {
- Status = Fs->OpenVolume (Fs, &Root);
- if (!EFI_ERROR (Status)) {
- // Get information about the volume
- Size = 0;
- Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- File->FsInfo = AllocatePool (Size);
- Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
- }
-
- // Get information about the file
- Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0);
- if (!EFI_ERROR (Status)) {
- Size = 0;
- Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- File->FsFileInfo = AllocatePool (Size);
- Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo);
- if (!EFI_ERROR (Status)) {
- File->Size = (UINTN)File->FsFileInfo->FileSize;
- File->MaxPosition = (UINT64)File->Size;
- }
- }
- }
-
- Root->Close (Root);
- }
- }
- } else if (File->Type == EfiOpenBlockIo) {
- File->Size = (UINTN)File->MaxPosition;
- }
-
- return Status;
-}
-
-#define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
-
-EFI_STATUS
-CompareGuidToString (
- IN EFI_GUID *Guid,
- IN CHAR8 *String
- )
-{
- CHAR8 AsciiGuid[64];
- CHAR8 *StringPtr;
- CHAR8 *GuidPtr;
-
- AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid);
-
- StringPtr = String;
- GuidPtr = AsciiGuid;
-
- while ((*StringPtr != '\0') && (*GuidPtr != '\0')) {
- // Skip dashes
- if (*StringPtr == '-') {
- StringPtr++;
- continue;
- }
-
- if (*GuidPtr == '-') {
- GuidPtr++;
- continue;
- }
-
- if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) {
- return EFI_NOT_FOUND;
- }
-
- StringPtr++;
- GuidPtr++;
- }
-
- return EFI_SUCCESS;
-}
-
-
-/**
- Internal work function to fill in EFI_OPEN_FILE information for the FV
-
- @param File Open file handle
- @param FileName Name of file after device stripped off
-
-
-**/
-EFI_STATUS
-EblFvFileDevicePath (
- IN OUT EFI_OPEN_FILE *File,
- IN CHAR8 *FileName,
- IN CONST UINT64 OpenMode
- )
-{
- EFI_STATUS Status;
- EFI_STATUS GetNextFileStatus;
- MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode;
- EFI_DEVICE_PATH_PROTOCOL *DevicePath;
- UINTN Key;
- UINT32 AuthenticationStatus;
- CHAR8 AsciiSection[MAX_PATHNAME];
- VOID *Section;
- UINTN SectionSize;
- EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
- EFI_LBA Lba;
- UINTN BlockSize;
- UINTN NumberOfBlocks;
-
-
- Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- DevicePath = DevicePathFromHandle (File->EfiHandle);
-
- if (*FileName == '\0') {
- File->DevicePath = DuplicateDevicePath (DevicePath);
- } else {
- Key = 0;
- do {
- File->FvType = EFI_FV_FILETYPE_ALL;
- GetNextFileStatus = File->Fv->GetNextFile (
- File->Fv,
- &Key,
- &File->FvType,
- &File->FvNameGuid,
- &File->FvAttributes,
- &File->Size
- );
- if (!EFI_ERROR (GetNextFileStatus)) {
- Section = NULL;
-
- // Compare GUID first
- Status = CompareGuidToString (&File->FvNameGuid, FileName);
- if (!EFI_ERROR(Status)) {
- break;
- }
-
- Status = File->Fv->ReadSection (
- File->Fv,
- &File->FvNameGuid,
- EFI_SECTION_USER_INTERFACE,
- 0,
- &Section,
- &SectionSize,
- &AuthenticationStatus
- );
- if (!EFI_ERROR (Status)) {
- UnicodeStrToAsciiStr (Section, AsciiSection);
- if (AsciiStriCmp (FileName, AsciiSection) == 0) {
- FreePool (Section);
- break;
- }
- FreePool (Section);
- }
- }
- } while (!EFI_ERROR (GetNextFileStatus));
-
- if (EFI_ERROR (GetNextFileStatus)) {
- return GetNextFileStatus;
- }
-
- File->MaxPosition = File->Size;
- EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);
- File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
- }
-
-
- // Get FVB Info about the handle
- Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
- if (!EFI_ERROR (Status)) {
- Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);
- if (!EFI_ERROR (Status)) {
- for (Lba = 0, File->FvSize = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
- Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
- if (EFI_ERROR (Status)) {
- break;
- }
- }
- }
- }
-
- // FVB not required if FV was soft loaded...
- return EFI_SUCCESS;
-}
-
-
-
-
-/**
- Open a device named by PathName. The PathName includes a device name and
- path seperated by a :. See file header for more details on the PathName
- syntax. There is no checking to prevent a file from being opened more than
- one type.
-
- SectionType is only used to open an FV. Each file in an FV contains multiple
- secitons and only the SectionType section is opened.
-
- For any file that is opened with EfiOpen() must be closed with EfiClose().
-
- @param PathName Path to parse to open
- @param OpenMode Same as EFI_FILE.Open()
- @param SectionType Section in FV to open.
-
- @return NULL Open failed
- @return Valid EFI_OPEN_FILE handle
-
-**/
-EFI_OPEN_FILE *
-EfiOpen (
- IN CHAR8 *PathName,
- IN CONST UINT64 OpenMode,
- IN CONST EFI_SECTION_TYPE SectionType
- )
-{
- EFI_STATUS Status;
- EFI_OPEN_FILE *File;
- EFI_OPEN_FILE FileData;
- UINTN StrLen;
- UINTN FileStart;
- UINTN DevNumber = 0;
- EFI_OPEN_FILE_GUARD *GuardFile;
- BOOLEAN VolumeNameMatch;
- EFI_DEVICE_PATH_PROTOCOL *DevicePath;
- UINTN Size;
- EFI_IP_ADDRESS Ip;
-
- EblUpdateDeviceLists ();
-
- File = &FileData;
- ZeroMem (File, sizeof (EFI_OPEN_FILE));
- File->FvSectionType = SectionType;
-
- StrLen = AsciiStrSize (PathName);
- if (StrLen <= 2) {
- // Smallest valid path is 1 char and a null
- return NULL;
- }
-
- for (FileStart = 0; FileStart < StrLen; FileStart++) {
- if (PathName[FileStart] == ':') {
- FileStart++;
- break;
- }
- }
-
- if (FileStart == 0) {
- // We could add a current working diretory concept
- return NULL;
- }
-
- //
- // Matching volume name has precedence over handle based names
- //
- VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
- if (!VolumeNameMatch) {
- DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName);
- }
-
- File->DeviceName = AllocatePool (StrLen);
- AsciiStrCpy (File->DeviceName, PathName);
- File->DeviceName[FileStart - 1] = '\0';
- File->FileName = &File->DeviceName[FileStart];
-
- //
- // Use best match algorithm on the dev names so we only need to look at the
- // first few charters to match the full device name. Short name forms are
- // legal from the caller.
- //
- Status = EFI_SUCCESS;
- if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) {
- if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) {
- if (DevNumber >= mFsCount) {
- goto ErrorExit;
- }
- File->Type = EfiOpenFileSystem;
- File->EfiHandle = mFs[DevNumber];
- Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode);
-
- } else if (PathName[1] == 'v' || PathName[1] == 'V') {
- if (DevNumber >= mFvCount) {
- goto ErrorExit;
- }
- File->Type = EfiOpenFirmwareVolume;
- File->EfiHandle = mFv[DevNumber];
-
- if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) {
- // Skip leading / as its not really needed for the FV since no directories are supported
- FileStart++;
- }
- Status = EblFvFileDevicePath (File, &PathName[FileStart], OpenMode);
- }
- } else if ((*PathName == 'A') || (*PathName == 'a')) {
- // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
- File->Type = EfiOpenMemoryBuffer;
- // 1st colon is at PathName[FileStart - 1]
- File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]);
-
- // Find 2nd colon
- while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
- FileStart++;
- }
-
- // If we ran out of string, there's no extra data
- if (PathName[FileStart] == '\0') {
- File->Size = 0;
- } else {
- File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
- }
-
- // if there's no number after the second colon, default
- // the end of memory
- if (File->Size == 0) {
- File->Size = (UINTN)(0 - (UINTN)File->Buffer);
- }
-
- File->MaxPosition = File->Size;
- File->BaseOffset = (UINTN)File->Buffer;
-
- } else if (*PathName== 'l' || *PathName == 'L') {
- if (DevNumber >= mLoadFileCount) {
- goto ErrorExit;
- }
- File->Type = EfiOpenLoadFile;
- File->EfiHandle = mLoadFile[DevNumber];
-
- Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile);
- if (EFI_ERROR (Status)) {
- goto ErrorExit;
- }
-
- Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath);
- if (EFI_ERROR (Status)) {
- goto ErrorExit;
- }
- File->DevicePath = DuplicateDevicePath (DevicePath);
-
- } else if (*PathName == 'b' || *PathName == 'B') {
- // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
- if (DevNumber >= mBlkIoCount) {
- goto ErrorExit;
- }
- File->Type = EfiOpenBlockIo;
- File->EfiHandle = mBlkIo[DevNumber];
- EblFileDevicePath (File, "", OpenMode);
-
- // 1st colon is at PathName[FileStart - 1]
- File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]);
-
- // Find 2nd colon
- while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
- FileStart++;
- }
-
- // If we ran out of string, there's no extra data
- if (PathName[FileStart] == '\0') {
- Size = 0;
- } else {
- Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
- }
-
- // if a zero size is passed in (or the size is left out entirely),
- // go to the end of the device.
- if (Size == 0) {
- File->Size = File->Size - File->DiskOffset;
- } else {
- File->Size = Size;
- }
-
- File->MaxPosition = File->Size;
- File->BaseOffset = File->DiskOffset;
- } else if ((*PathName) >= '0' && (*PathName <= '9')) {
-
- // Get current IP address
- Status = EblGetCurrentIpAddress (&Ip);
- if (EFI_ERROR(Status)) {
- AsciiPrint("Device IP Address is not configured.\n");
- goto ErrorExit;
- }
-
-
- // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
- File->Type = EfiOpenTftp;
- File->IsDirty = FALSE;
- File->IsBufferValid = FALSE;
-
- Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp);
- }
-
- if (EFI_ERROR (Status)) {
- goto ErrorExit;
- }
-
- GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD));
- if (GuardFile == NULL) {
- goto ErrorExit;
- }
-
- GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER;
- CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE));
- GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER;
-
- return &(GuardFile->File);
-
-ErrorExit:
- FreePool (File->DeviceName);
- return NULL;
-}
-
-#define FILE_COPY_CHUNK 0x01000000
-
-EFI_STATUS
-EfiCopyFile (
- IN CHAR8 *DestinationFile,
- IN CHAR8 *SourceFile
- )
-{
- EFI_OPEN_FILE *Source = NULL;
- EFI_OPEN_FILE *Destination = NULL;
- EFI_STATUS Status = EFI_SUCCESS;
- VOID *Buffer = NULL;
- UINTN Size;
- UINTN Offset;
- UINTN Chunk = FILE_COPY_CHUNK;
-
- Source = EfiOpen(SourceFile, EFI_FILE_MODE_READ, 0);
- if (Source == NULL) {
- AsciiPrint("Source file open error.\n");
- Status = EFI_NOT_FOUND;
- goto Exit;
- }
-
- Destination = EfiOpen(DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
- if (Destination == NULL) {
- AsciiPrint("Destination file open error.\n");
- Status = EFI_NOT_FOUND;
- goto Exit;
- }
-
- Buffer = AllocatePool(FILE_COPY_CHUNK);
- if (Buffer == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Exit;
- }
-
- Size = EfiTell(Source, NULL);
-
- for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {
- Chunk = FILE_COPY_CHUNK;
-
- Status = EfiRead(Source, Buffer, &Chunk);
- if (EFI_ERROR(Status)) {
- AsciiPrint("Read file error\n");
- goto Exit;
- }
-
- Status = EfiWrite(Destination, Buffer, &Chunk);
- if (EFI_ERROR(Status)) {
- AsciiPrint("Write file error\n");
- goto Exit;
- }
- }
-
- // Any left over?
- if (Offset < Size) {
- Chunk = Size - Offset;
-
- Status = EfiRead(Source, Buffer, &Chunk);
- if (EFI_ERROR(Status)) {
- AsciiPrint("Read file error\n");
- goto Exit;
- }
-
- Status = EfiWrite(Destination, Buffer, &Chunk);
- if (EFI_ERROR(Status)) {
- AsciiPrint("Write file error\n");
- goto Exit;
- }
- }
-
-Exit:
- if (Source != NULL) {
- Status = EfiClose(Source);
- if (EFI_ERROR(Status)) {
- AsciiPrint("Source close error");
- }
- }
-
- if (Destination != NULL) {
- Status = EfiClose(Destination);
- if (EFI_ERROR(Status)) {
- AsciiPrint("Destination close error");
- }
- }
-
- if (Buffer != NULL) {
- FreePool(Buffer);
- }
-
- return Status;
-}
-
-/**
- Use DeviceType and Index to form a valid PathName and try and open it.
-
- @param DeviceType Device type to open
- @param Index Device Index to use. Zero relative.
-
- @return NULL Open failed
- @return Valid EFI_OPEN_FILE handle
-
-**/
-EFI_OPEN_FILE *
-EfiDeviceOpenByType (
- IN EFI_OPEN_FILE_TYPE DeviceType,
- IN UINTN Index
- )
-{
- CHAR8 *DevStr;
- CHAR8 Path[MAX_CMD_LINE];
-
- switch (DeviceType) {
- case EfiOpenLoadFile:
- DevStr = "loadfile%d:";
- break;
- case EfiOpenFirmwareVolume:
- DevStr = "fv%d:";
- break;
- case EfiOpenFileSystem:
- DevStr = "fs%d:";
- break;
- case EfiOpenBlockIo:
- DevStr = "blk%d:";
- break;
- case EfiOpenMemoryBuffer:
- DevStr = "a%d:";
- break;
- default:
- return NULL;
- }
-
- AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index);
-
- return EfiOpen (Path, EFI_FILE_MODE_READ, 0);
-}
-
-
-/**
- Close a file handle opened by EfiOpen() and free all resources allocated by
- EfiOpen().
-
- @param Stream Open File Handle
-
- @return EFI_INVALID_PARAMETER Stream is not an Open File
- @return EFI_SUCCESS Steam closed
-
-**/
-EFI_STATUS
-EfiClose (
- IN EFI_OPEN_FILE *File
- )
-{
- EFI_STATUS Status;
- UINT64 TftpBufferSize;
-
- if (!FileHandleValid (File)) {
- return EFI_INVALID_PARAMETER;
- }
-
- //Write the buffer contents to TFTP file.
- if ((File->Type == EfiOpenTftp) && (File->IsDirty)) {
-
- TftpBufferSize = File->Size;
- Status = EblMtftp (
- EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
- File->Buffer,
- TRUE,
- &TftpBufferSize,
- NULL,
- &File->ServerIp,
- (UINT8 *)File->FileName,
- NULL,
- FALSE
- );
- if (EFI_ERROR(Status)) {
- AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status);
- return Status;
- }
- }
-
- if ((File->Type == EfiOpenLoadFile) ||
- ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE))) {
- EblFreePool(File->Buffer);
- }
-
- EblFreePool (File->DevicePath);
- EblFreePool (File->DeviceName);
- EblFreePool (File->FsFileInfo);
- EblFreePool (File->FsInfo);
-
- if (File->FsFileHandle != NULL) {
- File->FsFileHandle->Close (File->FsFileHandle);
- }
-
- // Need to free File and it's Guard structures
- EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File));
- return EFI_SUCCESS;
-}
-
-
-/**
- Return the size of the file represented by Stream. Also return the current
- Seek position. Opening a file will enable a valid file size to be returned.
- LoadFile is an exception as a load file size is set to zero.
-
- @param Stream Open File Handle
-
- @return 0 Stream is not an Open File or a valid LoadFile handle
-
-**/
-UINTN
-EfiTell (
- IN EFI_OPEN_FILE *File,
- OUT EFI_LBA *CurrentPosition OPTIONAL
- )
-{
- EFI_STATUS Status;
- UINT64 BufferSize = 0;
-
- if (!FileHandleValid (File)) {
- return 0;
- }
-
- if (CurrentPosition != NULL) {
- *CurrentPosition = File->CurrentPosition;
- }
-
- if (File->Type == EfiOpenLoadFile) {
- // Figure out the File->Size
- File->Buffer = NULL;
- File->Size = 0;
- Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer);
- if (Status != EFI_BUFFER_TOO_SMALL) {
- return 0;
- }
-
- File->MaxPosition = (UINT64)File->Size;
- } else if (File->Type == EfiOpenTftp) {
-
- Status = EblMtftp (
- EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
- NULL,
- FALSE,
- &BufferSize,
- NULL,
- &File->ServerIp,
- (UINT8 *)File->FileName,
- NULL,
- TRUE
- );
- if (EFI_ERROR(Status)) {
- AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);
- return 0;
- }
-
- File->Size = BufferSize;
- File->MaxPosition = File->Size;
- }
-
- return File->Size;
-}
-
-
-/**
- Seek to the Offset locaiton in the file. LoadFile and FV device types do
- not support EfiSeek(). It is not possible to grow the file size using
- EfiSeek().
-
- SeekType defines how use Offset to calculate the new file position:
- EfiSeekStart : Position = Offset
- EfiSeekCurrent: Position is Offset bytes from the current position
- EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
-
- @param Stream Open File Handle
- @param Offset Offset to seek too.
- @param SeekType Type of seek to perform
-
-
- @return EFI_INVALID_PARAMETER Stream is not an Open File
- @return EFI_UNSUPPORTED LoadFile and FV doe not support Seek
- @return EFI_NOT_FOUND Seek past the end of the file.
- @return EFI_SUCCESS Steam closed
-
-**/
-EFI_STATUS
-EfiSeek (
- IN EFI_OPEN_FILE *File,
- IN EFI_LBA Offset,
- IN EFI_SEEK_TYPE SeekType
- )
-{
- EFI_STATUS Status;
- UINT64 CurrentPosition;
-
- if (!FileHandleValid (File)) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (File->Type == EfiOpenLoadFile || File->Type == EfiOpenFirmwareVolume) {
- // LoadFile and FV do not support Seek
- return EFI_UNSUPPORTED;
- }
-
- CurrentPosition = File->CurrentPosition;
- switch (SeekType) {
- case EfiSeekStart:
- if (Offset > File->MaxPosition) {
- return EFI_NOT_FOUND;
- }
- CurrentPosition = Offset;
- break;
-
- case EfiSeekCurrent:
- if ((File->CurrentPosition + Offset) > File->MaxPosition) {
- return EFI_NOT_FOUND;
- }
- CurrentPosition += Offset;
- break;
-
- case EfiSeekEnd:
- if (Offset != 0) {
- // We don't support growing file size via seeking past end of file
- return EFI_UNSUPPORTED;
- }
- CurrentPosition = File->MaxPosition;
- break;
-
- default:
- return EFI_NOT_FOUND;
- }
-
- Status = EFI_SUCCESS;
- if (File->FsFileHandle != NULL) {
- Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition);
- }
-
- if (!EFI_ERROR (Status)) {
- File->CurrentPosition = CurrentPosition;
- }
-
- return Status;
-}
-
-EFI_STATUS
-CacheTftpFile (
- IN OUT EFI_OPEN_FILE *File
- )
-{
- EFI_STATUS Status;
- UINT64 TftpBufferSize;
-
- if (File->IsBufferValid) {
- return EFI_SUCCESS;
- }
-
- // Make sure the file size is set.
- EfiTell (File, NULL);
-
- //Allocate a buffer to hold the whole file.
- File->Buffer = AllocatePool(File->Size);
- if (File->Buffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- TftpBufferSize = File->Size;
-
- Status = EblMtftp (
- EFI_PXE_BASE_CODE_TFTP_READ_FILE,
- File->Buffer,
- FALSE,
- &TftpBufferSize,
- NULL,
- &File->ServerIp,
- (UINT8 *)File->FileName,
- NULL,
- FALSE);
- if (EFI_ERROR(Status)) {
- AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status);
- FreePool(File->Buffer);
- return Status;
- }
-
- // Set the buffer valid flag.
- File->IsBufferValid = TRUE;
-
- return Status;
-}
-
-/**
- Read BufferSize bytes from the current locaiton in the file. For load file,
- FV, and TFTP case you must read the entire file.
-
- @param Stream Open File Handle
- @param Buffer Caller allocated buffer.
- @param BufferSize Size of buffer in bytes.
-
-
- @return EFI_SUCCESS Stream is not an Open File
- @return EFI_END_OF_FILE Tried to read past the end of the file
- @return EFI_INVALID_PARAMETER Stream is not an open file handle
- @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
- @return "other" Error returned from device read
-
-**/
-EFI_STATUS
-EfiRead (
- IN EFI_OPEN_FILE *File,
- OUT VOID *Buffer,
- OUT UINTN *BufferSize
- )
-{
- EFI_STATUS Status;
- UINT32 AuthenticationStatus;
- EFI_DISK_IO_PROTOCOL *DiskIo;
-
- if (!FileHandleValid (File)) {
- return EFI_INVALID_PARAMETER;
- }
-
- // Don't read past the end of the file.
- if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
- return EFI_END_OF_FILE;
- }
-
- switch (File->Type) {
- case EfiOpenMemoryBuffer:
- CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
- File->CurrentPosition += *BufferSize;
- Status = EFI_SUCCESS;
- break;
-
- case EfiOpenLoadFile:
- // Figure out the File->Size
- EfiTell (File, NULL);
-
- Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer);
- break;
-
- case EfiOpenFirmwareVolume:
- if (File->FvSectionType == EFI_SECTION_ALL) {
- Status = File->Fv->ReadFile (
- File->Fv,
- &File->FvNameGuid,
- &Buffer,
- BufferSize,
- &File->FvType,
- &File->FvAttributes,
- &AuthenticationStatus
- );
- } else {
- Status = File->Fv->ReadSection (
- File->Fv,
- &File->FvNameGuid,
- File->FvSectionType,
- 0,
- &Buffer,
- BufferSize,
- &AuthenticationStatus
- );
- }
- break;
-
- case EfiOpenFileSystem:
- Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer);
- File->CurrentPosition += *BufferSize;
- break;
-
- case EfiOpenBlockIo:
- Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
- if (!EFI_ERROR(Status)) {
- Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia.MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
- }
- File->CurrentPosition += *BufferSize;
- break;
-
- case EfiOpenTftp:
- // Cache the file if it hasn't been cached yet.
- if (File->IsBufferValid == FALSE) {
- Status = CacheTftpFile (File);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- }
-
- // Copy out the requested data
- CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
- File->CurrentPosition += *BufferSize;
-
- Status = EFI_SUCCESS;
- break;
-
- default:
- return EFI_INVALID_PARAMETER;
- };
-
- return Status;
-}
-
-
-/**
- Read the entire file into a buffer. This routine allocates the buffer and
- returns it to the user full of the read data.
-
- This is very useful for load flie where it's hard to know how big the buffer
- must be.
-
- @param Stream Open File Handle
- @param Buffer Pointer to buffer to return.
- @param BufferSize Pointer to Size of buffer return..
-
-
- @return EFI_SUCCESS Stream is not an Open File
- @return EFI_END_OF_FILE Tried to read past the end of the file
- @return EFI_INVALID_PARAMETER Stream is not an open file handle
- @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
- @return "other" Error returned from device read
-
-**/
-EFI_STATUS
-EfiReadAllocatePool (
- IN EFI_OPEN_FILE *File,
- OUT VOID **Buffer,
- OUT UINTN *BufferSize
- )
-{
- if (!FileHandleValid (File)) {
- return EFI_INVALID_PARAMETER;
- }
-
- // Loadfile defers file size determination on Open so use tell to find it
- EfiTell (File, NULL);
-
- *BufferSize = File->Size;
- *Buffer = AllocatePool (*BufferSize);
- if (*Buffer == NULL) {
- return EFI_NOT_FOUND;
- }
-
- return EfiRead (File, *Buffer, BufferSize);
-}
-
-
-/**
- Write data back to the file. For TFTP case you must write the entire file.
-
- @param Stream Open File Handle
- @param Buffer Pointer to buffer to return.
- @param BufferSize Pointer to Size of buffer return..
-
-
- @return EFI_SUCCESS Stream is not an Open File
- @return EFI_END_OF_FILE Tried to read past the end of the file
- @return EFI_INVALID_PARAMETER Stream is not an open file handle
- @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
- @return "other" Error returned from device write
-
-**/
-EFI_STATUS
-EfiWrite (
- IN EFI_OPEN_FILE *File,
- OUT VOID *Buffer,
- OUT UINTN *BufferSize
- )
-{
- EFI_STATUS Status;
- EFI_FV_WRITE_FILE_DATA FileData;
- EFI_DISK_IO_PROTOCOL *DiskIo;
-
- if (!FileHandleValid (File)) {
- return EFI_INVALID_PARAMETER;
- }
-
- switch (File->Type) {
- case EfiOpenMemoryBuffer:
- if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
- return EFI_END_OF_FILE;
- }
-
- CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
- File->CurrentPosition += *BufferSize;
- Status = EFI_SUCCESS;
-
- case EfiOpenLoadFile:
- // LoadFile device is read only be definition
- Status = EFI_UNSUPPORTED;
-
- case EfiOpenFirmwareVolume:
- if (File->FvSectionType != EFI_SECTION_ALL) {
- // Writes not support to a specific section. You have to update entire file
- return EFI_UNSUPPORTED;
- }
-
- FileData.NameGuid = &(File->FvNameGuid);
- FileData.Type = File->FvType;
- FileData.FileAttributes = File->FvAttributes;
- FileData.Buffer = Buffer;
- FileData.BufferSize = (UINT32)*BufferSize;
- Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData);
- break;
-
- case EfiOpenFileSystem:
- Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer);
- File->CurrentPosition += *BufferSize;
- break;
-
- case EfiOpenBlockIo:
- if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
- return EFI_END_OF_FILE;
- }
-
- Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
- if (!EFI_ERROR(Status)) {
- Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia.MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
- }
- File->CurrentPosition += *BufferSize;
- break;
-
- case EfiOpenTftp:
- // Cache the file if it hasn't been cached yet.
- if (File->IsBufferValid == FALSE) {
- Status = CacheTftpFile(File);
- if (EFI_ERROR(Status)) {
- return Status;
- }
- }
-
- // Don't overwrite the buffer
- if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
- UINT8 *TempBuffer;
-
- TempBuffer = File->Buffer;
-
- File->Buffer = AllocatePool (File->CurrentPosition + *BufferSize);
- if (File->Buffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- CopyMem (File->Buffer, TempBuffer, File->Size);
-
- FreePool (TempBuffer);
-
- File->Size = File->CurrentPosition + *BufferSize;
- File->MaxPosition = File->Size;
- }
-
- // Copy in the requested data
- CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
- File->CurrentPosition += *BufferSize;
-
- // Mark the file dirty
- File->IsDirty = TRUE;
-
- Status = EFI_SUCCESS;
- break;
-
- default:
- Status = EFI_INVALID_PARAMETER;
- };
-
- return Status;
-}
-
+/** @file\r
+File IO routines inspired by Streams with an EFI flavor\r
+\r
+Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>\r
+Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Basic support for opening files on different device types. The device string\r
+is in the form of DevType:Path. Current DevType is required as there is no\r
+current mounted device concept of current working directory concept implement\r
+by this library.\r
+\r
+Device names are case insensitive and only check the leading characters for\r
+unique matches. Thus the following are all the same:\r
+LoadFile0:\r
+l0:\r
+L0:\r
+Lo0:\r
+\r
+Supported Device Names:\r
+A0x1234:0x12 - A memory buffer starting at address 0x1234 for 0x12 bytes\r
+l1: - EFI LoadFile device one.\r
+B0: - EFI BlockIo zero.\r
+fs3: - EFI Simple File System device 3\r
+Fv2: - EFI Firmware VOlume device 2\r
+10.0.1.102: - TFTP service IP followed by the file name\r
+**/\r
+\r
+#include <PiDxe.h>\r
+#include <Protocol/BlockIo.h> \r
+#include <Protocol/DiskIo.h> \r
+#include <Protocol/SimpleFileSystem.h> \r
+#include <Protocol/FirmwareVolume2.h> \r
+#include <Protocol/LoadFile.h>\r
+#include <Protocol/FirmwareVolumeBlock.h> \r
+#include <Guid/FileInfo.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiLib.h> \r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/EfiFileLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/EblNetworkLib.h>\r
+\r
+\r
+CHAR8 *gCwd = NULL;\r
+\r
+CONST EFI_GUID gZeroGuid = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };\r
+\r
+#define EFI_OPEN_FILE_GUARD_HEADER 0x4B4D4641\r
+#define EFI_OPEN_FILE_GUARD_FOOTER 0x444D5A56\r
+\r
+// Need to defend against this overflowing\r
+#define MAX_CMD_LINE 0x200\r
+\r
+typedef struct {\r
+ UINT32 Header;\r
+ EFI_OPEN_FILE File;\r
+ UINT32 Footer;\r
+} EFI_OPEN_FILE_GUARD;\r
+\r
+\r
+// globals to store current open device info\r
+EFI_HANDLE *mBlkIo = NULL;\r
+UINTN mBlkIoCount = 0;\r
+\r
+EFI_HANDLE *mFs = NULL;\r
+UINTN mFsCount = 0;\r
+// mFsInfo[] array entries must match mFs[] handles\r
+EFI_FILE_SYSTEM_INFO **mFsInfo = NULL;\r
+\r
+EFI_HANDLE *mFv = NULL;\r
+UINTN mFvCount = 0;\r
+EFI_HANDLE *mLoadFile = NULL;\r
+UINTN mLoadFileCount = 0;\r
+\r
+\r
+\r
+/**\r
+Internal worker function to validate a File handle.\r
+\r
+@param File Open File Handle\r
+\r
+@return TRUE File is valid\r
+@return FALSE File is not valid\r
+\r
+\r
+**/\r
+BOOLEAN\r
+FileHandleValid (\r
+ IN EFI_OPEN_FILE *File\r
+ )\r
+{\r
+ EFI_OPEN_FILE_GUARD *GuardFile;\r
+\r
+ // Look right before and after file structure for the correct signatures\r
+ GuardFile = BASE_CR (File, EFI_OPEN_FILE_GUARD, File);\r
+ if ((GuardFile->Header != EFI_OPEN_FILE_GUARD_HEADER) ||\r
+ (GuardFile->Footer != EFI_OPEN_FILE_GUARD_FOOTER) ) {\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+Internal worker function. If Buffer is not NULL free it.\r
+\r
+@param Buffer Buffer to FreePool()\r
+\r
+**/\r
+VOID\r
+EblFreePool (\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ if (Buffer != NULL) {\r
+ FreePool (Buffer);\r
+ }\r
+}\r
+\r
+/**\r
+Update Device List Global Variables\r
+\r
+**/\r
+VOID\r
+EblUpdateDeviceLists (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Size;\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+ EFI_FILE_HANDLE Root;\r
+ UINTN Index;\r
+\r
+ if (mBlkIo != NULL) {\r
+ FreePool (mBlkIo);\r
+ }\r
+ gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &mBlkIoCount, &mBlkIo);\r
+\r
+\r
+\r
+ if (mFv != NULL) {\r
+ FreePool (mFv);\r
+ }\r
+ gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &mFvCount, &mFv);\r
+\r
+ if (mLoadFile != NULL) {\r
+ FreePool (mLoadFile);\r
+ }\r
+ gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &mLoadFileCount, &mLoadFile);\r
+\r
+ if (mFs != NULL) {\r
+ FreePool (mFs);\r
+ }\r
+\r
+ if (&mFsInfo[0] != NULL) {\r
+ // Need to Free the mFsInfo prior to recalculating mFsCount so don't move this code\r
+ for (Index = 0; Index < mFsCount; Index++) {\r
+ if (mFsInfo[Index] != NULL) {\r
+ FreePool (mFsInfo[Index]);\r
+ }\r
+ }\r
+ FreePool (mFsInfo);\r
+ }\r
+\r
+ gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &mFsCount, &mFs);\r
+\r
+\r
+ mFsInfo = AllocateZeroPool (mFsCount * sizeof (EFI_FILE_SYSTEM_INFO *));\r
+ if (mFsInfo == NULL) {\r
+ // If we can't do this then we can't support file system entries\r
+ mFsCount = 0;\r
+ } else {\r
+ // Loop through all the file system structures and cache the file system info data\r
+ for (Index =0; Index < mFsCount; Index++) {\r
+ Status = gBS->HandleProtocol (mFs[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = Fs->OpenVolume (Fs, &Root);\r
+ if (!EFI_ERROR (Status)) {\r
+ // Get information about the volume\r
+ Size = 0;\r
+ Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ mFsInfo[Index] = AllocatePool (Size);\r
+ Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);\r
+ }\r
+\r
+ Root->Close (Root);\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.\r
+Return TRUE if the <devce name> prefix of PathName matches a file system\r
+Volume Name. MatchIndex is the array index in mFsInfo[] of the match, \r
+and it can be used with mFs[] to find the handle that needs to be opened\r
+\r
+@param PathName PathName to check\r
+@param FileStart Index of the first character of the <path>\r
+@param MatchIndex Index in mFsInfo[] that matches\r
+\r
+@return TRUE PathName matches a Volume Label and MatchIndex is valid\r
+@return FALSE PathName does not match a Volume Label MatchIndex undefined\r
+\r
+**/\r
+BOOLEAN\r
+EblMatchVolumeName (\r
+ IN CHAR8 *PathName,\r
+ IN UINTN FileStart,\r
+ OUT UINTN *MatchIndex\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN Compare;\r
+ UINTN VolStrLen;\r
+ BOOLEAN Match;\r
+\r
+ for (Index =0; Index < mFsCount; Index++) {\r
+ if (mFsInfo[Index] == NULL) {\r
+ // FsInfo is not valid so skip it\r
+ continue;\r
+ }\r
+ VolStrLen = StrLen (mFsInfo[Index]->VolumeLabel);\r
+ for (Compare = 0, Match = TRUE; Compare < (FileStart - 1); Compare++) {\r
+ if (Compare > VolStrLen) {\r
+ Match = FALSE;\r
+ break;\r
+ }\r
+ if (PathName[Compare] != (CHAR8)mFsInfo[Index]->VolumeLabel[Compare]) {\r
+ // If the VolumeLabel has a space allow a _ to match with it in addition to ' '\r
+ if (!((PathName[Compare] == '_') && (mFsInfo[Index]->VolumeLabel[Compare] == L' '))) {\r
+ Match = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (Match) {\r
+ *MatchIndex = Index;\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+Return the number of devices of the current type active in the system\r
+\r
+@param Type Device type to check\r
+\r
+@return 0 Invalid type\r
+\r
+**/\r
+UINTN\r
+EfiGetDeviceCounts (\r
+ IN EFI_OPEN_FILE_TYPE DeviceType\r
+ )\r
+{\r
+ switch (DeviceType) {\r
+ case EfiOpenLoadFile:\r
+ return mLoadFileCount;\r
+ case EfiOpenFirmwareVolume:\r
+ return mFvCount;\r
+ case EfiOpenFileSystem:\r
+ return mFsCount;\r
+ case EfiOpenBlockIo:\r
+ return mBlkIoCount;\r
+ default:\r
+ return 0;\r
+ }\r
+}\r
+\r
+EFI_STATUS\r
+ConvertIpStringToEfiIp (\r
+ IN CHAR8 *PathName, \r
+ OUT EFI_IP_ADDRESS *ServerIp\r
+ )\r
+{\r
+ CHAR8 *Str;\r
+\r
+ Str = PathName;\r
+ ServerIp->v4.Addr[0] = (UINT8)AsciiStrDecimalToUintn (Str);\r
+\r
+ Str = AsciiStrStr (Str, ".");\r
+ if (Str == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ServerIp->v4.Addr[1] = (UINT8)AsciiStrDecimalToUintn (++Str);\r
+\r
+ Str = AsciiStrStr (Str, ".");\r
+ if (Str == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ServerIp->v4.Addr[2] = (UINT8)AsciiStrDecimalToUintn (++Str);\r
+\r
+ Str = AsciiStrStr (Str, ".");\r
+ if (Str == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ServerIp->v4.Addr[3] = (UINT8)AsciiStrDecimalToUintn (++Str);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+Internal work function to extract a device number from a string skipping \r
+text. Easy way to extract numbers from strings like blk7:.\r
+\r
+@param Str String to extract device number form\r
+\r
+@return -1 Device string is not valid\r
+@return Device #\r
+\r
+**/\r
+UINTN\r
+EblConvertDevStringToNumber (\r
+ IN CHAR8 *Str\r
+ )\r
+{\r
+ UINTN Max;\r
+ UINTN Index;\r
+\r
+\r
+ // Find the first digit \r
+ Max = AsciiStrLen (Str);\r
+ for (Index = 0; !((*Str >= '0') && (*Str <= '9')) && (Index < Max); Index++) {\r
+ Str++;\r
+ }\r
+ if (Index == Max) {\r
+ return (UINTN)-1;\r
+ }\r
+\r
+ return AsciiStrDecimalToUintn (Str);\r
+}\r
+\r
+\r
+/**\r
+Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo\r
+\r
+@param File Open file handle\r
+@param FileName Name of file after device stripped off\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+EblFileDevicePath (\r
+ IN OUT EFI_OPEN_FILE *File,\r
+ IN CHAR8 *FileName,\r
+ IN CONST UINT64 OpenMode\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Size;\r
+ FILEPATH_DEVICE_PATH *FilePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;\r
+ CHAR16 UnicodeFileName[MAX_PATHNAME];\r
+ EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+ EFI_FILE_HANDLE Root;\r
+\r
+\r
+ if ( *FileName != 0 ) {\r
+ AsciiStrToUnicodeStr (FileName, UnicodeFileName);\r
+ } else {\r
+ AsciiStrToUnicodeStr ("\\", UnicodeFileName);\r
+ }\r
+\r
+ Size = StrSize (UnicodeFileName);\r
+ FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL));\r
+ if (FileDevicePath != NULL) {\r
+ FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;\r
+ FilePath->Header.Type = MEDIA_DEVICE_PATH;\r
+ FilePath->Header.SubType = MEDIA_FILEPATH_DP;\r
+ CopyMem (&FilePath->PathName, UnicodeFileName, Size);\r
+ SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);\r
+ SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));\r
+\r
+ if (File->EfiHandle != NULL) {\r
+ File->DevicePath = DevicePathFromHandle (File->EfiHandle);\r
+ }\r
+\r
+ File->DevicePath = AppendDevicePath (File->DevicePath, FileDevicePath);\r
+ FreePool (FileDevicePath);\r
+ }\r
+\r
+ Status = gBS->HandleProtocol (File->EfiHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo);\r
+ if (!EFI_ERROR (Status)) {\r
+ File->FsBlockIoMedia = BlkIo->Media;\r
+ File->FsBlockIo = BlkIo;\r
+\r
+ // If we are not opening the device this will get over written with file info\r
+ File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize);\r
+ }\r
+\r
+ if (File->Type == EfiOpenFileSystem) {\r
+ Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = Fs->OpenVolume (Fs, &Root);\r
+ if (!EFI_ERROR (Status)) {\r
+ // Get information about the volume\r
+ Size = 0;\r
+ Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ File->FsInfo = AllocatePool (Size);\r
+ Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);\r
+ }\r
+\r
+ // Get information about the file\r
+ Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0);\r
+ if (!EFI_ERROR (Status)) {\r
+ Size = 0;\r
+ Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL);\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ File->FsFileInfo = AllocatePool (Size);\r
+ Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo);\r
+ if (!EFI_ERROR (Status)) {\r
+ File->Size = (UINTN)File->FsFileInfo->FileSize;\r
+ File->MaxPosition = (UINT64)File->Size;\r
+ }\r
+ }\r
+ }\r
+\r
+ Root->Close (Root);\r
+ }\r
+ }\r
+ } else if (File->Type == EfiOpenBlockIo) {\r
+ File->Size = (UINTN)File->MaxPosition;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+#define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))\r
+\r
+EFI_STATUS\r
+CompareGuidToString (\r
+ IN EFI_GUID *Guid,\r
+ IN CHAR8 *String\r
+ )\r
+{\r
+ CHAR8 AsciiGuid[64];\r
+ CHAR8 *StringPtr;\r
+ CHAR8 *GuidPtr;\r
+\r
+ AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid);\r
+\r
+ StringPtr = String;\r
+ GuidPtr = AsciiGuid;\r
+\r
+ while ((*StringPtr != '\0') && (*GuidPtr != '\0')) {\r
+ // Skip dashes\r
+ if (*StringPtr == '-') {\r
+ StringPtr++;\r
+ continue;\r
+ }\r
+\r
+ if (*GuidPtr == '-') {\r
+ GuidPtr++;\r
+ continue;\r
+ }\r
+\r
+ if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ StringPtr++;\r
+ GuidPtr++;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+Internal work function to fill in EFI_OPEN_FILE information for the FV\r
+\r
+@param File Open file handle\r
+@param FileName Name of file after device stripped off\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+EblFvFileDevicePath (\r
+ IN OUT EFI_OPEN_FILE *File,\r
+ IN CHAR8 *FileName,\r
+ IN CONST UINT64 OpenMode\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS GetNextFileStatus;\r
+ MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ UINTN Key;\r
+ UINT32 AuthenticationStatus;\r
+ CHAR8 AsciiSection[MAX_PATHNAME];\r
+ VOID *Section;\r
+ UINTN SectionSize;\r
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
+ EFI_LBA Lba;\r
+ UINTN BlockSize;\r
+ UINTN NumberOfBlocks;\r
+ EFI_FIRMWARE_VOLUME_HEADER *FvHeader = NULL;\r
+ UINTN Index;\r
+\r
+\r
+ Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Get FVB Info about the handle\r
+ Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);\r
+ if (!EFI_ERROR (Status)) {\r
+ FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)File->FvStart;\r
+ File->FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER);\r
+ for (Index = 0; FvHeader->BlockMap[Index].Length !=0; Index++) {\r
+ File->FvHeaderSize += sizeof (EFI_FV_BLOCK_MAP_ENTRY);\r
+ }\r
+\r
+ for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {\r
+ Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ DevicePath = DevicePathFromHandle (File->EfiHandle);\r
+\r
+ if (*FileName == '\0') {\r
+ File->DevicePath = DuplicateDevicePath (DevicePath);\r
+ File->Size = File->FvSize;\r
+ File->MaxPosition = File->Size;\r
+ } else {\r
+ Key = 0;\r
+ do {\r
+ File->FvType = EFI_FV_FILETYPE_ALL;\r
+ GetNextFileStatus = File->Fv->GetNextFile (\r
+ File->Fv, \r
+ &Key,\r
+ &File->FvType, \r
+ &File->FvNameGuid, \r
+ &File->FvAttributes, \r
+ &File->Size\r
+ );\r
+ if (!EFI_ERROR (GetNextFileStatus)) {\r
+ // Compare GUID first\r
+ Status = CompareGuidToString (&File->FvNameGuid, FileName);\r
+ if (!EFI_ERROR(Status)) {\r
+ break;\r
+ }\r
+\r
+ Section = NULL;\r
+ Status = File->Fv->ReadSection (\r
+ File->Fv,\r
+ &File->FvNameGuid,\r
+ EFI_SECTION_USER_INTERFACE,\r
+ 0,\r
+ &Section,\r
+ &SectionSize,\r
+ &AuthenticationStatus\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ UnicodeStrToAsciiStr (Section, AsciiSection);\r
+ if (AsciiStriCmp (FileName, AsciiSection) == 0) {\r
+ FreePool (Section);\r
+ break;\r
+ }\r
+ FreePool (Section);\r
+ }\r
+ }\r
+ } while (!EFI_ERROR (GetNextFileStatus));\r
+\r
+ if (EFI_ERROR (GetNextFileStatus)) {\r
+ return GetNextFileStatus;\r
+ }\r
+\r
+ if (OpenMode != EFI_SECTION_ALL) {\r
+ // Calculate the size of the section we are targeting\r
+ Section = NULL;\r
+ File->Size = 0;\r
+ Status = File->Fv->ReadSection (\r
+ File->Fv,\r
+ &File->FvNameGuid,\r
+ (EFI_SECTION_TYPE)OpenMode,\r
+ 0,\r
+ &Section,\r
+ &File->Size,\r
+ &AuthenticationStatus\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ File->MaxPosition = File->Size;\r
+ EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);\r
+ File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);\r
+ }\r
+\r
+\r
+ // FVB not required if FV was soft loaded...\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+\r
+/**\r
+Open a device named by PathName. The PathName includes a device name and \r
+path separated by a :. See file header for more details on the PathName\r
+syntax. There is no checking to prevent a file from being opened more than\r
+one type. \r
+\r
+SectionType is only used to open an FV. Each file in an FV contains multiple\r
+sections and only the SectionType section is opened.\r
+\r
+For any file that is opened with EfiOpen() must be closed with EfiClose().\r
+\r
+@param PathName Path to parse to open \r
+@param OpenMode Same as EFI_FILE.Open()\r
+@param SectionType Section in FV to open.\r
+\r
+@return NULL Open failed\r
+@return Valid EFI_OPEN_FILE handle\r
+\r
+**/\r
+EFI_OPEN_FILE *\r
+EfiOpen (\r
+ IN CHAR8 *PathName,\r
+ IN CONST UINT64 OpenMode,\r
+ IN CONST EFI_SECTION_TYPE SectionType\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_OPEN_FILE *File;\r
+ EFI_OPEN_FILE FileData;\r
+ UINTN StrLen;\r
+ UINTN FileStart;\r
+ UINTN DevNumber = 0;\r
+ EFI_OPEN_FILE_GUARD *GuardFile;\r
+ BOOLEAN VolumeNameMatch;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ UINTN Size;\r
+ EFI_IP_ADDRESS Ip;\r
+ CHAR8 *CwdPlusPathName;\r
+ UINTN Index;\r
+ EFI_SECTION_TYPE ModifiedSectionType;\r
+\r
+ EblUpdateDeviceLists ();\r
+\r
+ File = &FileData;\r
+ ZeroMem (File, sizeof (EFI_OPEN_FILE));\r
+\r
+ StrLen = AsciiStrSize (PathName);\r
+ if (StrLen <= 1) {\r
+ // Smallest valid path is 1 char and a null\r
+ return NULL;\r
+ }\r
+\r
+ for (FileStart = 0; FileStart < StrLen; FileStart++) {\r
+ if (PathName[FileStart] == ':') {\r
+ FileStart++;\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Matching volume name has precedence over handle based names\r
+ //\r
+ VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);\r
+ if (!VolumeNameMatch) {\r
+ if (FileStart == StrLen) {\r
+ // No Volume name or device name, so try Current Working Directory\r
+ if (gCwd == NULL) {\r
+ // No CWD\r
+ return NULL;\r
+ }\r
+\r
+ // We could add a current working directory concept\r
+ CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName));\r
+ if (CwdPlusPathName == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ if ((PathName[0] == '/') || (PathName[0] == '\\')) {\r
+ // PathName starts in / so this means we go to the root of the device in the CWD. \r
+ CwdPlusPathName[0] = '\0';\r
+ for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) {\r
+ CwdPlusPathName[FileStart] = gCwd[FileStart];\r
+ if (gCwd[FileStart] == ':') {\r
+ FileStart++;\r
+ CwdPlusPathName[FileStart] = '\0';\r
+ break;\r
+ }\r
+ }\r
+ } else {\r
+ AsciiStrCpy (CwdPlusPathName, gCwd);\r
+ StrLen = AsciiStrLen (gCwd);\r
+ if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {\r
+ AsciiStrCat (CwdPlusPathName, "\\");\r
+ }\r
+ }\r
+\r
+ AsciiStrCat (CwdPlusPathName, PathName);\r
+ if (AsciiStrStr (CwdPlusPathName, ":") == NULL) {\r
+ // Extra error check to make sure we don't recurse and blow stack\r
+ return NULL;\r
+ }\r
+\r
+ File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);\r
+ FreePool (CwdPlusPathName);\r
+ return File;\r
+ }\r
+\r
+ DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName); \r
+ }\r
+\r
+ File->DeviceName = AllocatePool (StrLen);\r
+ AsciiStrCpy (File->DeviceName, PathName);\r
+ File->DeviceName[FileStart - 1] = '\0';\r
+ File->FileName = &File->DeviceName[FileStart];\r
+ if (File->FileName[0] == '\0') {\r
+ // if it is just a file name use / as root\r
+ File->FileName = "\\";\r
+ } \r
+\r
+ //\r
+ // Use best match algorithm on the dev names so we only need to look at the\r
+ // first few charters to match the full device name. Short name forms are \r
+ // legal from the caller.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) {\r
+ if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) {\r
+ if (DevNumber >= mFsCount) {\r
+ goto ErrorExit;\r
+ }\r
+ File->Type = EfiOpenFileSystem;\r
+ File->EfiHandle = mFs[DevNumber];\r
+ Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode);\r
+\r
+ } else if (PathName[1] == 'v' || PathName[1] == 'V') { \r
+ if (DevNumber >= mFvCount) {\r
+ goto ErrorExit;\r
+ }\r
+ File->Type = EfiOpenFirmwareVolume;\r
+ File->EfiHandle = mFv[DevNumber];\r
+\r
+ if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) {\r
+ // Skip leading / as its not really needed for the FV since no directories are supported\r
+ FileStart++;\r
+ }\r
+\r
+ // Check for 2nd :\r
+ ModifiedSectionType = SectionType;\r
+ for (Index = FileStart; PathName[Index] != '\0'; Index++) {\r
+ if (PathName[Index] == ':') {\r
+ // Support fv0:\DxeCore:0x10\r
+ // This means open the PE32 Section of the file \r
+ ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]);\r
+ PathName[Index] = '\0';\r
+ }\r
+ }\r
+ File->FvSectionType = ModifiedSectionType;\r
+ Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType);\r
+ }\r
+ } else if ((*PathName == 'A') || (*PathName == 'a')) {\r
+ // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE\r
+ File->Type = EfiOpenMemoryBuffer;\r
+ // 1st colon is at PathName[FileStart - 1]\r
+ File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]);\r
+\r
+ // Find 2nd colon\r
+ while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {\r
+ FileStart++;\r
+ }\r
+\r
+ // If we ran out of string, there's no extra data\r
+ if (PathName[FileStart] == '\0') {\r
+ File->Size = 0;\r
+ } else {\r
+ File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);\r
+ }\r
+\r
+ // if there's no number after the second colon, default\r
+ // the end of memory\r
+ if (File->Size == 0) {\r
+ File->Size = (UINTN)(0 - (UINTN)File->Buffer);\r
+ }\r
+\r
+ File->MaxPosition = File->Size;\r
+ File->BaseOffset = (UINTN)File->Buffer;\r
+\r
+ } else if (*PathName== 'l' || *PathName == 'L') {\r
+ if (DevNumber >= mLoadFileCount) {\r
+ goto ErrorExit;\r
+ }\r
+ File->Type = EfiOpenLoadFile;\r
+ File->EfiHandle = mLoadFile[DevNumber];\r
+\r
+ Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
+\r
+ Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
+ File->DevicePath = DuplicateDevicePath (DevicePath);\r
+\r
+ } else if (*PathName == 'b' || *PathName == 'B') {\r
+ // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE\r
+ if (DevNumber >= mBlkIoCount) {\r
+ goto ErrorExit;\r
+ }\r
+ File->Type = EfiOpenBlockIo;\r
+ File->EfiHandle = mBlkIo[DevNumber];\r
+ EblFileDevicePath (File, "", OpenMode);\r
+\r
+ // 1st colon is at PathName[FileStart - 1]\r
+ File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]);\r
+\r
+ // Find 2nd colon\r
+ while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {\r
+ FileStart++;\r
+ }\r
+\r
+ // If we ran out of string, there's no extra data\r
+ if (PathName[FileStart] == '\0') {\r
+ Size = 0;\r
+ } else {\r
+ Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);\r
+ }\r
+\r
+ // if a zero size is passed in (or the size is left out entirely),\r
+ // go to the end of the device.\r
+ if (Size == 0) {\r
+ File->Size = File->Size - File->DiskOffset;\r
+ } else {\r
+ File->Size = Size;\r
+ }\r
+\r
+ File->MaxPosition = File->Size;\r
+ File->BaseOffset = File->DiskOffset;\r
+ } else if ((*PathName) >= '0' && (*PathName <= '9')) {\r
+\r
+ // Get current IP address\r
+ Status = EblGetCurrentIpAddress (&Ip);\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("Device IP Address is not configured.\n");\r
+ goto ErrorExit;\r
+ }\r
+\r
+\r
+ // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...\r
+ File->Type = EfiOpenTftp;\r
+ File->IsDirty = FALSE;\r
+ File->IsBufferValid = FALSE;\r
+\r
+ Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp);\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
+\r
+ GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD));\r
+ if (GuardFile == NULL) {\r
+ goto ErrorExit;\r
+ }\r
+\r
+ GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER;\r
+ CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE));\r
+ GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER;\r
+\r
+ return &(GuardFile->File);\r
+\r
+ErrorExit:\r
+ FreePool (File->DeviceName);\r
+ return NULL;\r
+}\r
+\r
+#define FILE_COPY_CHUNK 0x01000000\r
+\r
+EFI_STATUS\r
+EfiCopyFile (\r
+ IN CHAR8 *DestinationFile,\r
+ IN CHAR8 *SourceFile\r
+ )\r
+{\r
+ EFI_OPEN_FILE *Source = NULL;\r
+ EFI_OPEN_FILE *Destination = NULL;\r
+ EFI_STATUS Status = EFI_SUCCESS;\r
+ VOID *Buffer = NULL;\r
+ UINTN Size;\r
+ UINTN Offset;\r
+ UINTN Chunk = FILE_COPY_CHUNK;\r
+\r
+ Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0);\r
+ if (Source == NULL) {\r
+ AsciiPrint("Source file open error.\n");\r
+ Status = EFI_NOT_FOUND;\r
+ goto Exit;\r
+ }\r
+\r
+ Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);\r
+ if (Destination == NULL) {\r
+ AsciiPrint("Destination file open error.\n");\r
+ Status = EFI_NOT_FOUND;\r
+ goto Exit;\r
+ }\r
+\r
+ Buffer = AllocatePool(FILE_COPY_CHUNK);\r
+ if (Buffer == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+\r
+ Size = EfiTell(Source, NULL);\r
+\r
+ for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {\r
+ Chunk = FILE_COPY_CHUNK;\r
+\r
+ Status = EfiRead(Source, Buffer, &Chunk);\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("Read file error %r\n", Status);\r
+ goto Exit;\r
+ }\r
+\r
+ Status = EfiWrite(Destination, Buffer, &Chunk);\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("Write file error %r\n", Status);\r
+ goto Exit;\r
+ } \r
+ }\r
+\r
+ // Any left over?\r
+ if (Offset < Size) {\r
+ Chunk = Size - Offset;\r
+\r
+ Status = EfiRead(Source, Buffer, &Chunk);\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("Read file error\n");\r
+ goto Exit;\r
+ }\r
+\r
+ Status = EfiWrite(Destination, Buffer, &Chunk);\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("Write file error\n");\r
+ goto Exit;\r
+ } \r
+ }\r
+\r
+Exit:\r
+ if (Source != NULL) {\r
+ Status = EfiClose(Source);\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("Source close error");\r
+ }\r
+ }\r
+\r
+ if (Destination != NULL) {\r
+ Status = EfiClose(Destination);\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("Destination close error");\r
+ }\r
+ }\r
+\r
+ if (Buffer != NULL) {\r
+ FreePool(Buffer);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+Use DeviceType and Index to form a valid PathName and try and open it.\r
+\r
+@param DeviceType Device type to open\r
+@param Index Device Index to use. Zero relative.\r
+\r
+@return NULL Open failed\r
+@return Valid EFI_OPEN_FILE handle\r
+\r
+**/\r
+EFI_OPEN_FILE *\r
+EfiDeviceOpenByType (\r
+ IN EFI_OPEN_FILE_TYPE DeviceType,\r
+ IN UINTN Index\r
+ )\r
+{\r
+ CHAR8 *DevStr;\r
+ CHAR8 Path[MAX_CMD_LINE];\r
+\r
+ switch (DeviceType) {\r
+ case EfiOpenLoadFile:\r
+ DevStr = "loadfile%d:";\r
+ break;\r
+ case EfiOpenFirmwareVolume:\r
+ DevStr = "fv%d:"; \r
+ break;\r
+ case EfiOpenFileSystem:\r
+ DevStr = "fs%d:"; \r
+ break;\r
+ case EfiOpenBlockIo:\r
+ DevStr = "blk%d:"; \r
+ break;\r
+ case EfiOpenMemoryBuffer:\r
+ DevStr = "a%d:"; \r
+ break;\r
+ default:\r
+ return NULL;\r
+ }\r
+\r
+ AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index);\r
+\r
+ return EfiOpen (Path, EFI_FILE_MODE_READ, 0);\r
+}\r
+\r
+\r
+/**\r
+Close a file handle opened by EfiOpen() and free all resources allocated by\r
+EfiOpen().\r
+\r
+@param Stream Open File Handle\r
+\r
+@return EFI_INVALID_PARAMETER Stream is not an Open File\r
+@return EFI_SUCCESS Steam closed\r
+\r
+**/\r
+EFI_STATUS\r
+EfiClose (\r
+ IN EFI_OPEN_FILE *File\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 TftpBufferSize;\r
+\r
+ if (!FileHandleValid (File)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //Write the buffer contents to TFTP file.\r
+ if ((File->Type == EfiOpenTftp) && (File->IsDirty)) {\r
+\r
+ TftpBufferSize = File->Size;\r
+ Status = EblMtftp (\r
+ EFI_PXE_BASE_CODE_TFTP_WRITE_FILE, \r
+ File->Buffer, \r
+ TRUE, \r
+ &TftpBufferSize, \r
+ NULL, \r
+ &File->ServerIp, \r
+ (UINT8 *)File->FileName, \r
+ NULL, \r
+ FALSE\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status);\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ if ((File->Type == EfiOpenLoadFile) || \r
+ ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) ||\r
+ ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) {\r
+ EblFreePool(File->Buffer);\r
+ }\r
+\r
+ EblFreePool (File->DevicePath);\r
+ EblFreePool (File->DeviceName);\r
+ EblFreePool (File->FsFileInfo);\r
+ EblFreePool (File->FsInfo);\r
+\r
+ if (File->FsFileHandle != NULL) {\r
+ File->FsFileHandle->Close (File->FsFileHandle);\r
+ }\r
+\r
+ // Need to free File and it's Guard structures\r
+ EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+Return the size of the file represented by Stream. Also return the current \r
+Seek position. Opening a file will enable a valid file size to be returned.\r
+LoadFile is an exception as a load file size is set to zero. \r
+\r
+@param Stream Open File Handle\r
+\r
+@return 0 Stream is not an Open File or a valid LoadFile handle\r
+\r
+**/\r
+UINTN\r
+EfiTell (\r
+ IN EFI_OPEN_FILE *File,\r
+ OUT EFI_LBA *CurrentPosition OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 BufferSize = 0;\r
+\r
+ if (!FileHandleValid (File)) {\r
+ return 0;\r
+ }\r
+\r
+ if (CurrentPosition != NULL) {\r
+ *CurrentPosition = File->CurrentPosition;\r
+ }\r
+\r
+ if (File->Type == EfiOpenLoadFile) {\r
+ // Figure out the File->Size\r
+ File->Buffer = NULL;\r
+ File->Size = 0;\r
+ Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer);\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ return 0;\r
+ }\r
+\r
+ File->MaxPosition = (UINT64)File->Size;\r
+ } else if (File->Type == EfiOpenTftp) {\r
+\r
+ Status = EblMtftp (\r
+ EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
+ NULL,\r
+ FALSE,\r
+ &BufferSize,\r
+ NULL,\r
+ &File->ServerIp,\r
+ (UINT8 *)File->FileName,\r
+ NULL,\r
+ TRUE\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);\r
+ return 0;\r
+ }\r
+\r
+ File->Size = (UINTN)BufferSize;\r
+ File->MaxPosition = File->Size;\r
+ }\r
+\r
+ return File->Size;\r
+}\r
+\r
+\r
+/**\r
+Seek to the Offset location in the file. LoadFile and FV device types do\r
+not support EfiSeek(). It is not possible to grow the file size using \r
+EfiSeek().\r
+\r
+SeekType defines how use Offset to calculate the new file position:\r
+EfiSeekStart : Position = Offset\r
+EfiSeekCurrent: Position is Offset bytes from the current position\r
+EfiSeekEnd : Only supported if Offset is zero to seek to end of file.\r
+\r
+@param Stream Open File Handle\r
+@param Offset Offset to seek too. \r
+@param SeekType Type of seek to perform\r
+\r
+\r
+@return EFI_INVALID_PARAMETER Stream is not an Open File\r
+@return EFI_UNSUPPORTED LoadFile and FV do not support Seek\r
+@return EFI_NOT_FOUND Seek past the end of the file.\r
+@return EFI_SUCCESS Steam closed\r
+\r
+**/\r
+EFI_STATUS\r
+EfiSeek (\r
+ IN EFI_OPEN_FILE *File,\r
+ IN EFI_LBA Offset,\r
+ IN EFI_SEEK_TYPE SeekType\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 CurrentPosition;\r
+\r
+ if (!FileHandleValid (File)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (File->Type == EfiOpenLoadFile) {\r
+ // LoadFile does not support Seek\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ CurrentPosition = File->CurrentPosition;\r
+ switch (SeekType) {\r
+ case EfiSeekStart:\r
+ if (Offset > File->MaxPosition) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ CurrentPosition = Offset;\r
+ break;\r
+\r
+ case EfiSeekCurrent:\r
+ if ((File->CurrentPosition + Offset) > File->MaxPosition) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ CurrentPosition += Offset;\r
+ break;\r
+\r
+ case EfiSeekEnd:\r
+ if (Offset != 0) {\r
+ // We don't support growing file size via seeking past end of file\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ CurrentPosition = File->MaxPosition;\r
+ break;\r
+\r
+ default:\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ if (File->FsFileHandle != NULL) {\r
+ Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition);\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ File->CurrentPosition = CurrentPosition;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+EFI_STATUS\r
+CacheTftpFile (\r
+ IN OUT EFI_OPEN_FILE *File\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 TftpBufferSize;\r
+\r
+ if (File->IsBufferValid) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Make sure the file size is set.\r
+ EfiTell (File, NULL);\r
+\r
+ //Allocate a buffer to hold the whole file.\r
+ File->Buffer = AllocatePool(File->Size);\r
+ if (File->Buffer == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ TftpBufferSize = File->Size;\r
+\r
+ Status = EblMtftp (\r
+ EFI_PXE_BASE_CODE_TFTP_READ_FILE, \r
+ File->Buffer, \r
+ FALSE, \r
+ &TftpBufferSize, \r
+ NULL, \r
+ &File->ServerIp, \r
+ (UINT8 *)File->FileName, \r
+ NULL, \r
+ FALSE);\r
+ if (EFI_ERROR(Status)) {\r
+ AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status);\r
+ FreePool(File->Buffer);\r
+ return Status;\r
+ }\r
+\r
+ // Set the buffer valid flag.\r
+ File->IsBufferValid = TRUE;\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+Read BufferSize bytes from the current location in the file. For load file,\r
+FV, and TFTP case you must read the entire file. \r
+\r
+@param Stream Open File Handle\r
+@param Buffer Caller allocated buffer. \r
+@param BufferSize Size of buffer in bytes.\r
+\r
+\r
+@return EFI_SUCCESS Stream is not an Open File\r
+@return EFI_END_OF_FILE Tried to read past the end of the file\r
+@return EFI_INVALID_PARAMETER Stream is not an open file handle\r
+@return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read\r
+@return "other" Error returned from device read\r
+\r
+**/\r
+EFI_STATUS\r
+EfiRead (\r
+ IN EFI_OPEN_FILE *File,\r
+ OUT VOID *Buffer,\r
+ OUT UINTN *BufferSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 AuthenticationStatus;\r
+ EFI_DISK_IO_PROTOCOL *DiskIo;\r
+\r
+ if (!FileHandleValid (File)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Don't read past the end of the file.\r
+ if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {\r
+ return EFI_END_OF_FILE;\r
+ }\r
+\r
+ switch (File->Type) {\r
+ case EfiOpenLoadFile:\r
+ // Figure out the File->Size\r
+ EfiTell (File, NULL);\r
+\r
+ Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer);\r
+ break;\r
+\r
+ case EfiOpenFirmwareVolume:\r
+ if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) {\r
+ // This is the entire FV device, so treat like a memory buffer \r
+ CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize);\r
+ File->CurrentPosition += *BufferSize;\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ if (File->Buffer == NULL) {\r
+ if (File->FvSectionType == EFI_SECTION_ALL) {\r
+ Status = File->Fv->ReadFile (\r
+ File->Fv,\r
+ &File->FvNameGuid,\r
+ (VOID **)&File->Buffer,\r
+ &File->Size,\r
+ &File->FvType,\r
+ &File->FvAttributes,\r
+ &AuthenticationStatus\r
+ );\r
+ } else {\r
+ Status = File->Fv->ReadSection (\r
+ File->Fv,\r
+ &File->FvNameGuid,\r
+ File->FvSectionType,\r
+ 0,\r
+ (VOID **)&File->Buffer,\r
+ &File->Size,\r
+ &AuthenticationStatus\r
+ );\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ File->IsBufferValid = TRUE;\r
+ }\r
+ // Operate on the cached buffer so Seek will work\r
+ CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);\r
+ File->CurrentPosition += *BufferSize;\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ break;\r
+\r
+ case EfiOpenMemoryBuffer:\r
+ CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);\r
+ File->CurrentPosition += *BufferSize;\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+\r
+ case EfiOpenFileSystem:\r
+ Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer);\r
+ File->CurrentPosition += *BufferSize;\r
+ break;\r
+\r
+ case EfiOpenBlockIo:\r
+ Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);\r
+ if (!EFI_ERROR(Status)) {\r
+ Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);\r
+ }\r
+ File->CurrentPosition += *BufferSize;\r
+ break;\r
+\r
+ case EfiOpenTftp:\r
+ // Cache the file if it hasn't been cached yet.\r
+ if (File->IsBufferValid == FALSE) {\r
+ Status = CacheTftpFile (File);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ // Copy out the requested data\r
+ CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);\r
+ File->CurrentPosition += *BufferSize;\r
+\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ };\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+Read the entire file into a buffer. This routine allocates the buffer and\r
+returns it to the user full of the read data. \r
+\r
+This is very useful for load file where it's hard to know how big the buffer\r
+must be.\r
+\r
+@param Stream Open File Handle\r
+@param Buffer Pointer to buffer to return. \r
+@param BufferSize Pointer to Size of buffer return..\r
+\r
+\r
+@return EFI_SUCCESS Stream is not an Open File\r
+@return EFI_END_OF_FILE Tried to read past the end of the file\r
+@return EFI_INVALID_PARAMETER Stream is not an open file handle\r
+@return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read\r
+@return "other" Error returned from device read\r
+\r
+**/\r
+EFI_STATUS\r
+EfiReadAllocatePool (\r
+ IN EFI_OPEN_FILE *File,\r
+ OUT VOID **Buffer,\r
+ OUT UINTN *BufferSize\r
+ )\r
+{\r
+ if (!FileHandleValid (File)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Loadfile defers file size determination on Open so use tell to find it\r
+ EfiTell (File, NULL);\r
+\r
+ *BufferSize = File->Size;\r
+ *Buffer = AllocatePool (*BufferSize);\r
+ if (*Buffer == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ return EfiRead (File, *Buffer, BufferSize);\r
+}\r
+\r
+\r
+/**\r
+Write data back to the file. For TFTP case you must write the entire file. \r
+\r
+@param Stream Open File Handle\r
+@param Buffer Pointer to buffer to return. \r
+@param BufferSize Pointer to Size of buffer return..\r
+\r
+\r
+@return EFI_SUCCESS Stream is not an Open File\r
+@return EFI_END_OF_FILE Tried to read past the end of the file\r
+@return EFI_INVALID_PARAMETER Stream is not an open file handle\r
+@return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read\r
+@return "other" Error returned from device write\r
+\r
+**/\r
+EFI_STATUS\r
+EfiWrite (\r
+ IN EFI_OPEN_FILE *File,\r
+ OUT VOID *Buffer,\r
+ OUT UINTN *BufferSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FV_WRITE_FILE_DATA FileData;\r
+ EFI_DISK_IO_PROTOCOL *DiskIo; \r
+\r
+ if (!FileHandleValid (File)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ switch (File->Type) {\r
+ case EfiOpenMemoryBuffer:\r
+ if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {\r
+ return EFI_END_OF_FILE;\r
+ }\r
+\r
+ CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);\r
+ File->CurrentPosition += *BufferSize;\r
+ Status = EFI_SUCCESS;\r
+\r
+ case EfiOpenLoadFile:\r
+ // LoadFile device is read only be definition\r
+ Status = EFI_UNSUPPORTED;\r
+\r
+ case EfiOpenFirmwareVolume:\r
+ if (File->FvSectionType != EFI_SECTION_ALL) {\r
+ // Writes not support to a specific section. You have to update entire file\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ FileData.NameGuid = &(File->FvNameGuid);\r
+ FileData.Type = File->FvType;\r
+ FileData.FileAttributes = File->FvAttributes;\r
+ FileData.Buffer = Buffer;\r
+ FileData.BufferSize = (UINT32)*BufferSize;\r
+ Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData);\r
+ break;\r
+\r
+ case EfiOpenFileSystem:\r
+ Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer);\r
+ File->CurrentPosition += *BufferSize;\r
+ break;\r
+\r
+ case EfiOpenBlockIo:\r
+ if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {\r
+ return EFI_END_OF_FILE;\r
+ }\r
+\r
+ Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);\r
+ if (!EFI_ERROR(Status)) {\r
+ Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);\r
+ }\r
+ File->CurrentPosition += *BufferSize;\r
+ break;\r
+\r
+ case EfiOpenTftp:\r
+ // Cache the file if it hasn't been cached yet.\r
+ if (File->IsBufferValid == FALSE) {\r
+ Status = CacheTftpFile(File);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ // Don't overwrite the buffer\r
+ if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {\r
+ UINT8 *TempBuffer;\r
+\r
+ TempBuffer = File->Buffer;\r
+\r
+ File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));\r
+ if (File->Buffer == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (File->Buffer, TempBuffer, File->Size);\r
+\r
+ FreePool (TempBuffer);\r
+\r
+ File->Size = (UINTN)(File->CurrentPosition + *BufferSize);\r
+ File->MaxPosition = (UINT64)File->Size;\r
+ }\r
+\r
+ // Copy in the requested data\r
+ CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);\r
+ File->CurrentPosition += *BufferSize;\r
+\r
+ // Mark the file dirty\r
+ File->IsDirty = TRUE;\r
+\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+\r
+ default:\r
+ Status = EFI_INVALID_PARAMETER;\r
+ };\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+Given Cwd expand Path to remove .. and replace them with real \r
+directory names.\r
+\r
+@param Cwd Current Working Directory\r
+@param Path Path to expand\r
+\r
+@return NULL Cwd or Path are not valid\r
+@return 'other' Path with .. expanded\r
+\r
+**/\r
+CHAR8 *\r
+ExpandPath (\r
+ IN CHAR8 *Cwd,\r
+ IN CHAR8 *Path\r
+ )\r
+{\r
+ CHAR8 *NewPath;\r
+ CHAR8 *Work, *Start, *End;\r
+ UINTN StrLen;\r
+ INTN i;\r
+\r
+ if (Cwd == NULL || Path == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ StrLen = AsciiStrSize (Cwd);\r
+ if (StrLen <= 2) {\r
+ // Smallest valid path is 1 char and a null\r
+ return NULL;\r
+ }\r
+\r
+ StrLen = AsciiStrSize (Path);\r
+ NewPath = AllocatePool (AsciiStrSize (Cwd) + StrLen + 1);\r
+ if (NewPath == NULL) {\r
+ return NULL;\r
+ }\r
+ AsciiStrCpy (NewPath, Cwd);\r
+\r
+ End = Path + StrLen;\r
+ for (Start = Path ;;) {\r
+ Work = AsciiStrStr (Start, "..") ;\r
+ if (Work == NULL) {\r
+ // Remaining part of Path contains no more ..\r
+ break;\r
+ } \r
+\r
+ // append path prior to .. \r
+ AsciiStrnCat (NewPath, Start, Work - Start);\r
+ StrLen = AsciiStrLen (NewPath);\r
+ for (i = StrLen; i >= 0; i--) {\r
+ if (NewPath[i] == ':') {\r
+ // too many ..\r
+ return NULL;\r
+ }\r
+ if (NewPath[i] == '/' || NewPath[i] == '\\') {\r
+ if ((i > 0) && (NewPath[i-1] == ':')) {\r
+ // leave the / before a :\r
+ NewPath[i+1] = '\0';\r
+ } else {\r
+ // replace / will Null to remove trailing file/dir reference\r
+ NewPath[i] = '\0';\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+ Start = Work + 3;\r
+ } \r
+\r
+ // Handle the path that remains after the ..\r
+ AsciiStrnCat (NewPath, Start, End - Start);\r
+\r
+ return NewPath;\r
+}\r
+\r
+\r
+/**\r
+Set the Current Working Directory (CWD). If a call is made to EfiOpen () and\r
+the path does not contain a device name, The CWD is prepended to the path.\r
+\r
+@param Cwd Current Working Directory to set\r
+\r
+\r
+@return EFI_SUCCESS CWD is set\r
+@return EFI_INVALID_PARAMETER Cwd is not a valid device:path\r
+\r
+**/\r
+EFI_STATUS\r
+EfiSetCwd (\r
+ IN CHAR8 *Cwd\r
+ ) \r
+{\r
+ EFI_OPEN_FILE *File;\r
+ UINTN Len;\r
+ CHAR8 *Path;\r
+\r
+ if (Cwd == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (AsciiStrCmp (Cwd, ".") == 0) {\r
+ // cd . is a no-op\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Path = Cwd;\r
+ if (AsciiStrStr (Cwd, "..") != NULL) {\r
+ if (gCwd == NULL) {\r
+ // no parent \r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Len = AsciiStrLen (gCwd);\r
+ if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) {\r
+ // parent is device so nothing to do\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Expand .. in Cwd, given we know current working directory\r
+ Path = ExpandPath (gCwd, Cwd);\r
+ if (Path == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ }\r
+\r
+ File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);\r
+ if (File == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (gCwd != NULL) {\r
+ FreePool (gCwd);\r
+ }\r
+\r
+ // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be\r
+ // relative to the current gCwd or not.\r
+ gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10);\r
+ if (gCwd == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ AsciiStrCpy (gCwd, File->DeviceName);\r
+ if (File->FileName == NULL) {\r
+ AsciiStrCat (gCwd, ":\\");\r
+ } else {\r
+ AsciiStrCat (gCwd, ":");\r
+ AsciiStrCat (gCwd, File->FileName);\r
+ }\r
+\r
+\r
+ EfiClose (File);\r
+ if (Path != Cwd) {\r
+ FreePool (Path);\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+Set the Current Working Directory (CWD). If a call is made to EfiOpen () and\r
+the path does not contain a device name, The CWD is prepended to the path.\r
+The CWD buffer is only valid until a new call is made to EfiSetCwd(). After\r
+a call to EfiSetCwd() it is not legal to use the pointer returned by \r
+this function.\r
+\r
+@param Cwd Current Working Directory \r
+\r
+\r
+@return "" No CWD set\r
+@return 'other' Returns buffer that contains CWD.\r
+\r
+**/\r
+CHAR8 *\r
+EfiGetCwd (\r
+ VOID\r
+ )\r
+{\r
+ if (gCwd == NULL) {\r
+ return "";\r
+ }\r
+ return gCwd;\r
+}\r
+\r
+\r