/** @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));
}