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