【UEFI实战】网络启动

【UEFI实战】网络启动

简述

UEFI下实现了若干中网络启动的方式,比如HTTP启动,PXE启动等。对应的默认如下:

!if $(PXE_ENABLE) == TRUE

NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf

!endif

!if $(HTTP_BOOT_ENABLE) == TRUE

NetworkPkg/DnsDxe/DnsDxe.inf

NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf

NetworkPkg/HttpDxe/HttpDxe.inf

NetworkPkg/HttpBootDxe/HttpBootDxe.inf

!endif

从它们的宏定义就可以看出来具体HTTP启动和PXE启动对应哪些代码。下面分别介绍这两种网络启动方式。

PXE启动

PXE网路启动是一种比较老的网路启动方式,在PXE简介及使用说明有介绍具体的使用方式,而这里主要介绍它的实现。首先查看NetworkPkg\UefiPxeBcDxe\UefiPxeBcDxe.inf文件,从这里可以看到它实际上实现了两个Protocol用于包装整个PXE的动作:

[Protocols]

## TO_START

## SOMETIMES_CONSUMES

gEfiDevicePathProtocolGuid

gEfiNetworkInterfaceIdentifierProtocolGuid_31 ## SOMETIMES_CONSUMES

gEfiArpServiceBindingProtocolGuid ## TO_START

gEfiArpProtocolGuid ## TO_START

gEfiIp4ServiceBindingProtocolGuid ## TO_START

gEfiIp4ProtocolGuid ## TO_START

gEfiIp4Config2ProtocolGuid ## TO_START

gEfiIp6ServiceBindingProtocolGuid ## TO_START

gEfiIp6ProtocolGuid ## TO_START

gEfiIp6ConfigProtocolGuid ## TO_START

gEfiUdp4ServiceBindingProtocolGuid ## TO_START

gEfiUdp4ProtocolGuid ## TO_START

gEfiMtftp4ServiceBindingProtocolGuid ## TO_START

gEfiMtftp4ProtocolGuid ## TO_START

gEfiDhcp4ServiceBindingProtocolGuid ## TO_START

gEfiDhcp4ProtocolGuid ## TO_START

gEfiUdp6ServiceBindingProtocolGuid ## TO_START

gEfiUdp6ProtocolGuid ## TO_START

gEfiMtftp6ServiceBindingProtocolGuid ## TO_START

gEfiMtftp6ProtocolGuid ## TO_START

gEfiDhcp6ServiceBindingProtocolGuid ## TO_START

gEfiDhcp6ProtocolGuid ## TO_START

gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES

gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES

gEfiPxeBaseCodeCallbackProtocolGuid ## SOMETIMES_PRODUCES

gEfiPxeBaseCodeProtocolGuid ## BY_START

gEfiLoadFileProtocolGuid ## BY_START

gEfiAdapterInformationProtocolGuid ## SOMETIMES_CONSUMES

这里的BY_START表示的是生成Protocol,可以直接在NetworkPkg\UefiPxeBcDxe\PxeBcDriver.c中找到它们的安装代码:

//

// Create a new handle for IPv4 virtual nic,

// and install PxeBaseCode, LoadFile and DevicePath protocols.

//

Status = gBS->InstallMultipleProtocolInterfaces (

&Private->Ip4Nic->Controller,

&gEfiDevicePathProtocolGuid,

Private->Ip4Nic->DevicePath,

&gEfiLoadFileProtocolGuid,

&Private->Ip4Nic->LoadFile,

&gEfiPxeBaseCodeProtocolGuid,

&Private->PxeBc,

NULL

);

实际上会安装两次,对应IPv4和IPv6,我们还是以IPv4为例,可以看到这里除了安装上文提到的两个Protocol,还安装了Device Path Protocol,它是作为通用需求安装的,这里不做特别说明。

///

/// The EFI_LOAD_FILE_PROTOCOL is a simple protocol used to obtain files from arbitrary devices.

///

struct _EFI_LOAD_FILE_PROTOCOL {

EFI_LOAD_FILE LoadFile;

};

它就是一个获取文件的接口。事实上PXE说到底也只是一个通过网络获取BootLoader的过程,所以这里的接口也很符合。还需要说明,事实上HTTP启动也是实现了这个接口,因为它也只是通过网络获取BootLoader而已。

