2 EBL commands for EFI and PI Devices
4 Copyright (c) 2007, Intel Corporation<BR>
5 Portions copyright (c) 2008-2009, Apple Inc. All rights reserved.
7 All rights reserved. This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 EFI_DXE_SERVICES
*gDS
= NULL
;
24 Print information about the File System device.
26 @param File Open File for the device
31 IN EFI_OPEN_FILE
*File
40 AsciiPrint (" %a: ", File
->DeviceName
);
41 if (File
->FsInfo
!= NULL
) {
42 for (Str
= File
->FsInfo
->VolumeLabel
; *Str
!= '\0'; Str
++) {
44 // UI makes you enter _ for space, so make the printout match that
47 AsciiPrint ("%c", *Str
);
50 if (File
->FsInfo
->ReadOnly
) {
51 AsciiPrint ("ReadOnly");
61 Print information about the FV devices.
63 @param File Open File for the device
68 IN EFI_OPEN_FILE
*File
75 AsciiPrint (" %a: 0x%08lx - 0x%08lx : 0x%08x\n", File
->DeviceName
, File
->FvStart
, File
->FvStart
+ File
->FvSize
- 1, File
->FvSize
);
81 Print information about the Blk IO devices.
82 If the device supports PXE dump out extra information
84 @param File Open File for the device
89 IN EFI_OPEN_FILE
*File
95 EFI_OPEN_FILE
*FsFile
;
101 AsciiPrint (" %a: ", File
->DeviceName
);
103 // print out name of file system, if any, on this block device
104 Max
= EfiGetDeviceCounts (EfiOpenFileSystem
);
106 for (Index
= 0; Index
< Max
; Index
++) {
107 FsFile
= EfiDeviceOpenByType (EfiOpenFileSystem
, Index
);
108 if (FsFile
!= NULL
) {
109 if (FsFile
->EfiHandle
== File
->EfiHandle
) {
110 AsciiPrint ("fs%d: ", Index
);
119 // Print out useful Block IO media properties
120 if (File
->FsBlockIoMedia
.RemovableMedia
) {
121 AsciiPrint ("Removable ");
123 if (!File
->FsBlockIoMedia
.MediaPresent
) {
124 AsciiPrint ("No Media ");
126 if (File
->FsBlockIoMedia
.LogicalPartition
) {
127 AsciiPrint ("Partition ");
129 DeviceSize
= MultU64x32 (File
->FsBlockIoMedia
.LastBlock
+ 1, File
->FsBlockIoMedia
.BlockSize
);
130 AsciiPrint ("Size = 0x%lX\n", DeviceSize
);
136 Print information about the Load File devices.
137 If the device supports PXE dump out extra information
139 @param File Open File for the device
143 EblPrintLoadFileInfo (
144 IN EFI_OPEN_FILE
*File
147 EFI_DEVICE_PATH_PROTOCOL
*DevicePathNode
;
148 MAC_ADDR_DEVICE_PATH
*MacAddr
;
156 AsciiPrint (" %a: %a ", File
->DeviceName
, EblLoadFileBootTypeString (File
->EfiHandle
));
158 if (File
->DevicePath
!= NULL
) {
159 // Try to print out the MAC address
160 for (DevicePathNode
= File
->DevicePath
;
161 !IsDevicePathEnd (DevicePathNode
);
162 DevicePathNode
= NextDevicePathNode (DevicePathNode
)) {
164 if ((DevicePathType (DevicePathNode
) == MESSAGING_DEVICE_PATH
) && (DevicePathSubType (DevicePathNode
) == MSG_MAC_ADDR_DP
)) {
165 MacAddr
= (MAC_ADDR_DEVICE_PATH
*)DevicePathNode
;
167 HwAddressSize
= sizeof (EFI_MAC_ADDRESS
);
168 if (MacAddr
->IfType
== 0x01 || MacAddr
->IfType
== 0x00) {
173 for (Index
= 0; Index
< HwAddressSize
; Index
++) {
174 AsciiPrint ("%02x", MacAddr
->MacAddress
.Addr
[Index
] & 0xff);
188 Dump information about devices in the system.
190 fv: PI Firmware Volume
191 fs: EFI Simple File System
193 LoadFile: EFI Load File Protocol (commonly PXE network boot)
197 @param Argc Number of command arguments in Argv
198 @param Argv Array of strings that represent the parsed command line.
199 Argv[0] is the comamnd name
216 // Need to call here to make sure Device Counts are valid
217 EblUpdateDeviceLists ();
219 Max
= EfiGetDeviceCounts (EfiOpenFirmwareVolume
);
221 AsciiPrint ("Firmware Volume Devices:\n");
222 for (Index
= 0; Index
< Max
; Index
++) {
223 EblPrintFvbInfo (EfiDeviceOpenByType (EfiOpenFirmwareVolume
, Index
));
224 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, TRUE
)) {
230 Max
= EfiGetDeviceCounts (EfiOpenFileSystem
);
232 AsciiPrint ("File System Devices:\n");
233 for (Index
= 0; Index
< Max
; Index
++) {
234 EblPrintFsInfo (EfiDeviceOpenByType (EfiOpenFileSystem
, Index
));
235 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, TRUE
)) {
241 Max
= EfiGetDeviceCounts (EfiOpenBlockIo
);
243 AsciiPrint ("Block IO Devices:\n");
244 for (Index
= 0; Index
< Max
; Index
++) {
245 EblPrintBlkIoInfo (EfiDeviceOpenByType (EfiOpenBlockIo
, Index
));
246 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, TRUE
)) {
252 Max
= EfiGetDeviceCounts (EfiOpenLoadFile
);
254 AsciiPrint ("LoadFile Devices: (usually network)\n");
255 for (Index
= 0; Index
< Max
; Index
++) {
256 EblPrintLoadFileInfo (EfiDeviceOpenByType (EfiOpenLoadFile
, Index
));
257 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, TRUE
)) {
268 Start an EFI image (PE32+ with EFI defined entry point).
271 Argv[1] - device name and path
272 Argv[2] - "" string to pass into image being started
274 start fs1:\Temp\Fv.Fv "arg to pass" ; load an FV from the disk and pass the
275 ; ascii string arg to pass to the image
276 start fv0:\FV ; load an FV from an FV (not common)
277 start LoadFile0: ; load an FV via a PXE boot
279 @param Argc Number of command arguments in Argv
280 @param Argv Array of strings that represent the parsed command line.
281 Argv[0] is the comamnd name
294 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
295 EFI_HANDLE ImageHandle
;
300 EFI_LOADED_IMAGE_PROTOCOL
*ImageInfo
;
305 return EFI_INVALID_PARAMETER
;
308 File
= EfiOpen (Argv
[1], EFI_FILE_MODE_READ
, 0);
310 return EFI_INVALID_PARAMETER
;
313 DevicePath
= File
->DevicePath
;
314 if (DevicePath
!= NULL
) {
315 // check for device path form: blk, fv, fs, and loadfile
316 Status
= gBS
->LoadImage (FALSE
, gImageHandle
, DevicePath
, NULL
, 0, &ImageHandle
);
318 // Check for buffer form: A0x12345678:0x1234 syntax.
319 // Means load using buffer starting at 0x12345678 of size 0x1234.
321 Status
= EfiReadAllocatePool (File
, &Buffer
, &BufferSize
);
322 if (EFI_ERROR (Status
)) {
326 Status
= gBS
->LoadImage (FALSE
, gImageHandle
, DevicePath
, Buffer
, BufferSize
, &ImageHandle
);
333 if (!EFI_ERROR (Status
)) {
335 // Argv[2] is a "" string that we pass directly to the EFI application without the ""
336 // We don't pass Argv[0] to the EFI Application (it's name) just the args
337 Status
= gBS
->HandleProtocol (ImageHandle
, &gEfiLoadedImageProtocolGuid
, (VOID
**)&ImageInfo
);
338 ASSERT_EFI_ERROR (Status
);
340 ImageInfo
->LoadOptionsSize
= (UINT32
)AsciiStrSize (Argv
[2]);
341 ImageInfo
->LoadOptions
= AllocatePool (ImageInfo
->LoadOptionsSize
);
342 AsciiStrCpy (ImageInfo
->LoadOptions
, Argv
[2]);
345 // Transfer control to the EFI image we loaded with LoadImage()
346 Status
= gBS
->StartImage (ImageHandle
, &ExitDataSize
, &ExitData
);
354 Load a Firmware Volume (FV) into memory from a device. This causes drivers in
355 the FV to be dispatched if the dependancies of the drivers are met.
358 Argv[1] - device name and path
360 loadfv fs1:\Temp\Fv.Fv ; load an FV from the disk
361 loadfv fv0:\FV ; load an FV from an FV (not common)
362 loadfv LoadFile0: ; load an FV via a PXE boot
364 @param Argc Number of command arguments in Argv
365 @param Argv Array of strings that represent the parsed command line.
366 Argv[0] is the comamnd name
385 return EFI_INVALID_PARAMETER
;
388 File
= EfiOpen (Argv
[1], EFI_FILE_MODE_READ
, 0);
390 return EFI_INVALID_PARAMETER
;
393 if (File
->Type
== EfiOpenMemoryBuffer
) {
394 // If it is a address just use it.
395 Status
= gDS
->ProcessFirmwareVolume (File
->Buffer
, File
->Size
, &FvHandle
);
397 // If it is a file read it into memory and use it
398 Status
= EfiReadAllocatePool (File
, &FvStart
, &FvSize
);
400 if (EFI_ERROR (Status
)) {
404 Status
= gDS
->ProcessFirmwareVolume (FvStart
, FvSize
, &FvHandle
);
412 Perform an EFI connect to connect devices that follow the EFI driver model.
413 If it is a PI system also call the dispatcher in case a new FV was made
414 availible by one of the connect EFI drivers (this is not a common case).
418 @param Argc Number of command arguments in Argv
419 @param Argv Array of strings that represent the parsed command line.
420 Argv[0] is the comamnd name
433 EFI_HANDLE
*HandleBuffer
;
440 if ((*Argv
[1] == 'd') || (*Argv
[1] == 'D')) {
441 Status
= gBS
->LocateHandleBuffer (
448 if (EFI_ERROR (Status
)) {
452 for (Index
= 0; Index
< HandleCount
; Index
++) {
453 gBS
->DisconnectController (HandleBuffer
[Index
], NULL
, NULL
);
457 // Given we disconnect our console we should go and do a connect now
460 File
= EfiOpen (Argv
[1], EFI_FILE_MODE_READ
, 0);
462 AsciiPrint ("Connecting %a\n", Argv
[1]);
463 gBS
->ConnectController (File
->EfiHandle
, NULL
, NULL
, TRUE
);
472 Status
= gBS
->LocateHandleBuffer (
479 if (EFI_ERROR (Status
)) {
483 for (Index
= 0; Index
< HandleCount
; Index
++) {
484 gBS
->ConnectController (HandleBuffer
[Index
], NULL
, NULL
, TRUE
);
487 FreePool (HandleBuffer
);
490 // Check to see if it's possible to dispatch an more DXE drivers.
491 // The BdsLibConnectAllEfi () may have made new DXE drivers show up.
492 // If anything is Dispatched Status == EFI_SUCCESS and we will try
493 // the connect again.
496 Status
= EFI_NOT_FOUND
;
498 Status
= gDS
->Dispatch ();
499 if (!EFI_ERROR (Status
)) {
504 } while (!EFI_ERROR (Status
));
507 AsciiPrint ("Connected and dispatched\n");
509 AsciiPrint ("Connect\n");
517 CHAR8
*gMemMapType
[] = {
536 Dump out the EFI memory map
540 @param Argc Number of command arguments in Argv
541 @param Argv Array of strings that represent the parsed command line.
542 Argv[0] is the comamnd name
554 EFI_MEMORY_DESCRIPTOR
*MemMap
;
555 EFI_MEMORY_DESCRIPTOR
*OrigMemMap
;
558 UINTN DescriptorSize
;
559 UINT32 DescriptorVersion
;
560 UINT64 PageCount
[EfiMaxMemoryType
];
566 ZeroMem (PageCount
, sizeof (PageCount
));
568 AsciiPrint ("EFI Memory Map\n");
570 // First call is to figure out how big the buffer needs to be
573 Status
= gBS
->GetMemoryMap (&MemMapSize
, MemMap
, &MapKey
, &DescriptorSize
, &DescriptorVersion
);
574 if (Status
== EFI_BUFFER_TOO_SMALL
) {
575 // In case the AllocatPool changes the memory map we added in some extra descriptors
576 MemMapSize
+= (DescriptorSize
* 0x100);
577 OrigMemMap
= MemMap
= AllocatePool (MemMapSize
);
578 if (OrigMemMap
!= NULL
) {
579 // 2nd time we get the data
580 Status
= gBS
->GetMemoryMap (&MemMapSize
, MemMap
, &MapKey
, &DescriptorSize
, &DescriptorVersion
);
581 if (!EFI_ERROR (Status
)) {
582 for (Index
= 0, CurrentRow
= 0; Index
< MemMapSize
/DescriptorSize
; Index
++) {
583 EntrySize
= LShiftU64 (MemMap
->NumberOfPages
, 12);
584 AsciiPrint ("\n%a %016lx - %016lx: # %08lx %016lx", gMemMapType
[MemMap
->Type
% EfiMaxMemoryType
], MemMap
->PhysicalStart
, MemMap
->PhysicalStart
+ EntrySize
-1, MemMap
->NumberOfPages
, MemMap
->Attribute
);
585 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, TRUE
)) {
589 PageCount
[MemMap
->Type
% EfiMaxMemoryType
] += MemMap
->NumberOfPages
;
590 MemMap
= NEXT_MEMORY_DESCRIPTOR (MemMap
, DescriptorSize
);
594 for (Index
= 0, TotalMemory
= 0; Index
< EfiMaxMemoryType
; Index
++) {
595 if (PageCount
[Index
] != 0) {
596 AsciiPrint ("\n %a %,7ld Pages (%,14ld)", gMemMapType
[Index
], PageCount
[Index
], LShiftU64 (PageCount
[Index
], 12));
597 if (Index
== EfiLoaderCode
||
598 Index
== EfiLoaderData
||
599 Index
== EfiBootServicesCode
||
600 Index
== EfiBootServicesData
||
601 Index
== EfiRuntimeServicesCode
||
602 Index
== EfiRuntimeServicesData
||
603 Index
== EfiConventionalMemory
||
604 Index
== EfiACPIReclaimMemory
||
605 Index
== EfiACPIMemoryNVS
||
608 // Count total memory
609 TotalMemory
+= PageCount
[Index
];
614 AsciiPrint ("\nTotal Memory: %,ld MB (%,ld bytes)\n", RShiftU64 (TotalMemory
, 8), LShiftU64 (TotalMemory
, 12));
616 FreePool (OrigMemMap
);
628 Load a file into memory and optionally jump to it. A load addres can be
629 specified or automatically allocated. A quoted command line can optionally
630 be passed into the image.
633 Argv[1] - Device Name:path for the file to load
634 Argv[2] - Address to load to or '*' if the load address will be allocated
635 Argv[3] - Optional Entry point to the image. Image will be called if present
636 Argv[4] - "" string that will be passed as Argc & Argv to EntryPoint. Needs
637 to include the command name
639 go fv1:\EblCmdX 0x10000 0x10010 "EblCmdX Arg2 Arg3 Arg4"; - load EblCmdX
640 from FV1 to location 0x10000 and call the entry point at 0x10010 passing
641 in "EblCmdX Arg2 Arg3 Arg4" as the arguments.
643 go fv0:\EblCmdX * 0x10 "EblCmdX Arg2 Arg3 Arg4"; - load EblCmdX from FS0
644 to location allocated by this comamnd and call the entry point at offset 0x10
645 passing in "EblCmdX Arg2 Arg3 Arg4" as the arguments.
647 go fv1:\EblCmdX 0x10000; Load EblCmdX to address 0x10000 and return
649 @param Argc Number of command arguments in Argv
650 @param Argv Array of strings that represent the parsed command line.
651 Argv[0] is the comamnd name
666 EBL_COMMMAND EntryPoint
;
667 UINTN EntryPointArgc
;
668 CHAR8
*EntryPointArgv
[MAX_ARGS
];
672 // device name and laod address are required
676 File
= EfiOpen (Argv
[1], EFI_FILE_MODE_READ
, 0);
678 AsciiPrint (" %a is not a valid path\n", Argv
[1]);
682 EntryPoint
= (EBL_COMMMAND
)((Argc
> 3) ? (UINTN
)AsciiStrHexToUintn (Argv
[3]) : (UINTN
)NULL
);
683 if (Argv
[2][0] == '*') {
684 // * Means allocate the buffer
685 Status
= EfiReadAllocatePool (File
, &Address
, &Size
);
687 // EntryPoint is relatvie to the start of the image
688 EntryPoint
= (EBL_COMMMAND
)((UINTN
)EntryPoint
+ (UINTN
)Address
);
691 Address
= (VOID
*)AsciiStrHexToUintn (Argv
[2]);
694 // File->Size for LoadFile is lazy so we need to use the tell to figure it out
695 EfiTell (File
, NULL
);
696 Status
= EfiRead (File
, Address
, &Size
);
699 if (!EFI_ERROR (Status
)) {
700 AsciiPrint ("Loaded %,d bytes to 0x%08x\n", Size
, Address
);
704 ParseArguments (Argv
[4], &EntryPointArgc
, EntryPointArgv
);
707 EntryPointArgv
[0] = File
->FileName
;
710 Status
= EntryPoint (EntryPointArgc
, EntryPointArgv
);
718 #define FILE_COPY_CHUNK 0x20000
726 EFI_OPEN_FILE
*Source
= NULL
;
727 EFI_OPEN_FILE
*Destination
= NULL
;
728 EFI_STATUS Status
= EFI_SUCCESS
;
732 UINTN Chunk
= FILE_COPY_CHUNK
;
735 return EFI_INVALID_PARAMETER
;
738 Source
= EfiOpen(Argv
[1], EFI_FILE_MODE_READ
, 0);
739 if (Source
== NULL
) {
740 AsciiPrint("Source file open error.\n");
741 return EFI_NOT_FOUND
;
744 Destination
= EfiOpen(Argv
[2], EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
, 0);
745 if (Destination
== NULL
) {
746 AsciiPrint("Destination file open error.\n");
747 return EFI_NOT_FOUND
;
750 Buffer
= AllocatePool(FILE_COPY_CHUNK
);
751 if (Buffer
== NULL
) {
755 Size
= EfiTell(Source
, NULL
);
757 for (Offset
= 0; Offset
+ FILE_COPY_CHUNK
<= Size
; Offset
+= Chunk
) {
758 Chunk
= FILE_COPY_CHUNK
;
760 Status
= EfiRead(Source
, Buffer
, &Chunk
);
761 if (EFI_ERROR(Status
)) {
762 AsciiPrint("Read file error\n");
766 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
767 if (EFI_ERROR(Status
)) {
768 AsciiPrint("Write file error\n");
775 Chunk
= Size
- Offset
;
777 Status
= EfiRead(Source
, Buffer
, &Chunk
);
778 if (EFI_ERROR(Status
)) {
779 AsciiPrint("Read file error\n");
783 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
784 if (EFI_ERROR(Status
)) {
785 AsciiPrint("Write file error\n");
791 if (Source
!= NULL
) {
792 Status
= EfiClose(Source
);
793 if (EFI_ERROR(Status
)) {
794 AsciiPrint("Source close error %r\n", Status
);
798 if (Destination
!= NULL
) {
799 Status
= EfiClose(Destination
);
800 if (EFI_ERROR(Status
)) {
801 AsciiPrint("Destination close error %r\n", Status
);
805 if (Buffer
!= NULL
) {
818 EFI_OPEN_FILE
*File1
= NULL
;
819 EFI_OPEN_FILE
*File2
= NULL
;
820 EFI_STATUS Status
= EFI_SUCCESS
;
821 VOID
*Buffer1
= NULL
;
822 VOID
*Buffer2
= NULL
;
826 UINTN Chunk
= FILE_COPY_CHUNK
;
829 return EFI_INVALID_PARAMETER
;
832 File1
= EfiOpen(Argv
[1], EFI_FILE_MODE_READ
, 0);
834 AsciiPrint("File 1 open error.\n");
835 return EFI_NOT_FOUND
;
838 File2
= EfiOpen(Argv
[2], EFI_FILE_MODE_READ
, 0);
840 AsciiPrint("File 2 open error.\n");
841 return EFI_NOT_FOUND
;
844 Size1
= EfiTell(File1
, NULL
);
845 Size2
= EfiTell(File2
, NULL
);
847 if (Size1
!= Size2
) {
848 AsciiPrint("Files differ.\n");
852 Buffer1
= AllocatePool(FILE_COPY_CHUNK
);
853 if (Buffer1
== NULL
) {
857 Buffer2
= AllocatePool(FILE_COPY_CHUNK
);
858 if (Buffer2
== NULL
) {
862 for (Offset
= 0; Offset
+ FILE_COPY_CHUNK
<= Size1
; Offset
+= Chunk
) {
863 Chunk
= FILE_COPY_CHUNK
;
865 Status
= EfiRead(File1
, Buffer1
, &Chunk
);
866 if (EFI_ERROR(Status
)) {
867 AsciiPrint("File 1 read error\n");
871 Status
= EfiRead(File2
, Buffer2
, &Chunk
);
872 if (EFI_ERROR(Status
)) {
873 AsciiPrint("File 2 read error\n");
877 if (CompareMem(Buffer1
, Buffer2
, Chunk
) != 0) {
878 AsciiPrint("Files differ.\n");
884 if (Offset
< Size1
) {
885 Chunk
= Size1
- Offset
;
887 Status
= EfiRead(File1
, Buffer1
, &Chunk
);
888 if (EFI_ERROR(Status
)) {
889 AsciiPrint("File 1 read error\n");
893 Status
= EfiRead(File2
, Buffer2
, &Chunk
);
894 if (EFI_ERROR(Status
)) {
895 AsciiPrint("File 2 read error\n");
900 if (CompareMem(Buffer1
, Buffer2
, Chunk
) != 0) {
901 AsciiPrint("Files differ.\n");
903 AsciiPrint("Files are identical.\n");
908 Status
= EfiClose(File1
);
909 if (EFI_ERROR(Status
)) {
910 AsciiPrint("File 1 close error %r\n", Status
);
915 Status
= EfiClose(File2
);
916 if (EFI_ERROR(Status
)) {
917 AsciiPrint("File 2 close error %r\n", Status
);
921 if (Buffer1
!= NULL
) {
925 if (Buffer2
!= NULL
) {
932 GLOBAL_REMOVE_IF_UNREFERENCED
const EBL_COMMAND_TABLE mCmdDeviceTemplate
[] =
936 "[d]; Connect all EFI devices. d means disconnect",
942 "; Show information about boot devices",
948 " dev:path loadaddress entrypoint args; load to given address and jump in",
954 " devname; Load PI FV from device",
960 " path; EFI Boot Device:filepath. fs1:\\EFI\\BOOT.EFI",
966 "; dump EFI memory map",
972 " file1 file2; copy file",
978 " file1 file2; compare files",
986 Initialize the commands in this in this file
990 EblInitializeDeviceCmd (
994 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
995 EblAddCommands (mCmdDeviceTemplate
, sizeof (mCmdDeviceTemplate
)/sizeof (EBL_COMMAND_TABLE
));