某些情况下,我们有在自己的程序中调用另外一个 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 這樣要怎麼解決呢?
没遇到过…..