Home | History | Annotate | Download | only in Dhcp4Dxe
      1 /** @file
      2   This file implement the EFI_DHCP4_PROTOCOL interface.
      3 
      4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 
     16 #include "Dhcp4Impl.h"
     17 
     18 /**
     19   Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver.
     20 
     21   The GetModeData() function returns the current operating mode and cached data
     22   packet for the EFI DHCPv4 Protocol driver.
     23 
     24   @param[in]  This          Pointer to the EFI_DHCP4_PROTOCOL instance.
     25   @param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure.
     26 
     27   @retval EFI_SUCCESS           The mode data was returned.
     28   @retval EFI_INVALID_PARAMETER This is NULL.
     29 
     30 **/
     31 EFI_STATUS
     32 EFIAPI
     33 EfiDhcp4GetModeData (
     34   IN  EFI_DHCP4_PROTOCOL    *This,
     35   OUT EFI_DHCP4_MODE_DATA   *Dhcp4ModeData
     36   );
     37 
     38 /**
     39   Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver.
     40 
     41   The Configure() function is used to initialize, change, or reset the operational
     42   settings of the EFI DHCPv4 Protocol driver for the communication device on which
     43   the EFI DHCPv4 Service Binding Protocol is installed. This function can be
     44   successfully called only if both of the following are true:
     45   * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init,
     46     Dhcp4InitReboot, or Dhcp4Bound states.
     47   * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI
     48     DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4
     49     Protocol driver.
     50   When this driver is in the Dhcp4Stopped state, it can transfer into one of the
     51   following two possible initial states:
     52   * Dhcp4Init
     53   * Dhcp4InitReboot
     54   The driver can transfer into these states by calling Configure() with a non-NULL
     55   Dhcp4CfgData. The driver will transfer into the appropriate state based on the
     56   supplied client network address in the ClientAddress parameter and DHCP options
     57   in the OptionList parameter as described in RFC 2131.
     58   When Configure() is called successfully while Dhcp4CfgData is set to NULL, the
     59   default configuring data will be reset in the EFI DHCPv4 Protocol driver and
     60   the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance
     61   wants to make it possible for another instance to configure the EFI DHCPv4 Protocol
     62   driver, it must call this function with Dhcp4CfgData set to NULL.
     63 
     64   @param[in]  This                   Pointer to the EFI_DHCP4_PROTOCOL instance.
     65   @param[in]  Dhcp4CfgData           Pointer to the EFI_DHCP4_CONFIG_DATA.
     66 
     67   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or
     68                                 Dhcp4InitReboot state, if the original state of this driver
     69                                 was Dhcp4Stopped and the value of Dhcp4CfgData was
     70                                 not NULL. Otherwise, the state was left unchanged.
     71   @retval EFI_ACCESS_DENIED     This instance of the EFI DHCPv4 Protocol driver was not in the
     72                                 Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state;
     73                                 Or onother instance of this EFI DHCPv4 Protocol driver is already
     74                                 in a valid configured state.
     75   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
     76   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
     77   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
     78 
     79 **/
     80 EFI_STATUS
     81 EFIAPI
     82 EfiDhcp4Configure (
     83   IN EFI_DHCP4_PROTOCOL     *This,
     84   IN EFI_DHCP4_CONFIG_DATA  *Dhcp4CfgData       OPTIONAL
     85   );
     86 
     87 /**
     88   Starts the DHCP configuration process.
     89 
     90   The Start() function starts the DHCP configuration process. This function can
     91   be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
     92   Dhcp4InitReboot state.
     93   If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
     94   driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
     95   Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
     96   If the process aborts, either by the user or by some unexpected network error,
     97   the state is restored to the Dhcp4Init state. The Start() function can be called
     98   again to restart the process.
     99   Refer to RFC 2131 for precise state transitions during this process. At the
    100   time when each event occurs in this process, the callback function that was set
    101   by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
    102   opportunity to control the process.
    103 
    104   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
    105   @param[in]  CompletionEvent If not NULL, indicates the event that will be signaled when the
    106                               EFI DHCPv4 Protocol driver is transferred into the
    107                               Dhcp4Bound state or when the DHCP process is aborted.
    108                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
    109                               check the completion status. If NULL,
    110                               EFI_DHCP4_PROTOCOL.Start() will wait until the driver
    111                               is transferred into the Dhcp4Bound state or the process fails.
    112 
    113   @retval EFI_SUCCESS           The DHCP configuration process has started, or it has completed
    114                                 when CompletionEvent is NULL.
    115   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
    116                                 state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
    117   @retval EFI_INVALID_PARAMETER This is NULL.
    118   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
    119   @retval EFI_TIMEOUT           The DHCP configuration process failed because no response was
    120                                 received from the server within the specified timeout value.
    121   @retval EFI_ABORTED           The user aborted the DHCP process.
    122   @retval EFI_ALREADY_STARTED   Some other EFI DHCPv4 Protocol instance already started the
    123                                 DHCP process.
    124   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    125 
    126 **/
    127 EFI_STATUS
    128 EFIAPI
    129 EfiDhcp4Start (
    130   IN EFI_DHCP4_PROTOCOL     *This,
    131   IN EFI_EVENT              CompletionEvent   OPTIONAL
    132   );
    133 
    134 /**
    135   Extends the lease time by sending a request packet.
    136 
    137   The RenewRebind() function is used to manually extend the lease time when the
    138   EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
    139   not expired yet. This function will send a request packet to the previously
    140   found server (or to any server when RebindRequest is TRUE) and transfer the
    141   state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
    142   TRUE). When a response is received, the state is returned to Dhcp4Bound.
    143   If no response is received before the try count is exceeded (the RequestTryCount
    144   field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
    145   was issued by the previous server expires, the driver will return to the Dhcp4Bound
    146   state and the previous configuration is restored. The outgoing and incoming packets
    147   can be captured by the EFI_DHCP4_CALLBACK function.
    148 
    149   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
    150   @param[in]  RebindRequest   If TRUE, this function broadcasts the request packets and enters
    151                               the Dhcp4Rebinding state. Otherwise, it sends a unicast
    152                               request packet and enters the Dhcp4Renewing state.
    153   @param[in]  CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
    154                               completes or some error occurs.
    155                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
    156                               check the completion status. If NULL,
    157                               EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
    158                               until the DHCP process finishes.
    159 
    160   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the
    161                                 Dhcp4Renewing state or is back to the Dhcp4Bound state.
    162   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
    163                                 state. EFI_DHCP4_PROTOCOL.Configure() needs to
    164                                 be called.
    165   @retval EFI_INVALID_PARAMETER This is NULL.
    166   @retval EFI_TIMEOUT           There was no response from the server when the try count was
    167                                 exceeded.
    168   @retval EFI_ACCESS_DENIED     The driver is not in the Dhcp4Bound state.
    169   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    170 
    171 **/
    172 EFI_STATUS
    173 EFIAPI
    174 EfiDhcp4RenewRebind (
    175   IN EFI_DHCP4_PROTOCOL     *This,
    176   IN BOOLEAN                RebindRequest,
    177   IN EFI_EVENT              CompletionEvent   OPTIONAL
    178   );
    179 
    180 /**
    181   Releases the current address configuration.
    182 
    183   The Release() function releases the current configured IP address by doing either
    184   of the following:
    185   * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
    186     Dhcp4Bound state
    187   * Setting the previously assigned IP address that was provided with the
    188     EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
    189     Dhcp4InitReboot state
    190   After a successful call to this function, the EFI DHCPv4 Protocol driver returns
    191   to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
    192 
    193   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
    194 
    195   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
    196   @retval EFI_INVALID_PARAMETER This is NULL.
    197   @retval EFI_ACCESS_DENIED     The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
    198   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    199 
    200 **/
    201 EFI_STATUS
    202 EFIAPI
    203 EfiDhcp4Release (
    204   IN EFI_DHCP4_PROTOCOL     *This
    205   );
    206 
    207 /**
    208   Stops the current address configuration.
    209 
    210   The Stop() function is used to stop the DHCP configuration process. After this
    211   function is called successfully, the EFI DHCPv4 Protocol driver is transferred
    212   into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
    213   before DHCP configuration process can be started again. This function can be
    214   called when the EFI DHCPv4 Protocol driver is in any state.
    215 
    216   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
    217 
    218   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
    219   @retval EFI_INVALID_PARAMETER This is NULL.
    220 
    221 **/
    222 EFI_STATUS
    223 EFIAPI
    224 EfiDhcp4Stop (
    225   IN EFI_DHCP4_PROTOCOL     *This
    226   );
    227 
    228 /**
    229   Builds a DHCP packet, given the options to be appended or deleted or replaced.
    230 
    231   The Build() function is used to assemble a new packet from the original packet
    232   by replacing or deleting existing options or appending new options. This function
    233   does not change any state of the EFI DHCPv4 Protocol driver and can be used at
    234   any time.
    235 
    236   @param[in]  This        Pointer to the EFI_DHCP4_PROTOCOL instance.
    237   @param[in]  SeedPacket  Initial packet to be used as a base for building new packet.
    238   @param[in]  DeleteCount Number of opcodes in the DeleteList.
    239   @param[in]  DeleteList  List of opcodes to be deleted from the seed packet.
    240                           Ignored if DeleteCount is zero.
    241   @param[in]  AppendCount Number of entries in the OptionList.
    242   @param[in]  AppendList  Pointer to a DHCP option list to be appended to SeedPacket.
    243                           If SeedPacket also contains options in this list, they are
    244                           replaced by new options (except pad option). Ignored if
    245                           AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
    246   @param[out] NewPacket   Pointer to storage for the pointer to the new allocated packet.
    247                           Use the EFI Boot Service FreePool() on the resulting pointer
    248                           when done with the packet.
    249 
    250   @retval EFI_SUCCESS           The new packet was built.
    251   @retval EFI_OUT_OF_RESOURCES  Storage for the new packet could not be allocated.
    252   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
    253 
    254 **/
    255 EFI_STATUS
    256 EFIAPI
    257 EfiDhcp4Build (
    258   IN EFI_DHCP4_PROTOCOL       *This,
    259   IN EFI_DHCP4_PACKET         *SeedPacket,
    260   IN UINT32                   DeleteCount,
    261   IN UINT8                    *DeleteList OPTIONAL,
    262   IN UINT32                   AppendCount,
    263   IN EFI_DHCP4_PACKET_OPTION  *AppendList[] OPTIONAL,
    264   OUT EFI_DHCP4_PACKET        **NewPacket
    265   );
    266 
    267 /**
    268   Transmits a DHCP formatted packet and optionally waits for responses.
    269 
    270   The TransmitReceive() function is used to transmit a DHCP packet and optionally
    271   wait for the response from servers. This function does not change the state of
    272   the EFI DHCPv4 Protocol driver and thus can be used at any time.
    273 
    274   @param[in]  This    Pointer to the EFI_DHCP4_PROTOCOL instance.
    275   @param[in]  Token   Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
    276 
    277   @retval EFI_SUCCESS           The packet was successfully queued for transmission.
    278   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
    279   @retval EFI_NOT_READY         The previous call to this function has not finished yet. Try to call
    280                                 this function after collection process completes.
    281   @retval EFI_NO_MAPPING        The default station address is not available yet.
    282   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
    283   @retval Others                Some other unexpected error occurred.
    284 
    285 **/
    286 EFI_STATUS
    287 EFIAPI
    288 EfiDhcp4TransmitReceive (
    289   IN EFI_DHCP4_PROTOCOL                *This,
    290   IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token
    291   );
    292 
    293 /**
    294   Parses the packed DHCP option data.
    295 
    296   The Parse() function is used to retrieve the option list from a DHCP packet.
    297   If *OptionCount isn't zero, and there is enough space for all the DHCP options
    298   in the Packet, each element of PacketOptionList is set to point to somewhere in
    299   the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
    300   the caller should reassemble the parsed DHCP options to get the finial result.
    301   If *OptionCount is zero or there isn't enough space for all of them, the number
    302   of DHCP options in the Packet is returned in OptionCount.
    303 
    304   @param  This             Pointer to the EFI_DHCP4_PROTOCOL instance.
    305   @param  Packet           Pointer to packet to be parsed.
    306   @param  OptionCount      On input, the number of entries in the PacketOptionList.
    307                            On output, the number of entries that were written into the
    308                            PacketOptionList.
    309   @param  PacketOptionList List of packet option entries to be filled in. End option or pad
    310                            options are not included.
    311 
    312   @retval EFI_SUCCESS           The packet was successfully parsed.
    313   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
    314   @retval EFI_BUFFER_TOO_SMALL  One or more of the following conditions is TRUE:
    315                                 1) *OptionCount is smaller than the number of options that
    316                                 were found in the Packet.
    317                                 2) PacketOptionList is NULL.
    318 
    319 **/
    320 EFI_STATUS
    321 EFIAPI
    322 EfiDhcp4Parse (
    323   IN EFI_DHCP4_PROTOCOL       *This,
    324   IN EFI_DHCP4_PACKET         *Packet,
    325   IN OUT UINT32               *OptionCount,
    326   OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
    327   );
    328 
    329 EFI_DHCP4_PROTOCOL  mDhcp4ProtocolTemplate = {
    330   EfiDhcp4GetModeData,
    331   EfiDhcp4Configure,
    332   EfiDhcp4Start,
    333   EfiDhcp4RenewRebind,
    334   EfiDhcp4Release,
    335   EfiDhcp4Stop,
    336   EfiDhcp4Build,
    337   EfiDhcp4TransmitReceive,
    338   EfiDhcp4Parse
    339 };
    340 
    341 /**
    342   Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver.
    343 
    344   The GetModeData() function returns the current operating mode and cached data
    345   packet for the EFI DHCPv4 Protocol driver.
    346 
    347   @param[in]  This          Pointer to the EFI_DHCP4_PROTOCOL instance.
    348   @param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure.
    349 
    350   @retval EFI_SUCCESS           The mode data was returned.
    351   @retval EFI_INVALID_PARAMETER This is NULL.
    352 
    353 **/
    354 EFI_STATUS
    355 EFIAPI
    356 EfiDhcp4GetModeData (
    357   IN  EFI_DHCP4_PROTOCOL    *This,
    358   OUT EFI_DHCP4_MODE_DATA   *Dhcp4ModeData
    359   )
    360 {
    361   DHCP_PROTOCOL             *Instance;
    362   DHCP_SERVICE              *DhcpSb;
    363   DHCP_PARAMETER            *Para;
    364   EFI_TPL                   OldTpl;
    365   IP4_ADDR                  Ip;
    366 
    367   //
    368   // First validate the parameters.
    369   //
    370   if ((This == NULL) || (Dhcp4ModeData == NULL)) {
    371     return EFI_INVALID_PARAMETER;
    372   }
    373 
    374   Instance = DHCP_INSTANCE_FROM_THIS (This);
    375 
    376   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
    377   DhcpSb  = Instance->Service;
    378 
    379   //
    380   // Caller can use GetModeData to retrieve current DHCP states
    381   // no matter whether it is the active child or not.
    382   //
    383   Dhcp4ModeData->State = (EFI_DHCP4_STATE) DhcpSb->DhcpState;
    384   CopyMem (&Dhcp4ModeData->ConfigData, &DhcpSb->ActiveConfig, sizeof (Dhcp4ModeData->ConfigData));
    385   CopyMem (&Dhcp4ModeData->ClientMacAddress, &DhcpSb->Mac, sizeof (Dhcp4ModeData->ClientMacAddress));
    386 
    387   Ip = HTONL (DhcpSb->ClientAddr);
    388   CopyMem (&Dhcp4ModeData->ClientAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
    389 
    390   Ip = HTONL (DhcpSb->Netmask);
    391   CopyMem (&Dhcp4ModeData->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
    392 
    393   Ip = HTONL (DhcpSb->ServerAddr);
    394   CopyMem (&Dhcp4ModeData->ServerAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
    395 
    396   Para = DhcpSb->Para;
    397 
    398   if (Para != NULL) {
    399     Ip = HTONL (Para->Router);
    400     CopyMem (&Dhcp4ModeData->RouterAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
    401     Dhcp4ModeData->LeaseTime = Para->Lease;
    402   } else {
    403     ZeroMem (&Dhcp4ModeData->RouterAddress, sizeof (EFI_IPv4_ADDRESS));
    404     Dhcp4ModeData->LeaseTime = 0xffffffff;
    405   }
    406 
    407   Dhcp4ModeData->ReplyPacket = DhcpSb->Selected;
    408 
    409   gBS->RestoreTPL (OldTpl);
    410   return EFI_SUCCESS;
    411 }
    412 
    413 
    414 /**
    415   Free the resource related to the configure parameters.
    416   DHCP driver will make a copy of the user's configure
    417   such as the time out value.
    418 
    419   @param  Config                 The DHCP configure data
    420 
    421 **/
    422 VOID
    423 DhcpCleanConfigure (
    424   IN OUT EFI_DHCP4_CONFIG_DATA  *Config
    425   )
    426 {
    427   UINT32                    Index;
    428 
    429   if (Config->DiscoverTimeout != NULL) {
    430     FreePool (Config->DiscoverTimeout);
    431   }
    432 
    433   if (Config->RequestTimeout != NULL) {
    434     FreePool (Config->RequestTimeout);
    435   }
    436 
    437   if (Config->OptionList != NULL) {
    438     for (Index = 0; Index < Config->OptionCount; Index++) {
    439       if (Config->OptionList[Index] != NULL) {
    440         FreePool (Config->OptionList[Index]);
    441       }
    442     }
    443 
    444     FreePool (Config->OptionList);
    445   }
    446 
    447   ZeroMem (Config, sizeof (EFI_DHCP4_CONFIG_DATA));
    448 }
    449 
    450 
    451 /**
    452   Allocate memory for configure parameter such as timeout value for Dst,
    453   then copy the configure parameter from Src to Dst.
    454 
    455   @param[out]  Dst                    The destination DHCP configure data.
    456   @param[in]   Src                    The source DHCP configure data.
    457 
    458   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
    459   @retval EFI_SUCCESS            The configure is copied.
    460 
    461 **/
    462 EFI_STATUS
    463 DhcpCopyConfigure (
    464   OUT EFI_DHCP4_CONFIG_DATA  *Dst,
    465   IN  EFI_DHCP4_CONFIG_DATA  *Src
    466   )
    467 {
    468   EFI_DHCP4_PACKET_OPTION   **DstOptions;
    469   EFI_DHCP4_PACKET_OPTION   **SrcOptions;
    470   UINTN                     Len;
    471   UINT32                    Index;
    472 
    473   CopyMem (Dst, Src, sizeof (*Dst));
    474   Dst->DiscoverTimeout  = NULL;
    475   Dst->RequestTimeout   = NULL;
    476   Dst->OptionList       = NULL;
    477 
    478   //
    479   // Allocate a memory then copy DiscoverTimeout to it
    480   //
    481   if (Src->DiscoverTimeout != NULL) {
    482     Len                   = Src->DiscoverTryCount * sizeof (UINT32);
    483     Dst->DiscoverTimeout  = AllocatePool (Len);
    484 
    485     if (Dst->DiscoverTimeout == NULL) {
    486       return EFI_OUT_OF_RESOURCES;
    487     }
    488 
    489     for (Index = 0; Index < Src->DiscoverTryCount; Index++) {
    490       Dst->DiscoverTimeout[Index] = MAX (Src->DiscoverTimeout[Index], 1);
    491     }
    492   }
    493 
    494   //
    495   // Allocate a memory then copy RequestTimeout to it
    496   //
    497   if (Src->RequestTimeout != NULL) {
    498     Len                 = Src->RequestTryCount * sizeof (UINT32);
    499     Dst->RequestTimeout = AllocatePool (Len);
    500 
    501     if (Dst->RequestTimeout == NULL) {
    502       goto ON_ERROR;
    503     }
    504 
    505     for (Index = 0; Index < Src->RequestTryCount; Index++) {
    506       Dst->RequestTimeout[Index] = MAX (Src->RequestTimeout[Index], 1);
    507     }
    508   }
    509 
    510   //
    511   // Allocate an array of dhcp option point, then allocate memory
    512   // for each option and copy the source option to it
    513   //
    514   if (Src->OptionList != NULL) {
    515     Len             = Src->OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *);
    516     Dst->OptionList = AllocateZeroPool (Len);
    517 
    518     if (Dst->OptionList == NULL) {
    519       goto ON_ERROR;
    520     }
    521 
    522     DstOptions  = Dst->OptionList;
    523     SrcOptions  = Src->OptionList;
    524 
    525     for (Index = 0; Index < Src->OptionCount; Index++) {
    526       Len = sizeof (EFI_DHCP4_PACKET_OPTION) + MAX (SrcOptions[Index]->Length - 1, 0);
    527 
    528       DstOptions[Index] = AllocatePool (Len);
    529 
    530       if (DstOptions[Index] == NULL) {
    531         goto ON_ERROR;
    532       }
    533 
    534       CopyMem (DstOptions[Index], SrcOptions[Index], Len);
    535     }
    536   }
    537 
    538   return EFI_SUCCESS;
    539 
    540 ON_ERROR:
    541   DhcpCleanConfigure (Dst);
    542   return EFI_OUT_OF_RESOURCES;
    543 }
    544 
    545 
    546 /**
    547   Give up the control of the DHCP service to let other child
    548   resume. Don't change the service's DHCP state and the Client
    549   address and option list configure as required by RFC2131.
    550 
    551   @param  DhcpSb                 The DHCP service instance.
    552 
    553 **/
    554 VOID
    555 DhcpYieldControl (
    556   IN DHCP_SERVICE           *DhcpSb
    557   )
    558 {
    559   EFI_DHCP4_CONFIG_DATA     *Config;
    560 
    561   Config    = &DhcpSb->ActiveConfig;
    562 
    563   DhcpSb->ServiceState  = DHCP_UNCONFIGED;
    564   DhcpSb->ActiveChild   = NULL;
    565 
    566   if (Config->DiscoverTimeout != NULL) {
    567     FreePool (Config->DiscoverTimeout);
    568 
    569     Config->DiscoverTryCount  = 0;
    570     Config->DiscoverTimeout   = NULL;
    571   }
    572 
    573   if (Config->RequestTimeout != NULL) {
    574     FreePool (Config->RequestTimeout);
    575 
    576     Config->RequestTryCount = 0;
    577     Config->RequestTimeout  = NULL;
    578   }
    579 
    580   Config->Dhcp4Callback   = NULL;
    581   Config->CallbackContext = NULL;
    582 }
    583 
    584 
    585 /**
    586   Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver.
    587 
    588   The Configure() function is used to initialize, change, or reset the operational
    589   settings of the EFI DHCPv4 Protocol driver for the communication device on which
    590   the EFI DHCPv4 Service Binding Protocol is installed. This function can be
    591   successfully called only if both of the following are true:
    592   * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init,
    593     Dhcp4InitReboot, or Dhcp4Bound states.
    594   * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI
    595     DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4
    596     Protocol driver.
    597   When this driver is in the Dhcp4Stopped state, it can transfer into one of the
    598   following two possible initial states:
    599   * Dhcp4Init
    600   * Dhcp4InitReboot
    601   The driver can transfer into these states by calling Configure() with a non-NULL
    602   Dhcp4CfgData. The driver will transfer into the appropriate state based on the
    603   supplied client network address in the ClientAddress parameter and DHCP options
    604   in the OptionList parameter as described in RFC 2131.
    605   When Configure() is called successfully while Dhcp4CfgData is set to NULL, the
    606   default configuring data will be reset in the EFI DHCPv4 Protocol driver and
    607   the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance
    608   wants to make it possible for another instance to configure the EFI DHCPv4 Protocol
    609   driver, it must call this function with Dhcp4CfgData set to NULL.
    610 
    611   @param[in]  This                   Pointer to the EFI_DHCP4_PROTOCOL instance.
    612   @param[in]  Dhcp4CfgData           Pointer to the EFI_DHCP4_CONFIG_DATA.
    613 
    614   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or
    615                                 Dhcp4InitReboot state, if the original state of this driver
    616                                 was Dhcp4Stopped and the value of Dhcp4CfgData was
    617                                 not NULL. Otherwise, the state was left unchanged.
    618   @retval EFI_ACCESS_DENIED     This instance of the EFI DHCPv4 Protocol driver was not in the
    619                                 Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state;
    620                                 Or onother instance of this EFI DHCPv4 Protocol driver is already
    621                                 in a valid configured state.
    622   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
    623   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
    624   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    625 
    626 **/
    627 EFI_STATUS
    628 EFIAPI
    629 EfiDhcp4Configure (
    630   IN EFI_DHCP4_PROTOCOL     *This,
    631   IN EFI_DHCP4_CONFIG_DATA  *Dhcp4CfgData       OPTIONAL
    632   )
    633 {
    634   EFI_DHCP4_CONFIG_DATA     *Config;
    635   DHCP_PROTOCOL             *Instance;
    636   DHCP_SERVICE              *DhcpSb;
    637   EFI_STATUS                Status;
    638   EFI_TPL                   OldTpl;
    639   UINT32                    Index;
    640   IP4_ADDR                  Ip;
    641 
    642   //
    643   // First validate the parameters
    644   //
    645   if (This == NULL) {
    646     return EFI_INVALID_PARAMETER;
    647   }
    648 
    649   if (Dhcp4CfgData != NULL) {
    650     if ((Dhcp4CfgData->DiscoverTryCount != 0) && (Dhcp4CfgData->DiscoverTimeout == NULL)) {
    651       return EFI_INVALID_PARAMETER;
    652     }
    653 
    654     if ((Dhcp4CfgData->RequestTryCount != 0) && (Dhcp4CfgData->RequestTimeout == NULL)) {
    655       return EFI_INVALID_PARAMETER;
    656     }
    657 
    658     if ((Dhcp4CfgData->OptionCount != 0) && (Dhcp4CfgData->OptionList == NULL)) {
    659       return EFI_INVALID_PARAMETER;
    660     }
    661 
    662     CopyMem (&Ip, &Dhcp4CfgData->ClientAddress, sizeof (IP4_ADDR));
    663     if (IP4_IS_LOCAL_BROADCAST(NTOHL (Ip))) {
    664       return EFI_INVALID_PARAMETER;
    665     }
    666   }
    667 
    668   Instance = DHCP_INSTANCE_FROM_THIS (This);
    669 
    670   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
    671     return EFI_INVALID_PARAMETER;
    672   }
    673 
    674   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
    675 
    676   DhcpSb  = Instance->Service;
    677   Config  = &DhcpSb->ActiveConfig;
    678 
    679   Status  = EFI_ACCESS_DENIED;
    680 
    681   if ((DhcpSb->DhcpState != Dhcp4Stopped) &&
    682       (DhcpSb->DhcpState != Dhcp4Init) &&
    683       (DhcpSb->DhcpState != Dhcp4InitReboot) &&
    684       (DhcpSb->DhcpState != Dhcp4Bound)) {
    685 
    686     goto ON_EXIT;
    687   }
    688 
    689   if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) {
    690     goto ON_EXIT;
    691   }
    692 
    693   if (Dhcp4CfgData != NULL) {
    694     Status = EFI_OUT_OF_RESOURCES;
    695     DhcpCleanConfigure (Config);
    696 
    697     if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) {
    698       goto ON_EXIT;
    699     }
    700 
    701     DhcpSb->UserOptionLen = 0;
    702 
    703     for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) {
    704       DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2;
    705     }
    706 
    707     DhcpSb->ActiveChild = Instance;
    708 
    709     if (DhcpSb->DhcpState == Dhcp4Stopped) {
    710       DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress);
    711 
    712       if (DhcpSb->ClientAddr != 0) {
    713         DhcpSb->DhcpState = Dhcp4InitReboot;
    714       } else {
    715         DhcpSb->DhcpState = Dhcp4Init;
    716       }
    717     }
    718 
    719     DhcpSb->ServiceState  = DHCP_CONFIGED;
    720     Status                = EFI_SUCCESS;
    721 
    722   } else if (DhcpSb->ActiveChild == Instance) {
    723     Status = EFI_SUCCESS;
    724     DhcpYieldControl (DhcpSb);
    725   }
    726 
    727 ON_EXIT:
    728   gBS->RestoreTPL (OldTpl);
    729   return Status;
    730 }
    731 
    732 
    733 /**
    734   Starts the DHCP configuration process.
    735 
    736   The Start() function starts the DHCP configuration process. This function can
    737   be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
    738   Dhcp4InitReboot state.
    739   If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
    740   driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
    741   Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
    742   If the process aborts, either by the user or by some unexpected network error,
    743   the state is restored to the Dhcp4Init state. The Start() function can be called
    744   again to restart the process.
    745   Refer to RFC 2131 for precise state transitions during this process. At the
    746   time when each event occurs in this process, the callback function that was set
    747   by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
    748   opportunity to control the process.
    749 
    750   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
    751   @param[in]  CompletionEvent If not NULL, indicates the event that will be signaled when the
    752                               EFI DHCPv4 Protocol driver is transferred into the
    753                               Dhcp4Bound state or when the DHCP process is aborted.
    754                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
    755                               check the completion status. If NULL,
    756                               EFI_DHCP4_PROTOCOL.Start() will wait until the driver
    757                               is transferred into the Dhcp4Bound state or the process fails.
    758 
    759   @retval EFI_SUCCESS           The DHCP configuration process has started, or it has completed
    760                                 when CompletionEvent is NULL.
    761   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
    762                                 state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
    763   @retval EFI_INVALID_PARAMETER This is NULL.
    764   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
    765   @retval EFI_TIMEOUT           The DHCP configuration process failed because no response was
    766                                 received from the server within the specified timeout value.
    767   @retval EFI_ABORTED           The user aborted the DHCP process.
    768   @retval EFI_ALREADY_STARTED   Some other EFI DHCPv4 Protocol instance already started the
    769                                 DHCP process.
    770   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    771   @retval EFI_NO_MEDIA          There was a media error.
    772 
    773 **/
    774 EFI_STATUS
    775 EFIAPI
    776 EfiDhcp4Start (
    777   IN EFI_DHCP4_PROTOCOL     *This,
    778   IN EFI_EVENT              CompletionEvent   OPTIONAL
    779   )
    780 {
    781   DHCP_PROTOCOL             *Instance;
    782   DHCP_SERVICE              *DhcpSb;
    783   EFI_STATUS                Status;
    784   EFI_TPL                   OldTpl;
    785 
    786   //
    787   // First validate the parameters
    788   //
    789   if (This == NULL) {
    790     return EFI_INVALID_PARAMETER;
    791   }
    792 
    793   Instance = DHCP_INSTANCE_FROM_THIS (This);
    794 
    795   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
    796     return EFI_INVALID_PARAMETER;
    797   }
    798 
    799   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
    800   DhcpSb  = Instance->Service;
    801 
    802   if (DhcpSb->DhcpState == Dhcp4Stopped) {
    803     Status = EFI_NOT_STARTED;
    804     goto ON_ERROR;
    805   }
    806 
    807   if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) {
    808     Status = EFI_ALREADY_STARTED;
    809     goto ON_ERROR;
    810   }
    811 
    812   DhcpSb->IoStatus = EFI_ALREADY_STARTED;
    813 
    814   if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) {
    815     goto ON_ERROR;
    816   }
    817 
    818 
    819   Instance->CompletionEvent = CompletionEvent;
    820 
    821   //
    822   // Restore the TPL now, don't call poll function at TPL_CALLBACK.
    823   //
    824   gBS->RestoreTPL (OldTpl);
    825 
    826   if (CompletionEvent == NULL) {
    827     while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
    828       DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
    829     }
    830 
    831     return DhcpSb->IoStatus;
    832   }
    833 
    834   return EFI_SUCCESS;
    835 
    836 ON_ERROR:
    837   gBS->RestoreTPL (OldTpl);
    838   return Status;
    839 }
    840 
    841 
    842 /**
    843   Extends the lease time by sending a request packet.
    844 
    845   The RenewRebind() function is used to manually extend the lease time when the
    846   EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
    847   not expired yet. This function will send a request packet to the previously
    848   found server (or to any server when RebindRequest is TRUE) and transfer the
    849   state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
    850   TRUE). When a response is received, the state is returned to Dhcp4Bound.
    851   If no response is received before the try count is exceeded (the RequestTryCount
    852   field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
    853   was issued by the previous server expires, the driver will return to the Dhcp4Bound
    854   state and the previous configuration is restored. The outgoing and incoming packets
    855   can be captured by the EFI_DHCP4_CALLBACK function.
    856 
    857   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
    858   @param[in]  RebindRequest   If TRUE, this function broadcasts the request packets and enters
    859                               the Dhcp4Rebinding state. Otherwise, it sends a unicast
    860                               request packet and enters the Dhcp4Renewing state.
    861   @param[in]  CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
    862                               completes or some error occurs.
    863                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
    864                               check the completion status. If NULL,
    865                               EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
    866                               until the DHCP process finishes.
    867 
    868   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the
    869                                 Dhcp4Renewing state or is back to the Dhcp4Bound state.
    870   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
    871                                 state. EFI_DHCP4_PROTOCOL.Configure() needs to
    872                                 be called.
    873   @retval EFI_INVALID_PARAMETER This is NULL.
    874   @retval EFI_TIMEOUT           There was no response from the server when the try count was
    875                                 exceeded.
    876   @retval EFI_ACCESS_DENIED     The driver is not in the Dhcp4Bound state.
    877   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    878 
    879 **/
    880 EFI_STATUS
    881 EFIAPI
    882 EfiDhcp4RenewRebind (
    883   IN EFI_DHCP4_PROTOCOL     *This,
    884   IN BOOLEAN                RebindRequest,
    885   IN EFI_EVENT              CompletionEvent   OPTIONAL
    886   )
    887 {
    888   DHCP_PROTOCOL             *Instance;
    889   DHCP_SERVICE              *DhcpSb;
    890   EFI_STATUS                Status;
    891   EFI_TPL                   OldTpl;
    892 
    893   //
    894   // First validate the parameters
    895   //
    896   if (This == NULL) {
    897     return EFI_INVALID_PARAMETER;
    898   }
    899 
    900   Instance = DHCP_INSTANCE_FROM_THIS (This);
    901 
    902   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
    903     return EFI_INVALID_PARAMETER;
    904   }
    905 
    906   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
    907   DhcpSb  = Instance->Service;
    908 
    909   if (DhcpSb->DhcpState == Dhcp4Stopped) {
    910     Status = EFI_NOT_STARTED;
    911     goto ON_EXIT;
    912   }
    913 
    914   if (DhcpSb->DhcpState != Dhcp4Bound) {
    915     Status = EFI_ACCESS_DENIED;
    916     goto ON_EXIT;
    917   }
    918 
    919   if (DHCP_IS_BOOTP (DhcpSb->Para)) {
    920     Status = EFI_SUCCESS;
    921     goto ON_EXIT;
    922   }
    923 
    924   //
    925   // Transit the states then send a extra DHCP request
    926   //
    927   if (!RebindRequest) {
    928     DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
    929   } else {
    930     DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
    931   }
    932 
    933   //
    934   // Clear initial time to make sure that elapsed-time
    935   // is set to 0 for first REQUEST in renewal process.
    936   //
    937   Instance->ElaspedTime = 0;
    938 
    939   Status = DhcpSendMessage (
    940              DhcpSb,
    941              DhcpSb->Selected,
    942              DhcpSb->Para,
    943              DHCP_MSG_REQUEST,
    944              (UINT8 *) "Extra renew/rebind by the application"
    945              );
    946 
    947   if (EFI_ERROR (Status)) {
    948     DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
    949     goto ON_EXIT;
    950   }
    951 
    952   DhcpSb->ExtraRefresh        = TRUE;
    953   DhcpSb->IoStatus            = EFI_ALREADY_STARTED;
    954   Instance->RenewRebindEvent  = CompletionEvent;
    955 
    956   gBS->RestoreTPL (OldTpl);
    957 
    958   if (CompletionEvent == NULL) {
    959     while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
    960       DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
    961 
    962     }
    963 
    964     return DhcpSb->IoStatus;
    965   }
    966 
    967   return EFI_SUCCESS;
    968 
    969 ON_EXIT:
    970   gBS->RestoreTPL (OldTpl);
    971   return Status;
    972 }
    973 
    974 
    975 /**
    976   Releases the current address configuration.
    977 
    978   The Release() function releases the current configured IP address by doing either
    979   of the following:
    980   * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
    981     Dhcp4Bound state
    982   * Setting the previously assigned IP address that was provided with the
    983     EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
    984     Dhcp4InitReboot state
    985   After a successful call to this function, the EFI DHCPv4 Protocol driver returns
    986   to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
    987 
    988   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
    989 
    990   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
    991   @retval EFI_INVALID_PARAMETER This is NULL.
    992   @retval EFI_ACCESS_DENIED     The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
    993   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    994 
    995 **/
    996 EFI_STATUS
    997 EFIAPI
    998 EfiDhcp4Release (
    999   IN EFI_DHCP4_PROTOCOL     *This
   1000   )
   1001 {
   1002   DHCP_PROTOCOL             *Instance;
   1003   DHCP_SERVICE              *DhcpSb;
   1004   EFI_STATUS                Status;
   1005   EFI_TPL                   OldTpl;
   1006 
   1007   //
   1008   // First validate the parameters
   1009   //
   1010   if (This == NULL) {
   1011     return EFI_INVALID_PARAMETER;
   1012   }
   1013 
   1014   Instance = DHCP_INSTANCE_FROM_THIS (This);
   1015 
   1016   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
   1017     return EFI_INVALID_PARAMETER;
   1018   }
   1019 
   1020   Status  = EFI_SUCCESS;
   1021   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
   1022   DhcpSb  = Instance->Service;
   1023 
   1024   if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) {
   1025     Status = EFI_ACCESS_DENIED;
   1026     goto ON_EXIT;
   1027   }
   1028 
   1029   if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) {
   1030     Status = DhcpSendMessage (
   1031                DhcpSb,
   1032                DhcpSb->Selected,
   1033                DhcpSb->Para,
   1034                DHCP_MSG_RELEASE,
   1035                NULL
   1036                );
   1037 
   1038     if (EFI_ERROR (Status)) {
   1039       Status = EFI_DEVICE_ERROR;
   1040       goto ON_EXIT;
   1041     }
   1042   }
   1043 
   1044   DhcpCleanLease (DhcpSb);
   1045 
   1046 ON_EXIT:
   1047   gBS->RestoreTPL (OldTpl);
   1048   return Status;
   1049 }
   1050 
   1051 
   1052 /**
   1053   Stops the current address configuration.
   1054 
   1055   The Stop() function is used to stop the DHCP configuration process. After this
   1056   function is called successfully, the EFI DHCPv4 Protocol driver is transferred
   1057   into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
   1058   before DHCP configuration process can be started again. This function can be
   1059   called when the EFI DHCPv4 Protocol driver is in any state.
   1060 
   1061   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
   1062 
   1063   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
   1064   @retval EFI_INVALID_PARAMETER This is NULL.
   1065 
   1066 **/
   1067 EFI_STATUS
   1068 EFIAPI
   1069 EfiDhcp4Stop (
   1070   IN EFI_DHCP4_PROTOCOL     *This
   1071   )
   1072 {
   1073   DHCP_PROTOCOL             *Instance;
   1074   DHCP_SERVICE              *DhcpSb;
   1075   EFI_TPL                   OldTpl;
   1076 
   1077   //
   1078   // First validate the parameters
   1079   //
   1080   if (This == NULL) {
   1081     return EFI_INVALID_PARAMETER;
   1082   }
   1083 
   1084   Instance = DHCP_INSTANCE_FROM_THIS (This);
   1085 
   1086   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
   1087     return EFI_INVALID_PARAMETER;
   1088   }
   1089 
   1090   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
   1091   DhcpSb  = Instance->Service;
   1092 
   1093   DhcpCleanLease (DhcpSb);
   1094 
   1095   DhcpSb->DhcpState     = Dhcp4Stopped;
   1096   DhcpSb->ServiceState  = DHCP_UNCONFIGED;
   1097 
   1098   gBS->RestoreTPL (OldTpl);
   1099   return EFI_SUCCESS;
   1100 }
   1101 
   1102 
   1103 /**
   1104   Builds a DHCP packet, given the options to be appended or deleted or replaced.
   1105 
   1106   The Build() function is used to assemble a new packet from the original packet
   1107   by replacing or deleting existing options or appending new options. This function
   1108   does not change any state of the EFI DHCPv4 Protocol driver and can be used at
   1109   any time.
   1110 
   1111   @param[in]  This        Pointer to the EFI_DHCP4_PROTOCOL instance.
   1112   @param[in]  SeedPacket  Initial packet to be used as a base for building new packet.
   1113   @param[in]  DeleteCount Number of opcodes in the DeleteList.
   1114   @param[in]  DeleteList  List of opcodes to be deleted from the seed packet.
   1115                           Ignored if DeleteCount is zero.
   1116   @param[in]  AppendCount Number of entries in the OptionList.
   1117   @param[in]  AppendList  Pointer to a DHCP option list to be appended to SeedPacket.
   1118                           If SeedPacket also contains options in this list, they are
   1119                           replaced by new options (except pad option). Ignored if
   1120                           AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
   1121   @param[out] NewPacket   Pointer to storage for the pointer to the new allocated packet.
   1122                           Use the EFI Boot Service FreePool() on the resulting pointer
   1123                           when done with the packet.
   1124 
   1125   @retval EFI_SUCCESS           The new packet was built.
   1126   @retval EFI_OUT_OF_RESOURCES  Storage for the new packet could not be allocated.
   1127   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
   1128 
   1129 **/
   1130 EFI_STATUS
   1131 EFIAPI
   1132 EfiDhcp4Build (
   1133   IN EFI_DHCP4_PROTOCOL       *This,
   1134   IN EFI_DHCP4_PACKET         *SeedPacket,
   1135   IN UINT32                   DeleteCount,
   1136   IN UINT8                    *DeleteList OPTIONAL,
   1137   IN UINT32                   AppendCount,
   1138   IN EFI_DHCP4_PACKET_OPTION  *AppendList[] OPTIONAL,
   1139   OUT EFI_DHCP4_PACKET        **NewPacket
   1140   )
   1141 {
   1142   //
   1143   // First validate the parameters
   1144   //
   1145   if ((This == NULL) || (NewPacket == NULL)) {
   1146     return EFI_INVALID_PARAMETER;
   1147   }
   1148 
   1149   if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
   1150       EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) {
   1151 
   1152     return EFI_INVALID_PARAMETER;
   1153   }
   1154 
   1155   if (((DeleteCount == 0) && (AppendCount == 0)) ||
   1156       ((DeleteCount != 0) && (DeleteList == NULL)) ||
   1157       ((AppendCount != 0) && (AppendList == NULL))) {
   1158 
   1159     return EFI_INVALID_PARAMETER;
   1160   }
   1161 
   1162   return DhcpBuild (
   1163            SeedPacket,
   1164            DeleteCount,
   1165            DeleteList,
   1166            AppendCount,
   1167            AppendList,
   1168            NewPacket
   1169            );
   1170 }
   1171 
   1172 /**
   1173   Callback by UdpIoCreatePort() when creating UdpIo for this Dhcp4 instance.
   1174 
   1175   @param[in] UdpIo      The UdpIo being created.
   1176   @param[in] Context    Dhcp4 instance.
   1177 
   1178   @retval EFI_SUCCESS   UdpIo is configured successfully.
   1179   @retval other         Other error occurs.
   1180 **/
   1181 EFI_STATUS
   1182 EFIAPI
   1183 Dhcp4InstanceConfigUdpIo (
   1184   IN UDP_IO       *UdpIo,
   1185   IN VOID         *Context
   1186   )
   1187 {
   1188   DHCP_PROTOCOL                     *Instance;
   1189   DHCP_SERVICE                      *DhcpSb;
   1190   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
   1191   EFI_UDP4_CONFIG_DATA              UdpConfigData;
   1192   IP4_ADDR                          ClientAddr;
   1193   IP4_ADDR                          Ip;
   1194   INTN                              Class;
   1195   IP4_ADDR                          SubnetMask;
   1196 
   1197   Instance = (DHCP_PROTOCOL *) Context;
   1198   DhcpSb   = Instance->Service;
   1199   Token    = Instance->Token;
   1200 
   1201   ZeroMem (&UdpConfigData, sizeof (EFI_UDP4_CONFIG_DATA));
   1202 
   1203   UdpConfigData.AcceptBroadcast    = TRUE;
   1204   UdpConfigData.AllowDuplicatePort = TRUE;
   1205   UdpConfigData.TimeToLive         = 64;
   1206   UdpConfigData.DoNotFragment      = TRUE;
   1207 
   1208   ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
   1209   Ip = HTONL (ClientAddr);
   1210   CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
   1211 
   1212   if (DhcpSb->Netmask == 0) {
   1213     //
   1214     // The Dhcp4.TransmitReceive() API should be able to used at any time according to
   1215     // UEFI spec, while in classless addressing network, the netmask must be explicitly
   1216     // provided together with the station address.
   1217     // If the DHCP instance haven't be configured with a valid netmask, we could only
   1218     // compute it accroding to the classful addressing rule.
   1219     //
   1220     Class = NetGetIpClass (ClientAddr);
   1221     ASSERT (Class < IP4_ADDR_CLASSE);
   1222     SubnetMask = gIp4AllMasks[Class << 3];
   1223   } else {
   1224     SubnetMask = DhcpSb->Netmask;
   1225   }
   1226 
   1227   Ip = HTONL (SubnetMask);
   1228   CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
   1229 
   1230   if ((Token->ListenPointCount == 0) || (Token->ListenPoints[0].ListenPort == 0)) {
   1231     UdpConfigData.StationPort = DHCP_CLIENT_PORT;
   1232   } else {
   1233     UdpConfigData.StationPort = Token->ListenPoints[0].ListenPort;
   1234   }
   1235 
   1236   return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
   1237 }
   1238 
   1239 /**
   1240   Create UdpIo for this Dhcp4 instance.
   1241 
   1242   @param Instance   The Dhcp4 instance.
   1243 
   1244   @retval EFI_SUCCESS                UdpIo is created successfully.
   1245   @retval EFI_OUT_OF_RESOURCES       Fails to create UdpIo because of limited
   1246                                      resources or configuration failure.
   1247 **/
   1248 EFI_STATUS
   1249 Dhcp4InstanceCreateUdpIo (
   1250   IN OUT DHCP_PROTOCOL  *Instance
   1251   )
   1252 {
   1253   DHCP_SERVICE  *DhcpSb;
   1254   EFI_STATUS    Status;
   1255   VOID          *Udp4;
   1256 
   1257   ASSERT (Instance->Token != NULL);
   1258 
   1259   DhcpSb          = Instance->Service;
   1260   Instance->UdpIo = UdpIoCreateIo (
   1261                       DhcpSb->Controller,
   1262                       DhcpSb->Image,
   1263                       Dhcp4InstanceConfigUdpIo,
   1264                       UDP_IO_UDP4_VERSION,
   1265                       Instance
   1266                       );
   1267   if (Instance->UdpIo == NULL) {
   1268     return EFI_OUT_OF_RESOURCES;
   1269   } else {
   1270     Status = gBS->OpenProtocol (
   1271                     Instance->UdpIo->UdpHandle,
   1272                     &gEfiUdp4ProtocolGuid,
   1273                     (VOID **) &Udp4,
   1274                     Instance->Service->Image,
   1275                     Instance->Handle,
   1276                     EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
   1277                     );
   1278     if (EFI_ERROR (Status)) {
   1279       UdpIoFreeIo (Instance->UdpIo);
   1280       Instance->UdpIo = NULL;
   1281     }
   1282     return Status;
   1283   }
   1284 }
   1285 
   1286 /**
   1287   Callback of Dhcp packet. Does nothing.
   1288 
   1289   @param Arg           The context.
   1290 
   1291 **/
   1292 VOID
   1293 EFIAPI
   1294 DhcpDummyExtFree (
   1295   IN VOID                   *Arg
   1296   )
   1297 {
   1298 }
   1299 
   1300 /**
   1301   Callback of UdpIoRecvDatagram() that handles a Dhcp4 packet.
   1302 
   1303   Only BOOTP responses will be handled that correspond to the Xid of the request
   1304   sent out. The packet will be queued to the response queue.
   1305 
   1306   @param UdpPacket        The Dhcp4 packet.
   1307   @param EndPoint         Udp4 address pair.
   1308   @param IoStatus         Status of the input.
   1309   @param Context          Extra info for the input.
   1310 
   1311 **/
   1312 VOID
   1313 EFIAPI
   1314 PxeDhcpInput (
   1315   NET_BUF                   *UdpPacket,
   1316   UDP_END_POINT             *EndPoint,
   1317   EFI_STATUS                IoStatus,
   1318   VOID                      *Context
   1319   )
   1320 {
   1321   DHCP_PROTOCOL                     *Instance;
   1322   EFI_DHCP4_HEADER                  *Head;
   1323   NET_BUF                           *Wrap;
   1324   EFI_DHCP4_PACKET                  *Packet;
   1325   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
   1326   UINT32                            Len;
   1327   EFI_STATUS                        Status;
   1328 
   1329   Wrap     = NULL;
   1330   Instance = (DHCP_PROTOCOL *) Context;
   1331   Token    = Instance->Token;
   1332 
   1333   //
   1334   // Don't restart receive if error occurs or DHCP is destroyed.
   1335   //
   1336   if (EFI_ERROR (IoStatus)) {
   1337     return ;
   1338   }
   1339 
   1340   ASSERT (UdpPacket != NULL);
   1341 
   1342   //
   1343   // Validate the packet received
   1344   //
   1345   if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
   1346     goto RESTART;
   1347   }
   1348 
   1349   //
   1350   // Copy the DHCP message to a continuous memory block, make the buffer size
   1351   // of the EFI_DHCP4_PACKET a multiple of 4-byte.
   1352   //
   1353   Len  = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4);
   1354   Wrap = NetbufAlloc (Len);
   1355   if (Wrap == NULL) {
   1356     goto RESTART;
   1357   }
   1358 
   1359   Packet         = (EFI_DHCP4_PACKET *) NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL);
   1360   ASSERT (Packet != NULL);
   1361 
   1362   Packet->Size   = Len;
   1363   Head           = &Packet->Dhcp4.Header;
   1364   Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
   1365 
   1366   if (Packet->Length != UdpPacket->TotalSize) {
   1367     goto RESTART;
   1368   }
   1369 
   1370   //
   1371   // Is this packet the answer to our packet?
   1372   //
   1373   if ((Head->OpCode != BOOTP_REPLY) ||
   1374       (Head->Xid != Token->Packet->Dhcp4.Header.Xid) ||
   1375       (CompareMem (&Token->Packet->Dhcp4.Header.ClientHwAddr[0], Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
   1376     goto RESTART;
   1377   }
   1378 
   1379   //
   1380   // Validate the options and retrieve the interested options
   1381   //
   1382   if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
   1383       (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
   1384       EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
   1385 
   1386     goto RESTART;
   1387   }
   1388 
   1389   //
   1390   // Keep this packet in the ResponseQueue.
   1391   //
   1392   NET_GET_REF (Wrap);
   1393   NetbufQueAppend (&Instance->ResponseQueue, Wrap);
   1394 
   1395 RESTART:
   1396 
   1397   NetbufFree (UdpPacket);
   1398 
   1399   if (Wrap != NULL) {
   1400     NetbufFree (Wrap);
   1401   }
   1402 
   1403   Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
   1404   if (EFI_ERROR (Status)) {
   1405     PxeDhcpDone (Instance);
   1406   }
   1407 }
   1408 
   1409 /**
   1410   Complete a Dhcp4 transaction and signal the upper layer.
   1411 
   1412   @param Instance      Dhcp4 instance.
   1413 
   1414 **/
   1415 VOID
   1416 PxeDhcpDone (
   1417   IN DHCP_PROTOCOL  *Instance
   1418   )
   1419 {
   1420   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
   1421 
   1422   Token = Instance->Token;
   1423 
   1424   Token->ResponseCount = Instance->ResponseQueue.BufNum;
   1425   if (Token->ResponseCount != 0) {
   1426     Token->ResponseList = (EFI_DHCP4_PACKET *) AllocatePool (Instance->ResponseQueue.BufSize);
   1427     if (Token->ResponseList == NULL) {
   1428       Token->Status = EFI_OUT_OF_RESOURCES;
   1429       goto SIGNAL_USER;
   1430     }
   1431 
   1432     //
   1433     // Copy the received DHCP responses.
   1434     //
   1435     NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *) Token->ResponseList);
   1436     Token->Status = EFI_SUCCESS;
   1437   } else {
   1438     Token->ResponseList = NULL;
   1439     Token->Status       = EFI_TIMEOUT;
   1440   }
   1441 
   1442 SIGNAL_USER:
   1443   //
   1444   // Clean up the resources dedicated for this transmit receive transaction.
   1445   //
   1446   NetbufQueFlush (&Instance->ResponseQueue);
   1447   UdpIoCleanIo (Instance->UdpIo);
   1448   gBS->CloseProtocol (
   1449          Instance->UdpIo->UdpHandle,
   1450          &gEfiUdp4ProtocolGuid,
   1451          Instance->Service->Image,
   1452          Instance->Handle
   1453          );
   1454   UdpIoFreeIo (Instance->UdpIo);
   1455   Instance->UdpIo = NULL;
   1456   Instance->Token = NULL;
   1457 
   1458   if (Token->CompletionEvent != NULL) {
   1459     gBS->SignalEvent (Token->CompletionEvent);
   1460   }
   1461 }
   1462 
   1463 
   1464 /**
   1465   Transmits a DHCP formatted packet and optionally waits for responses.
   1466 
   1467   The TransmitReceive() function is used to transmit a DHCP packet and optionally
   1468   wait for the response from servers. This function does not change the state of
   1469   the EFI DHCPv4 Protocol driver and thus can be used at any time.
   1470 
   1471   @param[in]  This    Pointer to the EFI_DHCP4_PROTOCOL instance.
   1472   @param[in]  Token   Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
   1473 
   1474   @retval EFI_SUCCESS           The packet was successfully queued for transmission.
   1475   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
   1476   @retval EFI_NOT_READY         The previous call to this function has not finished yet. Try to call
   1477                                 this function after collection process completes.
   1478   @retval EFI_NO_MAPPING        The default station address is not available yet.
   1479   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
   1480   @retval Others                Some other unexpected error occurred.
   1481 
   1482 **/
   1483 EFI_STATUS
   1484 EFIAPI
   1485 EfiDhcp4TransmitReceive (
   1486   IN EFI_DHCP4_PROTOCOL                *This,
   1487   IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token
   1488   )
   1489 {
   1490   DHCP_PROTOCOL  *Instance;
   1491   EFI_TPL        OldTpl;
   1492   EFI_STATUS     Status;
   1493   NET_FRAGMENT   Frag;
   1494   NET_BUF        *Wrap;
   1495   UDP_END_POINT  EndPoint;
   1496   IP4_ADDR       Ip;
   1497   DHCP_SERVICE   *DhcpSb;
   1498   EFI_IP_ADDRESS Gateway;
   1499   IP4_ADDR       ClientAddr;
   1500 
   1501   if ((This == NULL) || (Token == NULL) || (Token->Packet == NULL)) {
   1502     return EFI_INVALID_PARAMETER;
   1503   }
   1504 
   1505   Instance = DHCP_INSTANCE_FROM_THIS (This);
   1506   DhcpSb   = Instance->Service;
   1507 
   1508   if (Instance->Token != NULL) {
   1509     //
   1510     // The previous call to TransmitReceive is not finished.
   1511     //
   1512     return EFI_NOT_READY;
   1513   }
   1514 
   1515   if ((Token->Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC)                   ||
   1516       (NTOHL (Token->Packet->Dhcp4.Header.Xid) == Instance->Service->Xid) ||
   1517       (Token->TimeoutValue == 0)                                          ||
   1518       ((Token->ListenPointCount != 0) && (Token->ListenPoints == NULL))   ||
   1519       EFI_ERROR (DhcpValidateOptions (Token->Packet, NULL))               ||
   1520       EFI_IP4_EQUAL (&Token->RemoteAddress, &mZeroIp4Addr)
   1521       ) {
   1522     //
   1523     // The DHCP packet isn't well-formed, the Transaction ID is already used,
   1524     // the timeout value is zero, the ListenPoint is invalid, or the
   1525     // RemoteAddress is zero.
   1526     //
   1527     return EFI_INVALID_PARAMETER;
   1528   }
   1529 
   1530   ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
   1531 
   1532   if (ClientAddr == 0) {
   1533     return EFI_NO_MAPPING;
   1534   }
   1535 
   1536   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
   1537 
   1538   //
   1539   // Save the token and the timeout value.
   1540   //
   1541   Instance->Token   = Token;
   1542   Instance->Timeout = Token->TimeoutValue;
   1543 
   1544   //
   1545   // Create a UDP IO for this transmit receive transaction.
   1546   //
   1547   Status = Dhcp4InstanceCreateUdpIo (Instance);
   1548   if (EFI_ERROR (Status)) {
   1549     goto ON_ERROR;
   1550   }
   1551 
   1552   //
   1553   // Save the Client Address is sent out
   1554   //
   1555   CopyMem (
   1556     &DhcpSb->ClientAddressSendOut[0],
   1557     &Token->Packet->Dhcp4.Header.ClientHwAddr[0],
   1558     Token->Packet->Dhcp4.Header.HwAddrLen
   1559     );
   1560 
   1561   //
   1562   // Wrap the DHCP packet into a net buffer.
   1563   //
   1564   Frag.Bulk = (UINT8 *) &Token->Packet->Dhcp4;
   1565   Frag.Len  = Token->Packet->Length;
   1566   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
   1567   if (Wrap == NULL) {
   1568     Status = EFI_OUT_OF_RESOURCES;
   1569     goto ON_ERROR;
   1570   }
   1571 
   1572   //
   1573   // Set the local address and local port to ZERO.
   1574   //
   1575   ZeroMem (&EndPoint, sizeof (UDP_END_POINT));
   1576 
   1577   //
   1578   // Set the destination address and destination port.
   1579   //
   1580   CopyMem (&Ip, &Token->RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
   1581   EndPoint.RemoteAddr.Addr[0] = NTOHL (Ip);
   1582 
   1583   if (Token->RemotePort == 0) {
   1584     EndPoint.RemotePort = DHCP_SERVER_PORT;
   1585   } else {
   1586     EndPoint.RemotePort = Token->RemotePort;
   1587   }
   1588 
   1589   //
   1590   // Get the gateway.
   1591   //
   1592   ZeroMem (&Gateway, sizeof (Gateway));
   1593   if (!IP4_NET_EQUAL (ClientAddr, EndPoint.RemoteAddr.Addr[0], DhcpSb->Netmask)) {
   1594     CopyMem (&Gateway.v4, &Token->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
   1595     Gateway.Addr[0] = NTOHL (Gateway.Addr[0]);
   1596   }
   1597 
   1598   //
   1599   // Transmit the DHCP packet.
   1600   //
   1601   Status = UdpIoSendDatagram (Instance->UdpIo, Wrap, &EndPoint, &Gateway, DhcpOnPacketSent, NULL);
   1602   if (EFI_ERROR (Status)) {
   1603     NetbufFree (Wrap);
   1604     goto ON_ERROR;
   1605   }
   1606 
   1607   //
   1608   // Start to receive the DHCP response.
   1609   //
   1610   Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
   1611   if (EFI_ERROR (Status)) {
   1612     goto ON_ERROR;
   1613   }
   1614 
   1615 ON_ERROR:
   1616 
   1617   if (EFI_ERROR (Status) && (Instance->UdpIo != NULL)) {
   1618     UdpIoCleanIo (Instance->UdpIo);
   1619     gBS->CloseProtocol (
   1620            Instance->UdpIo->UdpHandle,
   1621            &gEfiUdp4ProtocolGuid,
   1622            Instance->Service->Image,
   1623            Instance->Handle
   1624            );
   1625     UdpIoFreeIo (Instance->UdpIo);
   1626     Instance->UdpIo = NULL;
   1627     Instance->Token = NULL;
   1628   }
   1629 
   1630   gBS->RestoreTPL (OldTpl);
   1631 
   1632   if (!EFI_ERROR (Status) && (Token->CompletionEvent == NULL)) {
   1633     //
   1634     // Keep polling until timeout if no error happens and the CompletionEvent
   1635     // is NULL.
   1636     //
   1637     while (TRUE) {
   1638       OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
   1639       //
   1640       // Raise TPL to protect the UDPIO in instance, in case that DhcpOnTimerTick
   1641       // free it when timeout.
   1642       //
   1643       if (Instance->Timeout > 0) {
   1644         Instance->UdpIo->Protocol.Udp4->Poll (Instance->UdpIo->Protocol.Udp4);
   1645         gBS->RestoreTPL (OldTpl);
   1646       } else {
   1647         gBS->RestoreTPL (OldTpl);
   1648         break;
   1649       }
   1650     }
   1651   }
   1652 
   1653   return Status;
   1654 }
   1655 
   1656 
   1657 /**
   1658   Callback function for DhcpIterateOptions. This callback sets the
   1659   EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point
   1660   the individual DHCP option in the packet.
   1661 
   1662   @param[in]  Tag                    The DHCP option type
   1663   @param[in]  Len                    Length of the DHCP option data
   1664   @param[in]  Data                   The DHCP option data
   1665   @param[in]  Context                The context, to pass several parameters in.
   1666 
   1667   @retval EFI_SUCCESS            It always returns EFI_SUCCESS
   1668 
   1669 **/
   1670 EFI_STATUS
   1671 Dhcp4ParseCheckOption (
   1672   IN UINT8                  Tag,
   1673   IN UINT8                  Len,
   1674   IN UINT8                  *Data,
   1675   IN VOID                   *Context
   1676   )
   1677 {
   1678   DHCP_PARSE_CONTEXT        *Parse;
   1679 
   1680   Parse = (DHCP_PARSE_CONTEXT *) Context;
   1681   Parse->Index++;
   1682 
   1683   if (Parse->Index <= Parse->OptionCount) {
   1684     //
   1685     // Use BASE_CR to get the memory position of EFI_DHCP4_PACKET_OPTION for
   1686     // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only
   1687     // pass in the point to option data.
   1688     //
   1689     Parse->Option[Parse->Index - 1] = BASE_CR (Data, EFI_DHCP4_PACKET_OPTION, Data);
   1690   }
   1691 
   1692   return EFI_SUCCESS;
   1693 }
   1694 
   1695 
   1696 /**
   1697   Parses the packed DHCP option data.
   1698 
   1699   The Parse() function is used to retrieve the option list from a DHCP packet.
   1700   If *OptionCount isn't zero, and there is enough space for all the DHCP options
   1701   in the Packet, each element of PacketOptionList is set to point to somewhere in
   1702   the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
   1703   the caller should reassemble the parsed DHCP options to get the finial result.
   1704   If *OptionCount is zero or there isn't enough space for all of them, the number
   1705   of DHCP options in the Packet is returned in OptionCount.
   1706 
   1707   @param  This             Pointer to the EFI_DHCP4_PROTOCOL instance.
   1708   @param  Packet           Pointer to packet to be parsed.
   1709   @param  OptionCount      On input, the number of entries in the PacketOptionList.
   1710                            On output, the number of entries that were written into the
   1711                            PacketOptionList.
   1712   @param  PacketOptionList List of packet option entries to be filled in. End option or pad
   1713                            options are not included.
   1714 
   1715   @retval EFI_SUCCESS           The packet was successfully parsed.
   1716   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
   1717   @retval EFI_BUFFER_TOO_SMALL  One or more of the following conditions is TRUE:
   1718                                 1) *OptionCount is smaller than the number of options that
   1719                                 were found in the Packet.
   1720                                 2) PacketOptionList is NULL.
   1721 
   1722 **/
   1723 EFI_STATUS
   1724 EFIAPI
   1725 EfiDhcp4Parse (
   1726   IN EFI_DHCP4_PROTOCOL       *This,
   1727   IN EFI_DHCP4_PACKET         *Packet,
   1728   IN OUT UINT32               *OptionCount,
   1729   OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
   1730   )
   1731 {
   1732   DHCP_PARSE_CONTEXT        Context;
   1733   EFI_STATUS                Status;
   1734 
   1735   //
   1736   // First validate the parameters
   1737   //
   1738   if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {
   1739     return EFI_INVALID_PARAMETER;
   1740   }
   1741 
   1742   if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) ||
   1743       (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
   1744       EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
   1745 
   1746     return EFI_INVALID_PARAMETER;
   1747   }
   1748 
   1749   if ((*OptionCount != 0) && (PacketOptionList == NULL)) {
   1750     return EFI_BUFFER_TOO_SMALL;
   1751   }
   1752 
   1753   ZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
   1754 
   1755   Context.Option      = PacketOptionList;
   1756   Context.OptionCount = *OptionCount;
   1757   Context.Index       = 0;
   1758 
   1759   Status              = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context);
   1760 
   1761   if (EFI_ERROR (Status)) {
   1762     return Status;
   1763   }
   1764 
   1765   *OptionCount = Context.Index;
   1766 
   1767   if (Context.Index > Context.OptionCount) {
   1768     return EFI_BUFFER_TOO_SMALL;
   1769   }
   1770 
   1771   return EFI_SUCCESS;
   1772 }
   1773 
   1774 /**
   1775   Set the elapsed time based on the given instance and the pointer to the
   1776   elapsed time option.
   1777 
   1778   @param[in]      Elapsed       The pointer to the position to append.
   1779   @param[in]      Instance      The pointer to the Dhcp4 instance.
   1780 **/
   1781 VOID
   1782 SetElapsedTime (
   1783   IN     UINT16                 *Elapsed,
   1784   IN     DHCP_PROTOCOL          *Instance
   1785   )
   1786 {
   1787   WriteUnaligned16 (Elapsed, HTONS(Instance->ElaspedTime));
   1788 }
   1789