Bootstrap

【UEFI基础】EDK网络框架(PXE)

PXE

PXE代码综述

PXE的全称是Preboot eXecute Environment,它可以认为是BIOS特有的协议,因为它的作用就是通过网络启动操作系统。其实现在NetworkPkg\UefiPxeBcDxe\UefiPxeBcDxe.inf,入口如下:

EFI_STATUS
EFIAPI
PxeBcDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  Status = EfiLibInstallDriverBindingComponentName2 (
             ImageHandle,
             SystemTable,
             &gPxeBcIp4DriverBinding,
             ImageHandle,
             &gPxeBcComponentName,
             &gPxeBcComponentName2
             );
}

这里有IPv4和IPv6两个版本,这里以IPv4为例,它也是一个UEFI Driver Model,安装的EFI_DRIVER_BINDING_PROTOCOL如下:

EFI_DRIVER_BINDING_PROTOCOL  gPxeBcIp4DriverBinding = {
  PxeBcIp4DriverBindingSupported,
  PxeBcIp4DriverBindingStart,
  PxeBcIp4DriverBindingStop,
  0xa,
  NULL,
  NULL
};

PxeBcIp4DriverBindingSupported

Supported函数如下:

EFI_STATUS
EFIAPI
PxeBcIp4DriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
  )
{
  return PxeBcSupported (
           This,
           ControllerHandle,
           RemainingDevicePath,
           IP_VERSION_4
           );
}

这里用PxeBcSupported()包装一层,是为了统一处理IPv4和IPv6,对于IPv4来说,其依赖的Protocol:

EFI_STATUS
EFIAPI
PxeBcSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL,
  IN UINT8                        IpVersion
  )
{
  EFI_STATUS  Status;
  EFI_GUID    *DhcpServiceBindingGuid;
  EFI_GUID    *MtftpServiceBindingGuid;

  if (IpVersion == IP_VERSION_4) {
    if (PcdGet8 (PcdIPv4PXESupport) == PXE_DISABLED) {
      return EFI_UNSUPPORTED;
    }

    DhcpServiceBindingGuid  = &gEfiDhcp4ServiceBindingProtocolGuid;
    MtftpServiceBindingGuid = &gEfiMtftp4ServiceBindingProtocolGuid;

可以看到其依赖的是gEfiDhcp4ServiceBindingProtocolGuidgEfiMtftp4ServiceBindingProtocolGuid

PxeBcIp4DriverBindingStart

Start函数如下:

EFI_STATUS
EFIAPI
PxeBcIp4DriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
  )
{
  return PxeBcStart (
           This,
           ControllerHandle,
           RemainingDevicePath,
           IP_VERSION_4
           );
}

同样PxeBcStart()也包装了IPv4和IPv6的实现,这里只关注IPv4的版本。其流程大致如下:

  • 创建和初始化PXEBC_PRIVATE_DATA结构体。
  • 执行PxeBcCreateIp4Children(),该函数的大部分代码也是初始化PXEBC_PRIVATE_DATA结构体中的内容,其中最重要的是其成员Ip4Nic,它表示的是一个PXE子项。最终还会安装gEfiLoadFileProtocolGuidgEfiPxeBaseCodeProtocolGuid对应的两个Protocol,分别对应:
struct _EFI_LOAD_FILE_PROTOCOL {
  EFI_LOAD_FILE    LoadFile;
};

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

后面会进一步介绍这些Protocol。不过此前先要说明PXEBC_PRIVATE_DATA这个结构体。

PXEBC_PRIVATE_DATA

PXEBC_PRIVATE_DATAPxeBcStart()函数中创建:

EFI_STATUS
EFIAPI
PxeBcStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL,
  IN UINT8                        IpVersion
  )
{
  PXEBC_PRIVATE_DATA      *Private;
  EFI_STATUS              Status;
  PXEBC_PRIVATE_PROTOCOL  *Id;
  BOOLEAN                 FirstStart;

  FirstStart = FALSE;
  Status     = gBS->OpenProtocol (
                      ControllerHandle,
                      &gEfiCallerIdGuid,
                      (VOID **)&Id,
                      This->DriverBindingHandle,
                      ControllerHandle,
                      EFI_OPEN_PROTOCOL_GET_PROTOCOL
                      );
  if (!EFI_ERROR (Status)) {
    //
    // Skip the initialization if the driver has been started already.
    //
    Private = PXEBC_PRIVATE_DATA_FROM_ID (Id);
  } else {
    FirstStart = TRUE;
    //
    // If the driver has not been started yet, it should do initialization.
    //
    Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA));

