先写一点:
使用场景
UEFI启动时候会有一个BootOrder,会根据BootOrder来依次启动相应的启动项
比如:硬盘启动-U盘启动-PXE启动-shell之类的
具体的可以在OS下使用efibootmgr命令查看
man efibootmgr可以查看相应命令,这里可以看github地址和相应说明
如apt-get install无法下载,
另外,man中提到了/sys/firmware/efi/var中会提供很多变量
我们经常在UEFI中使用SetVarible GetVarible,OS下也会使用这个的
这个文件夹恰好就有BootOption,BootCurrent(当前启动项),BootOrder,以及对应的GUID
根据GUID,可以找到是EFIGLOBLE(具体可以查一下)
启动项BootOption结构体
在bios界面经常有各种启动项,比如硬盘,和u盘启动下项,主要是通过BootOption来进行注册和调用的,我们先看下BootOption组成
//
// Common structure definition for DriverOption and BootOption
//
typedef struct {
//
// Data read from UEFI NV variables
//
UINTN OptionNumber; // #### numerical value, could be LoadOptionNumberUnassigned
EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType; // LoadOptionTypeBoot or LoadOptionTypeDriver
UINT32 Attributes; // Load Option Attributes
CHAR16 *Description; // Load Option Description
EFI_DEVICE_PATH_PROTOCOL *FilePath; // Load Option Device Path
UINT8 *OptionalData; // Load Option optional data to pass into image
UINT32 OptionalDataSize; // Load Option size of OptionalData
EFI_GUID VendorGuid;
//
// Used at runtime
//
EFI_STATUS Status; // Status returned from boot attempt gBS->StartImage ()
CHAR16 *ExitData; // Exit data returned from gBS->StartImage ()
UINTN ExitDataSize; // Size of ExitData
} EFI_BOOT_MANAGER_LOAD_OPTION;
每一个启动项都是一个EFI_BOOT_MANAGER_LOAD_OPTION类型变量
其中OptionNumber就是0,1,2,3,4,5依次递增
OptionType这里一般是 LoadOptionTypeBoot
Desciption就是描述,这里一般自定义,比如NVME(xx-xx-xx),然后会显示在bios界面上,当你选中这个描述,就会执行对应的FilePath.
FilePath就是比较重要的,比如PciRoot(0x0)/Pci(0xF,0x0)/Pci(0x0,0x0)/NVMe(0x1,3E-12-30-80-44-A7-79-64)/HD(6,GPT,C75CD876-DC80-4DF8-B6A4-7B2111703BA6,0x16E7B000,0x3C78000)
这是个NVME硬盘,然后选中这个NVME,根据这个FIlePath,代码会继续找这个NVME这个分区中的/boot/efi/BOOTARM64.EFI文件来打开这个.efi启动Grub界面。
如果BootOption不是这种NVME,比方说,你想要设置一个硬盘里面的/test/test.efi作为一个启动项,你可以使用PciRoot(0x0)/Pci(0xF,0x0)/Pci(0x0,0x0)/NVMe(0x1,3E-12-30-80-44-A7-79-64)/HD(6,GPT,C75CD876-DC80-4DF8-B6A4-7B2111703BA6,0x16E7B000,0x3C78000)/\\test\\test.efi
作为某个FilePath,然后使用
Status = EfiBootManagerInitializeLoadOption (
&BootOptions,
LoadOptionNumberUnassigned,
LoadOptionTypeBoot,
LOAD_OPTION_ACTIVE,
Description,
FilePath,
NULL,
0
);
这个函数来进行注册。第一个参数就是生成的EFI_BOOT_MANAGER_LOAD_OPTION结构体,是不需要进行初始化的。
生成nvme中某个目录下的FilePath
然后这里很多人就会不知道根据文件路径生成一个FilePath
下面是代码,
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *Handles;
UINTN Index;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_FILE_PROTOCOL *RootFile;
BOOLEAN HasOkrEfi = FALSE;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&HandleCount,
&Handles
);
DEBUG((EFI_D_INFO, "%a:HandleCount:%d Status:%r\n",__FUNCTION__,HandleCount,Status));
if(!EFI_ERROR(Status))
{
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->HandleProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
(VOID**)&Fs
);
Status = Fs->OpenVolume(Fs, &RootFile);
if(IsFilePresentSize(RootFile,L"\\test\\test.efi")){
return Handles[Index];
}
}
BOOLEAN
IsFilePresentSize (
EFI_FILE_PROTOCOL* RootFile,
CHAR16 *FilePathName
){
EFI_STATUS Status;
EFI_FILE_PROTOCOL *File = NULL;
Status = RootFile->Open(
RootFile,
&File,
FilePathName,
EFI_FILE_MODE_READ,
0
);
if(EFI_ERROR(Status)){
DEBUG((EFI_D_ERROR, "Open %s:%r\n", FilePathName, Status));
return FALSE;
}
return TRUE;
}
}
这个FilePathName,就是L"\\test\\test.efi
得到的这个 Handles[Index],就是有这个文件的Hanles,比如PciRoot(0x0)/Pci(0xF,0x0)/Pci(0x0,0x0)/NVMe(0x1,3E-12-30-80-44-A7-79-64)/HD(6,GPT,C75CD876-DC80-4DF8-B6A4-7B2111703BA6,0x16E7B000,0x3C78000)(这些是在shell中也会显示的Map)
这里 Handles[Index],再使用edk标准函数FileDevicePath 可以得到最终的目录DevicePath
DevicePath = FileDevicePath (Handles,L“\\test\\test.efi");
Path = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
DEBUG((EFI_D_ERROR, "line %d. DevPath=%s\n", __LINE__,Path));
这个Handles就是上面传回来的Handles[Index];
最终的DevPath 打印出来就是DevPath=PciRoot(0x0)/Pci(0xF,0x0)/Pci(0x0,0x0)/NVMe(0x1,3E-12-30-80-44-A7-79-64)/HD(6,GPT,C75CD876-DC80-4DF8-B6A4-7B2111703BA6,0x16E7B000,0x3C78000)/\test\test.efi
生成fdf中包含的文件,比如Uefi Shell
VOID test(
EFI_GUID *FileGuid,
CHAR16 *Description,
UINT32 Attributes
)
{
EFI_STATUS Status;
UINTN OptionIndex;
EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
UINTN BootOptionCount;
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
Status = gBS->HandleProtocol (gImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage);
ASSERT_EFI_ERROR (Status);
EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
DevicePath = AppendDevicePathNode (
DevicePathFromHandle (LoadedImage->DeviceHandle),
(EFI_DEVICE_PATH_PROTOCOL *) &FileNode
);
Status = EfiBootManagerInitializeLoadOption (
&NewOption,
LoadOptionNumberUnassigned,
LoadOptionTypeBoot,
Attributes,
Description,
DevicePath,
NULL,
0
);
这个FileGuid,比如
gPkgTokenSpaceGuid.PcdFile|{ 0x76, 0x45, 0x74, 0xdf, 0x1f, 0x0d, 0xe8, 0x34, 0x43, 0x45, 0x34, 0x16, 0x55, 0x65, 0x34, 0x34 }|VOID*|0x40000045
和包含在inf中的.efi文件guid是一样的。