mirror of https://github.com/acidanthera/audk.git
1060 lines
27 KiB
C
1060 lines
27 KiB
C
/** @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>
|
|
(C) Copyright 2015 Hewlett Packard Enterprise Development LP<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
|
|
EFIAPI
|
|
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
|
|
EFIAPI
|
|
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
|
|
EFIAPI
|
|
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
|
|
EFIAPI
|
|
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
|
|
EFIAPI
|
|
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
|
|
EFIAPI
|
|
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
|
|
EFIAPI
|
|
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
|
|
EFIAPI
|
|
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));
|
|
}
|
|
|