而且不同于之前的网路协议中的数据结构会根据服务结构体来配置,PXE只有一个PXEBC_PRIVATE_DATA就足够了。该结构体位于NetworkPkg\UefiPxeBcDxe\PxeBcImpl.h:

struct _PXEBC_PRIVATE_DATA {
  UINT32                                       Signature;
  EFI_HANDLE                                   Controller;
  EFI_HANDLE                                   Image;

  PXEBC_PRIVATE_PROTOCOL                       Id;
  EFI_SIMPLE_NETWORK_PROTOCOL                  *Snp;

  PXEBC_VIRTUAL_NIC                            *Ip4Nic;
  PXEBC_VIRTUAL_NIC                            *Ip6Nic;

  EFI_HANDLE                                   ArpChild;
  EFI_HANDLE                                   Ip4Child;
  EFI_HANDLE                                   Dhcp4Child;
  EFI_HANDLE                                   Mtftp4Child;
  EFI_HANDLE                                   Udp4ReadChild;
  EFI_HANDLE                                   Udp4WriteChild;

  EFI_ARP_PROTOCOL                             *Arp;
  EFI_IP4_PROTOCOL                             *Ip4;
  EFI_IP4_CONFIG2_PROTOCOL                     *Ip4Config2;
  EFI_DHCP4_PROTOCOL                           *Dhcp4;
  EFI_MTFTP4_PROTOCOL                          *Mtftp4;
  EFI_UDP4_PROTOCOL                            *Udp4Read;
  EFI_UDP4_PROTOCOL                            *Udp4Write;

  EFI_HANDLE                                   Ip6Child;
  EFI_HANDLE                                   Dhcp6Child;
  EFI_HANDLE                                   Mtftp6Child;
  EFI_HANDLE                                   Udp6ReadChild;
  EFI_HANDLE                                   Udp6WriteChild;

  EFI_IP6_PROTOCOL                             *Ip6;
  EFI_IP6_CONFIG_PROTOCOL                      *Ip6Cfg;
  EFI_DHCP6_PROTOCOL                           *Dhcp6;
  EFI_MTFTP6_PROTOCOL                          *Mtftp6;
  EFI_UDP6_PROTOCOL                            *Udp6Read;
  EFI_UDP6_PROTOCOL                            *Udp6Write;
  EFI_DNS6_PROTOCOL                            *Dns6;

  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL    *Nii;
  EFI_PXE_BASE_CODE_PROTOCOL                   PxeBc;
  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL          LoadFileCallback;
  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL          *PxeBcCallback;
  EFI_DEVICE_PATH_PROTOCOL                     *DevicePath;

  EFI_PXE_BASE_CODE_MODE                       Mode;
  EFI_PXE_BASE_CODE_FUNCTION                   Function;
  UINT32                                       Ip6Policy;
  UINT32                                       SolicitTimes;
  UINT64                                       ElapsedTime;

  EFI_UDP4_CONFIG_DATA                         Udp4CfgData;
  EFI_UDP6_CONFIG_DATA                         Udp6CfgData;
  EFI_IP4_CONFIG_DATA                          Ip4CfgData;
  EFI_IP6_CONFIG_DATA                          Ip6CfgData;

  EFI_EVENT                                    UdpTimeOutEvent;
  EFI_EVENT                                    ArpUpdateEvent;
  EFI_IP4_COMPLETION_TOKEN                     IcmpToken;
  EFI_IP6_COMPLETION_TOKEN                     Icmp6Token;

  BOOLEAN                                      IsAddressOk;
  BOOLEAN                                      IsOfferSorted;
  BOOLEAN                                      IsProxyRecved;
  BOOLEAN                                      IsDoDiscover;

  EFI_IP_ADDRESS                               TmpStationIp;
  EFI_IP_ADDRESS                               StationIp;
  EFI_IP_ADDRESS                               SubnetMask;
  EFI_IP_ADDRESS                               GatewayIp;
  EFI_IP_ADDRESS                               ServerIp;
  EFI_IPv6_ADDRESS                             *DnsServer;
  UINT16                                       CurSrcPort;
  UINT32                                       IaId;