LoadFile的实现如下(NetworkPkg\UefiPxeBcDxe\PxeBcImpl.c):

EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate = { EfiPxeLoadFile };

它的具体实现是以来于的PxeBc。

gEfiPxeBaseCodeProtocolGuid对应的Protocol稍微复杂些(MdePkg\Include\Protocol\PxeBaseCode.h):

///

/// The EFI_PXE_BASE_CODE_PROTOCOL is used to control PXE-compatible devices.

/// An EFI_PXE_BASE_CODE_PROTOCOL will be layered on top of an

/// EFI_MANAGED_NETWORK_PROTOCOL protocol in order to perform packet level transactions.

/// The EFI_PXE_BASE_CODE_PROTOCOL handle also supports the

/// EFI_LOAD_FILE_PROTOCOL protocol. This provides a clean way to obtain control from the

/// boot manager if the boot path is from the remote device.

///

struct _EFI_PXE_BASE_CODE_PROTOCOL {

///

/// The revision of the EFI_PXE_BASE_CODE_PROTOCOL. All future revisions must

/// be backwards compatible. If a future version is not backwards compatible

/// it is not the same GUID.

///

UINT64 Revision;

EFI_PXE_BASE_CODE_START Start;

EFI_PXE_BASE_CODE_STOP Stop;

EFI_PXE_BASE_CODE_DHCP Dhcp;

EFI_PXE_BASE_CODE_DISCOVER Discover;

EFI_PXE_BASE_CODE_MTFTP Mtftp;

EFI_PXE_BASE_CODE_UDP_WRITE UdpWrite;

EFI_PXE_BASE_CODE_UDP_READ UdpRead;

EFI_PXE_BASE_CODE_SET_IP_FILTER SetIpFilter;

EFI_PXE_BASE_CODE_ARP Arp;

EFI_PXE_BASE_CODE_SET_PARAMETERS SetParameters;

EFI_PXE_BASE_CODE_SET_STATION_IP SetStationIp;

EFI_PXE_BASE_CODE_SET_PACKETS SetPackets;

///

/// The pointer to the EFI_PXE_BASE_CODE_MODE data for this device.

///

EFI_PXE_BASE_CODE_MODE *Mode;

};

它实际上需要完成网络通信相关的动作,比如DHCP/TFPT等。PxeBc的实现如下(NetworkPkg\UefiPxeBcDxe\PxeBcImpl.c):

EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate = {

EFI_PXE_BASE_CODE_PROTOCOL_REVISION,

EfiPxeBcStart,

EfiPxeBcStop,

EfiPxeBcDhcp,

EfiPxeBcDiscover,

EfiPxeBcMtftp,

EfiPxeBcUdpWrite,

EfiPxeBcUdpRead,

EfiPxeBcSetIpFilter,

EfiPxeBcArp,

EfiPxeBcSetParameters,

EfiPxeBcSetStationIP,

EfiPxeBcSetPackets,

NULL

};

而PxeBc和LoadFile之间的关系是通过如下的结构体连接在一起的(NetworkPkg\UefiPxeBcDxe\PxeBcImpl.h):

struct _PXEBC_VIRTUAL_NIC {

UINT32 Signature;

EFI_HANDLE Controller;

EFI_LOAD_FILE_PROTOCOL LoadFile;

EFI_DEVICE_PATH_PROTOCOL *DevicePath;

PXEBC_PRIVATE_DATA *Private;

};

HTTP启动

HTTP虽然本身也不是什么新东西,但是用作网络启动还是比PXE要新。相比PXE使用TFTP下载BootLoader,HTTP启动当然就是使用HTTP来下载Boot Loader。两者虽没有本质的区别,但是HTTP具有更广泛的适用性。下面是HTTP启动的环境框图:

BootLoader位于HTTP服务器上,而不是PXE启动的TFTP服务器上。且HTTP使用URI来访问服务器设备,而PXE使用IP来访问服务器设备。

HTTP启动的实现相比PXE启动要稍微复杂些,涉及到DNS/DHCP/URL的等内容。不过关键还是NetworkPkg\HttpBootDxe\HttpBootDxe.inf模块,它实现了EFI_LOAD_FILE_PROTOCOL(NetworkPkg\HttpBootDxe\HttpBootDxe.c):

