某些情况下,我们有在自己的程序中调用另外一个 EFI 程序的需求。
关于这个问题【参考1】建议参考Shell的源程序。如果有时间,建议阅读这一段代码,相信对于具体的实现很有帮助。
“RunCommandOrFile()
==> case Efi_Application:
InternalShellExecuteDevicePath()
==> Status = gBS->LoadImage( ... ) ”
另外,【参考2】介绍了一下调用的流程:
1. BS->LoadImage 加载你要调用的 EFI 到内存
2. BS->StartImage 执行你加载的EFI程序
3. BS->UnLoadImage 执行完成之后释放EFI
了解了基本流程原理,下面就要认真阅读函数的原型。
LoadImage的原型如下,来自【参考3】
BootPolicy 告诉加载的EFI是否为可启动的选项
ParentImageHandle 是调用者的Handle
DevicePath 告诉要调用的EFI文件的位置
SourceBuffer 可选如果不为NULL的话,是指向内存中的要加载的EFI的指针
SourceSize 如果上面这个指针存在的话,给出指向内存的大小
EFI_HANDLE 加载之后Image的Handle
StartImage 原型
ImageHandle 前面LoadImage给出来的EFI Image Handle
ExitDataSize 下面ExitData的大小
ExitData 看起来在一个 EFI 结束的时候,可以返回一些内容
UnLoadImage 原型
给出要释放的EFI的Handle即可
根据上面的介绍,再结合Shell.c中的具体实现,编写程序如下。为了方便验证和保持整个程序的简洁,程序固定调用“HellowWorld.efi”。这个程序的作用是输出一段String。
#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; /** 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 char **Argv ) { EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_HANDLE NewHandle; EFI_STATUS Status; UINTN ExitDataSizePtr; CHAR16 *R=L"HelloWorld.efi"; Print(L"File [%s]\n",R); DevicePath=ShellGetDevicePath(R); // // 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); } // // 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]\n",Status); return (Status); } gBS->UnloadImage (NewHandle); return EFI_SUCCESS; }
运行结果:
工作的视频:
http://www.tudou.com/programs/view/92MTmguSCZk/?resourceId=414535982_06_02_99
代码下载
最后,如果你只是想简单的执行一个程序,可以考虑直接使用 EFI_SHELL_PROTOCOL 的 EfiShellExecute 或者 EFI_SHELL_ENVIRONMENT2的 Execute ,这样会简单许多。
参考:
1. http://biosren.com/viewthread.php?tid=7440&highlight=%BC%D3%D4%D8%2B%B3%CC%D0%F2 请问:在shell下,应用程序的.efi文件被加载到内存的基地址为多少?
2. http://blog.csdn.net/kaven128708/article/details/6042307 EFI Load Image
3. UEFI Spec 2.4 P196
版大,你好
我的shell上面會出現
Error during StartImage [3]
但是不清楚到底哪裡出問題了?
你要 Load 的代码是不是用到 clib
我这边发现无法 run clib编写的程序,原因还不清楚,回头我debug一下,怀疑是加载命令行参数的问题
查了一下 3 代表EFI_UNSUPPORTED~
你可以试试 ShellExecute 这个不存在问题
好的 ,ShellExecute好像不用System table來調用service就能使用?
而LoadImage要調用System table 才能使用,如gBS->LoadImage
樓主 想問一下 UnloadImage 出現 Invalid parameter 這樣要怎麼解決呢?
没遇到过.....