/** @file EBL commands for EFI and PI Devices Copyright (c) 2007, Intel Corporation. All rights reserved.
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include "Ebl.h" EFI_DXE_SERVICES *gDS = NULL; /** Print information about the File System device. @param File Open File for the device **/ VOID EblPrintFsInfo ( IN EFI_OPEN_FILE *File ) { CHAR16 *Str; if (File == NULL) { return; } AsciiPrint (" %a: ", File->DeviceName); if (File->FsInfo != NULL) { for (Str = File->FsInfo->VolumeLabel; *Str != '\0'; Str++) { if (*Str == ' ') { // UI makes you enter _ for space, so make the printout match that *Str = '_'; } AsciiPrint ("%c", *Str); } AsciiPrint (":"); if (File->FsInfo->ReadOnly) { AsciiPrint ("ReadOnly"); } } AsciiPrint ("\n"); EfiClose (File); } /** Print information about the FV devices. @param File Open File for the device **/ VOID EblPrintFvbInfo ( IN EFI_OPEN_FILE *File ) { if (File == NULL) { return; } AsciiPrint (" %a: 0x%08lx - 0x%08lx : 0x%08x\n", File->DeviceName, File->FvStart, File->FvStart + File->FvSize - 1, File->FvSize); EfiClose (File); } /** Print information about the Blk IO devices. If the device supports PXE dump out extra information @param File Open File for the device **/ VOID EblPrintBlkIoInfo ( IN EFI_OPEN_FILE *File ) { UINT64 DeviceSize; UINTN Index; UINTN Max; EFI_OPEN_FILE *FsFile; if (File == NULL) { return; } AsciiPrint (" %a: ", File->DeviceName); // print out name of file system, if any, on this block device Max = EfiGetDeviceCounts (EfiOpenFileSystem); if (Max != 0) { for (Index = 0; Index < Max; Index++) { FsFile = EfiDeviceOpenByType (EfiOpenFileSystem, Index); if (FsFile != NULL) { if (FsFile->EfiHandle == File->EfiHandle) { AsciiPrint ("fs%d: ", Index); EfiClose (FsFile); break; } EfiClose (FsFile); } } } // Print out useful Block IO media properties if (File->FsBlockIoMedia->RemovableMedia) { AsciiPrint ("Removable "); } if (!File->FsBlockIoMedia->MediaPresent) { AsciiPrint ("No Media\n"); } else { if (File->FsBlockIoMedia->LogicalPartition) { AsciiPrint ("Partition "); } DeviceSize = MultU64x32 (File->FsBlockIoMedia->LastBlock + 1, File->FsBlockIoMedia->BlockSize); AsciiPrint ("Size = 0x%lX\n", DeviceSize); } EfiClose (File); } /** Print information about the Load File devices. If the device supports PXE dump out extra information @param File Open File for the device **/ VOID EblPrintLoadFileInfo ( IN EFI_OPEN_FILE *File ) { EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; MAC_ADDR_DEVICE_PATH *MacAddr; UINTN HwAddressSize; UINTN Index; if (File == NULL) { return; } AsciiPrint (" %a: %a ", File->DeviceName, EblLoadFileBootTypeString (File->EfiHandle)); if (File->DevicePath != NULL) { // Try to print out the MAC address for (DevicePathNode = File->DevicePath; !IsDevicePathEnd (DevicePathNode); DevicePathNode = NextDevicePathNode (DevicePathNode)) { if ((DevicePathType (DevicePathNode) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (DevicePathNode) == MSG_MAC_ADDR_DP)) { MacAddr = (MAC_ADDR_DEVICE_PATH *)DevicePathNode; HwAddressSize = sizeof (EFI_MAC_ADDRESS); if (MacAddr->IfType == 0x01 || MacAddr->IfType == 0x00) { HwAddressSize = 6; } AsciiPrint ("MAC "); for (Index = 0; Index < HwAddressSize; Index++) { AsciiPrint ("%02x", MacAddr->MacAddress.Addr[Index] & 0xff); } } } } AsciiPrint ("\n"); EfiClose (File); return; } /** Dump information about devices in the system. fv: PI Firmware Volume fs: EFI Simple File System blk: EFI Block IO LoadFile: EFI Load File Protocol (commonly PXE network boot) Argv[0] - "device" @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 EblDeviceCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { UINTN Index; UINTN CurrentRow; UINTN Max; CurrentRow = 0; // Need to call here to make sure Device Counts are valid EblUpdateDeviceLists (); // Now we can print out the info... Max = EfiGetDeviceCounts (EfiOpenFirmwareVolume); if (Max != 0) { AsciiPrint ("Firmware Volume Devices:\n"); for (Index = 0; Index < Max; Index++) { EblPrintFvbInfo (EfiDeviceOpenByType (EfiOpenFirmwareVolume, Index)); if (EblAnyKeyToContinueQtoQuit (&CurrentRow, TRUE)) { break; } } } Max = EfiGetDeviceCounts (EfiOpenFileSystem); if (Max != 0) { AsciiPrint ("File System Devices:\n"); for (Index = 0; Index < Max; Index++) { EblPrintFsInfo (EfiDeviceOpenByType (EfiOpenFileSystem, Index)); if (EblAnyKeyToContinueQtoQuit (&CurrentRow, TRUE)) { break; } } } Max = EfiGetDeviceCounts (EfiOpenBlockIo); if (Max != 0) { AsciiPrint ("Block IO Devices:\n"); for (Index = 0; Index < Max; Index++) { EblPrintBlkIoInfo (EfiDeviceOpenByType (EfiOpenBlockIo, Index)); if (EblAnyKeyToContinueQtoQuit (&CurrentRow, TRUE)) { break; } } } Max = EfiGetDeviceCounts (EfiOpenLoadFile); if (Max != 0) { AsciiPrint ("LoadFile Devices: (usually network)\n"); for (Index = 0; Index < Max; Index++) { EblPrintLoadFileInfo (EfiDeviceOpenByType (EfiOpenLoadFile, Index)); if (EblAnyKeyToContinueQtoQuit (&CurrentRow, TRUE)) { break; } } } 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; } /** 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; } /** 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; } CHAR8 *gMemMapType[] = { "reserved ", "LoaderCode", "LoaderData", "BS_code ", "BS_data ", "RT_code ", "RT_data ", "available ", "Unusable ", "ACPI_recl ", "ACPI_NVS ", "MemMapIO ", "MemPortIO ", "PAL_code " }; /** Dump out the EFI memory map Argv[0] - "memmap" @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 EblMemMapCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; EFI_MEMORY_DESCRIPTOR *MemMap; EFI_MEMORY_DESCRIPTOR *OrigMemMap; UINTN MemMapSize; UINTN MapKey; UINTN DescriptorSize; UINT32 DescriptorVersion; UINT64 PageCount[EfiMaxMemoryType]; UINTN Index; UINT64 EntrySize; UINTN CurrentRow; UINT64 TotalMemory; ZeroMem (PageCount, sizeof (PageCount)); AsciiPrint ("EFI Memory Map\n"); // First call is to figure out how big the buffer needs to be MemMapSize = 0; MemMap = NULL; Status = gBS->GetMemoryMap (&MemMapSize, MemMap, &MapKey, &DescriptorSize, &DescriptorVersion); if (Status == EFI_BUFFER_TOO_SMALL) { // In case the AllocatPool changes the memory map we added in some extra descriptors MemMapSize += (DescriptorSize * 0x100); OrigMemMap = MemMap = AllocatePool (MemMapSize); if (OrigMemMap != NULL) { // 2nd time we get the data Status = gBS->GetMemoryMap (&MemMapSize, MemMap, &MapKey, &DescriptorSize, &DescriptorVersion); if (!EFI_ERROR (Status)) { for (Index = 0, CurrentRow = 0; Index < MemMapSize/DescriptorSize; Index++) { EntrySize = LShiftU64 (MemMap->NumberOfPages, 12); AsciiPrint ("\n%a %016lx - %016lx: # %08lx %016lx", gMemMapType[MemMap->Type % EfiMaxMemoryType], MemMap->PhysicalStart, MemMap->PhysicalStart + EntrySize -1, MemMap->NumberOfPages, MemMap->Attribute); if (EblAnyKeyToContinueQtoQuit (&CurrentRow, TRUE)) { break; } PageCount[MemMap->Type % EfiMaxMemoryType] += MemMap->NumberOfPages; MemMap = NEXT_MEMORY_DESCRIPTOR (MemMap, DescriptorSize); } } for (Index = 0, TotalMemory = 0; Index < EfiMaxMemoryType; Index++) { if (PageCount[Index] != 0) { AsciiPrint ("\n %a %,7ld Pages (%,14ld)", gMemMapType[Index], PageCount[Index], LShiftU64 (PageCount[Index], 12)); if (Index == EfiLoaderCode || Index == EfiLoaderData || Index == EfiBootServicesCode || Index == EfiBootServicesData || Index == EfiRuntimeServicesCode || Index == EfiRuntimeServicesData || Index == EfiConventionalMemory || Index == EfiACPIReclaimMemory || Index == EfiACPIMemoryNVS || Index == EfiPalCode ) { // Count total memory TotalMemory += PageCount[Index]; } } } AsciiPrint ("\nTotal Memory: %,ld MB (%,ld bytes)\n", RShiftU64 (TotalMemory, 8), LShiftU64 (TotalMemory, 12)); FreePool (OrigMemMap); } } return EFI_SUCCESS; } /** 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; } #define FILE_COPY_CHUNK 0x20000 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; } 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; } GLOBAL_REMOVE_IF_UNREFERENCED const EBL_COMMAND_TABLE mCmdDeviceTemplate[] = { { "connect", "[d]; Connect all EFI devices. d means disconnect", NULL, EblConnectCmd }, { "device", "; Show information about boot devices", NULL, EblDeviceCmd }, { "go", " dev:path loadaddress entrypoint args; load to given address and jump in", NULL, EblGoCmd }, { "loadfv", " devname; Load PI FV from device", NULL, EblLoadFvCmd }, { "start", " path; EFI Boot Device:filepath. fs1:\\EFI\\BOOT.EFI", NULL, EblStartCmd }, { "memmap", "; dump EFI memory map", NULL, EblMemMapCmd }, { "cp", " file1 file2; copy file only.", NULL, EblFileCopyCmd }, { "diff", " file1 file2; compare files", NULL, EblFileDiffCmd } }; /** Initialize the commands in this in this file **/ VOID EblInitializeDeviceCmd ( VOID ) { EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS); EblAddCommands (mCmdDeviceTemplate, sizeof (mCmdDeviceTemplate)/sizeof (EBL_COMMAND_TABLE)); }