//

// Create a child handle for the HTTP boot and install DevPath and Load file protocol on it.

//

CopyMem (&Private->Ip4Nic->LoadFile, &gHttpBootDxeLoadFile, sizeof (EFI_LOAD_FILE_PROTOCOL));

Status = gBS->InstallMultipleProtocolInterfaces (

&Private->Ip4Nic->Controller,

&gEfiLoadFileProtocolGuid,

&Private->Ip4Nic->LoadFile,

&gEfiDevicePathProtocolGuid,

Private->Ip4Nic->DevicePath,

NULL

);

if (EFI_ERROR (Status)) {

goto ON_ERROR;

}

它也分为IPv4和IPv6两种类型。对应的LoadFile实现(NetworkPkg\HttpBootDxe\HttpBootImpl.c):

///

/// Load File Protocol instance

///

GLOBAL_REMOVE_IF_UNREFERENCED

EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = {

HttpBootDxeLoadFile

};

另外还有一些Protocol用来处理HTTP通信的内容,比如:

///

/// EFI_HTTP_UTILITIES_PROTOCOL

/// designed to be used by EFI drivers and applications to parse HTTP

/// headers from a byte stream. This driver is neither dependent on

/// network connectivity, nor the existence of an underlying network

/// infrastructure.

///

struct _EFI_HTTP_UTILITIES_PROTOCOL {

EFI_HTTP_UTILS_BUILD Build;

EFI_HTTP_UTILS_PARSE Parse;

};

///

/// The EFI_DNS4_Protocol provides the function to get the host name and address

/// mapping, also provides pass through interface to retrieve arbitrary information

/// from DNS.

///

struct _EFI_DNS4_PROTOCOL {

EFI_DNS4_GET_MODE_DATA GetModeData;

EFI_DNS4_CONFIGURE Configure;

EFI_DNS4_HOST_NAME_TO_IP HostNameToIp;

EFI_DNS4_IP_TO_HOST_NAME IpToHostName;

EFI_DNS4_GENERAL_LOOKUP GeneralLookUp;

EFI_DNS4_UPDATE_DNS_CACHE UpdateDnsCache;

EFI_DNS4_POLL Poll;

EFI_DNS4_CANCEL Cancel;

};

关于HTTP启动,还可以参考:https://github.com/tianocore/tianocore.github.io/wiki/HTTP-Boot。

网路启动项

简单介绍完HTTP启动和PXE启动,后续需要关注的是如何使用这些启动实现。其实前面已经介绍过,这主要通过EFI_LOAD_FILE_PROTOCOL 来完成。对于网路启动,大致的代码如下:

Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles);

if (EFI_ERROR (Status)) {

HandleCount = 0;

Handles = NULL;

}

NextFullPath = NULL;

GetNext = (BOOLEAN)(FullPath == NULL);

