簡述
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沒有大的差別。