Home | History | Annotate | Download | only in UefiPxeBcDxe
      1 /** @file
      2   This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.
      3 
      4   Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
      5 
      6   This program and the accompanying materials
      7   are licensed and made available under the terms and conditions of the BSD License
      8   which accompanies this distribution.  The full text of the license may be found at
      9   http://opensource.org/licenses/bsd-license.php.
     10 
     11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include "PxeBcImpl.h"
     17 
     18 
     19 /**
     20   Enables the use of the PXE Base Code Protocol functions.
     21 
     22   This function enables the use of the PXE Base Code Protocol functions. If the
     23   Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then
     24   EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted
     25   addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted
     26   addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported
     27   field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will
     28   be returned. If there is not enough memory or other resources to start the PXE
     29   Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the
     30   PXE Base Code Protocol will be started.
     31 
     32   @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
     33   @param[in]  UseIpv6           Specifies the type of IP addresses that are to be
     34                                 used during the session that is being started.
     35                                 Set to TRUE for IPv6, and FALSE for IPv4.
     36 
     37   @retval EFI_SUCCESS           The PXE Base Code Protocol was started.
     38   @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
     39   @retval EFI_UNSUPPORTED       UseIpv6 is TRUE, but the Ipv6Supported field of the
     40                                 EFI_PXE_BASE_CODE_MODE structure is FALSE.
     41   @retval EFI_ALREADY_STARTED   The PXE Base Code Protocol is already in the started state.
     42   @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
     43                                 EFI_PXE_BASE_CODE_PROTOCOL structure.
     44   @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory or other resources to start the
     45                                 PXE Base Code Protocol.
     46 
     47 **/
     48 EFI_STATUS
     49 EFIAPI
     50 EfiPxeBcStart (
     51   IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
     52   IN BOOLEAN                          UseIpv6
     53   )
     54 {
     55   PXEBC_PRIVATE_DATA      *Private;
     56   EFI_PXE_BASE_CODE_MODE  *Mode;
     57   UINTN                   Index;
     58   EFI_STATUS              Status;
     59 
     60   if (This == NULL) {
     61     return EFI_INVALID_PARAMETER;
     62   }
     63 
     64   Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
     65   Mode    = Private->PxeBc.Mode;
     66 
     67   if (Mode->Started) {
     68     return EFI_ALREADY_STARTED;
     69   }
     70 
     71   //
     72   // Detect whether using IPv6 or not, and set it into mode data.
     73   //
     74   if (UseIpv6 && Mode->Ipv6Available && Mode->Ipv6Supported && Private->Ip6Nic != NULL) {
     75     Mode->UsingIpv6 = TRUE;
     76   } else if (!UseIpv6 && Private->Ip4Nic != NULL) {
     77     Mode->UsingIpv6 = FALSE;
     78   } else {
     79     return EFI_UNSUPPORTED;
     80   }
     81 
     82   if (Mode->UsingIpv6) {
     83     AsciiPrint ("\n>>Start PXE over IPv6");
     84     //
     85     // Configure udp6 instance to receive data.
     86     //
     87     Status = Private->Udp6Read->Configure (
     88                                   Private->Udp6Read,
     89                                   &Private->Udp6CfgData
     90                                   );
     91     if (EFI_ERROR (Status)) {
     92       goto ON_ERROR;
     93     }
     94 
     95     //
     96     // Configure block size for TFTP as a default value to handle all link layers.
     97     //
     98     Private->BlockSize = (UINTN) (Private->Ip6MaxPacketSize -
     99                            PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);
    100 
    101     //
    102     // PXE over IPv6 starts here, initialize the fields and list header.
    103     //
    104     Private->Ip6Policy                          = PXEBC_IP6_POLICY_MAX;
    105     Private->ProxyOffer.Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;
    106     Private->DhcpAck.Dhcp6.Packet.Ack.Size      = PXEBC_DHCP6_PACKET_MAX_SIZE;
    107     Private->PxeReply.Dhcp6.Packet.Ack.Size     = PXEBC_DHCP6_PACKET_MAX_SIZE;
    108 
    109     for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {
    110       Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;
    111     }
    112 
    113     //
    114     // Create event and set status for token to capture ICMP6 error message.
    115     //
    116     Private->Icmp6Token.Status = EFI_NOT_READY;
    117     Status = gBS->CreateEvent (
    118                     EVT_NOTIFY_SIGNAL,
    119                     TPL_NOTIFY,
    120                     PxeBcIcmp6ErrorUpdate,
    121                     Private,
    122                     &Private->Icmp6Token.Event
    123                     );
    124     if (EFI_ERROR (Status)) {
    125       goto ON_ERROR;
    126     }
    127 
    128     //
    129     // Set Ip6 policy to Automatic to start the IP6 router discovery.
    130     //
    131     Status = PxeBcSetIp6Policy (Private);
    132     if (EFI_ERROR (Status)) {
    133       goto ON_ERROR;
    134     }
    135   } else {
    136     AsciiPrint ("\n>>Start PXE over IPv4");
    137     //
    138     // Configure udp4 instance to receive data.
    139     //
    140     Status = Private->Udp4Read->Configure (
    141                                   Private->Udp4Read,
    142                                   &Private->Udp4CfgData
    143                                   );
    144     if (EFI_ERROR (Status)) {
    145       goto ON_ERROR;
    146     }
    147 
    148     //
    149     // Configure block size for TFTP as a default value to handle all link layers.
    150     //
    151     Private->BlockSize = (UINTN) (Private->Ip4MaxPacketSize -
    152                            PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);
    153 
    154     //
    155     // PXE over IPv4 starts here, initialize the fields.
    156     //
    157     Private->ProxyOffer.Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;
    158     Private->DhcpAck.Dhcp4.Packet.Ack.Size      = PXEBC_DHCP4_PACKET_MAX_SIZE;
    159     Private->PxeReply.Dhcp4.Packet.Ack.Size     = PXEBC_DHCP4_PACKET_MAX_SIZE;
    160 
    161     for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {
    162       Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;
    163     }
    164 
    165     PxeBcSeedDhcp4Packet (&Private->SeedPacket, Private->Udp4Read);
    166 
    167     //
    168     // Create the event for Arp cache update.
    169     //
    170     Status = gBS->CreateEvent (
    171                     EVT_TIMER | EVT_NOTIFY_SIGNAL,
    172                     TPL_CALLBACK,
    173                     PxeBcArpCacheUpdate,
    174                     Private,
    175                     &Private->ArpUpdateEvent
    176                     );
    177     if (EFI_ERROR (Status)) {
    178       goto ON_ERROR;
    179     }
    180 
    181     //
    182     // Start a periodic timer by second to update Arp cache.
    183     //
    184     Status = gBS->SetTimer (
    185                     Private->ArpUpdateEvent,
    186                     TimerPeriodic,
    187                     TICKS_PER_SECOND
    188                     );
    189     if (EFI_ERROR (Status)) {
    190       goto ON_ERROR;
    191     }
    192 
    193     //
    194     // Create event and set status for token to capture ICMP error message.
    195     //
    196     Private->Icmp6Token.Status = EFI_NOT_READY;
    197     Status = gBS->CreateEvent (
    198                     EVT_NOTIFY_SIGNAL,
    199                     TPL_NOTIFY,
    200                     PxeBcIcmpErrorUpdate,
    201                     Private,
    202                     &Private->IcmpToken.Event
    203                     );
    204     if (EFI_ERROR (Status)) {
    205       goto ON_ERROR;
    206     }
    207 
    208     //
    209     //DHCP4 service allows only one of its children to be configured in
    210     //the active state, If the DHCP4 D.O.R.A started by IP4 auto
    211     //configuration and has not been completed, the Dhcp4 state machine
    212     //will not be in the right state for the PXE to start a new round D.O.R.A.
    213     //so we need to switch it's policy to static.
    214     //
    215     Status = PxeBcSetIp4Policy (Private);
    216     if (EFI_ERROR (Status)) {
    217       goto ON_ERROR;
    218     }
    219   }
    220 
    221   //
    222   // If PcdTftpBlockSize is set to non-zero, override the default value.
    223   //
    224   if (PcdGet64 (PcdTftpBlockSize) != 0) {
    225     Private->BlockSize   = (UINTN) PcdGet64 (PcdTftpBlockSize);
    226   }
    227 
    228   //
    229   // Create event for UdpRead/UdpWrite timeout since they are both blocking API.
    230   //
    231   Status = gBS->CreateEvent (
    232                   EVT_TIMER,
    233                   TPL_CALLBACK,
    234                   NULL,
    235                   NULL,
    236                   &Private->UdpTimeOutEvent
    237                   );
    238   if (EFI_ERROR (Status)) {
    239     goto ON_ERROR;
    240   }
    241 
    242   Private->IsAddressOk = FALSE;
    243   Mode->Started        = TRUE;
    244 
    245   return EFI_SUCCESS;
    246 
    247 ON_ERROR:
    248   if (Mode->UsingIpv6) {
    249     if (Private->Icmp6Token.Event != NULL) {
    250       gBS->CloseEvent (Private->Icmp6Token.Event);
    251       Private->Icmp6Token.Event = NULL;
    252     }
    253     Private->Udp6Read->Configure (Private->Udp6Read, NULL);
    254     Private->Ip6->Configure (Private->Ip6, NULL);
    255   } else {
    256     if (Private->ArpUpdateEvent != NULL) {
    257       gBS->CloseEvent (Private->ArpUpdateEvent);
    258       Private->ArpUpdateEvent = NULL;
    259     }
    260     if (Private->IcmpToken.Event != NULL) {
    261       gBS->CloseEvent (Private->IcmpToken.Event);
    262       Private->IcmpToken.Event = NULL;
    263     }
    264     Private->Udp4Read->Configure (Private->Udp4Read, NULL);
    265     Private->Ip4->Configure (Private->Ip4, NULL);
    266   }
    267   return Status;
    268 }
    269 
    270 
    271 /**
    272   Disable the use of the PXE Base Code Protocol functions.
    273 
    274   This function stops all activity on the network device. All the resources allocated
    275   in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is
    276   set to FALSE, and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE
    277   structure is already FALSE, then EFI_NOT_STARTED will be returned.
    278 
    279   @param[in]  This               Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
    280 
    281   @retval EFI_SUCCESS           The PXE Base Code Protocol was stopped.
    282   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is already in the stopped state.
    283   @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
    284                                 EFI_PXE_BASE_CODE_PROTOCOL structure.
    285   @retval Others
    286 
    287 **/
    288 EFI_STATUS
    289 EFIAPI
    290 EfiPxeBcStop (
    291   IN EFI_PXE_BASE_CODE_PROTOCOL       *This
    292   )
    293 {
    294   PXEBC_PRIVATE_DATA      *Private;
    295   EFI_PXE_BASE_CODE_MODE  *Mode;
    296   BOOLEAN                 Ipv6Supported;
    297   BOOLEAN                 Ipv6Available;
    298 
    299   if (This == NULL) {
    300     return EFI_INVALID_PARAMETER;
    301   }
    302 
    303   Private       = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
    304   Mode          = Private->PxeBc.Mode;
    305   Ipv6Supported = Mode->Ipv6Supported;
    306   Ipv6Available = Mode->Ipv6Available;
    307 
    308   if (!Mode->Started) {
    309     return EFI_NOT_STARTED;
    310   }
    311 
    312   if (Mode->UsingIpv6) {
    313     //
    314     // Configure all the instances for IPv6 as NULL.
    315     //
    316     ZeroMem (&Private->Udp6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));
    317     ZeroMem (&Private->Ip6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));
    318     Private->Dhcp6->Stop (Private->Dhcp6);
    319     Private->Dhcp6->Configure (Private->Dhcp6, NULL);
    320     Private->Udp6Write->Configure (Private->Udp6Write, NULL);
    321     Private->Udp6Read->Groups (Private->Udp6Read, FALSE, NULL);
    322     Private->Udp6Read->Configure (Private->Udp6Read, NULL);
    323     Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);
    324     Private->Ip6->Configure (Private->Ip6, NULL);
    325     PxeBcUnregisterIp6Address (Private);
    326     if (Private->Icmp6Token.Event != NULL) {
    327       gBS->CloseEvent (Private->Icmp6Token.Event);
    328       Private->Icmp6Token.Event = NULL;
    329     }
    330     if (Private->Dhcp6Request != NULL) {
    331       FreePool (Private->Dhcp6Request);
    332       Private->Dhcp6Request = NULL;
    333     }
    334     if (Private->BootFileName != NULL) {
    335       FreePool (Private->BootFileName);
    336       Private->BootFileName = NULL;
    337     }
    338   } else {
    339     //
    340     // Configure all the instances for IPv4 as NULL.
    341     //
    342     ZeroMem (&Private->Udp4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
    343     ZeroMem (&Private->Udp4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
    344     ZeroMem (&Private->Ip4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
    345     ZeroMem (&Private->Ip4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
    346     Private->Dhcp4->Stop (Private->Dhcp4);
    347     Private->Dhcp4->Configure (Private->Dhcp4, NULL);
    348     Private->Udp4Write->Configure (Private->Udp4Write, NULL);
    349     Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL);
    350     Private->Udp4Read->Configure (Private->Udp4Read, NULL);
    351     Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);
    352     Private->Ip4->Configure (Private->Ip4, NULL);
    353     if (Private->ArpUpdateEvent != NULL) {
    354       gBS->CloseEvent (Private->ArpUpdateEvent);
    355       Private->ArpUpdateEvent = NULL;
    356     }
    357     if (Private->IcmpToken.Event != NULL) {
    358       gBS->CloseEvent (Private->IcmpToken.Event);
    359       Private->IcmpToken.Event = NULL;
    360     }
    361     Private->BootFileName = NULL;
    362   }
    363 
    364   gBS->CloseEvent (Private->UdpTimeOutEvent);
    365   Private->CurSrcPort   = 0;
    366   Private->BootFileSize = 0;
    367   Private->SolicitTimes = 0;
    368   Private->ElapsedTime  = 0;
    369   ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS));
    370   ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS));
    371   ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS));
    372   ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));
    373 
    374   //
    375   // Reset the mode data.
    376   //
    377   ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE));
    378   Mode->Ipv6Available = Ipv6Available;
    379   Mode->Ipv6Supported = Ipv6Supported;
    380   Mode->AutoArp       = TRUE;
    381   Mode->TTL           = DEFAULT_TTL;
    382   Mode->ToS           = DEFAULT_ToS;
    383 
    384   return EFI_SUCCESS;
    385 }
    386 
    387 
    388 /**
    389   Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6
    390   S.A.R.R (solicit / advertise / request / reply) sequence.
    391 
    392   If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before
    393   they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will
    394   be tried in the order in which they are received. Please see the Preboot Execution
    395   Environment (PXE) Specification and Unified Extensible Firmware Interface (UEFI)
    396   Specification for additional details on the implementation of DHCP.
    397   If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
    398   then the DHCP sequence will be stopped and EFI_ABORTED will be returned.
    399 
    400   @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
    401   @param[in]  SortOffers        TRUE if the offers received should be sorted. Set to FALSE to
    402                                 try the offers in the order that they are received.
    403 
    404   @retval EFI_SUCCESS           Valid DHCP has completed.
    405   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
    406   @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
    407                                 EFI_PXE_BASE_CODE_PROTOCOL structure.
    408   @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
    409   @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory to complete the DHCP Protocol.
    410   @retval EFI_ABORTED           The callback function aborted the DHCP Protocol.
    411   @retval EFI_TIMEOUT           The DHCP Protocol timed out.
    412   @retval EFI_ICMP_ERROR        An ICMP error packet was received during the DHCP session.
    413   @retval EFI_NO_RESPONSE       Valid PXE offer was not received.
    414 
    415 **/
    416 EFI_STATUS
    417 EFIAPI
    418 EfiPxeBcDhcp (
    419   IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
    420   IN BOOLEAN                          SortOffers
    421   )
    422 {
    423   PXEBC_PRIVATE_DATA           *Private;
    424   EFI_PXE_BASE_CODE_MODE       *Mode;
    425   EFI_STATUS                   Status;
    426   EFI_PXE_BASE_CODE_IP_FILTER  IpFilter;
    427 
    428   if (This == NULL) {
    429     return EFI_INVALID_PARAMETER;
    430   }
    431 
    432   Status                  = EFI_SUCCESS;
    433   Private                 = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
    434   Mode                    = Private->PxeBc.Mode;
    435   Mode->IcmpErrorReceived = FALSE;
    436   Private->Function       = EFI_PXE_BASE_CODE_FUNCTION_DHCP;
    437   Private->IsOfferSorted  = SortOffers;
    438   Private->SolicitTimes   = 0;
    439   Private->ElapsedTime    = 0;
    440 
    441   if (!Mode->Started) {
    442     return EFI_NOT_STARTED;
    443   }
    444 
    445   if (Mode->UsingIpv6) {
    446 
    447     //
    448     // Stop Udp6Read instance
    449     //
    450     Private->Udp6Read->Configure (Private->Udp6Read, NULL);
    451 
    452     //
    453     // Start S.A.R.R. process to get a IPv6 address and other boot information.
    454     //
    455     Status = PxeBcDhcp6Sarr (Private, Private->Dhcp6);
    456   } else {
    457 
    458     //
    459     // Stop Udp4Read instance
    460     //
    461     Private->Udp4Read->Configure (Private->Udp4Read, NULL);
    462 
    463     //
    464     // Start D.O.R.A. process to get a IPv4 address and other boot information.
    465     //
    466     Status = PxeBcDhcp4Dora (Private, Private->Dhcp4);
    467   }
    468 
    469   //
    470   // Reconfigure the UDP instance with the default configuration.
    471   //
    472   if (Mode->UsingIpv6) {
    473     Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
    474   } else {
    475     Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData);
    476   }
    477   //
    478   // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
    479   // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
    480   //
    481   ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
    482   IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
    483   This->SetIpFilter (This, &IpFilter);
    484 
    485   return Status;
    486 }
    487 
    488 
    489 /**
    490   Attempts to complete the PXE Boot Server and/or boot image discovery sequence.
    491 
    492   This function attempts to complete the PXE Boot Server and/or boot image discovery
    493   sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the
    494   PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the
    495   EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the
    496   PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure
    497   will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE.
    498   In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[],
    499   has two uses: It is the Boot Server IP address list used for unicast discovery
    500   (if the UseUCast field is TRUE), and it is the list used for Boot Server verification
    501   (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure
    502   is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot
    503   Server reply of that type will be accepted. If the AcceptAnyResponse field is
    504   FALSE, only responses from Boot Servers with matching IP addresses will be accepted.
    505   This function can take at least 10 seconds to timeout and return control to the
    506   caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be
    507   returned. Please see the Preboot Execution Environment (PXE) Specification for
    508   additional details on the implementation of the Discovery sequence.
    509   If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
    510   then the Discovery sequence is stopped and EFI_ABORTED will be returned.
    511 
    512   @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
    513   @param[in]  Type              The type of bootstrap to perform.
    514   @param[in]  Layer             Pointer to the boot server layer number to discover, which must be
    515                                 PXE_BOOT_LAYER_INITIAL when a new server type is being
    516                                 discovered.
    517   @param[in]  UseBis            TRUE if Boot Integrity Services are to be used. FALSE otherwise.
    518   @param[in]  Info              Pointer to a data structure that contains additional information
    519                                 on the type of discovery operation that is to be performed.
    520                                 It is optional.
    521 
    522   @retval EFI_SUCCESS           The Discovery sequence has been completed.
    523   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
    524   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
    525   @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
    526   @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory to complete Discovery.
    527   @retval EFI_ABORTED           The callback function aborted the Discovery sequence.
    528   @retval EFI_TIMEOUT           The Discovery sequence timed out.
    529   @retval EFI_ICMP_ERROR        An ICMP error packet was received during the PXE discovery
    530                                 session.
    531 
    532 **/
    533 EFI_STATUS
    534 EFIAPI
    535 EfiPxeBcDiscover (
    536   IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
    537   IN UINT16                           Type,
    538   IN UINT16                           *Layer,
    539   IN BOOLEAN                          UseBis,
    540   IN EFI_PXE_BASE_CODE_DISCOVER_INFO  *Info   OPTIONAL
    541   )
    542 {
    543   PXEBC_PRIVATE_DATA              *Private;
    544   EFI_PXE_BASE_CODE_MODE          *Mode;
    545   EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;
    546   EFI_PXE_BASE_CODE_SRVLIST       *SrvList;
    547   PXEBC_BOOT_SVR_ENTRY            *BootSvrEntry;
    548   UINT16                          Index;
    549   EFI_STATUS                      Status;
    550   EFI_PXE_BASE_CODE_IP_FILTER     IpFilter;
    551   EFI_PXE_BASE_CODE_DISCOVER_INFO *NewCreatedInfo;
    552 
    553   if (This == NULL) {
    554     return EFI_INVALID_PARAMETER;
    555   }
    556 
    557   Private                 = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
    558   Mode                    = Private->PxeBc.Mode;
    559   Mode->IcmpErrorReceived = FALSE;
    560   BootSvrEntry            = NULL;
    561   SrvList                 = NULL;
    562   Status                  = EFI_DEVICE_ERROR;
    563   Private->Function       = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;
    564   NewCreatedInfo          = NULL;
    565 
    566   if (!Mode->Started) {
    567     return EFI_NOT_STARTED;
    568   }
    569 
    570   //
    571   // Station address should be ready before do discover.
    572   //
    573   if (!Private->IsAddressOk) {
    574     return EFI_INVALID_PARAMETER;
    575   }
    576 
    577   if (Mode->UsingIpv6) {
    578 
    579     //
    580     // Stop Udp6Read instance
    581     //
    582     Private->Udp6Read->Configure (Private->Udp6Read, NULL);
    583   } else {
    584 
    585     //
    586     // Stop Udp4Read instance
    587     //
    588     Private->Udp4Read->Configure (Private->Udp4Read, NULL);
    589   }
    590 
    591   //
    592   // There are 3 methods to get the information for discover.
    593   //
    594   ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO));
    595   if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) {
    596     //
    597     // 1. Take the previous setting as the discover info.
    598     //
    599     if (!Mode->PxeDiscoverValid ||
    600         !Mode->PxeReplyReceived ||
    601         (!Mode->PxeBisReplyReceived && UseBis)) {
    602       Status = EFI_INVALID_PARAMETER;
    603       goto ON_EXIT;
    604     }
    605 
    606     Info                         = &DefaultInfo;
    607     Info->IpCnt                  = 1;
    608     Info->UseUCast               = TRUE;
    609     SrvList                      = Info->SrvList;
    610     SrvList[0].Type              = Type;
    611     SrvList[0].AcceptAnyResponse = FALSE;
    612 
    613     CopyMem (&SrvList->IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
    614 
    615   } else if (Info == NULL) {
    616     //
    617     // 2. Extract the discover information from the cached packets if unspecified.
    618     //
    619     NewCreatedInfo = &DefaultInfo;
    620     Status = PxeBcExtractDiscoverInfo (Private, Type, &NewCreatedInfo, &BootSvrEntry, &SrvList);
    621     if (EFI_ERROR (Status)) {
    622       goto ON_EXIT;
    623     }
    624     ASSERT (NewCreatedInfo != NULL);
    625     Info = NewCreatedInfo;
    626   } else {
    627     //
    628     // 3. Take the pass-in information as the discover info, and validate the server list.
    629     //
    630     SrvList = Info->SrvList;
    631 
    632     if (!SrvList[0].AcceptAnyResponse) {
    633       for (Index = 1; Index < Info->IpCnt; Index++) {
    634         if (SrvList[Index].AcceptAnyResponse) {
    635           break;
    636         }
    637       }
    638       if (Index != Info->IpCnt) {
    639         //
    640         // It's invalid if the first server doesn't accecpt any response
    641         // but any of the other servers does accept any response.
    642         //
    643         Status = EFI_INVALID_PARAMETER;
    644         goto ON_EXIT;
    645       }
    646     }
    647   }
    648 
    649   //
    650   // Info and BootSvrEntry/SrvList are all ready by now, so execute discover by UniCast/BroadCast/MultiCast.
    651   //
    652   if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) ||
    653       (Info->MustUseList && Info->IpCnt == 0)) {
    654     Status = EFI_INVALID_PARAMETER;
    655     goto ON_EXIT;
    656   }
    657 
    658   Private->IsDoDiscover = TRUE;
    659 
    660   if (Info->UseMCast) {
    661     //
    662     // Do discover by multicast.
    663     //
    664     Status = PxeBcDiscoverBootServer (
    665                Private,
    666                Type,
    667                Layer,
    668                UseBis,
    669                &Info->ServerMCastIp,
    670                Info->IpCnt,
    671                SrvList
    672                );
    673 
    674   } else if (Info->UseBCast) {
    675     //
    676     // Do discover by broadcast, but only valid for IPv4.
    677     //
    678     ASSERT (!Mode->UsingIpv6);
    679     Status = PxeBcDiscoverBootServer (
    680                Private,
    681                Type,
    682                Layer,
    683                UseBis,
    684                NULL,
    685                Info->IpCnt,
    686                SrvList
    687                );
    688 
    689   } else if (Info->UseUCast) {
    690     //
    691     // Do discover by unicast.
    692     //
    693     for (Index = 0; Index < Info->IpCnt; Index++) {
    694       if (BootSvrEntry == NULL) {
    695         CopyMem (&Private->ServerIp, &SrvList[Index].IpAddr, sizeof (EFI_IP_ADDRESS));
    696       } else {
    697         ASSERT (!Mode->UsingIpv6);
    698         ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));
    699         CopyMem (&Private->ServerIp, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));
    700       }
    701 
    702       Status = PxeBcDiscoverBootServer (
    703                  Private,
    704                  Type,
    705                  Layer,
    706                  UseBis,
    707                  &Private->ServerIp,
    708                  Info->IpCnt,
    709                  SrvList
    710                  );
    711       }
    712   }
    713 
    714   if (!EFI_ERROR (Status)) {
    715     //
    716     // Parse the cached PXE reply packet, and store it into mode data if valid.
    717     //
    718     if (Mode->UsingIpv6) {
    719       Status = PxeBcParseDhcp6Packet (&Private->PxeReply.Dhcp6);
    720       if (!EFI_ERROR (Status)) {
    721         CopyMem (
    722           &Mode->PxeReply.Dhcpv6,
    723           &Private->PxeReply.Dhcp6.Packet.Ack.Dhcp6,
    724           Private->PxeReply.Dhcp6.Packet.Ack.Length
    725           );
    726         Mode->PxeReplyReceived = TRUE;
    727         Mode->PxeDiscoverValid = TRUE;
    728       }
    729     } else {
    730       Status = PxeBcParseDhcp4Packet (&Private->PxeReply.Dhcp4);
    731       if (!EFI_ERROR (Status)) {
    732         CopyMem (
    733           &Mode->PxeReply.Dhcpv4,
    734           &Private->PxeReply.Dhcp4.Packet.Ack.Dhcp4,
    735           Private->PxeReply.Dhcp4.Packet.Ack.Length
    736           );
    737         Mode->PxeReplyReceived = TRUE;
    738         Mode->PxeDiscoverValid = TRUE;
    739       }
    740     }
    741   }
    742 
    743 ON_EXIT:
    744 
    745   if (NewCreatedInfo != NULL && NewCreatedInfo != &DefaultInfo) {
    746     FreePool (NewCreatedInfo);
    747   }
    748 
    749   if (Mode->UsingIpv6) {
    750     Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
    751   } else {
    752     Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData);
    753   }
    754 
    755   //
    756   // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
    757   // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
    758   //
    759   ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
    760   IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
    761   This->SetIpFilter (This, &IpFilter);
    762 
    763   return Status;
    764 }
    765 
    766 
    767 /**
    768   Used to perform TFTP and MTFTP services.
    769 
    770   This function is used to perform TFTP and MTFTP services. This includes the
    771   TFTP operations to get the size of a file, read a directory, read a file, and
    772   write a file. It also includes the MTFTP operations to get the size of a file,
    773   read a directory, and read a file. The type of operation is specified by Operation.
    774   If the callback function that is invoked during the TFTP/MTFTP operation does
    775   not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will
    776   be returned.
    777   For read operations, the return data will be placed in the buffer specified by
    778   BufferPtr. If BufferSize is too small to contain the entire downloaded file,
    779   then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero,
    780   or the size of the requested file. (NOTE: the size of the requested file is only returned
    781   if the TFTP server supports TFTP options). If BufferSize is large enough for the
    782   read operation, then BufferSize will be set to the size of the downloaded file,
    783   and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services
    784   should use the get-file-size operations to determine the size of the downloaded
    785   file prior to using the read-file operations-especially when downloading large
    786   (greater than 64 MB) files-instead of making two calls to the read-file operation.
    787   Following this recommendation will save time if the file is larger than expected
    788   and the TFTP server does not support TFTP option extensions. Without TFTP option
    789   extension support, the client must download the entire file, counting and discarding
    790   the received packets, to determine the file size.
    791   For write operations, the data to be sent is in the buffer specified by BufferPtr.
    792   BufferSize specifies the number of bytes to send. If the write operation completes
    793   successfully, then EFI_SUCCESS will be returned.
    794   For TFTP "get file size" operations, the size of the requested file or directory
    795   is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server
    796   does not support options, the file will be downloaded into a bit bucket and the
    797   length of the downloaded file will be returned. For MTFTP "get file size" operations,
    798   if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED
    799   will be returned.
    800   This function can take up to 10 seconds to timeout and return control to the caller.
    801   If the TFTP sequence does not complete, EFI_TIMEOUT will be returned.
    802   If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
    803   then the TFTP sequence is stopped and EFI_ABORTED will be returned.
    804 
    805   @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
    806   @param[in]      Operation     The type of operation to perform.
    807   @param[in, out] BufferPtr     A pointer to the data buffer.
    808   @param[in]      Overwrite     Only used on write file operations. TRUE if a file on a remote
    809                                 server can be overwritten.
    810   @param[in, out] BufferSize    For get-file-size operations, *BufferSize returns the size of the
    811                                 requested file.
    812   @param[in]      BlockSize     The requested block size to be used during a TFTP transfer.
    813   @param[in]      ServerIp      The TFTP / MTFTP server IP address.
    814   @param[in]      Filename      A Null-terminated ASCII string that specifies a directory name
    815                                 or a file name.
    816   @param[in]      Info          Pointer to the MTFTP information.
    817   @param[in]      DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation.
    818 
    819   @retval EFI_SUCCESS           The TFTP/MTFTP operation was completed.
    820   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
    821   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
    822   @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
    823   @retval EFI_BUFFER_TOO_SMALL  The buffer is not large enough to complete the read operation.
    824   @retval EFI_ABORTED           The callback function aborted the TFTP/MTFTP operation.
    825   @retval EFI_TIMEOUT           The TFTP/MTFTP operation timed out.
    826   @retval EFI_ICMP_ERROR        An ICMP error packet was received during the MTFTP session.
    827   @retval EFI_TFTP_ERROR        A TFTP error packet was received during the MTFTP session.
    828 
    829 **/
    830 EFI_STATUS
    831 EFIAPI
    832 EfiPxeBcMtftp (
    833   IN     EFI_PXE_BASE_CODE_PROTOCOL       *This,
    834   IN     EFI_PXE_BASE_CODE_TFTP_OPCODE    Operation,
    835   IN OUT VOID                             *BufferPtr    OPTIONAL,
    836   IN     BOOLEAN                          Overwrite,
    837   IN OUT UINT64                           *BufferSize,
    838   IN     UINTN                            *BlockSize    OPTIONAL,
    839   IN     EFI_IP_ADDRESS                   *ServerIp,
    840   IN     UINT8                            *Filename,
    841   IN     EFI_PXE_BASE_CODE_MTFTP_INFO     *Info         OPTIONAL,
    842   IN     BOOLEAN                          DontUseBuffer
    843   )
    844 {
    845   PXEBC_PRIVATE_DATA              *Private;
    846   EFI_PXE_BASE_CODE_MODE          *Mode;
    847   EFI_MTFTP4_CONFIG_DATA          Mtftp4Config;
    848   EFI_MTFTP6_CONFIG_DATA          Mtftp6Config;
    849   VOID                            *Config;
    850   EFI_STATUS                      Status;
    851   EFI_PXE_BASE_CODE_IP_FILTER     IpFilter;
    852 
    853 
    854   if ((This == NULL) ||
    855       (Filename == NULL) ||
    856       (BufferSize == NULL) ||
    857       (ServerIp == NULL) ||
    858       ((BufferPtr == NULL) && DontUseBuffer) ||
    859       ((BlockSize != NULL) && (*BlockSize < PXE_MTFTP_DEFAULT_BLOCK_SIZE)) ||
    860       (!NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 0) && !NetIp6IsValidUnicast (&ServerIp->v6))) {
    861     return EFI_INVALID_PARAMETER;
    862   }
    863 
    864   Config    = NULL;
    865   Status    = EFI_DEVICE_ERROR;
    866   Private   = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
    867   Mode      = Private->PxeBc.Mode;
    868 
    869   if (Mode->UsingIpv6) {
    870     //
    871     // Set configuration data for Mtftp6 instance.
    872     //
    873     ZeroMem (&Mtftp6Config, sizeof (EFI_MTFTP6_CONFIG_DATA));
    874     Config                         = &Mtftp6Config;
    875     Mtftp6Config.TimeoutValue      = PXEBC_MTFTP_TIMEOUT;
    876     Mtftp6Config.TryCount          = PXEBC_MTFTP_RETRIES;
    877     CopyMem (&Mtftp6Config.StationIp, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
    878     CopyMem (&Mtftp6Config.ServerIp, &ServerIp->v6, sizeof (EFI_IPv6_ADDRESS));
    879     //
    880     // Stop Udp6Read instance
    881     //
    882     Private->Udp6Read->Configure (Private->Udp6Read, NULL);
    883   } else {
    884     //
    885     // Set configuration data for Mtftp4 instance.
    886     //
    887     ZeroMem (&Mtftp4Config, sizeof (EFI_MTFTP4_CONFIG_DATA));
    888     Config                         = &Mtftp4Config;
    889     Mtftp4Config.UseDefaultSetting = FALSE;
    890     Mtftp4Config.TimeoutValue      = PXEBC_MTFTP_TIMEOUT;
    891     Mtftp4Config.TryCount          = PXEBC_MTFTP_RETRIES;
    892     CopyMem (&Mtftp4Config.StationIp, &Private->StationIp.v4, sizeof (EFI_IPv4_ADDRESS));
    893     CopyMem (&Mtftp4Config.SubnetMask, &Private->SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));
    894     CopyMem (&Mtftp4Config.GatewayIp, &Private->GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));
    895     CopyMem (&Mtftp4Config.ServerIp, &ServerIp->v4, sizeof (EFI_IPv4_ADDRESS));
    896     //
    897     // Stop Udp4Read instance
    898     //
    899     Private->Udp4Read->Configure (Private->Udp4Read, NULL);
    900   }
    901 
    902   Mode->TftpErrorReceived = FALSE;
    903   Mode->IcmpErrorReceived = FALSE;
    904 
    905   switch (Operation) {
    906 
    907   case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE:
    908     //
    909     // Send TFTP request to get file size.
    910     //
    911     Status = PxeBcTftpGetFileSize (
    912                Private,
    913                Config,
    914                Filename,
    915                BlockSize,
    916                BufferSize
    917                );
    918 
    919     break;
    920 
    921   case EFI_PXE_BASE_CODE_TFTP_READ_FILE:
    922     //
    923     // Send TFTP request to read file.
    924     //
    925     Status = PxeBcTftpReadFile (
    926                Private,
    927                Config,
    928                Filename,
    929                BlockSize,
    930                BufferPtr,
    931                BufferSize,
    932                DontUseBuffer
    933                );
    934 
    935     break;
    936 
    937   case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:
    938     //
    939     // Send TFTP request to write file.
    940     //
    941     Status = PxeBcTftpWriteFile (
    942                Private,
    943                Config,
    944                Filename,
    945                Overwrite,
    946                BlockSize,
    947                BufferPtr,
    948                BufferSize
    949                );
    950 
    951     break;
    952 
    953   case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:
    954     //
    955     // Send TFTP request to read directory.
    956     //
    957     Status = PxeBcTftpReadDirectory (
    958                Private,
    959                Config,
    960                Filename,
    961                BlockSize,
    962                BufferPtr,
    963                BufferSize,
    964                DontUseBuffer
    965                );
    966 
    967     break;
    968 
    969   case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE:
    970   case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:
    971   case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:
    972     Status = EFI_UNSUPPORTED;
    973 
    974     break;
    975 
    976   default:
    977     Status = EFI_INVALID_PARAMETER;
    978 
    979     break;
    980   }
    981 
    982   if (Status == EFI_ICMP_ERROR) {
    983     Mode->IcmpErrorReceived = TRUE;
    984   }
    985 
    986   //
    987   // Reconfigure the UDP instance with the default configuration.
    988   //
    989   if (Mode->UsingIpv6) {
    990     Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
    991   } else {
    992     Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData);
    993   }
    994   //
    995   // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
    996   // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
    997   //
    998   ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
    999   IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
   1000   This->SetIpFilter (This, &IpFilter);
   1001 
   1002   return Status;
   1003 }
   1004 
   1005 
   1006 /**
   1007   Writes a UDP packet to the network interface.
   1008 
   1009   This function writes a UDP packet specified by the (optional HeaderPtr and)
   1010   BufferPtr parameters to the network interface. The UDP header is automatically
   1011   built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp,
   1012   SrcIp, and SrcPort to build this header. If the packet is successfully built and
   1013   transmitted through the network interface, then EFI_SUCCESS will be returned.
   1014   If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will
   1015   be returned. If an ICMP error occurs during the transmission of the packet, then
   1016   the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and
   1017   EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return
   1018   EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned.
   1019 
   1020   @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
   1021   @param[in]      OpFlags       The UDP operation flags.
   1022   @param[in]      DestIp        The destination IP address.
   1023   @param[in]      DestPort      The destination UDP port number.
   1024   @param[in]      GatewayIp     The gateway IP address.
   1025   @param[in]      SrcIp         The source IP address.
   1026   @param[in, out] SrcPort       The source UDP port number.
   1027   @param[in]      HeaderSize    An optional field which may be set to the length of a header
   1028                                 at HeaderPtr to be prefixed to the data at BufferPtr.
   1029   @param[in]  HeaderPtr         If HeaderSize is not NULL, a pointer to a header to be
   1030                                 prefixed to the data at BufferPtr.
   1031   @param[in]  BufferSize        A pointer to the size of the data at BufferPtr.
   1032   @param[in]  BufferPtr         A pointer to the data to be written.
   1033 
   1034   @retval EFI_SUCCESS           The UDP Write operation completed.
   1035   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
   1036   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
   1037   @retval EFI_BAD_BUFFER_SIZE   The buffer is too long to be transmitted.
   1038   @retval EFI_ABORTED           The callback function aborted the UDP Write operation.
   1039   @retval EFI_TIMEOUT           The UDP Write operation timed out.
   1040   @retval EFI_ICMP_ERROR        An ICMP error packet was received during the UDP write session.
   1041 
   1042 **/
   1043 EFI_STATUS
   1044 EFIAPI
   1045 EfiPxeBcUdpWrite (
   1046   IN     EFI_PXE_BASE_CODE_PROTOCOL       *This,
   1047   IN     UINT16                           OpFlags,
   1048   IN     EFI_IP_ADDRESS                   *DestIp,
   1049   IN     EFI_PXE_BASE_CODE_UDP_PORT       *DestPort,
   1050   IN     EFI_IP_ADDRESS                   *GatewayIp  OPTIONAL,
   1051   IN     EFI_IP_ADDRESS                   *SrcIp      OPTIONAL,
   1052   IN OUT EFI_PXE_BASE_CODE_UDP_PORT       *SrcPort    OPTIONAL,
   1053   IN     UINTN                            *HeaderSize OPTIONAL,
   1054   IN     VOID                             *HeaderPtr  OPTIONAL,
   1055   IN     UINTN                            *BufferSize,
   1056   IN     VOID                             *BufferPtr
   1057   )
   1058 {
   1059   PXEBC_PRIVATE_DATA        *Private;
   1060   EFI_PXE_BASE_CODE_MODE    *Mode;
   1061   EFI_UDP4_SESSION_DATA     Udp4Session;
   1062   EFI_UDP6_SESSION_DATA     Udp6Session;
   1063   EFI_STATUS                Status;
   1064   BOOLEAN                   DoNotFragment;
   1065 
   1066   if (This == NULL || DestIp == NULL || DestPort == NULL) {
   1067     return EFI_INVALID_PARAMETER;
   1068   }
   1069 
   1070   Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
   1071   Mode    = Private->PxeBc.Mode;
   1072 
   1073   if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT) != 0) {
   1074     DoNotFragment = FALSE;
   1075   } else {
   1076     DoNotFragment = TRUE;
   1077   }
   1078 
   1079   if (!Mode->UsingIpv6 && GatewayIp != NULL && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 0)) {
   1080     //
   1081     // Gateway is provided but it's not a unicast IPv4 address, while it will be ignored for IPv6.
   1082     //
   1083     return EFI_INVALID_PARAMETER;
   1084   }
   1085 
   1086   if (HeaderSize != NULL && (*HeaderSize == 0 || HeaderPtr == NULL)) {
   1087     return EFI_INVALID_PARAMETER;
   1088   }
   1089 
   1090   if (BufferSize == NULL || (*BufferSize != 0 && BufferPtr == NULL)) {
   1091     return EFI_INVALID_PARAMETER;
   1092   }
   1093 
   1094   if (!Mode->Started) {
   1095     return EFI_NOT_STARTED;
   1096   }
   1097 
   1098   if (!Private->IsAddressOk && SrcIp == NULL) {
   1099     return EFI_INVALID_PARAMETER;
   1100   }
   1101 
   1102   if (Private->CurSrcPort == 0 ||
   1103       (SrcPort != NULL && *SrcPort != Private->CurSrcPort)) {
   1104     //
   1105     // Reconfigure UDPv4/UDPv6 for UdpWrite if the source port changed.
   1106     //
   1107     if (SrcPort != NULL) {
   1108       Private->CurSrcPort = *SrcPort;
   1109     }
   1110   }
   1111 
   1112   if (Mode->UsingIpv6) {
   1113     Status = PxeBcConfigUdp6Write (
   1114                Private->Udp6Write,
   1115                &Private->StationIp.v6,
   1116                &Private->CurSrcPort
   1117                );
   1118   } else {
   1119     //
   1120     // Configure the UDPv4 instance with gateway information from DHCP server as default.
   1121     //
   1122     Status = PxeBcConfigUdp4Write (
   1123                Private->Udp4Write,
   1124                &Private->StationIp.v4,
   1125                &Private->SubnetMask.v4,
   1126                &Private->GatewayIp.v4,
   1127                &Private->CurSrcPort,
   1128                DoNotFragment
   1129                );
   1130   }
   1131 
   1132   if (EFI_ERROR (Status)) {
   1133     Private->CurSrcPort = 0;
   1134     return EFI_INVALID_PARAMETER;
   1135   } else if (SrcPort != NULL) {
   1136     *SrcPort = Private->CurSrcPort;
   1137   }
   1138 
   1139   //
   1140   // Start a timer as timeout event for this blocking API.
   1141   //
   1142   gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);
   1143 
   1144   if (Mode->UsingIpv6) {
   1145     //
   1146     // Construct UDPv6 session data.
   1147     //
   1148     ZeroMem (&Udp6Session, sizeof (EFI_UDP6_SESSION_DATA));
   1149     CopyMem (&Udp6Session.DestinationAddress, DestIp, sizeof (EFI_IPv6_ADDRESS));
   1150     Udp6Session.DestinationPort = *DestPort;
   1151     if (SrcIp != NULL) {
   1152       CopyMem (&Udp6Session.SourceAddress, SrcIp, sizeof (EFI_IPv6_ADDRESS));
   1153     }
   1154     if (SrcPort != NULL) {
   1155       Udp6Session.SourcePort = *SrcPort;
   1156     }
   1157 
   1158     Status = PxeBcUdp6Write (
   1159                Private->Udp6Write,
   1160                &Udp6Session,
   1161                Private->UdpTimeOutEvent,
   1162                HeaderSize,
   1163                HeaderPtr,
   1164                BufferSize,
   1165                BufferPtr
   1166                );
   1167   } else {
   1168     //
   1169     // Construct UDPv4 session data.
   1170     //
   1171     ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA));
   1172     CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
   1173     Udp4Session.DestinationPort = *DestPort;
   1174     if (SrcIp != NULL) {
   1175       CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS));
   1176     }
   1177     if (SrcPort != NULL) {
   1178       Udp4Session.SourcePort = *SrcPort;
   1179     }
   1180     //
   1181     // Override the gateway information if user specified.
   1182     //
   1183     Status = PxeBcUdp4Write (
   1184                Private->Udp4Write,
   1185                &Udp4Session,
   1186                Private->UdpTimeOutEvent,
   1187                (EFI_IPv4_ADDRESS *) GatewayIp,
   1188                HeaderSize,
   1189                HeaderPtr,
   1190                BufferSize,
   1191                BufferPtr
   1192                );
   1193   }
   1194 
   1195   gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);
   1196 
   1197 
   1198   //
   1199   // Reset the UdpWrite instance.
   1200   //
   1201   if (Mode->UsingIpv6) {
   1202     Private->Udp6Write->Configure (Private->Udp6Write, NULL);
   1203   } else {
   1204     Private->Udp4Write->Configure (Private->Udp4Write, NULL);
   1205   }
   1206 
   1207   return Status;
   1208 }
   1209 
   1210 
   1211 /**
   1212   Reads a UDP packet from the network interface.
   1213 +
   1214   This function reads a UDP packet from a network interface. The data contents
   1215   are returned in (the optional HeaderPtr and) BufferPtr, and the size of the
   1216   buffer received is returned in BufferSize . If the input BufferSize is smaller
   1217   than the UDP packet received (less optional HeaderSize), it will be set to the
   1218   required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the
   1219   contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is
   1220   successfully received, then EFI_SUCCESS will be returned, and the information
   1221   from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if
   1222   they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort,
   1223   SrcIp, and SrcPort input values, different types of UDP packet receive filtering
   1224   will be performed. The following tables summarize these receive filter operations.
   1225 
   1226   @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
   1227   @param[in]      OpFlags       The UDP operation flags.
   1228   @param[in, out] DestIp        The destination IP address.
   1229   @param[in, out] DestPort      The destination UDP port number.
   1230   @param[in, out] SrcIp         The source IP address.
   1231   @param[in, out] SrcPort       The source UDP port number.
   1232   @param[in]      HeaderSize    An optional field which may be set to the length of a
   1233                                 header at HeaderPtr to be prefixed to the data at BufferPtr.
   1234   @param[in]      HeaderPtr     If HeaderSize is not NULL, a pointer to a header to be
   1235                                 prefixed to the data at BufferPtr.
   1236   @param[in, out] BufferSize    A pointer to the size of the data at BufferPtr.
   1237   @param[in]      BufferPtr     A pointer to the data to be read.
   1238 
   1239   @retval EFI_SUCCESS           The UDP Read operation was completed.
   1240   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
   1241   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
   1242   @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
   1243   @retval EFI_BUFFER_TOO_SMALL  The packet is larger than Buffer can hold.
   1244   @retval EFI_ABORTED           The callback function aborted the UDP Read operation.
   1245   @retval EFI_TIMEOUT           The UDP Read operation timed out.
   1246 
   1247 **/
   1248 EFI_STATUS
   1249 EFIAPI
   1250 EfiPxeBcUdpRead (
   1251   IN     EFI_PXE_BASE_CODE_PROTOCOL   *This,
   1252   IN     UINT16                       OpFlags,
   1253   IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,
   1254   IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,
   1255   IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,
   1256   IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL,
   1257   IN     UINTN                        *HeaderSize  OPTIONAL,
   1258   IN     VOID                         *HeaderPtr   OPTIONAL,
   1259   IN OUT UINTN                        *BufferSize,
   1260   IN     VOID                         *BufferPtr
   1261   )
   1262 {
   1263   PXEBC_PRIVATE_DATA          *Private;
   1264   EFI_PXE_BASE_CODE_MODE      *Mode;
   1265   EFI_UDP4_COMPLETION_TOKEN   Udp4Token;
   1266   EFI_UDP6_COMPLETION_TOKEN   Udp6Token;
   1267   EFI_UDP4_RECEIVE_DATA       *Udp4Rx;
   1268   EFI_UDP6_RECEIVE_DATA       *Udp6Rx;
   1269   EFI_STATUS                  Status;
   1270   BOOLEAN                     IsDone;
   1271   BOOLEAN                     IsMatched;
   1272   UINTN                       CopiedLen;
   1273   UINTN                       HeaderLen;
   1274   UINTN                       HeaderCopiedLen;
   1275   UINTN                       BufferCopiedLen;
   1276   UINT32                      FragmentLength;
   1277   UINTN                       FragmentIndex;
   1278   UINT8                       *FragmentBuffer;
   1279 
   1280   if (This == NULL) {
   1281     return EFI_INVALID_PARAMETER;
   1282   }
   1283 
   1284   Private   = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
   1285   Mode      = Private->PxeBc.Mode;
   1286   IsDone    = FALSE;
   1287   IsMatched = FALSE;
   1288   Udp4Rx    = NULL;
   1289   Udp6Rx    = NULL;
   1290 
   1291   if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) == 0 && DestPort == NULL) ||
   1292       ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) == 0 && SrcIp == NULL) ||
   1293       ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) == 0 && SrcPort == NULL)) {
   1294     return EFI_INVALID_PARAMETER;
   1295   }
   1296 
   1297   if ((HeaderSize != NULL && *HeaderSize == 0) || (HeaderSize != NULL && HeaderPtr == NULL)) {
   1298     return EFI_INVALID_PARAMETER;
   1299   }
   1300 
   1301   if ((BufferSize == NULL) || (BufferPtr == NULL)) {
   1302     return EFI_INVALID_PARAMETER;
   1303   }
   1304 
   1305   if (!Mode->Started) {
   1306     return EFI_NOT_STARTED;
   1307   }
   1308 
   1309   ZeroMem (&Udp6Token, sizeof (EFI_UDP6_COMPLETION_TOKEN));
   1310   ZeroMem (&Udp4Token, sizeof (EFI_UDP4_COMPLETION_TOKEN));
   1311 
   1312   if (Mode->UsingIpv6) {
   1313     Status = gBS->CreateEvent (
   1314                     EVT_NOTIFY_SIGNAL,
   1315                     TPL_NOTIFY,
   1316                     PxeBcCommonNotify,
   1317                     &IsDone,
   1318                     &Udp6Token.Event
   1319                     );
   1320     if (EFI_ERROR (Status)) {
   1321       return EFI_OUT_OF_RESOURCES;
   1322     }
   1323   } else {
   1324     Status = gBS->CreateEvent (
   1325                     EVT_NOTIFY_SIGNAL,
   1326                     TPL_NOTIFY,
   1327                     PxeBcCommonNotify,
   1328                     &IsDone,
   1329                     &Udp4Token.Event
   1330                     );
   1331     if (EFI_ERROR (Status)) {
   1332       return EFI_OUT_OF_RESOURCES;
   1333     }
   1334   }
   1335 
   1336   //
   1337   // Start a timer as timeout event for this blocking API.
   1338   //
   1339   gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);
   1340   Mode->IcmpErrorReceived = FALSE;
   1341 
   1342   //
   1343   // Read packet by Udp4Read/Udp6Read until matched or timeout.
   1344   //
   1345   while (!IsMatched && !EFI_ERROR (Status)) {
   1346     if (Mode->UsingIpv6) {
   1347       Status = PxeBcUdp6Read (
   1348                  Private->Udp6Read,
   1349                  &Udp6Token,
   1350                  Mode,
   1351                  Private->UdpTimeOutEvent,
   1352                  OpFlags,
   1353                  &IsDone,
   1354                  &IsMatched,
   1355                  DestIp,
   1356                  DestPort,
   1357                  SrcIp,
   1358                  SrcPort
   1359                  );
   1360     } else {
   1361       Status = PxeBcUdp4Read (
   1362                  Private->Udp4Read,
   1363                  &Udp4Token,
   1364                  Mode,
   1365                  Private->UdpTimeOutEvent,
   1366                  OpFlags,
   1367                  &IsDone,
   1368                  &IsMatched,
   1369                  DestIp,
   1370                  DestPort,
   1371                  SrcIp,
   1372                  SrcPort
   1373                  );
   1374     }
   1375   }
   1376 
   1377   if (Status == EFI_ICMP_ERROR ||
   1378       Status == EFI_NETWORK_UNREACHABLE ||
   1379       Status == EFI_HOST_UNREACHABLE ||
   1380       Status == EFI_PROTOCOL_UNREACHABLE ||
   1381       Status == EFI_PORT_UNREACHABLE) {
   1382     //
   1383     // Get different return status for icmp error from Udp, refers to UEFI spec.
   1384     //
   1385     Mode->IcmpErrorReceived = TRUE;
   1386   }
   1387   gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);
   1388 
   1389   if (IsMatched) {
   1390     //
   1391     // Copy the rececived packet to user if matched by filter.
   1392     //
   1393     if (Mode->UsingIpv6) {
   1394       Udp6Rx = Udp6Token.Packet.RxData;
   1395       ASSERT (Udp6Rx != NULL);
   1396 
   1397       HeaderLen = 0;
   1398       if (HeaderSize != NULL) {
   1399         HeaderLen = MIN (*HeaderSize, Udp6Rx->DataLength);
   1400       }
   1401 
   1402       if (Udp6Rx->DataLength - HeaderLen > *BufferSize) {
   1403         Status = EFI_BUFFER_TOO_SMALL;
   1404       } else {
   1405         if (HeaderSize != NULL) {
   1406           *HeaderSize = HeaderLen;
   1407         }
   1408         *BufferSize = Udp6Rx->DataLength - HeaderLen;
   1409 
   1410         HeaderCopiedLen = 0;
   1411         BufferCopiedLen = 0;
   1412         for (FragmentIndex = 0; FragmentIndex < Udp6Rx->FragmentCount; FragmentIndex++) {
   1413           FragmentLength = Udp6Rx->FragmentTable[FragmentIndex].FragmentLength;
   1414           FragmentBuffer = Udp6Rx->FragmentTable[FragmentIndex].FragmentBuffer;
   1415           if (HeaderCopiedLen + FragmentLength < HeaderLen) {
   1416             //
   1417             // Copy the header part of received data.
   1418             //
   1419             CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, FragmentLength);
   1420             HeaderCopiedLen += FragmentLength;
   1421           } else if (HeaderCopiedLen < HeaderLen) {
   1422             //
   1423             // Copy the header part of received data.
   1424             //
   1425             CopiedLen = HeaderLen - HeaderCopiedLen;
   1426             CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, CopiedLen);
   1427             HeaderCopiedLen += CopiedLen;
   1428 
   1429             //
   1430             // Copy the other part of received data.
   1431             //
   1432             CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer + CopiedLen, FragmentLength - CopiedLen);
   1433             BufferCopiedLen += (FragmentLength - CopiedLen);
   1434           } else {
   1435             //
   1436             // Copy the other part of received data.
   1437             //
   1438             CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer, FragmentLength);
   1439             BufferCopiedLen += FragmentLength;
   1440           }
   1441         }
   1442       }
   1443       //
   1444       // Recycle the receiving buffer after copy to user.
   1445       //
   1446       gBS->SignalEvent (Udp6Rx->RecycleSignal);
   1447     } else {
   1448       Udp4Rx = Udp4Token.Packet.RxData;
   1449       ASSERT (Udp4Rx != NULL);
   1450 
   1451       HeaderLen = 0;
   1452       if (HeaderSize != NULL) {
   1453         HeaderLen = MIN (*HeaderSize, Udp4Rx->DataLength);
   1454       }
   1455 
   1456       if (Udp4Rx->DataLength - HeaderLen > *BufferSize) {
   1457         Status = EFI_BUFFER_TOO_SMALL;
   1458       } else {
   1459         if (HeaderSize != NULL) {
   1460           *HeaderSize = HeaderLen;
   1461         }
   1462         *BufferSize = Udp4Rx->DataLength - HeaderLen;
   1463 
   1464         HeaderCopiedLen = 0;
   1465         BufferCopiedLen = 0;
   1466         for (FragmentIndex = 0; FragmentIndex < Udp4Rx->FragmentCount; FragmentIndex++) {
   1467           FragmentLength = Udp4Rx->FragmentTable[FragmentIndex].FragmentLength;
   1468           FragmentBuffer = Udp4Rx->FragmentTable[FragmentIndex].FragmentBuffer;
   1469           if (HeaderCopiedLen + FragmentLength < HeaderLen) {
   1470             //
   1471             // Copy the header part of received data.
   1472             //
   1473             CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, FragmentLength);
   1474             HeaderCopiedLen += FragmentLength;
   1475           } else if (HeaderCopiedLen < HeaderLen) {
   1476             //
   1477             // Copy the header part of received data.
   1478             //
   1479             CopiedLen = HeaderLen - HeaderCopiedLen;
   1480             CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, CopiedLen);
   1481             HeaderCopiedLen += CopiedLen;
   1482 
   1483             //
   1484             // Copy the other part of received data.
   1485             //
   1486             CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer + CopiedLen, FragmentLength - CopiedLen);
   1487             BufferCopiedLen += (FragmentLength - CopiedLen);
   1488           } else {
   1489             //
   1490             // Copy the other part of received data.
   1491             //
   1492             CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer, FragmentLength);
   1493             BufferCopiedLen += FragmentLength;
   1494           }
   1495         }
   1496       }
   1497       //
   1498       // Recycle the receiving buffer after copy to user.
   1499       //
   1500       gBS->SignalEvent (Udp4Rx->RecycleSignal);
   1501     }
   1502   }
   1503 
   1504   if (Mode->UsingIpv6) {
   1505     Private->Udp6Read->Cancel (Private->Udp6Read, &Udp6Token);
   1506     gBS->CloseEvent (Udp6Token.Event);
   1507   } else {
   1508     Private->Udp4Read->Cancel (Private->Udp4Read, &Udp4Token);
   1509     gBS->CloseEvent (Udp4Token.Event);
   1510   }
   1511 
   1512   return Status;
   1513 }
   1514 
   1515 
   1516 /**
   1517   Updates the IP receive filters of a network device and enables software filtering.
   1518 
   1519   The NewFilter field is used to modify the network device's current IP receive
   1520   filter settings and to enable a software filter. This function updates the IpFilter
   1521   field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter.
   1522   The software filter is used when the USE_FILTER in OpFlags is set to UdpRead().
   1523   The current hardware filter remains in effect no matter what the settings of OpFlags.
   1524   This is so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those
   1525   packets whose reception is enabled in hardware-physical NIC address (unicast),
   1526   broadcast address, logical address or addresses (multicast), or all (promiscuous).
   1527   UdpRead() does not modify the IP filter settings.
   1528   Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive
   1529   filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
   1530   If an application or driver wishes to preserve the IP receive filter settings,
   1531   it will have to preserve the IP receive filter settings before these calls, and
   1532   use SetIpFilter() to restore them after the calls. If incompatible filtering is
   1533   requested (for example, PROMISCUOUS with anything else), or if the device does not
   1534   support a requested filter setting and it cannot be accommodated in software
   1535   (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned.
   1536   The IPlist field is used to enable IPs other than the StationIP. They may be
   1537   multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP,
   1538   then both the StationIP and the IPs from the IPlist will be used.
   1539 
   1540   @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
   1541   @param[in]  NewFilter         Pointer to the new set of IP receive filters.
   1542 
   1543   @retval EFI_SUCCESS           The IP receive filter settings were updated.
   1544   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
   1545   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
   1546 
   1547 **/
   1548 EFI_STATUS
   1549 EFIAPI
   1550 EfiPxeBcSetIpFilter (
   1551   IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
   1552   IN EFI_PXE_BASE_CODE_IP_FILTER      *NewFilter
   1553   )
   1554 {
   1555   EFI_STATUS                Status;
   1556   PXEBC_PRIVATE_DATA        *Private;
   1557   EFI_PXE_BASE_CODE_MODE    *Mode;
   1558   EFI_UDP4_CONFIG_DATA      *Udp4Cfg;
   1559   EFI_UDP6_CONFIG_DATA      *Udp6Cfg;
   1560   UINTN                     Index;
   1561   BOOLEAN                   NeedPromiscuous;
   1562   BOOLEAN                   AcceptPromiscuous;
   1563   BOOLEAN                   AcceptBroadcast;
   1564   BOOLEAN                   MultiCastUpdate;
   1565 
   1566   if (This == NULL || NewFilter == NULL) {
   1567     return EFI_INVALID_PARAMETER;
   1568   }
   1569 
   1570   Private         = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
   1571   Mode            = Private->PxeBc.Mode;
   1572   Status          = EFI_SUCCESS;
   1573   NeedPromiscuous = FALSE;
   1574 
   1575   if (!Mode->Started) {
   1576     return EFI_NOT_STARTED;
   1577   }
   1578 
   1579   for (Index = 0; Index < NewFilter->IpCnt; Index++) {
   1580     ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);
   1581     if (!Mode->UsingIpv6 &&
   1582         IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) {
   1583       //
   1584       // IPv4 broadcast address should not be in IP filter.
   1585       //
   1586       return EFI_INVALID_PARAMETER;
   1587     }
   1588     if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&
   1589         (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) ||
   1590          NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6))) {
   1591       //
   1592       // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IPv4/IPv6 address
   1593       // is in IpList, promiscuous mode is needed.
   1594       //
   1595       NeedPromiscuous = TRUE;
   1596     }
   1597   }
   1598 
   1599   AcceptPromiscuous = FALSE;
   1600   AcceptBroadcast   = FALSE;
   1601   MultiCastUpdate   = FALSE;
   1602 
   1603   if (NeedPromiscuous ||
   1604       (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0 ||
   1605       (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) {
   1606     //
   1607     // Configure UDPv4/UDPv6 as promiscuous mode to receive all packets.
   1608     //
   1609     AcceptPromiscuous = TRUE;
   1610   } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) {
   1611     //
   1612     // Configure UDPv4 to receive all broadcast packets.
   1613     //
   1614     AcceptBroadcast  = TRUE;
   1615   }
   1616 
   1617   //
   1618   // In multicast condition when Promiscuous FALSE and IpCnt no-zero.
   1619   // Here check if there is any update of the multicast ip address. If yes,
   1620   // we need leave the old multicast group (by Config UDP instance to NULL),
   1621   // and join the new multicast group.
   1622   //
   1623   if (!AcceptPromiscuous) {
   1624     if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) {
   1625       if (Mode->IpFilter.IpCnt != NewFilter->IpCnt) {
   1626         MultiCastUpdate = TRUE;
   1627       } else if (CompareMem (Mode->IpFilter.IpList, NewFilter->IpList, NewFilter->IpCnt * sizeof (EFI_IP_ADDRESS)) != 0 ) {
   1628         MultiCastUpdate = TRUE;
   1629       }
   1630     }
   1631   }
   1632 
   1633   if (!Mode->UsingIpv6) {
   1634     //
   1635     // Check whether we need reconfigure the UDP4 instance.
   1636     //
   1637     Udp4Cfg = &Private->Udp4CfgData;
   1638     if ((AcceptPromiscuous != Udp4Cfg->AcceptPromiscuous)   ||
   1639     	  (AcceptBroadcast != Udp4Cfg->AcceptBroadcast)     || MultiCastUpdate) {
   1640       //
   1641       // Clear the UDP4 instance configuration, all joined groups will be left
   1642       // during the operation.
   1643       //
   1644       Private->Udp4Read->Configure (Private->Udp4Read, NULL);
   1645 
   1646       //
   1647       // Configure the UDP instance with the new configuration.
   1648       //
   1649       Udp4Cfg->AcceptPromiscuous = AcceptPromiscuous;
   1650       Udp4Cfg->AcceptBroadcast   = AcceptBroadcast;
   1651       Status = Private->Udp4Read->Configure (Private->Udp4Read, Udp4Cfg);
   1652       if (EFI_ERROR (Status)) {
   1653         return Status;
   1654       }
   1655 
   1656       //
   1657       // In not Promiscuous mode, need to join the new multicast group.
   1658       //
   1659       if (!AcceptPromiscuous) {
   1660         for (Index = 0; Index < NewFilter->IpCnt; ++Index) {
   1661           if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) {
   1662             //
   1663             // Join the mutilcast group.
   1664             //
   1665             Status = Private->Udp4Read->Groups (Private->Udp4Read, TRUE, &NewFilter->IpList[Index].v4);
   1666             if (EFI_ERROR (Status)) {
   1667               return Status;
   1668             }
   1669           }
   1670         }
   1671       }
   1672     }
   1673   } else {
   1674     //
   1675     // Check whether we need reconfigure the UDP6 instance.
   1676     //
   1677     Udp6Cfg = &Private->Udp6CfgData;
   1678     if ((AcceptPromiscuous != Udp6Cfg->AcceptPromiscuous) || MultiCastUpdate) {
   1679       //
   1680       // Clear the UDP6 instance configuration, all joined groups will be left
   1681       // during the operation.
   1682       //
   1683       Private->Udp6Read->Configure (Private->Udp6Read, NULL);
   1684 
   1685       //
   1686       // Configure the UDP instance with the new configuration.
   1687       //
   1688       Udp6Cfg->AcceptPromiscuous = AcceptPromiscuous;
   1689       Status = Private->Udp6Read->Configure (Private->Udp6Read, Udp6Cfg);
   1690       if (EFI_ERROR (Status)) {
   1691         return Status;
   1692       }
   1693 
   1694       //
   1695       // In not Promiscuous mode, need to join the new multicast group.
   1696       //
   1697       if (!AcceptPromiscuous) {
   1698         for (Index = 0; Index < NewFilter->IpCnt; ++Index) {
   1699           if (IP6_IS_MULTICAST (&NewFilter->IpList[Index].v6)) {
   1700             //
   1701             // Join the mutilcast group.
   1702             //
   1703             Status = Private->Udp6Read->Groups (Private->Udp6Read, TRUE, &NewFilter->IpList[Index].v6);
   1704             if (EFI_ERROR (Status)) {
   1705               return Status;
   1706             }
   1707           }
   1708         }
   1709       }
   1710     }
   1711   }
   1712 
   1713   //
   1714   // Save the new IP filter into mode data.
   1715   //
   1716   CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter));
   1717 
   1718   return Status;
   1719 }
   1720 
   1721 
   1722 /**
   1723   Uses the ARP protocol to resolve a MAC address. It is not supported for IPv6.
   1724 
   1725   This function uses the ARP protocol to resolve a MAC address. The IP address specified
   1726   by IpAddr is used to resolve a MAC address. If the ARP protocol succeeds in resolving
   1727   the specified address, then the ArpCacheEntries and ArpCache fields of the mode data
   1728   are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved
   1729   MAC address is placed there as well.  If the PXE Base Code protocol is in the
   1730   stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters
   1731   a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is
   1732   returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
   1733   then EFI_ABORTED is returned.
   1734 
   1735   @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
   1736   @param[in]  IpAddr            Pointer to the IP address that is used to resolve a MAC address.
   1737   @param[in]  MacAddr           If not NULL, a pointer to the MAC address that was resolved with the
   1738                                 ARP protocol.
   1739 
   1740   @retval EFI_SUCCESS           The IP or MAC address was resolved.
   1741   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
   1742   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
   1743   @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
   1744   @retval EFI_ICMP_ERROR        An error occur with the ICMP packet message.
   1745 
   1746 **/
   1747 EFI_STATUS
   1748 EFIAPI
   1749 EfiPxeBcArp (
   1750   IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
   1751   IN EFI_IP_ADDRESS                   *IpAddr,
   1752   IN EFI_MAC_ADDRESS                  *MacAddr OPTIONAL
   1753   )
   1754 {
   1755   PXEBC_PRIVATE_DATA      *Private;
   1756   EFI_PXE_BASE_CODE_MODE  *Mode;
   1757   EFI_EVENT               ResolvedEvent;
   1758   EFI_STATUS              Status;
   1759   EFI_MAC_ADDRESS         TempMac;
   1760   EFI_MAC_ADDRESS         ZeroMac;
   1761   BOOLEAN                 IsResolved;
   1762 
   1763   if (This == NULL || IpAddr == NULL) {
   1764     return EFI_INVALID_PARAMETER;
   1765   }
   1766 
   1767   Private       = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
   1768   Mode          = Private->PxeBc.Mode;
   1769   ResolvedEvent = NULL;
   1770   Status        = EFI_SUCCESS;
   1771   IsResolved    = FALSE;
   1772 
   1773   if (!Mode->Started) {
   1774     return EFI_NOT_STARTED;
   1775   }
   1776 
   1777   if (Mode->UsingIpv6) {
   1778     return EFI_UNSUPPORTED;
   1779   }
   1780 
   1781   //
   1782   // Station address should be ready before do arp.
   1783   //
   1784   if (!Private->IsAddressOk) {
   1785     return EFI_INVALID_PARAMETER;
   1786   }
   1787 
   1788   Mode->IcmpErrorReceived = FALSE;
   1789   ZeroMem (&TempMac, sizeof (EFI_MAC_ADDRESS));
   1790   ZeroMem (&ZeroMac, sizeof (EFI_MAC_ADDRESS));
   1791 
   1792   if (!Mode->AutoArp) {
   1793     //
   1794     // If AutoArp is FALSE, only search in the current Arp cache.
   1795     //
   1796     PxeBcArpCacheUpdate (NULL, Private);
   1797     if (!PxeBcCheckArpCache (Mode, &IpAddr->v4, &TempMac)) {
   1798       Status = EFI_DEVICE_ERROR;
   1799       goto ON_EXIT;
   1800     }
   1801   } else {
   1802     Status = gBS->CreateEvent (
   1803                     EVT_NOTIFY_SIGNAL,
   1804                     TPL_NOTIFY,
   1805                     PxeBcCommonNotify,
   1806                     &IsResolved,
   1807                     &ResolvedEvent
   1808                     );
   1809     if (EFI_ERROR (Status)) {
   1810       goto ON_EXIT;
   1811     }
   1812 
   1813     //
   1814     // If AutoArp is TRUE, try to send Arp request on initiative.
   1815     //
   1816     Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, ResolvedEvent, &TempMac);
   1817     if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {
   1818       goto ON_EXIT;
   1819     }
   1820 
   1821     while (!IsResolved) {
   1822       if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {
   1823         break;
   1824       }
   1825     }
   1826     if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {
   1827       Status = EFI_SUCCESS;
   1828     } else {
   1829       Status = EFI_TIMEOUT;
   1830     }
   1831   }
   1832 
   1833   //
   1834   // Copy the Mac address to user if needed.
   1835   //
   1836   if (MacAddr != NULL && !EFI_ERROR (Status)) {
   1837     CopyMem (MacAddr, &TempMac, sizeof (EFI_MAC_ADDRESS));
   1838   }
   1839 
   1840 ON_EXIT:
   1841   if (ResolvedEvent != NULL) {
   1842     gBS->CloseEvent (ResolvedEvent);
   1843   }
   1844   return Status;
   1845 }
   1846 
   1847 
   1848 /**
   1849   Updates the parameters that affect the operation of the PXE Base Code Protocol.
   1850 
   1851   This function sets parameters that affect the operation of the PXE Base Code Protocol.
   1852   The parameter specified by NewAutoArp is used to control the generation of ARP
   1853   protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated
   1854   as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP
   1855   Protocol packets will be generated. In this case, the only mappings that are
   1856   available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure.
   1857   If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol
   1858   service, then the service will fail. This function updates the AutoArp field of
   1859   the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp.
   1860   The SetParameters() call must be invoked after a Callback Protocol is installed
   1861   to enable the use of callbacks.
   1862 
   1863   @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
   1864   @param[in]  NewAutoArp        If not NULL, a pointer to a value that specifies whether to replace the
   1865                                 current value of AutoARP.
   1866   @param[in]  NewSendGUID       If not NULL, a pointer to a value that specifies whether to replace the
   1867                                 current value of SendGUID.
   1868   @param[in]  NewTTL            If not NULL, a pointer to be used in place of the current value of TTL,
   1869                                 the "time to live" field of the IP header.
   1870   @param[in]  NewToS            If not NULL, a pointer to be used in place of the current value of ToS,
   1871                                 the "type of service" field of the IP header.
   1872   @param[in]  NewMakeCallback   If not NULL, a pointer to a value that specifies whether to replace the
   1873                                 current value of the MakeCallback field of the Mode structure.
   1874 
   1875   @retval EFI_SUCCESS           The new parameters values were updated.
   1876   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
   1877   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
   1878 
   1879 **/
   1880 EFI_STATUS
   1881 EFIAPI
   1882 EfiPxeBcSetParameters (
   1883   IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
   1884   IN BOOLEAN                          *NewAutoArp         OPTIONAL,
   1885   IN BOOLEAN                          *NewSendGUID        OPTIONAL,
   1886   IN UINT8                            *NewTTL             OPTIONAL,
   1887   IN UINT8                            *NewToS             OPTIONAL,
   1888   IN BOOLEAN                          *NewMakeCallback    OPTIONAL
   1889   )
   1890 {
   1891   PXEBC_PRIVATE_DATA      *Private;
   1892   EFI_PXE_BASE_CODE_MODE  *Mode;
   1893   EFI_GUID                SystemGuid;
   1894   EFI_STATUS              Status;
   1895 
   1896   if (This == NULL) {
   1897     return EFI_INVALID_PARAMETER;
   1898   }
   1899 
   1900   Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
   1901   Mode    = Private->PxeBc.Mode;
   1902 
   1903   if (!Mode->Started) {
   1904     return EFI_NOT_STARTED;
   1905   }
   1906 
   1907   if (NewMakeCallback != NULL) {
   1908     if (*NewMakeCallback) {
   1909       //
   1910       // Update the previous PxeBcCallback protocol.
   1911       //
   1912       Status = gBS->HandleProtocol (
   1913                       Private->Controller,
   1914                       &gEfiPxeBaseCodeCallbackProtocolGuid,
   1915                       (VOID **) &Private->PxeBcCallback
   1916                       );
   1917 
   1918       if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) {
   1919         return EFI_INVALID_PARAMETER;
   1920       }
   1921     } else {
   1922       Private->PxeBcCallback = NULL;
   1923     }
   1924     Mode->MakeCallbacks = *NewMakeCallback;
   1925   }
   1926 
   1927   if (NewSendGUID != NULL) {
   1928     if (*NewSendGUID && EFI_ERROR (NetLibGetSystemGuid (&SystemGuid))) {
   1929       return EFI_INVALID_PARAMETER;
   1930     }
   1931     Mode->SendGUID = *NewSendGUID;
   1932   }
   1933 
   1934   if (NewAutoArp != NULL) {
   1935     Mode->AutoArp = *NewAutoArp;
   1936   }
   1937 
   1938   if (NewTTL != NULL) {
   1939     Mode->TTL = *NewTTL;
   1940   }
   1941 
   1942   if (NewToS != NULL) {
   1943     Mode->ToS = *NewToS;
   1944   }
   1945 
   1946   return EFI_SUCCESS;
   1947 }
   1948 
   1949 
   1950 /**
   1951   Updates the station IP address and/or subnet mask values of a network device.
   1952 
   1953   This function updates the station IP address and/or subnet mask values of a network
   1954   device. The NewStationIp field is used to modify the network device's current IP address.
   1955   If NewStationIP is NULL, then the current IP address will not be modified. Otherwise,
   1956   this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure
   1957   with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet
   1958   mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified.
   1959   Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE
   1960   structure with NewSubnetMask.
   1961 
   1962   @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
   1963   @param[in]  NewStationIp      Pointer to the new IP address to be used by the network device.
   1964   @param[in]  NewSubnetMask     Pointer to the new subnet mask to be used by the network device.
   1965 
   1966   @retval EFI_SUCCESS           The new station IP address and/or subnet mask were updated.
   1967   @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
   1968   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
   1969 
   1970 **/
   1971 EFI_STATUS
   1972 EFIAPI
   1973 EfiPxeBcSetStationIP (
   1974   IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
   1975   IN EFI_IP_ADDRESS                   *NewStationIp    OPTIONAL,
   1976   IN EFI_IP_ADDRESS                   *NewSubnetMask   OPTIONAL
   1977   )
   1978 {
   1979   EFI_STATUS              Status;
   1980   PXEBC_PRIVATE_DATA      *Private;
   1981   EFI_PXE_BASE_CODE_MODE  *Mode;
   1982   EFI_ARP_CONFIG_DATA     ArpConfigData;
   1983 
   1984   if (This == NULL) {
   1985     return EFI_INVALID_PARAMETER;
   1986   }
   1987 
   1988   if (NewStationIp != NULL &&
   1989       (!NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0) &&
   1990        !NetIp6IsValidUnicast (&NewStationIp->v6))) {
   1991     return EFI_INVALID_PARAMETER;
   1992   }
   1993 
   1994   Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
   1995   Mode    = Private->PxeBc.Mode;
   1996   Status  = EFI_SUCCESS;
   1997 
   1998   if (!Mode->UsingIpv6 &&
   1999       NewSubnetMask != NULL &&
   2000       !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) {
   2001     return EFI_INVALID_PARAMETER;
   2002   }
   2003 
   2004   if (!Mode->Started) {
   2005     return EFI_NOT_STARTED;
   2006   }
   2007 
   2008   if (Mode->UsingIpv6 && NewStationIp != NULL) {
   2009     //
   2010     // Set the IPv6 address by Ip6Config protocol.
   2011     //
   2012     Status = PxeBcRegisterIp6Address (Private, &NewStationIp->v6);
   2013     if (EFI_ERROR (Status)) {
   2014       goto ON_EXIT;
   2015     }
   2016   } else if (!Mode->UsingIpv6 && NewStationIp != NULL) {
   2017     //
   2018     // Configure the corresponding ARP with the IPv4 address.
   2019     //
   2020     ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA));
   2021 
   2022     ArpConfigData.SwAddressType   = 0x0800;
   2023     ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS);
   2024     ArpConfigData.StationAddress  = &NewStationIp->v4;
   2025 
   2026     Private->Arp->Configure (Private->Arp, NULL);
   2027     Private->Arp->Configure (Private->Arp, &ArpConfigData);
   2028 
   2029     if (NewSubnetMask != NULL) {
   2030       Mode->RouteTableEntries                = 1;
   2031       Mode->RouteTable[0].IpAddr.Addr[0]     = NewStationIp->Addr[0] & NewSubnetMask->Addr[0];
   2032       Mode->RouteTable[0].SubnetMask.Addr[0] = NewSubnetMask->Addr[0];
   2033       Mode->RouteTable[0].GwAddr.Addr[0]     = 0;
   2034     }
   2035 
   2036     Private->IsAddressOk = TRUE;
   2037   }
   2038 
   2039   if (NewStationIp != NULL) {
   2040     CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));
   2041     CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));
   2042   }
   2043 
   2044   if (!Mode->UsingIpv6 && NewSubnetMask != NULL) {
   2045     CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS));
   2046     CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS));
   2047   }
   2048 
   2049   Status = PxeBcFlushStationIp (Private, NewStationIp, NewSubnetMask);
   2050 ON_EXIT:
   2051   return Status;
   2052 }
   2053 
   2054 
   2055 /**
   2056   Updates the contents of the cached DHCP and Discover packets.
   2057 
   2058   The pointers to the new packets are used to update the contents of the cached
   2059   packets in the EFI_PXE_BASE_CODE_MODE structure.
   2060 
   2061   @param[in]  This                   Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
   2062   @param[in]  NewDhcpDiscoverValid   Pointer to a value that will replace the current
   2063                                      DhcpDiscoverValid field.
   2064   @param[in]  NewDhcpAckReceived     Pointer to a value that will replace the current
   2065                                      DhcpAckReceived field.
   2066   @param[in]  NewProxyOfferReceived  Pointer to a value that will replace the current
   2067                                      ProxyOfferReceived field.
   2068   @param[in]  NewPxeDiscoverValid    Pointer to a value that will replace the current
   2069                                      ProxyOfferReceived field.
   2070   @param[in]  NewPxeReplyReceived    Pointer to a value that will replace the current
   2071                                      PxeReplyReceived field.
   2072   @param[in]  NewPxeBisReplyReceived Pointer to a value that will replace the current
   2073                                      PxeBisReplyReceived field.
   2074   @param[in]  NewDhcpDiscover        Pointer to the new cached DHCP Discover packet contents.
   2075   @param[in]  NewDhcpAck             Pointer to the new cached DHCP Ack packet contents.
   2076   @param[in]  NewProxyOffer          Pointer to the new cached Proxy Offer packet contents.
   2077   @param[in]  NewPxeDiscover         Pointer to the new cached PXE Discover packet contents.
   2078   @param[in]  NewPxeReply            Pointer to the new cached PXE Reply packet contents.
   2079   @param[in]  NewPxeBisReply         Pointer to the new cached PXE BIS Reply packet contents.
   2080 
   2081   @retval EFI_SUCCESS            The cached packet contents were updated.
   2082   @retval EFI_NOT_STARTED        The PXE Base Code Protocol is in the stopped state.
   2083   @retval EFI_INVALID_PARAMETER  This is NULL or does not point to a valid
   2084                                  EFI_PXE_BASE_CODE_PROTOCOL structure.
   2085 
   2086 **/
   2087 EFI_STATUS
   2088 EFIAPI
   2089 EfiPxeBcSetPackets (
   2090   IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
   2091   IN BOOLEAN                          *NewDhcpDiscoverValid      OPTIONAL,
   2092   IN BOOLEAN                          *NewDhcpAckReceived        OPTIONAL,
   2093   IN BOOLEAN                          *NewProxyOfferReceived     OPTIONAL,
   2094   IN BOOLEAN                          *NewPxeDiscoverValid       OPTIONAL,
   2095   IN BOOLEAN                          *NewPxeReplyReceived       OPTIONAL,
   2096   IN BOOLEAN                          *NewPxeBisReplyReceived    OPTIONAL,
   2097   IN EFI_PXE_BASE_CODE_PACKET         *NewDhcpDiscover           OPTIONAL,
   2098   IN EFI_PXE_BASE_CODE_PACKET         *NewDhcpAck                OPTIONAL,
   2099   IN EFI_PXE_BASE_CODE_PACKET         *NewProxyOffer             OPTIONAL,
   2100   IN EFI_PXE_BASE_CODE_PACKET         *NewPxeDiscover            OPTIONAL,
   2101   IN EFI_PXE_BASE_CODE_PACKET         *NewPxeReply               OPTIONAL,
   2102   IN EFI_PXE_BASE_CODE_PACKET         *NewPxeBisReply            OPTIONAL
   2103   )
   2104 {
   2105   PXEBC_PRIVATE_DATA      *Private;
   2106   EFI_PXE_BASE_CODE_MODE  *Mode;
   2107 
   2108   if (This == NULL) {
   2109     return EFI_INVALID_PARAMETER;
   2110   }
   2111 
   2112   Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
   2113   Mode    = Private->PxeBc.Mode;
   2114 
   2115   if (!Mode->Started) {
   2116     return EFI_NOT_STARTED;
   2117   }
   2118 
   2119   if (NewDhcpDiscoverValid != NULL) {
   2120     Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid;
   2121   }
   2122 
   2123   if (NewDhcpAckReceived != NULL) {
   2124     Mode->DhcpAckReceived = *NewDhcpAckReceived;
   2125   }
   2126 
   2127   if (NewProxyOfferReceived != NULL) {
   2128     Mode->ProxyOfferReceived = *NewProxyOfferReceived;
   2129   }
   2130 
   2131   if (NewPxeDiscoverValid != NULL) {
   2132     Mode->PxeDiscoverValid = *NewPxeDiscoverValid;
   2133   }
   2134 
   2135   if (NewPxeReplyReceived != NULL) {
   2136     Mode->PxeReplyReceived = *NewPxeReplyReceived;
   2137   }
   2138 
   2139   if (NewPxeBisReplyReceived != NULL) {
   2140     Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived;
   2141   }
   2142 
   2143   if (NewDhcpDiscover != NULL) {
   2144     CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
   2145   }
   2146 
   2147   if (NewDhcpAck != NULL) {
   2148     CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET));
   2149   }
   2150 
   2151   if (NewProxyOffer != NULL) {
   2152     CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET));
   2153   }
   2154 
   2155   if (NewPxeDiscover != NULL) {
   2156     CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
   2157   }
   2158 
   2159   if (NewPxeReply != NULL) {
   2160     CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
   2161   }
   2162 
   2163   if (NewPxeBisReply != NULL) {
   2164     CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
   2165   }
   2166 
   2167   return EFI_SUCCESS;
   2168 }
   2169 
   2170 EFI_PXE_BASE_CODE_PROTOCOL  gPxeBcProtocolTemplate = {
   2171   EFI_PXE_BASE_CODE_PROTOCOL_REVISION,
   2172   EfiPxeBcStart,
   2173   EfiPxeBcStop,
   2174   EfiPxeBcDhcp,
   2175   EfiPxeBcDiscover,
   2176   EfiPxeBcMtftp,
   2177   EfiPxeBcUdpWrite,
   2178   EfiPxeBcUdpRead,
   2179   EfiPxeBcSetIpFilter,
   2180   EfiPxeBcArp,
   2181   EfiPxeBcSetParameters,
   2182   EfiPxeBcSetStationIP,
   2183   EfiPxeBcSetPackets,
   2184   NULL
   2185 };
   2186 
   2187 
   2188 /**
   2189   Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has
   2190   received, or is waiting to receive a packet.
   2191 
   2192   This function is invoked when the PXE Base Code Protocol is about to transmit, has received,
   2193   or is waiting to receive a packet. Parameters Function and Received specify the type of event.
   2194   Parameters PacketLen and Packet specify the packet that generated the event. If these fields
   2195   are zero and NULL respectively, then this is a status update callback. If the operation specified
   2196   by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation
   2197   specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to
   2198   the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms.
   2199   The SetParameters() function must be called after a Callback Protocol is installed to enable the
   2200   use of callbacks.
   2201 
   2202   @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance.
   2203   @param[in]  Function          The PXE Base Code Protocol function that is waiting for an event.
   2204   @param[in]  Received          TRUE if the callback is being invoked due to a receive event. FALSE if
   2205                                 the callback is being invoked due to a transmit event.
   2206   @param[in]  PacketLength      The length, in bytes, of Packet. This field will have a value of zero if
   2207                                 this is a wait for receive event.
   2208   @param[in]  PacketPtr         If Received is TRUE, a pointer to the packet that was just received;
   2209                                 otherwise a pointer to the packet that is about to be transmitted.
   2210 
   2211   @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE If Function specifies a continue operation.
   2212   @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT    If Function specifies an abort operation.
   2213 
   2214 **/
   2215 EFI_PXE_BASE_CODE_CALLBACK_STATUS
   2216 EFIAPI
   2217 EfiPxeLoadFileCallback (
   2218   IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  *This,
   2219   IN EFI_PXE_BASE_CODE_FUNCTION           Function,
   2220   IN BOOLEAN                              Received,
   2221   IN UINT32                               PacketLength,
   2222   IN EFI_PXE_BASE_CODE_PACKET             *PacketPtr     OPTIONAL
   2223   )
   2224 {
   2225   EFI_INPUT_KEY       Key;
   2226   EFI_STATUS          Status;
   2227 
   2228   //
   2229   // Catch Ctrl-C or ESC to abort.
   2230   //
   2231   Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   2232 
   2233   if (!EFI_ERROR (Status)) {
   2234 
   2235     if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) {
   2236 
   2237       return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
   2238     }
   2239   }
   2240   //
   2241   // No print if receive packet
   2242   //
   2243   if (Received) {
   2244     return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
   2245   }
   2246   //
   2247   // Print only for three functions
   2248   //
   2249   switch (Function) {
   2250 
   2251   case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:
   2252     //
   2253     // Print only for open MTFTP packets, not every MTFTP packets
   2254     //
   2255     if (PacketLength != 0 && PacketPtr != NULL) {
   2256       if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {
   2257         return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
   2258       }
   2259     }
   2260     break;
   2261 
   2262   case EFI_PXE_BASE_CODE_FUNCTION_DHCP:
   2263   case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:
   2264     break;
   2265 
   2266   default:
   2267     return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
   2268   }
   2269 
   2270   if (PacketLength != 0 && PacketPtr != NULL) {
   2271     //
   2272     // Print '.' when transmit a packet
   2273     //
   2274     AsciiPrint (".");
   2275   }
   2276 
   2277   return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
   2278 }
   2279 
   2280 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate = {
   2281   EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,
   2282   EfiPxeLoadFileCallback
   2283 };
   2284 
   2285 
   2286 /**
   2287   Causes the driver to load a specified file.
   2288 
   2289   @param[in]      This                Protocol instance pointer.
   2290   @param[in]      FilePath            The device specific path of the file to load.
   2291   @param[in]      BootPolicy          If TRUE, indicates that the request originates from the
   2292                                       boot manager is attempting to load FilePath as a boot
   2293                                       selection. If FALSE, then FilePath must match an exact file
   2294                                       to be loaded.
   2295   @param[in, out] BufferSize          On input the size of Buffer in bytes. On output with a return
   2296                                       code of EFI_SUCCESS, the amount of data transferred to
   2297                                       Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
   2298                                       the size of Buffer required to retrieve the requested file.
   2299   @param[in]      Buffer              The memory buffer to transfer the file to. IF Buffer is NULL,
   2300                                       then no the size of the requested file is returned in
   2301                                       BufferSize.
   2302 
   2303   @retval EFI_SUCCESS                 The file was loaded.
   2304   @retval EFI_UNSUPPORTED             The device does not support the provided BootPolicy.
   2305   @retval EFI_INVALID_PARAMETER       FilePath is not a valid device path, or
   2306                                       BufferSize is NULL.
   2307   @retval EFI_NO_MEDIA                No medium was present to load the file.
   2308   @retval EFI_DEVICE_ERROR            The file was not loaded due to a device error.
   2309   @retval EFI_NO_RESPONSE             The remote system did not respond.
   2310   @retval EFI_NOT_FOUND               The file was not found.
   2311   @retval EFI_ABORTED                 The file load process was manually cancelled.
   2312 
   2313 **/
   2314 EFI_STATUS
   2315 EFIAPI
   2316 EfiPxeLoadFile (
   2317   IN     EFI_LOAD_FILE_PROTOCOL           *This,
   2318   IN     EFI_DEVICE_PATH_PROTOCOL         *FilePath,
   2319   IN     BOOLEAN                          BootPolicy,
   2320   IN OUT UINTN                            *BufferSize,
   2321   IN     VOID                             *Buffer       OPTIONAL
   2322   )
   2323 {
   2324   PXEBC_PRIVATE_DATA          *Private;
   2325   PXEBC_VIRTUAL_NIC           *VirtualNic;
   2326   EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
   2327   BOOLEAN                     UsingIpv6;
   2328   EFI_STATUS                  Status;
   2329   BOOLEAN                     MediaPresent;
   2330 
   2331   if (FilePath == NULL || !IsDevicePathEnd (FilePath)) {
   2332     return EFI_INVALID_PARAMETER;
   2333   }
   2334 
   2335   VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (This);
   2336   Private    = VirtualNic->Private;
   2337   PxeBc      = &Private->PxeBc;
   2338   UsingIpv6  = FALSE;
   2339   Status     = EFI_DEVICE_ERROR;
   2340 
   2341   if (This == NULL || BufferSize == NULL) {
   2342     return EFI_INVALID_PARAMETER;
   2343   }
   2344 
   2345   //
   2346   // Only support BootPolicy
   2347   //
   2348   if (!BootPolicy) {
   2349     return EFI_UNSUPPORTED;
   2350   }
   2351 
   2352   //
   2353   // Check media status before PXE start
   2354   //
   2355   MediaPresent = TRUE;
   2356   NetLibDetectMedia (Private->Controller, &MediaPresent);
   2357   if (!MediaPresent) {
   2358     return EFI_NO_MEDIA;
   2359   }
   2360 
   2361   //
   2362   // Check whether the virtual nic is using IPv6 or not.
   2363   //
   2364   if (VirtualNic == Private->Ip6Nic) {
   2365     UsingIpv6 = TRUE;
   2366   }
   2367 
   2368   //
   2369   // Start Pxe Base Code to initialize PXE boot.
   2370   //
   2371   Status = PxeBc->Start (PxeBc, UsingIpv6);
   2372   if (Status == EFI_ALREADY_STARTED && UsingIpv6 != PxeBc->Mode->UsingIpv6) {
   2373     //
   2374     // PxeBc protocol has already been started but not on the required IP version, restart it.
   2375     //
   2376     Status = PxeBc->Stop (PxeBc);
   2377     if (!EFI_ERROR (Status)) {
   2378       Status = PxeBc->Start (PxeBc, UsingIpv6);
   2379     }
   2380   }
   2381   if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {
   2382     Status = PxeBcLoadBootFile (Private, BufferSize, Buffer);
   2383   }
   2384 
   2385   if (Status != EFI_SUCCESS &&
   2386       Status != EFI_UNSUPPORTED &&
   2387       Status != EFI_BUFFER_TOO_SMALL) {
   2388     //
   2389     // There are three cases, which needn't stop pxebc here.
   2390     //   1. success to download file.
   2391     //   2. success to get file size.
   2392     //   3. unsupported.
   2393     //
   2394     PxeBc->Stop (PxeBc);
   2395   } else {
   2396     //
   2397     // The DHCP4 can have only one configured child instance so we need to stop
   2398     // reset the DHCP4 child before we return. Otherwise these programs which
   2399     // also need to use DHCP4 will be impacted.
   2400     //
   2401     if (!PxeBc->Mode->UsingIpv6) {
   2402       Private->Dhcp4->Stop (Private->Dhcp4);
   2403       Private->Dhcp4->Configure (Private->Dhcp4, NULL);
   2404     }
   2405   }
   2406 
   2407   return Status;
   2408 }
   2409 
   2410 EFI_LOAD_FILE_PROTOCOL  gLoadFileProtocolTemplate = { EfiPxeLoadFile };
   2411 
   2412