前面【参考1】提到了 StartImage 加载 CLib 编写Application 出错的原因,这篇文章介绍如何解决这个问题。
根据原因来看是因为找不到提供 Parameters 的Protocol,那么我们在调用之前给被加载的Application 装上需要的Protocol即可。安装 Protocol 需要用到 InstallProtocollInterface,具体定义如下【参考2】:
欲安装的 Protocol 实例则是从加载程序(Exec6)上面取下来的。
没有多少人愿意看大篇幅的代码,我这里列下最关键的部分:
首先,取出当前的 Shell Interface, 不同的环境下还可以使用 Shell Parameter Protocol , NT32 环境下只支持前者
//如果你在实体机上发现有问题,那么可以考虑这段代码的问题 Status = gBS->OpenProtocol(gImageHandle, &gEfiShellInterfaceGuid, (VOID **)&EfiShellInterface, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR(Status)) { Print(L"Shell Parameters Protocol not Found!\r\n",Status); return (Status); } //之后,将取下来的 Protocol 安装给被加载的 Application Status = gBS->InstallProtocolInterface ( &NewHandle, &gEfiShellInterfaceGuid, EFI_NATIVE_INTERFACE, EfiShellInterface ); if (EFI_ERROR(Status)) { Print(L"Protocol Interface Installed fail!\r\n",Status); return (Status); }
最后,安装之后不能忘记 Uninstall,还要调用一下,特别注意第一个参数传递的不是指针。
运行结果,可以看出 Hello1和 Hello2都可以被正常加载运行:
看到这里,这篇文章就可以结束了,下面列出 Exec6 的代码:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <stdio.h> #include <stdlib.h> #include <wchar.h> #include <Protocol/EfiShell.h> #include <Library/ShellLib.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; extern EFI_SHELL_PROTOCOL *gEfiShellProtocol; extern EFI_SHELL_ENVIRONMENT2 *mEfiShellEnvironment2; extern EFI_HANDLE gImageHandle; typedef struct { UINTN Signature; /// Image handle EFI_HANDLE Handle; /// Image type UINTN Type; /// If entrypoint has been called BOOLEAN Started; /// The image's entry point EFI_IMAGE_ENTRY_POINT EntryPoint; /// loaded image protocol EFI_LOADED_IMAGE_PROTOCOL Info; /// Location in memory EFI_PHYSICAL_ADDRESS ImageBasePage; } LOADED_IMAGE_PRIVATE_DATA_TEMP; #define _CR(Record, TYPE, Field) ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field))) #define LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(a) \ _CR(a, LOADED_IMAGE_PRIVATE_DATA_TEMP, Info) /** GET DEVICEPATH **/ EFI_DEVICE_PATH_PROTOCOL * EFIAPI ShellGetDevicePath ( IN CHAR16 * CONST DeviceName OPTIONAL ) { // // Check for UEFI Shell 2.0 protocols // if (gEfiShellProtocol != NULL) { return (gEfiShellProtocol->GetDevicePathFromFilePath(DeviceName)); } // // Check for EFI shell // if (mEfiShellEnvironment2 != NULL) { return (mEfiShellEnvironment2->NameToPath(DeviceName)); } return (NULL); } int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_HANDLE NewHandle; EFI_STATUS Status; LOADED_IMAGE_PRIVATE_DATA_TEMP *private = NULL; UINTN ExitDataSizePtr; EFI_LOADED_IMAGE_PROTOCOL *ImageInfo = NULL; EFI_SHELL_INTERFACE *EfiShellInterface=NULL; if (Argc!=2) { Print(L"Usage: Exec4 FileName\n"); return EFI_SUCCESS; } Print(L"File [%s]\n",Argv[1]); DevicePath=ShellGetDevicePath(Argv[1]); // // Load the image with: // FALSE - not from boot manager and NULL, 0 being not already in memory // Status = gBS->LoadImage( FALSE, gImageHandle, DevicePath, NULL, 0, &NewHandle); if (EFI_ERROR(Status)) { if (NewHandle != NULL) { gBS->UnloadImage(NewHandle); } Print(L"Error during LoadImage [%X]\n",Status); return (Status); } Status = gBS -> HandleProtocol ( NewHandle, &gEfiLoadedImageProtocolGuid, &ImageInfo ); private = LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(ImageInfo); Print(L"ImageBase in EFI_LOADED_IMAGE_PROTOCOL [%lX]\n",ImageInfo->ImageBase); Print(L"ImageBase in LOADED_IMAGE_PRIVATE_DATA_TEMP [%lX]\n",private->ImageBasePage); Print(L"Entry Point [%lX]\n",private->EntryPoint); Status = gBS->OpenProtocol(gImageHandle, &gEfiShellInterfaceGuid, (VOID **)&EfiShellInterface, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR(Status)) { Print(L"Shell Parameters Protocol not Found!\r\n",Status); return (Status); } Status = gBS->InstallProtocolInterface ( &NewHandle, &gEfiShellInterfaceGuid, EFI_NATIVE_INTERFACE, EfiShellInterface ); if (EFI_ERROR(Status)) { Print(L"Protocol Interface Installed fail!\r\n",Status); return (Status); } Print(L"================================RUN================================\r\n",Status); // // now start the image, passing up exit data if the caller requested it // Status = gBS->StartImage( NewHandle, &ExitDataSizePtr, NULL ); if (EFI_ERROR(Status)) { if (NewHandle != NULL) { gBS->UnloadImage(NewHandle); } Print(L"Error during StartImage [%X]\r\n",Status); return (Status); } Print(L"===============================EXIT================================\r\n",Status); Status = gBS->UninstallProtocolInterface ( NewHandle, &gEfiShellInterfaceGuid, EfiShellInterface ); if (EFI_ERROR(Status)) { Print(L"Protocol Interface Uninstalled fail!\r\n",Status); return (Status); } gBS->UnloadImage (NewHandle); Print(L"NewHandle [%lX]\n",NewHandle); return EFI_SUCCESS; }
完整代码下载:
exec6
至此,终于回答了 StartImage 执行Application 的问题,如果你发现本文有任何问题欢迎给我留言,或者你有什么其他问题,同样可以给我发 e-Mail。
就是这样。
参考:
1. http://www.lab-z.com/stu85/ StartImage CLib
2. Uefi Spec 2.4 P153