for (Index = 0; Index < HandleCount; Index++) {

NextFullPath = BmExpandLoadFile (Handles[Index], FilePath);

这里获取到所有实现了的EFI_LOAD_FILE_PROTOCOL ,针对每一个Protocol,调用其LoadFile来获取文件。但是需要注意,因为HTTP启动和PXE启动使用的地址是不同的,所以对应到Device Path也是不同的:

/**

Causes the driver to load a specified file.

@param This Protocol instance pointer.

@param FilePath The device specific path of the file to load.

@param BootPolicy If TRUE, indicates that the request originates from the

boot manager is attempting to load FilePath as a boot

selection. If FALSE, then FilePath must match as exact file

to be loaded.

@param BufferSize On input the size of Buffer in bytes. On output with a return

code of EFI_SUCCESS, the amount of data transferred to

Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,

the size of Buffer required to retrieve the requested file.

@param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,

then the size of the requested file is returned in

BufferSize.

@retval EFI_SUCCESS The file was loaded.

@retval EFI_UNSUPPORTED The device does not support the provided BootPolicy

@retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or

BufferSize is NULL.

@retval EFI_NO_MEDIA No medium was present to load the file.

@retval EFI_DEVICE_ERROR The file was not loaded due to a device error.

@retval EFI_NO_RESPONSE The remote system did not respond.

@retval EFI_NOT_FOUND The file was not found.

@retval EFI_ABORTED The file load process was manually cancelled.

@retval EFI_WARN_FILE_SYSTEM The resulting Buffer contains UEFI-compliant file system.

**/

typedef

EFI_STATUS

(EFIAPI *EFI_LOAD_FILE)(

IN EFI_LOAD_FILE_PROTOCOL *This,

IN EFI_DEVICE_PATH_PROTOCOL *FilePath,

IN BOOLEAN BootPolicy,

IN OUT UINTN *BufferSize,

IN VOID *Buffer OPTIONAL

);

即这里的FilePath有不同的取值。HTTP对应的Device Path结构体:

///

/// Uniform Resource Identifiers (URI) Device Path SubType

///

#define MSG_URI_DP 0x18

typedef struct {

EFI_DEVICE_PATH_PROTOCOL Header;

///

/// Instance of the URI pursuant to RFC 3986.

///

CHAR8 Uri[];

} URI_DEVICE_PATH;

在HttpBootDxeLoadFile实现中会需要使用到Device Path:

EFI_STATUS

EFIAPI

HttpBootDxeLoadFile (

IN EFI_LOAD_FILE_PROTOCOL *This,

IN EFI_DEVICE_PATH_PROTOCOL *FilePath,

IN BOOLEAN BootPolicy,

IN OUT UINTN *BufferSize,

IN VOID *Buffer OPTIONAL

)

{

HTTP_BOOT_PRIVATE_DATA *Private;

HTTP_BOOT_VIRTUAL_NIC *VirtualNic;

BOOLEAN MediaPresent;

BOOLEAN UsingIpv6;

EFI_STATUS Status;

HTTP_BOOT_IMAGE_TYPE ImageType;

if (This == NULL || BufferSize == NULL || FilePath == NULL) {

return EFI_INVALID_PARAMETER;

}

//

// Only support BootPolicy

//

if (!BootPolicy) {

return EFI_UNSUPPORTED;

}

VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This);

Private = VirtualNic->Private;

//

// Check media status before HTTP boot start

//

MediaPresent = TRUE;

NetLibDetectMedia (Private->Controller, &MediaPresent);

if (!MediaPresent) {

return EFI_NO_MEDIA;

}

//

// Check whether the virtual nic is using IPv6 or not.

//

UsingIpv6 = FALSE;

if (VirtualNic == Private->Ip6Nic) {

UsingIpv6 = TRUE;

}

//

// Initialize HTTP boot.

//

Status = HttpBootStart (Private, UsingIpv6, FilePath);

if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) {

return Status;

}

而PXE启动并不需要特别的Device Path:

EFI_STATUS

EFIAPI

EfiPxeLoadFile (

IN EFI_LOAD_FILE_PROTOCOL *This,

IN EFI_DEVICE_PATH_PROTOCOL *FilePath,

IN BOOLEAN BootPolicy,

IN OUT UINTN *BufferSize,

IN VOID *Buffer OPTIONAL

)

{

PXEBC_PRIVATE_DATA *Private;

PXEBC_VIRTUAL_NIC *VirtualNic;

EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;

BOOLEAN UsingIpv6;

EFI_STATUS Status;

BOOLEAN MediaPresent;

if (FilePath == NULL || !IsDevicePathEnd (FilePath)) {

return EFI_INVALID_PARAMETER;

}

这里只做了判断,但是没有真正使用。除了Device Path,两者调用LoadFile没有大的差别。

相关推荐

頼 是什么意思?
365会提款不成功吗

頼 是什么意思?

📅 07-19 👁️ 1178
回望1982年世界杯
365bet体育在线网址

回望1982年世界杯

📅 09-06 👁️ 1156
成都哪个市没疫情了?看疫情防控的现状与恢复 • 本地金
如何购买中石化加油充值卡?两种购买渠道的详细操作步骤分享
张丰毅最新电视剧
365会提款不成功吗

张丰毅最新电视剧

📅 07-16 👁️ 3464
西门子家电保养、清洁及维护 上下两门、三门冰箱
365会提款不成功吗

西门子家电保养、清洁及维护 上下两门、三门冰箱

📅 07-24 👁️ 6567