  UINT32                                       Ip4MaxPacketSize;
  UINT32                                       Ip6MaxPacketSize;
  UINT8                                        *BootFileName;
  UINTN                                        BootFileSize;
  UINTN                                        BlockSize;

  PXEBC_DHCP_PACKET_CACHE                      ProxyOffer;
  PXEBC_DHCP_PACKET_CACHE                      DhcpAck;
  PXEBC_DHCP_PACKET_CACHE                      PxeReply;
  EFI_DHCP6_PACKET                             *Dhcp6Request;
  EFI_DHCP4_PACKET                             SeedPacket;

  //
  // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer.
  //
  // It supposed that
  //
  //   OfferNum:    8
  //   OfferBuffer: [ProxyBinl, ProxyBinl, DhcpOnly, ProxyPxe10, DhcpOnly, DhcpPxe10, DhcpBinl, ProxyBinl]
  //   (OfferBuffer is 0-based.)
  //
  // And assume that (DhcpPxe10 is the first priority actually.)
  //
  //   SelectIndex:     2
  //   SelectProxyType: PXEBC_OFFER_TYPE_PROXY_BINL
  //   (SelectIndex is 1-based, and 0 means no one is selected.)
  //
  // So it should be
  //
  //                 DhcpOnly  DhcpPxe10  DhcpWfm11a  DhcpBinl  ProxyPxe10  ProxyWfm11a  ProxyBinl  Bootp
  //   OfferCount:  [    2(n),      1(n),       0(n),     1(n),       1(1),        0(1),      3(n),  1(1)]
  //
  //   OfferIndex: {[       2,         5,          0,        6,          3,           0,        *0,     0]
  //                [       4,         0,          0,        0,          0,           0,         1,     0]
  //                [       0,         0,          0,        0,          0,           0,         7,     0]
  //                ...                                                                                  ]}
  //   (OfferIndex is 0-based.)
  //
  //
  UINT32                     SelectIndex;
  UINT32                     SelectProxyType;
  PXEBC_DHCP_PACKET_CACHE    OfferBuffer[PXEBC_OFFER_MAX_NUM];
  UINT32                     OfferNum;
  UINT32                     OfferCount[PxeOfferTypeMax];
  UINT32                     OfferIndex[PxeOfferTypeMax][PXEBC_OFFER_MAX_NUM];
}

成员比较多,这里介绍其中比较重要的:

  • Id:它是一个Protocol,而且是以非指针的形式存在的,但是它并没有特别的实现,只是作为一个标记存在。
  • SnpSNP实例。
  • Ip4Nic:这是PXE的子项,其对应结构体:
struct _PXEBC_VIRTUAL_NIC {
  UINT32                      Signature;
  EFI_HANDLE                  Controller;
  EFI_LOAD_FILE_PROTOCOL      LoadFile;
  EFI_DEVICE_PATH_PROTOCOL    *DevicePath;
  PXEBC_PRIVATE_DATA          *Private;
};

它由PXE创建子项时初始化,其内容来自如下的函数PxeBcCreateIp4Children()

  Private->Ip4Nic->Private   = Private;
  Private->Ip4Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;

  ZeroMem (&Ip4Node, sizeof (IPv4_DEVICE_PATH));
  Ip4Node.Header.Type     = MESSAGING_DEVICE_PATH;
  Ip4Node.Header.SubType  = MSG_IPv4_DP;
  Ip4Node.StaticIpAddress = FALSE;
  SetDevicePathNodeLength (&Ip4Node.Header, sizeof (Ip4Node));
  Private->Ip4Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip4Node.Header);

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

Controller上还安装着一些后续需要使用到的Protocol:

  Status = gBS->InstallMultipleProtocolInterfaces (
                  &Private->Ip4Nic->Controller,
                  &gEfiDevicePathProtocolGuid,
                  Private->Ip4Nic->DevicePath,
                  &gEfiLoadFileProtocolGuid,
                  &Private->Ip4Nic->LoadFile,
                  &gEfiPxeBaseCodeProtocolGuid,
                  &Private->PxeBc,
                  NULL
                  );

  if (Private->Snp != NULL) {
    //
    // Install SNP protocol on purpose is for some OS loader backward
    // compatibility consideration.
    //
    Status = gBS->InstallProtocolInterface (
                    &Private->Ip4Nic->Controller,
                    &gEfiSimpleNetworkProtocolGuid,
                    EFI_NATIVE_INTERFACE,
                    Private->Snp
                    );
    if (EFI_ERROR (Status)) {
      goto ON_ERROR;
    }
  • XXXChild以及安装其上的Protocol:描述PXE需要使用到的底层网路协议的子项,从这里也可以看到PXE使用到的协议(注意这比Supported中的要多):
依赖
依赖
依赖
依赖
依赖
PXE
ARP
IP4
DHCP4
MTFTP4
UDP
  • PxeBc:对应EFI_PXE_BASE_CODE_PROTOCOL,后面会进一步介绍。
  • LoadFileCallbackPxeBcCallback:对应EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
struct _EFI_PXE_BASE_CODE_CALLBACK_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_CALLBACK    Callback;
};

