-/** @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
-\r
-#include <Guid/FileInfo.h>\r
-#include <Guid/ZeroGuid.h>\r
-\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
-#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
- AsciiStrToUnicodeStrS (FileName, UnicodeFileName,\r
- ARRAY_SIZE (UnicodeFileName));\r
- } else {\r
- AsciiStrToUnicodeStrS ("\\", UnicodeFileName, ARRAY_SIZE (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
- UnicodeStrToAsciiStrS (Section, AsciiSection, MAX_PATHNAME);\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
- UINTN AsciiLength;\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
- AsciiLength = AsciiStrSize (gCwd) + AsciiStrSize (PathName);\r
- CwdPlusPathName = AllocatePool (AsciiLength);\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
- AsciiStrCpyS (CwdPlusPathName, AsciiLength, gCwd);\r
- StrLen = AsciiStrLen (gCwd);\r
- if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {\r
- AsciiStrCatS (CwdPlusPathName, AsciiLength, "\\");\r
- }\r
- }\r
-\r
- AsciiStrCatS (CwdPlusPathName, AsciiLength, 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
- AsciiStrCpyS (File->DeviceName, StrLen, 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, AllocLen;\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
- AllocLen = AsciiStrSize (Cwd) + StrLen + 1;\r
- NewPath = AllocatePool (AllocLen);\r
- if (NewPath == NULL) {\r
- return NULL;\r
- }\r
- AsciiStrCpyS (NewPath, AllocLen, 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
- AsciiStrnCatS (NewPath, AllocLen, 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
- AsciiStrnCatS (NewPath, AllocLen, 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, AllocLen;\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
- AllocLen = AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10;\r
- gCwd = AllocatePool (AllocLen);\r
- if (gCwd == NULL) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- AsciiStrCpyS (gCwd, AllocLen, File->DeviceName);\r
- if (File->FileName == NULL) {\r
- AsciiStrCatS (gCwd, AllocLen, ":\\");\r
- } else {\r
- AsciiStrCatS (gCwd, AllocLen, ":");\r
- AsciiStrCatS (gCwd, AllocLen, 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