UEFI combat-network boot

Brief introduction

Under UEFI, a number of network boot methods are implemented, such as HTTP boot and PXE boot. The corresponding default is as follows:

!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

From their macro definitions, you can see which codes correspond to specific HTTP startup and PXE startup. The two network startup methods are introduced below.

 

PXE boot

PXE network startup is an older network startup method. The specific usage method is introduced in the PXE introduction and usage instructions , and here is mainly about its realization. First look at the NetworkPkg\UefiPxeBcDxe\UefiPxeBcDxe.inf file, from here you can see that it actually implements two Protocols for packaging the entire PXE action:

[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 here means to generate a protocol, you can find their installation code directly in 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
                  );

In fact, it will be installed twice, corresponding to IPv4 and IPv6. We still take IPv4 as an example. You can see that in addition to the two Protocols mentioned above, Device Path Protocol is also installed. It is installed as a general requirement. Here No special instructions.

///
/// 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;
};

It is an interface for obtaining files. In fact, PXE is only a process of obtaining BootLoader through the network, so the interface here is also very consistent. Also need to explain, in fact, HTTP startup also implements this interface, because it only obtains the BootLoader through the network.

The implementation of LoadFile is as follows (NetworkPkg\UefiPxeBcDxe\PxeBcImpl.c):

EFI_LOAD_FILE_PROTOCOL  gLoadFileProtocolTemplate = { EfiPxeLoadFile };

Its concrete realization is from PxeBc .

The protocol corresponding to gEfiPxeBaseCodeProtocolGuid is slightly more complicated (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;
};

It actually needs to complete network communication-related actions, such as DHCP/TFPT. The implementation of PxeBc is as follows (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
};

And PxeBc and LoadFile relationship between the structure are connected by the following together (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 start

Although HTTP itself is not new, it is still newer than PXE when used for network boot. Compared with PXE using TFTP to download Boot Loader, HTTP startup is of course using HTTP to download Boot Loader. Although there is no essential difference between the two, HTTP has wider applicability. The following is the environment diagram of HTTP startup:

BootLoader is located on the HTTP server, not on the TFTP server started by PXE. And HTTP uses URI to access server equipment, while PXE uses IP to access server equipment.

The implementation of HTTP startup is slightly more complicated than PXE startup, involving content such as DNS/DHCP/URL. But the key is the NetworkPkg\HttpBootDxe\HttpBootDxe.inf module, which implements 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;
  }

It is also divided into two types, IPv4 and IPv6. The corresponding LoadFile implementation (NetworkPkg\HttpBootDxe\HttpBootImpl.c):

///
/// Load File Protocol instance
///
GLOBAL_REMOVE_IF_UNREFERENCED 
EFI_LOAD_FILE_PROTOCOL  gHttpBootDxeLoadFile = {
  HttpBootDxeLoadFile
};

There are also some protocols used to handle HTTP communication, such as:

///
/// 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;
};

For HTTP boot, you can also refer to: https://github.com/tianocore/tianocore.github.io/wiki/HTTP-Boot .

 

Network startup items

After a brief introduction to HTTP startup and PXE startup, the follow-up need to pay attention to how to use these startup implementations. In fact, as mentioned earlier, this is mainly done through EFI_LOAD_FILE_PROTOCOL  . For network startup, the approximate code is as follows:

  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);

Here get all the implemented EFI_LOAD_FILE_PROTOCOL  , and for each protocol, call its LoadFile to get the file. However, it should be noted that because the addresses used for HTTP startup and PXE startup are different, the corresponding Device Path is also different:

/**
  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
  );

That is, the FilePath here has different values. Device Path structure corresponding to HTTP:

///
/// 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;

Device Path will be used in the implementation of HttpBootDxeLoadFile :

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;
  }

And PXE boot does not need a special 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;
  }

Only judgment is made here, but it is not really used. Except for Device Path, there is no big difference between the two calling LoadFile .

 

Guess you like

Origin blog.csdn.net/jiangwei0512/article/details/106753958