这里的两个Callback是互斥的,只会使用到其中的一个,这可以从下面的代码看出来:

EFI_STATUS
PxeBcInstallCallback (
  IN OUT PXEBC_PRIVATE_DATA  *Private,
  OUT BOOLEAN                *NewMakeCallback
  )
{
  Status = gBS->HandleProtocol (
                  Private->Mode.UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->Controller,
                  &gEfiPxeBaseCodeCallbackProtocolGuid,
                  (VOID **)&Private->PxeBcCallback
                  );
  if (Status == EFI_UNSUPPORTED) {
    CopyMem (
      &Private->LoadFileCallback,
      &gPxeBcCallBackTemplate,
      sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)
      );

    //
    // Install a default callback if user didn't offer one.
    //
    Status = gBS->InstallProtocolInterface (
                    Private->Mode.UsingIpv6 ? &Private->Ip6Nic->Controller : &Private->Ip4Nic->Controller,
                    &gEfiPxeBaseCodeCallbackProtocolGuid,
                    EFI_NATIVE_INTERFACE,
                    &Private->LoadFileCallback
                    );

    (*NewMakeCallback) = (BOOLEAN)(Status == EFI_SUCCESS);

    Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);
    if (EFI_ERROR (Status)) {
      PxeBc->Stop (PxeBc);
      return Status;
    }
  }

  return EFI_SUCCESS;
}

这里首先是查看gEfiPxeBaseCodeCallbackProtocolGuid是否已经安装,如果安装了则赋值给PxeBcCallback,如果没有安装则使用默认的版本,它会放到LoadFileCallback这个成员中,然后执行SetParameters()函数,在该函数中:

