/** 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); }
/** Execute the passed in file like a series of commands. The ; can be used on a single line to indicate multiple commands per line. The Ascii text file can contain any number of lines. The following line termination forms are supported: LF : Unix, Mac OS X*, BeOS CR+LF: MS-DOS*, Microsoft Windows* CR : Commodore, Apple II, and really Mac OS LF+CR: for simplicity and completeness Argv[0] - "script" Argv[1] - Device Name:path for the file to load script fv1:\script.txt @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblScriptCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; EFI_OPEN_FILE *File; VOID *Address; UINTN Size; CHAR8 *Ptr; CHAR8 *ScanPtr; UINTN CmdLineSize; if (Argc < 2) { // file name required return EFI_SUCCESS; } File = EfiOpen (Argv[1], EFI_FILE_MODE_READ, 0); if (File == NULL) { AsciiPrint (" %a is not a valid path\n", Argv[1]); return EFI_SUCCESS; } Status = EfiReadAllocatePool (File, &Address, &Size); if (!EFI_ERROR (Status)) { // Loop through each line in the text file for (Ptr = (CHAR8 *)Address; (Ptr < (((CHAR8 *)Address) + Size)) && !EFI_ERROR (Status); Ptr += CmdLineSize) { for (CmdLineSize = 0, ScanPtr = Ptr; ; CmdLineSize++, ScanPtr++) { // look for the end of the line if ((*ScanPtr == EBL_CR) || (*ScanPtr == EBL_LF)) { // convert to NULL as this is what input routine would do *ScanPtr = 0; if ((*(ScanPtr + 1) == EBL_CR) || (*(ScanPtr + 1) == EBL_LF)) { // if its a set get the 2nd EOL char CmdLineSize++; *(ScanPtr + 1) = 0; } CmdLineSize++; break; } } Status = ProcessCmdLine (Ptr, CmdLineSize); } FreePool (Address); } EfiClose (File); return Status; }
/** Load a Firmware Volume (FV) into memory from a device. This causes drivers in the FV to be dispatched if the dependencies of the drivers are met. Argv[0] - "loadfv" Argv[1] - device name and path loadfv fs1:\Temp\Fv.Fv ; load an FV from the disk loadfv fv0:\FV ; load an FV from an FV (not common) loadfv LoadFile0: ; load an FV via a PXE boot @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblLoadFvCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; EFI_OPEN_FILE *File; VOID *FvStart; UINTN FvSize; EFI_HANDLE FvHandle; if (Argc < 2) { return EFI_INVALID_PARAMETER; } File = EfiOpen (Argv[1], EFI_FILE_MODE_READ, 0); if (File == NULL) { return EFI_INVALID_PARAMETER; } if (File->Type == EfiOpenMemoryBuffer) { // If it is a address just use it. Status = gDS->ProcessFirmwareVolume (File->Buffer, File->Size, &FvHandle); } else { // If it is a file read it into memory and use it Status = EfiReadAllocatePool (File, &FvStart, &FvSize); EfiClose (File); if (EFI_ERROR (Status)) { return Status; } Status = gDS->ProcessFirmwareVolume (FvStart, FvSize, &FvHandle); if (EFI_ERROR (Status)) { FreePool (FvStart); } } return Status; }
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 %r\n", Status); goto Exit; } Status = EfiWrite(Destination, Buffer, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint("Write file error %r\n", Status); 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; }
/** Open a device named by PathName. The PathName includes a device name and path separated 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 sections 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; CHAR8 *CwdPlusPathName; UINTN Index; EFI_SECTION_TYPE ModifiedSectionType; EblUpdateDeviceLists (); File = &FileData; ZeroMem (File, sizeof (EFI_OPEN_FILE)); StrLen = AsciiStrSize (PathName); if (StrLen <= 1) { // Smallest valid path is 1 char and a null return NULL; } for (FileStart = 0; FileStart < StrLen; FileStart++) { if (PathName[FileStart] == ':') { FileStart++; break; } } // // Matching volume name has precedence over handle based names // VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber); if (!VolumeNameMatch) { if (FileStart == StrLen) { // No Volume name or device name, so try Current Working Directory if (gCwd == NULL) { // No CWD return NULL; } // We could add a current working directory concept CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName)); if (CwdPlusPathName == NULL) { return NULL; } if ((PathName[0] == '/') || (PathName[0] == '\\')) { // PathName starts in / so this means we go to the root of the device in the CWD. CwdPlusPathName[0] = '\0'; for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) { CwdPlusPathName[FileStart] = gCwd[FileStart]; if (gCwd[FileStart] == ':') { FileStart++; CwdPlusPathName[FileStart] = '\0'; break; } } } else { AsciiStrCpy (CwdPlusPathName, gCwd); StrLen = AsciiStrLen (gCwd); if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) { AsciiStrCat (CwdPlusPathName, "\\"); } } AsciiStrCat (CwdPlusPathName, PathName); if (AsciiStrStr (CwdPlusPathName, ":") == NULL) { // Extra error check to make sure we don't recurse and blow stack return NULL; } File = EfiOpen (CwdPlusPathName, OpenMode, SectionType); FreePool (CwdPlusPathName); return File; } DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName); } File->DeviceName = AllocatePool (StrLen); AsciiStrCpy (File->DeviceName, PathName); File->DeviceName[FileStart - 1] = '\0'; File->FileName = &File->DeviceName[FileStart]; if (File->FileName[0] == '\0') { // if it is just a file name use / as root File->FileName = "\\"; } // // 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++; } // Check for 2nd : ModifiedSectionType = SectionType; for (Index = FileStart; PathName[Index] != '\0'; Index++) { if (PathName[Index] == ':') { // Support fv0:\DxeCore:0x10 // This means open the PE32 Section of the file ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]); PathName[Index] = '\0'; } } File->FvSectionType = ModifiedSectionType; Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType); } } 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; }
/** Perform a dir on a device. The device must support Simple File System Protocol or the FV protocol. Argv[0] - "dir" Argv[1] - Device Name:path. Path is optional Argv[2] - Optional filename to match on. A leading * means match substring Argv[3] - Optional FV file type dir fs1:\efi ; perform a dir on fs1: device in the efi directory dir fs1:\efi *.efi; perform a dir on fs1: device in the efi directory but only print out files that contain the string *.efi dir fv1:\ ; perform a dir on fv1: device in the efi directory NOTE: fv devices do not contain subdirs dir fv1:\ * PEIM ; will match all files of type PEIM @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblDirCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; EFI_OPEN_FILE *File; EFI_FILE_INFO *DirInfo; UINTN ReadSize; UINTN CurrentRow; CHAR16 *MatchSubString; EFI_STATUS GetNextFileStatus; UINTN Key; EFI_FV_FILETYPE SearchType; EFI_FV_FILETYPE Type; EFI_FV_FILE_ATTRIBUTES Attributes; UINTN Size; EFI_GUID NameGuid; EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; UINT32 AuthenticationStatus; VOID *Section; UINTN SectionSize; EFI_FV_FILETYPE Index; UINTN Length; UINTN BestMatchCount; CHAR16 UnicodeFileName[MAX_CMD_LINE]; CHAR8 *Path; CHAR8 *TypeStr; UINTN TotalSize; if (Argc <= 1) { Path = EfiGetCwd (); if (Path == NULL) { return EFI_SUCCESS; } } else { Path = Argv[1]; } File = EfiOpen (Path, EFI_FILE_MODE_READ, 0); if (File == NULL) { return EFI_SUCCESS; } if (File->Type == EfiOpenFirmwareVolume) { // FV Dir SearchType = EFI_FV_FILETYPE_ALL; UnicodeFileName[0] = '\0'; MatchSubString = &UnicodeFileName[0]; if (Argc > 2) { AsciiStrToUnicodeStr (Argv[2], UnicodeFileName); if (UnicodeFileName[0] == '*') { // Handle *Name substring matching MatchSubString = &UnicodeFileName[1]; } // Handle file type matchs if (Argc > 3) { // match a specific file type, always last argument Length = AsciiStrLen (Argv[3]); for (Index = 1, BestMatchCount = 0; Index < sizeof (gFvFileType)/sizeof (CHAR8 *); Index++) { if (AsciiStriCmp (gFvFileType[Index], Argv[3]) == 0) { // exact match SearchType = Index; break; } if (AsciiStrniCmp (Argv[3], gFvFileType[Index], Length) == 0) { // partial match, so keep looking to make sure there is only one partial match BestMatchCount++; SearchType = Index; } } if (BestMatchCount > 1) { SearchType = EFI_FV_FILETYPE_ALL; } } } TotalSize = 0; Fv = File->Fv; Key = 0; CurrentRow = 0; do { Type = SearchType; GetNextFileStatus = Fv->GetNextFile ( Fv, &Key, &Type, &NameGuid, &Attributes, &Size ); if (!EFI_ERROR (GetNextFileStatus)) { TotalSize += Size; // Calculate size of entire file Section = NULL; Size = 0; Status = Fv->ReadFile ( Fv, &NameGuid, Section, &Size, &Type, &Attributes, &AuthenticationStatus ); if (!((Status == EFI_BUFFER_TOO_SMALL) || !EFI_ERROR (Status))) { // EFI_SUCCESS or EFI_BUFFER_TOO_SMALL mean size is valid Size = 0; } TypeStr = (Type <= EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE) ? gFvFileType[Type] : "UNKNOWN"; // read the UI seciton to do a name match. Section = NULL; Status = Fv->ReadSection ( Fv, &NameGuid, EFI_SECTION_USER_INTERFACE, 0, &Section, &SectionSize, &AuthenticationStatus ); if (!EFI_ERROR (Status)) { if (StrStr (Section, MatchSubString) != NULL) { AsciiPrint ("%,9d %7a %g %s\n", Size, TypeStr, &NameGuid, Section); if (EblAnyKeyToContinueQtoQuit (&CurrentRow, FALSE)) { break; } } FreePool (Section); } else { if (*MatchSubString == '\0') { AsciiPrint ("%,9d %7a %g\n", Size, TypeStr, &NameGuid); if (EblAnyKeyToContinueQtoQuit (&CurrentRow, FALSE)) { break; } } } } } while (!EFI_ERROR (GetNextFileStatus)); if (SearchType == EFI_FV_FILETYPE_ALL) { AsciiPrint ("%,20d bytes in files %,d bytes free\n", TotalSize, File->FvSize - File->FvHeaderSize - TotalSize); } } else if ((File->Type == EfiOpenFileSystem) || (File->Type == EfiOpenBlockIo)) { // Simple File System DIR if (File->FsFileInfo == NULL) { return EFI_SUCCESS; } if (!(File->FsFileInfo->Attribute & EFI_FILE_DIRECTORY)) { return EFI_SUCCESS; } // Handle *Name substring matching MatchSubString = NULL; UnicodeFileName[0] = '\0'; if (Argc > 2) { AsciiStrToUnicodeStr (Argv[2], UnicodeFileName); if (UnicodeFileName[0] == '*') { MatchSubString = &UnicodeFileName[1]; } } File->FsFileHandle->SetPosition (File->FsFileHandle, 0); for (CurrentRow = 0;;) { // First read gets the size DirInfo = NULL; ReadSize = 0; Status = File->FsFileHandle->Read (File->FsFileHandle, &ReadSize, DirInfo); if (Status == EFI_BUFFER_TOO_SMALL) { // Allocate the buffer for the real read DirInfo = AllocatePool (ReadSize); if (DirInfo == NULL) { goto Done; } // Read the data Status = File->FsFileHandle->Read (File->FsFileHandle, &ReadSize, DirInfo); if ((EFI_ERROR (Status)) || (ReadSize == 0)) { break; } } else { break; } if (MatchSubString != NULL) { if (StrStr (&DirInfo->FileName[0], MatchSubString) == NULL) { // does not match *name argument, so skip continue; } } else if (UnicodeFileName[0] != '\0') { // is not an exact match for name argument, so skip if (StrCmp (&DirInfo->FileName[0], UnicodeFileName) != 0) { continue; } } if (DirInfo->Attribute & EFI_FILE_DIRECTORY) { AsciiPrint (" <DIR> %s\n", &DirInfo->FileName[0]); } else { AsciiPrint ("%,14ld %s\n", DirInfo->FileSize, &DirInfo->FileName[0]); } if (EblAnyKeyToContinueQtoQuit (&CurrentRow, FALSE)) { break; } FreePool (DirInfo); } Done: if (DirInfo != NULL) { FreePool (DirInfo); } } EfiClose (File); return EFI_SUCCESS; }
/** Toggle page break global. This turns on and off prompting to Quit or hit any key to continue when a command is about to scroll the screen with its output Argv[0] - "hexdump"[.#] # is optional 1,2, or 4 for width Argv[1] - Device or File to dump. Argv[2] - Optional offset to start dumping Argv[3] - Optional number of bytes to dump @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EFIAPI EblHexdumpCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_OPEN_FILE *File; VOID *Location; UINTN Size; UINTN Width; UINTN Offset = 0; EFI_STATUS Status; UINTN Chunk = HEXDUMP_CHUNK; if ((Argc < 2) || (Argc > 4)) { return EFI_INVALID_PARAMETER; } Width = WidthFromCommandName (Argv[0], 1); if ((Width != 1) && (Width != 2) && (Width != 4)) { return EFI_INVALID_PARAMETER; } File = EfiOpen (Argv[1], EFI_FILE_MODE_READ, 0); if (File == NULL) { return EFI_NOT_FOUND; } Location = AllocatePool (Chunk); Size = (Argc > 3) ? AsciiStrHexToUintn (Argv[3]) : EfiTell (File, NULL); Offset = 0; if (Argc > 2) { Offset = AsciiStrHexToUintn (Argv[2]); if (Offset > 0) { // Make sure size includes the part of the file we have skipped Size += Offset; } } Status = EfiSeek (File, Offset, EfiSeekStart); if (EFI_ERROR (Status)) { goto Exit; } for (; Offset + HEXDUMP_CHUNK <= Size; Offset += Chunk) { Chunk = HEXDUMP_CHUNK; Status = EfiRead (File, Location, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint ("Error reading file content\n"); goto Exit; } Status = OutputData (Location, Chunk, Width, File->BaseOffset + Offset); if (EFI_ERROR(Status)) { if (Status == EFI_END_OF_FILE) { Status = EFI_SUCCESS; } goto Exit; } } // Any left over? if (Offset < Size) { Chunk = Size - Offset; Status = EfiRead (File, Location, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint ("Error reading file content\n"); goto Exit; } Status = OutputData (Location, Chunk, Width, File->BaseOffset + Offset); if (EFI_ERROR(Status)) { if (Status == EFI_END_OF_FILE) { Status = EFI_SUCCESS; } goto Exit; } } Exit: EfiClose (File); FreePool (Location); return EFI_SUCCESS; }
EFI_STATUS EblFileDiffCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_OPEN_FILE *File1 = NULL; EFI_OPEN_FILE *File2 = NULL; EFI_STATUS Status = EFI_SUCCESS; VOID *Buffer1 = NULL; VOID *Buffer2 = NULL; UINTN Size1; UINTN Size2; UINTN Offset; UINTN Chunk = FILE_COPY_CHUNK; if (Argc != 3) { return EFI_INVALID_PARAMETER; } File1 = EfiOpen(Argv[1], EFI_FILE_MODE_READ, 0); if (File1 == NULL) { AsciiPrint("File 1 open error.\n"); return EFI_NOT_FOUND; } File2 = EfiOpen(Argv[2], EFI_FILE_MODE_READ, 0); if (File2 == NULL) { AsciiPrint("File 2 open error.\n"); return EFI_NOT_FOUND; } Size1 = EfiTell(File1, NULL); Size2 = EfiTell(File2, NULL); if (Size1 != Size2) { AsciiPrint("Files differ.\n"); goto Exit; } Buffer1 = AllocatePool(FILE_COPY_CHUNK); if (Buffer1 == NULL) { goto Exit; } Buffer2 = AllocatePool(FILE_COPY_CHUNK); if (Buffer2 == NULL) { goto Exit; } for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size1; Offset += Chunk) { Chunk = FILE_COPY_CHUNK; Status = EfiRead(File1, Buffer1, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint("File 1 read error\n"); goto Exit; } Status = EfiRead(File2, Buffer2, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint("File 2 read error\n"); goto Exit; } if (CompareMem(Buffer1, Buffer2, Chunk) != 0) { AsciiPrint("Files differ.\n"); goto Exit; }; } // Any left over? if (Offset < Size1) { Chunk = Size1 - Offset; Status = EfiRead(File1, Buffer1, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint("File 1 read error\n"); goto Exit; } Status = EfiRead(File2, Buffer2, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint("File 2 read error\n"); goto Exit; } } if (CompareMem(Buffer1, Buffer2, Chunk) != 0) { AsciiPrint("Files differ.\n"); } else { AsciiPrint("Files are identical.\n"); } Exit: if (File1 != NULL) { Status = EfiClose(File1); if (EFI_ERROR(Status)) { AsciiPrint("File 1 close error %r\n", Status); } } if (File2 != NULL) { Status = EfiClose(File2); if (EFI_ERROR(Status)) { AsciiPrint("File 2 close error %r\n", Status); } } if (Buffer1 != NULL) { FreePool(Buffer1); } if (Buffer2 != NULL) { FreePool(Buffer2); } return Status; }
EFI_STATUS EblFileCopyCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { 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; UINTN FileNameLen; CHAR8* DestFileName; CHAR8* SrcFileName; CHAR8* SrcPtr; if (Argc < 3) { return EFI_INVALID_PARAMETER; } DestFileName = Argv[2]; FileNameLen = AsciiStrLen (DestFileName); // Check if the destination file name looks like a directory if ((DestFileName[FileNameLen-1] == '\\') || (DestFileName[FileNameLen-1] == ':')) { // Set the pointer after the source drive (eg: after fs1:) SrcPtr = AsciiStrStr (Argv[1], ":"); if (SrcPtr == NULL) { SrcPtr = Argv[1]; } else { SrcPtr++; if (*SrcPtr == '\\') { SrcPtr++; } } if (*SrcPtr == '\0') { AsciiPrint("Source file incorrect.\n"); } // Skip the Source Directories while (1) { SrcFileName = SrcPtr; SrcPtr = AsciiStrStr (SrcPtr,"\\"); if (SrcPtr != NULL) { SrcPtr++; } else { break; } } if (*SrcFileName == '\0') { AsciiPrint("Source file incorrect (Error 2).\n"); } // Construct the destination filepath DestFileName = (CHAR8*)AllocatePool (FileNameLen + AsciiStrLen (SrcFileName) + 1); AsciiStrCpy (DestFileName, Argv[2]); AsciiStrCat (DestFileName, SrcFileName); } Source = EfiOpen(Argv[1], EFI_FILE_MODE_READ, 0); if (Source == NULL) { AsciiPrint("Source file open error.\n"); return EFI_NOT_FOUND; } Destination = EfiOpen(DestFileName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); if (Destination == NULL) { AsciiPrint("Destination file open error.\n"); return EFI_NOT_FOUND; } Buffer = AllocatePool(FILE_COPY_CHUNK); if (Buffer == NULL) { 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 %r\n", Status); goto Exit; } Status = EfiWrite(Destination, Buffer, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint("Write file error %r\n", Status); goto Exit; } } // Any left over? if (Offset < Size) { Chunk = Size - Offset; Status = EfiRead(Source, Buffer, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint("Read file error %r\n", Status); goto Exit; } Status = EfiWrite(Destination, Buffer, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint("Write file error %r\n", Status); goto Exit; } } Exit: if (Source != NULL) { Status = EfiClose(Source); if (EFI_ERROR(Status)) { AsciiPrint("Source close error %r\n", Status); } } if (Destination != NULL) { Status = EfiClose(Destination); if (EFI_ERROR(Status)) { AsciiPrint("Destination close error %r\n", Status); } // Case when we have concated the filename to the destination directory if (DestFileName != Argv[2]) { FreePool (DestFileName); } } if (Buffer != NULL) { FreePool(Buffer); } return Status; }
/** Load a file into memory and optionally jump to it. A load address can be specified or automatically allocated. A quoted command line can optionally be passed into the image. Argv[0] - "go" Argv[1] - Device Name:path for the file to load Argv[2] - Address to load to or '*' if the load address will be allocated Argv[3] - Optional Entry point to the image. Image will be called if present Argv[4] - "" string that will be passed as Argc & Argv to EntryPoint. Needs to include the command name go fv1:\EblCmdX 0x10000 0x10010 "EblCmdX Arg2 Arg3 Arg4"; - load EblCmdX from FV1 to location 0x10000 and call the entry point at 0x10010 passing in "EblCmdX Arg2 Arg3 Arg4" as the arguments. go fv0:\EblCmdX * 0x10 "EblCmdX Arg2 Arg3 Arg4"; - load EblCmdX from FS0 to location allocated by this command and call the entry point at offset 0x10 passing in "EblCmdX Arg2 Arg3 Arg4" as the arguments. go fv1:\EblCmdX 0x10000; Load EblCmdX to address 0x10000 and return @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblGoCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; EFI_OPEN_FILE *File; VOID *Address; UINTN Size; EBL_COMMMAND EntryPoint; UINTN EntryPointArgc; CHAR8 *EntryPointArgv[MAX_ARGS]; if (Argc <= 2) { // device name and laod address are required return EFI_SUCCESS; } File = EfiOpen (Argv[1], EFI_FILE_MODE_READ, 0); if (File == NULL) { AsciiPrint (" %a is not a valid path\n", Argv[1]); return EFI_SUCCESS; } EntryPoint = (EBL_COMMMAND)((Argc > 3) ? (UINTN)AsciiStrHexToUintn (Argv[3]) : (UINTN)NULL); if (Argv[2][0] == '*') { // * Means allocate the buffer Status = EfiReadAllocatePool (File, &Address, &Size); // EntryPoint is relative to the start of the image EntryPoint = (EBL_COMMMAND)((UINTN)EntryPoint + (UINTN)Address); } else { Address = (VOID *)AsciiStrHexToUintn (Argv[2]); Size = File->Size; // File->Size for LoadFile is lazy so we need to use the tell to figure it out EfiTell (File, NULL); Status = EfiRead (File, Address, &Size); } if (!EFI_ERROR (Status)) { AsciiPrint ("Loaded %,d bytes to 0x%08x\n", Size, Address); if (Argc > 3) { if (Argc > 4) { ParseArguments (Argv[4], &EntryPointArgc, EntryPointArgv); } else { EntryPointArgc = 1; EntryPointArgv[0] = File->FileName; } Status = EntryPoint (EntryPointArgc, EntryPointArgv); } } EfiClose (File); return Status; }
/** Perform an EFI connect to connect devices that follow the EFI driver model. If it is a PI system also call the dispatcher in case a new FV was made available by one of the connect EFI drivers (this is not a common case). Argv[0] - "connect" @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblConnectCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; UINTN HandleCount; EFI_HANDLE *HandleBuffer; UINTN Index; BOOLEAN Dispatch; EFI_OPEN_FILE *File; if (Argc > 1) { if ((*Argv[1] == 'd') || (*Argv[1] == 'D')) { Status = gBS->LocateHandleBuffer ( AllHandles, NULL, NULL, &HandleCount, &HandleBuffer ); if (EFI_ERROR (Status)) { return Status; } for (Index = 0; Index < HandleCount; Index++) { gBS->DisconnectController (HandleBuffer[Index], NULL, NULL); } // // Given we disconnect our console we should go and do a connect now // } else { File = EfiOpen (Argv[1], EFI_FILE_MODE_READ, 0); if (File != NULL) { AsciiPrint ("Connecting %a\n", Argv[1]); gBS->ConnectController (File->EfiHandle, NULL, NULL, TRUE); EfiClose (File); return EFI_SUCCESS; } } } Dispatch = FALSE; do { Status = gBS->LocateHandleBuffer ( AllHandles, NULL, NULL, &HandleCount, &HandleBuffer ); if (EFI_ERROR (Status)) { return Status; } for (Index = 0; Index < HandleCount; Index++) { gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE); } FreePool (HandleBuffer); // // Check to see if it's possible to dispatch an more DXE drivers. // The BdsLibConnectAllEfi () may have made new DXE drivers show up. // If anything is Dispatched Status == EFI_SUCCESS and we will try // the connect again. // if (gDS == NULL) { Status = EFI_NOT_FOUND; } else { Status = gDS->Dispatch (); if (!EFI_ERROR (Status)) { Dispatch = TRUE; } } } while (!EFI_ERROR (Status)); if (Dispatch) { AsciiPrint ("Connected and dispatched\n"); } else { AsciiPrint ("Connect\n"); } return EFI_SUCCESS; }
/** Start an EFI image (PE32+ with EFI defined entry point). Argv[0] - "start" Argv[1] - device name and path Argv[2] - "" string to pass into image being started start fs1:\Temp\Fv.Fv "arg to pass" ; load an FV from the disk and pass the ; ascii string arg to pass to the image start fv0:\FV ; load an FV from an FV (not common) start LoadFile0: ; load an FV via a PXE boot @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblStartCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; EFI_OPEN_FILE *File; EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_HANDLE ImageHandle; UINTN ExitDataSize; CHAR16 *ExitData; VOID *Buffer; UINTN BufferSize; EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; ImageHandle = NULL; if (Argc < 2) { return EFI_INVALID_PARAMETER; } File = EfiOpen (Argv[1], EFI_FILE_MODE_READ, 0); if (File == NULL) { return EFI_INVALID_PARAMETER; } DevicePath = File->DevicePath; if (DevicePath != NULL) { // check for device path form: blk, fv, fs, and loadfile Status = gBS->LoadImage (FALSE, gImageHandle, DevicePath, NULL, 0, &ImageHandle); } else { // Check for buffer form: A0x12345678:0x1234 syntax. // Means load using buffer starting at 0x12345678 of size 0x1234. Status = EfiReadAllocatePool (File, &Buffer, &BufferSize); if (EFI_ERROR (Status)) { EfiClose (File); return Status; } Status = gBS->LoadImage (FALSE, gImageHandle, DevicePath, Buffer, BufferSize, &ImageHandle); FreePool (Buffer); } EfiClose (File); if (!EFI_ERROR (Status)) { if (Argc >= 3) { // Argv[2] is a "" string that we pass directly to the EFI application without the "" // We don't pass Argv[0] to the EFI Application (it's name) just the args Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo); ASSERT_EFI_ERROR (Status); ImageInfo->LoadOptionsSize = (UINT32)AsciiStrSize (Argv[2]); ImageInfo->LoadOptions = AllocatePool (ImageInfo->LoadOptionsSize); AsciiStrCpy (ImageInfo->LoadOptions, Argv[2]); } // Transfer control to the EFI image we loaded with LoadImage() Status = gBS->StartImage (ImageHandle, &ExitDataSize, &ExitData); } return Status; }