运行加载的镜像

成功加载UEFI应用程序镜像之后,下一步就是运行它了。

本节示例代码的目录为start-image (日文版为036_start_devpath)。

用来运行加载好的镜像的函数是EFI_BOOT_SERVICES中的StartImage(),图4.29展示了它的定义。

struct EFI_SYSTEM_TABLE { ... struct EFI_BOOT_SERVICES { ... // Image Services unsigned long long (*LoadImage)( unsigned char BootPolicy, void *ParentImageHandle, struct EFI_DEVICE_PATH_PROTOCOL *DevicePath, void *SourceBuffer, unsigned long long SourceSize, void **ImageHandle); unsigned long long (*StartImage)( void *ImageHandle, /* 要运行的镜像句柄 */ unsigned long long *ExitDataSize, /* 指向存放ExitData大小的变量的指针 * 当ExitData被设为NULL时,这个值也应设为NULL */ unsigned short **ExitData /* 指向存放运行的镜像调用EFI_BOOT_SERVICES.Exit()函数退出时返回的内容的缓冲区的指针 * 这里我们不使用,所以设为NULL */ ); ... } *BootServices; };

图4.29: StartImage()的定义(位于efi.h中)

在载入UEFI应用程序镜像后,就可以调用StartImage()来运行它了。图4.30是在上一节图4.27代码的基础上运行镜像的例子。

#include "efi.h" #include "common.h" void efi_main(void *ImageHandle, struct EFI_SYSTEM_TABLE *SystemTable) { struct EFI_LOADED_IMAGE_PROTOCOL *lip; struct EFI_DEVICE_PATH_PROTOCOL *dev_path; struct EFI_DEVICE_PATH_PROTOCOL *dev_node; struct EFI_DEVICE_PATH_PROTOCOL *dev_path_merged; unsigned long long status; void *image; efi_init(SystemTable); ST->ConOut->ClearScreen(ST->ConOut); /* 获取ImageHandle的EFI_LOADED_IMAGE_PROTOCOL(lip) */ status = ST->BootServices->OpenProtocol( ImageHandle, &lip_guid, (void **)&lip, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); assert(status, L"OpenProtocol(lip)"); /* 获取lip->DeviceHandle的EFI_DEVICE_PATH_PROTOCOL(dev_path) */ status = ST->BootServices->OpenProtocol( lip->DeviceHandle, &dpp_guid, (void **)&dev_path, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); assert(status, L"OpenProtocol(dpp)"); /* 创建test.efi的设备节点 */ dev_node = DPFTP->ConvertTextToDeviceNode(L"test.efi"); /* 把dev_node附加到dev_path后 */ dev_path_merged = DPUP->AppendDeviceNode(dev_path, dev_node); /* 把dev_path_merged转换成字符串并显示 */ puts(L"dev_path_merged: "); puts(DPTTP->ConvertDevicePathToText(dev_path_merged, FALSE, FALSE)); puts(L"\r\n"); /* 从dev_path_merged加载镜像 */ status = ST->BootServices->LoadImage(FALSE, ImageHandle, dev_path_merged, NULL, 0, &image); assert(status, L"LoadImage"); puts(L"LoadImage: Success!\r\n"); /* 新增(此处开始) */ /* 运行image */ status = ST->BootServices->StartImage(image, NULL, NULL); assert(status, L"StartImage"); puts(L"StartImage: Success!\r\n"); /* 新增(此处结束) */ while (TRUE); }

图4.30: 使用StartImage()的例子

运行这个程序,我们可以看到图4.31这样的情况,这表明我们成功地运行了“Hello UEFI!”这个程序。

成功加载并运行“Hello UEFI!”

图4.31: 成功加载并运行“Hello UEFI!”

今后,我们可以用同样的方法来运行其他的UEFI应用程序。

顺带一提,运行“Hello UEFI!”这个应用程序你会看到光标跳到了下一行,但没有返回行头。这是因为UEFI和Windows一样,是用CRLF来换行的,而第一部分的“Hello UEFI!”这个程序在换行时缺少了CR(Carriage Return/\r)来把光标放回行头。