EFI_STATUS
EFIAPI
EfiPxeBcSetParameters (
  IN EFI_PXE_BASE_CODE_PROTOCOL  *This,
  IN BOOLEAN                     *NewAutoArp         OPTIONAL,
  IN BOOLEAN                     *NewSendGUID        OPTIONAL,
  IN UINT8                       *NewTTL             OPTIONAL,
  IN UINT8                       *NewToS             OPTIONAL,
  IN BOOLEAN                     *NewMakeCallback    OPTIONAL
  )
{
  if (NewMakeCallback != NULL) {
    if (*NewMakeCallback) {
      //
      // Update the previous PxeBcCallback protocol.
      //
      Status = gBS->HandleProtocol (
                      Mode->UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->Controller,
                      &gEfiPxeBaseCodeCallbackProtocolGuid,
                      (VOID **)&Private->PxeBcCallback
                      );
}

到这里最终又赋值给了PxeBcCallback。这个Callback在如下的两个位置调用:

调用
调用
PxeBcDhcp4CallBack
PxeBcCallback
PxeBcMtftp4CheckPacket

它们分别在DHCP的配置和EFI_MTFTP4_TOKEN中使用:

  ///
  /// The callback function to intercept various events that occurred in
  /// the DHCP configuration process. Set to NULL to ignore all those events.
  ///
  EFI_DHCP4_CALLBACK         Dhcp4Callback;
  
  ///
  /// The pointer to the callback function to check the contents of the received packet.
  ///
  EFI_MTFTP4_CHECK_PACKET        CheckPacket;

而默认的回调函数是gPxeBcCallBackTemplate

EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  gPxeBcCallBackTemplate = {
  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,
  EfiPxeLoadFileCallback
};

EfiPxeLoadFileCallback()主要是一些显示输出和按键处理。

  • Mode:其结构体如下:
///
/// EFI_PXE_BASE_CODE_MODE.
/// The data values in this structure are read-only and
/// are updated by the code that produces the
/// EFI_PXE_BASE_CODE_PROTOCOL functions.
///
typedef struct {
  BOOLEAN                          Started;
  BOOLEAN                          Ipv6Available;
  BOOLEAN                          Ipv6Supported;
  BOOLEAN                          UsingIpv6;
  BOOLEAN                          BisSupported;
  BOOLEAN                          BisDetected;
  BOOLEAN                          AutoArp;
  BOOLEAN                          SendGUID;
  BOOLEAN                          DhcpDiscoverValid;
  BOOLEAN                          DhcpAckReceived;
  BOOLEAN                          ProxyOfferReceived;
  BOOLEAN                          PxeDiscoverValid;
  BOOLEAN                          PxeReplyReceived;
  BOOLEAN                          PxeBisReplyReceived;
  BOOLEAN                          IcmpErrorReceived;
  BOOLEAN                          TftpErrorReceived;
  BOOLEAN                          MakeCallbacks;
  UINT8                            TTL;
  UINT8                            ToS;
  EFI_IP_ADDRESS                   StationIp;
  EFI_IP_ADDRESS                   SubnetMask;
  EFI_PXE_BASE_CODE_PACKET         DhcpDiscover;
  EFI_PXE_BASE_CODE_PACKET         DhcpAck;
  EFI_PXE_BASE_CODE_PACKET         ProxyOffer;
  EFI_PXE_BASE_CODE_PACKET         PxeDiscover;
  EFI_PXE_BASE_CODE_PACKET         PxeReply;
  EFI_PXE_BASE_CODE_PACKET         PxeBisReply;
  EFI_PXE_BASE_CODE_IP_FILTER      IpFilter;
  UINT32                           ArpCacheEntries;
  EFI_PXE_BASE_CODE_ARP_ENTRY      ArpCache[EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES];
  UINT32                           RouteTableEntries;
  EFI_PXE_BASE_CODE_ROUTE_ENTRY    RouteTable[EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES];
  EFI_PXE_BASE_CODE_ICMP_ERROR     IcmpError;
  EFI_PXE_BASE_CODE_TFTP_ERROR     TftpError;
} EFI_PXE_BASE_CODE_MODE;

包含了PXE的一些属性和数据。

  • Function:对应的值:
///
/// Event type list for PXE Base Code Protocol function.
///
typedef enum {
  EFI_PXE_BASE_CODE_FUNCTION_FIRST,
  EFI_PXE_BASE_CODE_FUNCTION_DHCP,
  EFI_PXE_BASE_CODE_FUNCTION_DISCOVER,
  EFI_PXE_BASE_CODE_FUNCTION_MTFTP,
  EFI_PXE_BASE_CODE_FUNCTION_UDP_WRITE,
  EFI_PXE_BASE_CODE_FUNCTION_UDP_READ,
  EFI_PXE_BASE_CODE_FUNCTION_ARP,
  EFI_PXE_BASE_CODE_FUNCTION_IGMP,
  EFI_PXE_BASE_CODE_PXE_FUNCTION_LAST
} EFI_PXE_BASE_CODE_FUNCTION;

目前主要在Callback函数中用,即前面提到的EfiPxeLoadFileCallback()

  • XXXCfgData:一些配置参数。
  • UdpTimeOutEvent:关于该成员可以直接看代码中的说明:
  //
  // Create event for UdpRead/UdpWrite timeout since they are both blocking API.
  //
  Status = gBS->CreateEvent (
                  EVT_TIMER,
                  TPL_CALLBACK,
                  NULL,
                  NULL,
                  &Private->UdpTimeOutEvent
                  );
  • ArpUpdateEvent:用于更新ARP缓存的事件。

EFI_LOAD_FILE_PROTOCOL

该Protocol只有一个接口:

struct _EFI_LOAD_FILE_PROTOCOL {
  EFI_LOAD_FILE    LoadFile;
};

其实现是EfiPxeLoadFile(),流程如下:

EfiPxeLoadFile
PxeBcLoadBootFile
PxeBc->Mtftp
PxeBcInstallCallback
PxeBcDiscoverBootFile
PxeBcReadBootFileList
NetLibDetectMediaWaitTimeout
PxeBc->Start

从这里可以看出来该实现主要还是依赖于EFI_PXE_BASE_CODE_PROTOCOL

另外需要说明,EFI_LOAD_FILE_PROTOCOL并不是PXE特有的,所有支持下载Bootloader来启动操作系统的UEFI应用或驱动都可以实现该接口,这样EDK的基础代码就会为其创建启动项,从而从该设备启动操作系统。

;