继续阅读《UEFI 原理与编程》,其中的第三章介绍了一个 EFI Application如何在 Shell下加载运行的。ShellProtocol.c中的InternalShellExecuteDevicePath 是具体实现这个功能的函数。
函数头:
/** internal worker function to load and run an image via device path. @param ParentImageHandle A handle of the image that is executing the specified command line. @param DevicePath device path of the file to execute @param CommandLine Points to the NULL-terminated UCS-2 encoded string containing the command line. If NULL then the command- line will be empty. @param Environment Points to a NULL-terminated array of environment variables with the format 'x=y', where x is the environment variable name and y is the value. If this is NULL, then the current shell environment is used. @param[out] StartImageStatus Returned status from gBS->StartImage. @retval EFI_SUCCESS The command executed successfully. The status code returned by the command is pointed to by StatusCode. @retval EFI_INVALID_PARAMETER The parameters are invalid. @retval EFI_OUT_OF_RESOURCES Out of resources. @retval EFI_UNSUPPORTED Nested shell invocations are not allowed. **/ EFI_STATUS EFIAPI InternalShellExecuteDevicePath( IN CONST EFI_HANDLE *ParentImageHandle, IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN CONST CHAR16 *CommandLine OPTIONAL, IN CONST CHAR16 **Environment OPTIONAL, OUT EFI_STATUS *StartImageStatus OPTIONAL )
第一步,先将 EFI Application加载到内存中,生成Image对象。取得这个对象的句柄为 NewHandle。
// // Load the image with: // FALSE - not from boot manager and NULL, 0 being not already in memory // Status = gBS->LoadImage( FALSE, *ParentImageHandle, (EFI_DEVICE_PATH_PROTOCOL*)DevicePath, NULL, 0, &NewHandle); if (EFI_ERROR(Status)) { if (NewHandle != NULL) { gBS->UnloadImage(NewHandle); } return (Status); }
第二步,取得命令行参数,将命令行的参数交给 NewHandle
Status = gBS->OpenProtocol( NewHandle, &gEfiLoadedImageProtocolGuid, (VOID**)&LoadedImage, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (!EFI_ERROR(Status)) { ASSERT(LoadedImage->LoadOptionsSize == 0); if (NewCmdLine != NULL) { LoadedImage->LoadOptionsSize = (UINT32)StrSize(NewCmdLine); LoadedImage->LoadOptions = (VOID*)NewCmdLine; }
看到这里我有一个问题:前面加载之后,为什么第二步就能够在被加载的Image上找到“EFI_Loaded_Image_Protocol”? 带着这样的问题追踪了代码。首先要找到 gBS->LoadImage 的真正实现代码,在\MdeModulePkg\Core\Dxe\DxeMain\DxeMain.c有定义,
// // DXE Core Module Variables // EFI_BOOT_SERVICES mBootServices = { { ................. ................. (EFI_IMAGE_LOAD) CoreLoadImage, // LoadImage ................. ................. }
再追CoreLoadImage代码在 \MdeModulePkg\Core\Dxe\Image\Image.c
/** Loads an EFI image into memory and returns a handle to the image. @param BootPolicy If TRUE, indicates that the request originates from the boot manager, and that the boot manager is attempting to load FilePath as a boot selection. @param ParentImageHandle The caller's image handle. @param FilePath The specific file path from which the image is loaded. @param SourceBuffer If not NULL, a pointer to the memory location containing a copy of the image to be loaded. @param SourceSize The size in bytes of SourceBuffer. @param ImageHandle Pointer to the returned image handle that is created when the image is successfully loaded. @retval EFI_SUCCESS The image was loaded into memory. @retval EFI_NOT_FOUND The FilePath was not found. @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. @retval EFI_UNSUPPORTED The image type is not supported, or the device path cannot be parsed to locate the proper protocol for loading the file. @retval EFI_OUT_OF_RESOURCES Image was not loaded due to insufficient resources. @retval EFI_LOAD_ERROR Image was not loaded because the image format was corrupt or not understood. @retval EFI_DEVICE_ERROR Image was not loaded because the device returned a read error. @retval EFI_ACCESS_DENIED Image was not loaded because the platform policy prohibits the image from being loaded. NULL is returned in *ImageHandle. @retval EFI_SECURITY_VIOLATION Image was loaded and an ImageHandle was created with a valid EFI_LOADED_IMAGE_PROTOCOL. However, the current platform policy specifies that the image should not be started. **/ EFI_STATUS EFIAPI CoreLoadImage ( IN BOOLEAN BootPolicy, IN EFI_HANDLE ParentImageHandle, IN EFI_DEVICE_PATH_PROTOCOL *FilePath, IN VOID *SourceBuffer OPTIONAL, IN UINTN SourceSize, OUT EFI_HANDLE *ImageHandle ) { EFI_STATUS Status; UINT64 Tick; EFI_HANDLE Handle; Tick = 0; PERF_CODE ( Tick = GetPerformanceCounter (); ); Status = CoreLoadImageCommon ( BootPolicy, ParentImageHandle, FilePath, SourceBuffer, SourceSize, (EFI_PHYSICAL_ADDRESS) (UINTN) NULL, NULL, ImageHandle, NULL, EFI_LOAD_PE_IMAGE_ATTRIBUTE_RUNTIME_REGISTRATION | EFI_LOAD_PE_IMAGE_ATTRIBUTE_DEBUG_IMAGE_INFO_TABLE_REGISTRATION ); Handle = NULL; if (!EFI_ERROR (Status)) { // // ImageHandle will be valid only Status is success. // Handle = *ImageHandle; } PERF_START (Handle, "LoadImage:", NULL, Tick); PERF_END (Handle, "LoadImage:", NULL, 0); return Status; } 在CoreLoadImageCommon 中可以找到加载安装EFI_Loaded_Image_Protocol 的代码。 // //Reinstall loaded image protocol to fire any notifications // Status = CoreReinstallProtocolInterface ( Image->Handle, &gEfiLoadedImageProtocolGuid, &Image->Info, &Image->Info ); if (EFI_ERROR (Status)) { goto Done; }
就是说,在加载过程中,还要对Image 安装EFI_Loaded_Image_Protocol。因此,Load 的动作并不是简单的读取到内存中。
继续回到InternalShellExecuteDevicePath 代码中。
//第三步,将修改好的ShellParamsProtocol 再安装到 Image上。 // // Initialize and install a shell parameters protocol on the image. // ShellParamsProtocol.StdIn = ShellInfoObject.NewShellParametersProtocol->StdIn; ShellParamsProtocol.StdOut = ShellInfoObject.NewShellParametersProtocol->StdOut; ShellParamsProtocol.StdErr = ShellInfoObject.NewShellParametersProtocol->StdErr; Status = UpdateArgcArgv(&ShellParamsProtocol, NewCmdLine, NULL, NULL); ASSERT_EFI_ERROR(Status); // // Replace Argv[0] with the full path of the binary we're executing: // If the command line was "foo", the binary might be called "foo.efi". // "The first entry in [Argv] is always the full file path of the // executable" - UEFI Shell Spec section 2.3 // ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath); // The image we're executing isn't necessarily in a filesystem - it might // be memory mapped. In this case EfiShellGetFilePathFromDevicePath will // return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it. if (ImagePath != NULL) { if (ShellParamsProtocol.Argv == NULL) { // Command line was empty or null. // (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL) ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *)); if (ShellParamsProtocol.Argv == NULL) { Status = EFI_OUT_OF_RESOURCES; goto UnloadImage; } ShellParamsProtocol.Argc = 1; } else { // Free the string UpdateArgcArgv put in Argv[0]; FreePool (ShellParamsProtocol.Argv[0]); } ShellParamsProtocol.Argv[0] = ImagePath; } Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol); ASSERT_EFI_ERROR(Status); //第四步,用 gBS->StartImage执行Image。 // // now start the image and if the caller wanted the return code pass it to them... // if (!EFI_ERROR(Status)) { StartStatus = gBS->StartImage( NewHandle, 0, NULL ); if (StartImageStatus != NULL) { *StartImageStatus = StartStatus; }
您好
有個問題想請您
假設有一個efi 檔案, 想利用loadimage(),startimage()方式去執行
那參數的部分要用什麼方式去代入呢?
ex:
abc.efi -f file.txt
當中的"-f" 與"file.txt"
該如何代入到efi中呢?
这个问题我没有研究过,你可以参考一下 http://www.lab-z.com/stu98/ Shell加载EFI的分析,估计需要构造 ShellParamsProtocol