/** @file | |
EBL commands for EFI and PI Devices | |
Copyright (c) 2007, Intel Corporation. All rights reserved.<BR> | |
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> | |
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)); | |
} | |