Home | History | Annotate | Download | only in Dhcp4Dxe
      1 /** @file
      2   This file implement the EFI_DHCP4_PROTOCOL interface.
      3 
      4 Copyright (c) 2006 - 2015, 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 
    664     if ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) {
    665 
    666       return EFI_INVALID_PARAMETER;
    667     }
    668   }
    669 
    670   Instance = DHCP_INSTANCE_FROM_THIS (This);
    671 
    672   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
    673     return EFI_INVALID_PARAMETER;
    674   }
    675 
    676   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
    677 
    678   DhcpSb  = Instance->Service;
    679   Config  = &DhcpSb->ActiveConfig;
    680 
    681   Status  = EFI_ACCESS_DENIED;
    682 
    683   if ((DhcpSb->DhcpState != Dhcp4Stopped) &&
    684       (DhcpSb->DhcpState != Dhcp4Init) &&
    685       (DhcpSb->DhcpState != Dhcp4InitReboot) &&
    686       (DhcpSb->DhcpState != Dhcp4Bound)) {
    687 
    688     goto ON_EXIT;
    689   }
    690 
    691   if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) {
    692     goto ON_EXIT;
    693   }
    694 
    695   if (Dhcp4CfgData != NULL) {
    696     Status = EFI_OUT_OF_RESOURCES;
    697     DhcpCleanConfigure (Config);
    698 
    699     if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) {
    700       goto ON_EXIT;
    701     }
    702 
    703     DhcpSb->UserOptionLen = 0;
    704 
    705     for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) {
    706       DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2;
    707     }
    708 
    709     DhcpSb->ActiveChild = Instance;
    710 
    711     if (DhcpSb->DhcpState == Dhcp4Stopped) {
    712       DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress);
    713 
    714       if (DhcpSb->ClientAddr != 0) {
    715         DhcpSb->DhcpState = Dhcp4InitReboot;
    716       } else {
    717         DhcpSb->DhcpState = Dhcp4Init;
    718       }
    719     }
    720 
    721     DhcpSb->ServiceState  = DHCP_CONFIGED;
    722     Status                = EFI_SUCCESS;
    723 
    724   } else if (DhcpSb->ActiveChild == Instance) {
    725     Status = EFI_SUCCESS;
    726     DhcpYieldControl (DhcpSb);
    727   }
    728 
    729 ON_EXIT:
    730   gBS->RestoreTPL (OldTpl);
    731   return Status;
    732 }
    733 
    734 
    735 /**
    736   Starts the DHCP configuration process.
    737 
    738   The Start() function starts the DHCP configuration process. This function can
    739   be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
    740   Dhcp4InitReboot state.
    741   If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
    742   driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
    743   Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
    744   If the process aborts, either by the user or by some unexpected network error,
    745   the state is restored to the Dhcp4Init state. The Start() function can be called
    746   again to restart the process.
    747   Refer to RFC 2131 for precise state transitions during this process. At the
    748   time when each event occurs in this process, the callback function that was set
    749   by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
    750   opportunity to control the process.
    751 
    752   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
    753   @param[in]  CompletionEvent If not NULL, indicates the event that will be signaled when the
    754                               EFI DHCPv4 Protocol driver is transferred into the
    755                               Dhcp4Bound state or when the DHCP process is aborted.
    756                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
    757                               check the completion status. If NULL,
    758                               EFI_DHCP4_PROTOCOL.Start() will wait until the driver
    759                               is transferred into the Dhcp4Bound state or the process fails.
    760 
    761   @retval EFI_SUCCESS           The DHCP configuration process has started, or it has completed
    762                                 when CompletionEvent is NULL.
    763   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
    764                                 state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
    765   @retval EFI_INVALID_PARAMETER This is NULL.
    766   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
    767   @retval EFI_TIMEOUT           The DHCP configuration process failed because no response was
    768                                 received from the server within the specified timeout value.
    769   @retval EFI_ABORTED           The user aborted the DHCP process.
    770   @retval EFI_ALREADY_STARTED   Some other EFI DHCPv4 Protocol instance already started the
    771                                 DHCP process.
    772   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    773   @retval EFI_NO_MEDIA          There was a media error.
    774 
    775 **/
    776 EFI_STATUS
    777 EFIAPI
    778 EfiDhcp4Start (
    779   IN EFI_DHCP4_PROTOCOL     *This,
    780   IN EFI_EVENT              CompletionEvent   OPTIONAL
    781   )
    782 {
    783   DHCP_PROTOCOL             *Instance;
    784   DHCP_SERVICE              *DhcpSb;
    785   EFI_STATUS                Status;
    786   EFI_TPL                   OldTpl;
    787 
    788   //
    789   // First validate the parameters
    790   //
    791   if (This == NULL) {
    792     return EFI_INVALID_PARAMETER;
    793   }
    794 
    795   Instance = DHCP_INSTANCE_FROM_THIS (This);
    796 
    797   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
    798     return EFI_INVALID_PARAMETER;
    799   }
    800 
    801   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
    802   DhcpSb  = Instance->Service;
    803 
    804   if (DhcpSb->DhcpState == Dhcp4Stopped) {
    805     Status = EFI_NOT_STARTED;
    806     goto ON_ERROR;
    807   }
    808 
    809   if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) {
    810     Status = EFI_ALREADY_STARTED;
    811     goto ON_ERROR;
    812   }
    813 
    814   DhcpSb->IoStatus = EFI_ALREADY_STARTED;
    815 
    816   if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) {
    817     goto ON_ERROR;
    818   }
    819 
    820 
    821   Instance->CompletionEvent = CompletionEvent;
    822 
    823   //
    824   // Restore the TPL now, don't call poll function at TPL_CALLBACK.
    825   //
    826   gBS->RestoreTPL (OldTpl);
    827 
    828   if (CompletionEvent == NULL) {
    829     while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
    830       DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
    831     }
    832 
    833     return DhcpSb->IoStatus;
    834   }
    835 
    836   return EFI_SUCCESS;
    837 
    838 ON_ERROR:
    839   gBS->RestoreTPL (OldTpl);
    840   return Status;
    841 }
    842 
    843 
    844 /**
    845   Extends the lease time by sending a request packet.
    846 
    847   The RenewRebind() function is used to manually extend the lease time when the
    848   EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
    849   not expired yet. This function will send a request packet to the previously
    850   found server (or to any server when RebindRequest is TRUE) and transfer the
    851   state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
    852   TRUE). When a response is received, the state is returned to Dhcp4Bound.
    853   If no response is received before the try count is exceeded (the RequestTryCount
    854   field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
    855   was issued by the previous server expires, the driver will return to the Dhcp4Bound
    856   state and the previous configuration is restored. The outgoing and incoming packets
    857   can be captured by the EFI_DHCP4_CALLBACK function.
    858 
    859   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
    860   @param[in]  RebindRequest   If TRUE, this function broadcasts the request packets and enters
    861                               the Dhcp4Rebinding state. Otherwise, it sends a unicast
    862                               request packet and enters the Dhcp4Renewing state.
    863   @param[in]  CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
    864                               completes or some error occurs.
    865                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
    866                               check the completion status. If NULL,
    867                               EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
    868                               until the DHCP process finishes.
    869 
    870   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the
    871                                 Dhcp4Renewing state or is back to the Dhcp4Bound state.
    872   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
    873                                 state. EFI_DHCP4_PROTOCOL.Configure() needs to
    874                                 be called.
    875   @retval EFI_INVALID_PARAMETER This is NULL.
    876   @retval EFI_TIMEOUT           There was no response from the server when the try count was
    877                                 exceeded.
    878   @retval EFI_ACCESS_DENIED     The driver is not in the Dhcp4Bound state.
    879   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    880 
    881 **/
    882 EFI_STATUS
    883 EFIAPI
    884 EfiDhcp4RenewRebind (
    885   IN EFI_DHCP4_PROTOCOL     *This,
    886   IN BOOLEAN                RebindRequest,
    887   IN EFI_EVENT              CompletionEvent   OPTIONAL
    888   )
    889 {
    890   DHCP_PROTOCOL             *Instance;
    891   DHCP_SERVICE              *DhcpSb;
    892   EFI_STATUS                Status;
    893   EFI_TPL                   OldTpl;
    894 
    895   //
    896   // First validate the parameters
    897   //
    898   if (This == NULL) {
    899     return EFI_INVALID_PARAMETER;
    900   }
    901 
    902   Instance = DHCP_INSTANCE_FROM_THIS (This);
    903 
    904   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
    905     return EFI_INVALID_PARAMETER;
    906   }
    907 
    908   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
    909   DhcpSb  = Instance->Service;
    910 
    911   if (DhcpSb->DhcpState == Dhcp4Stopped) {
    912     Status = EFI_NOT_STARTED;
    913     goto ON_EXIT;
    914   }
    915 
    916   if (DhcpSb->DhcpState != Dhcp4Bound) {
    917     Status = EFI_ACCESS_DENIED;
    918     goto ON_EXIT;
    919   }
    920 
    921   if (DHCP_IS_BOOTP (DhcpSb->Para)) {
    922     Status = EFI_SUCCESS;
    923     goto ON_EXIT;
    924   }
    925 
    926   //
    927   // Transit the states then send a extra DHCP request
    928   //
    929   if (!RebindRequest) {
    930     DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
    931   } else {
    932     DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
    933   }
    934 
    935   //
    936   // Clear initial time to make sure that elapsed-time
    937   // is set to 0 for first REQUEST in renewal process.
    938   //
    939   Instance->ElaspedTime = 0;
    940 
    941   Status = DhcpSendMessage (
    942              DhcpSb,
    943              DhcpSb->Selected,
    944              DhcpSb->Para,
    945              DHCP_MSG_REQUEST,
    946              (UINT8 *) "Extra renew/rebind by the application"
    947              );
    948 
    949   if (EFI_ERROR (Status)) {
    950     DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
    951     goto ON_EXIT;
    952   }
    953 
    954   DhcpSb->ExtraRefresh        = TRUE;
    955   DhcpSb->IoStatus            = EFI_ALREADY_STARTED;
    956   Instance->RenewRebindEvent  = CompletionEvent;
    957 
    958   gBS->RestoreTPL (OldTpl);
    959 
    960   if (CompletionEvent == NULL) {
    961     while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
    962       DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
    963 
    964     }
    965 
    966     return DhcpSb->IoStatus;
    967   }
    968 
    969   return EFI_SUCCESS;
    970 
    971 ON_EXIT:
    972   gBS->RestoreTPL (OldTpl);
    973   return Status;
    974 }
    975 
    976 
    977 /**
    978   Releases the current address configuration.
    979 
    980   The Release() function releases the current configured IP address by doing either
    981   of the following:
    982   * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
    983     Dhcp4Bound state
    984   * Setting the previously assigned IP address that was provided with the
    985     EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
    986     Dhcp4InitReboot state
    987   After a successful call to this function, the EFI DHCPv4 Protocol driver returns
    988   to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
    989 
    990   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
    991 
    992   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
    993   @retval EFI_INVALID_PARAMETER This is NULL.
    994   @retval EFI_ACCESS_DENIED     The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
    995   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    996 
    997 **/
    998 EFI_STATUS
    999 EFIAPI
   1000 EfiDhcp4Release (
   1001   IN EFI_DHCP4_PROTOCOL     *This
   1002   )
   1003 {
   1004   DHCP_PROTOCOL             *Instance;
   1005   DHCP_SERVICE              *DhcpSb;
   1006   EFI_STATUS                Status;
   1007   EFI_TPL                   OldTpl;
   1008 
   1009   //
   1010   // First validate the parameters
   1011   //
   1012   if (This == NULL) {
   1013     return EFI_INVALID_PARAMETER;
   1014   }
   1015 
   1016   Instance = DHCP_INSTANCE_FROM_THIS (This);
   1017 
   1018   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
   1019     return EFI_INVALID_PARAMETER;
   1020   }
   1021 
   1022   Status  = EFI_SUCCESS;
   1023   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
   1024   DhcpSb  = Instance->Service;
   1025 
   1026   if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) {
   1027     Status = EFI_ACCESS_DENIED;
   1028     goto ON_EXIT;
   1029   }
   1030 
   1031   if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) {
   1032     Status = DhcpSendMessage (
   1033                DhcpSb,
   1034                DhcpSb->Selected,
   1035                DhcpSb->Para,
   1036                DHCP_MSG_RELEASE,
   1037                NULL
   1038                );
   1039 
   1040     if (EFI_ERROR (Status)) {
   1041       Status = EFI_DEVICE_ERROR;
   1042       goto ON_EXIT;
   1043     }
   1044   }
   1045 
   1046   DhcpCleanLease (DhcpSb);
   1047 
   1048 ON_EXIT:
   1049   gBS->RestoreTPL (OldTpl);
   1050   return Status;
   1051 }
   1052 
   1053 
   1054 /**
   1055   Stops the current address configuration.
   1056 
   1057   The Stop() function is used to stop the DHCP configuration process. After this
   1058   function is called successfully, the EFI DHCPv4 Protocol driver is transferred
   1059   into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
   1060   before DHCP configuration process can be started again. This function can be
   1061   called when the EFI DHCPv4 Protocol driver is in any state.
   1062 
   1063   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
   1064 
   1065   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
   1066   @retval EFI_INVALID_PARAMETER This is NULL.
   1067 
   1068 **/
   1069 EFI_STATUS
   1070 EFIAPI
   1071 EfiDhcp4Stop (
   1072   IN EFI_DHCP4_PROTOCOL     *This
   1073   )
   1074 {
   1075   DHCP_PROTOCOL             *Instance;
   1076   DHCP_SERVICE              *DhcpSb;
   1077   EFI_TPL                   OldTpl;
   1078 
   1079   //
   1080   // First validate the parameters
   1081   //
   1082   if (This == NULL) {
   1083     return EFI_INVALID_PARAMETER;
   1084   }
   1085 
   1086   Instance = DHCP_INSTANCE_FROM_THIS (This);
   1087 
   1088   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
   1089     return EFI_INVALID_PARAMETER;
   1090   }
   1091 
   1092   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
   1093   DhcpSb  = Instance->Service;
   1094 
   1095   DhcpCleanLease (DhcpSb);
   1096 
   1097   DhcpSb->DhcpState     = Dhcp4Stopped;
   1098   DhcpSb->ServiceState  = DHCP_UNCONFIGED;
   1099 
   1100   gBS->RestoreTPL (OldTpl);
   1101   return EFI_SUCCESS;
   1102 }
   1103 
   1104 
   1105 /**
   1106   Builds a DHCP packet, given the options to be appended or deleted or replaced.
   1107 
   1108   The Build() function is used to assemble a new packet from the original packet
   1109   by replacing or deleting existing options or appending new options. This function
   1110   does not change any state of the EFI DHCPv4 Protocol driver and can be used at
   1111   any time.
   1112 
   1113   @param[in]  This        Pointer to the EFI_DHCP4_PROTOCOL instance.
   1114   @param[in]  SeedPacket  Initial packet to be used as a base for building new packet.
   1115   @param[in]  DeleteCount Number of opcodes in the DeleteList.
   1116   @param[in]  DeleteList  List of opcodes to be deleted from the seed packet.
   1117                           Ignored if DeleteCount is zero.
   1118   @param[in]  AppendCount Number of entries in the OptionList.
   1119   @param[in]  AppendList  Pointer to a DHCP option list to be appended to SeedPacket.
   1120                           If SeedPacket also contains options in this list, they are
   1121                           replaced by new options (except pad option). Ignored if
   1122                           AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
   1123   @param[out] NewPacket   Pointer to storage for the pointer to the new allocated packet.
   1124                           Use the EFI Boot Service FreePool() on the resulting pointer
   1125                           when done with the packet.
   1126 
   1127   @retval EFI_SUCCESS           The new packet was built.
   1128   @retval EFI_OUT_OF_RESOURCES  Storage for the new packet could not be allocated.
   1129   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
   1130 
   1131 **/
   1132 EFI_STATUS
   1133 EFIAPI
   1134 EfiDhcp4Build (
   1135   IN EFI_DHCP4_PROTOCOL       *This,
   1136   IN EFI_DHCP4_PACKET         *SeedPacket,
   1137   IN UINT32                   DeleteCount,
   1138   IN UINT8                    *DeleteList OPTIONAL,
   1139   IN UINT32                   AppendCount,
   1140   IN EFI_DHCP4_PACKET_OPTION  *AppendList[] OPTIONAL,
   1141   OUT EFI_DHCP4_PACKET        **NewPacket
   1142   )
   1143 {
   1144   //
   1145   // First validate the parameters
   1146   //
   1147   if ((This == NULL) || (NewPacket == NULL)) {
   1148     return EFI_INVALID_PARAMETER;
   1149   }
   1150 
   1151   if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
   1152       EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) {
   1153 
   1154     return EFI_INVALID_PARAMETER;
   1155   }
   1156 
   1157   if (((DeleteCount == 0) && (AppendCount == 0)) ||
   1158       ((DeleteCount != 0) && (DeleteList == NULL)) ||
   1159       ((AppendCount != 0) && (AppendList == NULL))) {
   1160 
   1161     return EFI_INVALID_PARAMETER;
   1162   }
   1163 
   1164   return DhcpBuild (
   1165            SeedPacket,
   1166            DeleteCount,
   1167            DeleteList,
   1168            AppendCount,
   1169            AppendList,
   1170            NewPacket
   1171            );
   1172 }
   1173 
   1174 /**
   1175   Callback by UdpIoCreatePort() when creating UdpIo for this Dhcp4 instance.
   1176 
   1177   @param[in] UdpIo      The UdpIo being created.
   1178   @param[in] Context    Dhcp4 instance.
   1179 
   1180   @retval EFI_SUCCESS   UdpIo is configured successfully.
   1181   @retval other         Other error occurs.
   1182 **/
   1183 EFI_STATUS
   1184 EFIAPI
   1185 Dhcp4InstanceConfigUdpIo (
   1186   IN UDP_IO       *UdpIo,
   1187   IN VOID         *Context
   1188   )
   1189 {
   1190   DHCP_PROTOCOL                     *Instance;
   1191   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
   1192   EFI_UDP4_CONFIG_DATA              UdpConfigData;
   1193   IP4_ADDR                          ClientAddr;
   1194   IP4_ADDR                          Ip;
   1195   INTN                              Class;
   1196   IP4_ADDR                          SubnetMask;
   1197 
   1198   Instance = (DHCP_PROTOCOL *) Context;
   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   Class = NetGetIpClass (ClientAddr);
   1213   ASSERT (Class < IP4_ADDR_CLASSE);
   1214   SubnetMask = gIp4AllMasks[Class << 3];
   1215   Ip = HTONL (SubnetMask);
   1216   CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
   1217 
   1218   if ((Token->ListenPointCount == 0) || (Token->ListenPoints[0].ListenPort == 0)) {
   1219     UdpConfigData.StationPort = DHCP_CLIENT_PORT;
   1220   } else {
   1221     UdpConfigData.StationPort = Token->ListenPoints[0].ListenPort;
   1222   }
   1223 
   1224   return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
   1225 }
   1226 
   1227 /**
   1228   Create UdpIo for this Dhcp4 instance.
   1229 
   1230   @param Instance   The Dhcp4 instance.
   1231 
   1232   @retval EFI_SUCCESS                UdpIo is created successfully.
   1233   @retval EFI_OUT_OF_RESOURCES       Fails to create UdpIo because of limited
   1234                                      resources or configuration failure.
   1235 **/
   1236 EFI_STATUS
   1237 Dhcp4InstanceCreateUdpIo (
   1238   IN OUT DHCP_PROTOCOL  *Instance
   1239   )
   1240 {
   1241   DHCP_SERVICE  *DhcpSb;
   1242   EFI_STATUS    Status;
   1243   VOID          *Udp4;
   1244 
   1245   ASSERT (Instance->Token != NULL);
   1246 
   1247   DhcpSb          = Instance->Service;
   1248   Instance->UdpIo = UdpIoCreateIo (
   1249                       DhcpSb->Controller,
   1250                       DhcpSb->Image,
   1251                       Dhcp4InstanceConfigUdpIo,
   1252                       UDP_IO_UDP4_VERSION,
   1253                       Instance
   1254                       );
   1255   if (Instance->UdpIo == NULL) {
   1256     return EFI_OUT_OF_RESOURCES;
   1257   } else {
   1258     Status = gBS->OpenProtocol (
   1259                     Instance->UdpIo->UdpHandle,
   1260                     &gEfiUdp4ProtocolGuid,
   1261                     (VOID **) &Udp4,
   1262                     Instance->Service->Image,
   1263                     Instance->Handle,
   1264                     EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
   1265                     );
   1266     if (EFI_ERROR (Status)) {
   1267       UdpIoFreeIo (Instance->UdpIo);
   1268       Instance->UdpIo = NULL;
   1269     }
   1270     return Status;
   1271   }
   1272 }
   1273 
   1274 /**
   1275   Callback of Dhcp packet. Does nothing.
   1276 
   1277   @param Arg           The context.
   1278 
   1279 **/
   1280 VOID
   1281 EFIAPI
   1282 DhcpDummyExtFree (
   1283   IN VOID                   *Arg
   1284   )
   1285 {
   1286 }
   1287 
   1288 /**
   1289   Callback of UdpIoRecvDatagram() that handles a Dhcp4 packet.
   1290 
   1291   Only BOOTP responses will be handled that correspond to the Xid of the request
   1292   sent out. The packet will be queued to the response queue.
   1293 
   1294   @param UdpPacket        The Dhcp4 packet.
   1295   @param EndPoint         Udp4 address pair.
   1296   @param IoStatus         Status of the input.
   1297   @param Context          Extra info for the input.
   1298 
   1299 **/
   1300 VOID
   1301 EFIAPI
   1302 PxeDhcpInput (
   1303   NET_BUF                   *UdpPacket,
   1304   UDP_END_POINT             *EndPoint,
   1305   EFI_STATUS                IoStatus,
   1306   VOID                      *Context
   1307   )
   1308 {
   1309   DHCP_PROTOCOL                     *Instance;
   1310   EFI_DHCP4_HEADER                  *Head;
   1311   NET_BUF                           *Wrap;
   1312   EFI_DHCP4_PACKET                  *Packet;
   1313   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
   1314   UINT32                            Len;
   1315   EFI_STATUS                        Status;
   1316 
   1317   Wrap     = NULL;
   1318   Instance = (DHCP_PROTOCOL *) Context;
   1319   Token    = Instance->Token;
   1320 
   1321   //
   1322   // Don't restart receive if error occurs or DHCP is destroyed.
   1323   //
   1324   if (EFI_ERROR (IoStatus)) {
   1325     return ;
   1326   }
   1327 
   1328   ASSERT (UdpPacket != NULL);
   1329 
   1330   //
   1331   // Validate the packet received
   1332   //
   1333   if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
   1334     goto RESTART;
   1335   }
   1336 
   1337   //
   1338   // Copy the DHCP message to a continuous memory block, make the buffer size
   1339   // of the EFI_DHCP4_PACKET a multiple of 4-byte.
   1340   //
   1341   Len  = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4);
   1342   Wrap = NetbufAlloc (Len);
   1343   if (Wrap == NULL) {
   1344     goto RESTART;
   1345   }
   1346 
   1347   Packet         = (EFI_DHCP4_PACKET *) NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL);
   1348   ASSERT (Packet != NULL);
   1349 
   1350   Packet->Size   = Len;
   1351   Head           = &Packet->Dhcp4.Header;
   1352   Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
   1353 
   1354   if (Packet->Length != UdpPacket->TotalSize) {
   1355     goto RESTART;
   1356   }
   1357 
   1358   //
   1359   // Is this packet the answer to our packet?
   1360   //
   1361   if ((Head->OpCode != BOOTP_REPLY) ||
   1362       (Head->Xid != Token->Packet->Dhcp4.Header.Xid) ||
   1363       (CompareMem (&Token->Packet->Dhcp4.Header.ClientHwAddr[0], Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
   1364     goto RESTART;
   1365   }
   1366 
   1367   //
   1368   // Validate the options and retrieve the interested options
   1369   //
   1370   if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
   1371       (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
   1372       EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
   1373 
   1374     goto RESTART;
   1375   }
   1376 
   1377   //
   1378   // Keep this packet in the ResponseQueue.
   1379   //
   1380   NET_GET_REF (Wrap);
   1381   NetbufQueAppend (&Instance->ResponseQueue, Wrap);
   1382 
   1383 RESTART:
   1384 
   1385   NetbufFree (UdpPacket);
   1386 
   1387   if (Wrap != NULL) {
   1388     NetbufFree (Wrap);
   1389   }
   1390 
   1391   Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
   1392   if (EFI_ERROR (Status)) {
   1393     PxeDhcpDone (Instance);
   1394   }
   1395 }
   1396 
   1397 /**
   1398   Complete a Dhcp4 transaction and signal the upper layer.
   1399 
   1400   @param Instance      Dhcp4 instance.
   1401 
   1402 **/
   1403 VOID
   1404 PxeDhcpDone (
   1405   IN DHCP_PROTOCOL  *Instance
   1406   )
   1407 {
   1408   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
   1409 
   1410   Token = Instance->Token;
   1411 
   1412   Token->ResponseCount = Instance->ResponseQueue.BufNum;
   1413   if (Token->ResponseCount != 0) {
   1414     Token->ResponseList = (EFI_DHCP4_PACKET *) AllocatePool (Instance->ResponseQueue.BufSize);
   1415     if (Token->ResponseList == NULL) {
   1416       Token->Status = EFI_OUT_OF_RESOURCES;
   1417       goto SIGNAL_USER;
   1418     }
   1419 
   1420     //
   1421     // Copy the received DHCP responses.
   1422     //
   1423     NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *) Token->ResponseList);
   1424     Token->Status = EFI_SUCCESS;
   1425   } else {
   1426     Token->ResponseList = NULL;
   1427     Token->Status       = EFI_TIMEOUT;
   1428   }
   1429 
   1430 SIGNAL_USER:
   1431   //
   1432   // Clean up the resources dedicated for this transmit receive transaction.
   1433   //
   1434   NetbufQueFlush (&Instance->ResponseQueue);
   1435   UdpIoCleanIo (Instance->UdpIo);
   1436   gBS->CloseProtocol (
   1437          Instance->UdpIo->UdpHandle,
   1438          &gEfiUdp4ProtocolGuid,
   1439          Instance->Service->Image,
   1440          Instance->Handle
   1441          );
   1442   UdpIoFreeIo (Instance->UdpIo);
   1443   Instance->UdpIo = NULL;
   1444   Instance->Token = NULL;
   1445 
   1446   if (Token->CompletionEvent != NULL) {
   1447     gBS->SignalEvent (Token->CompletionEvent);
   1448   }
   1449 }
   1450 
   1451 
   1452 /**
   1453   Transmits a DHCP formatted packet and optionally waits for responses.
   1454 
   1455   The TransmitReceive() function is used to transmit a DHCP packet and optionally
   1456   wait for the response from servers. This function does not change the state of
   1457   the EFI DHCPv4 Protocol driver and thus can be used at any time.
   1458 
   1459   @param[in]  This    Pointer to the EFI_DHCP4_PROTOCOL instance.
   1460   @param[in]  Token   Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
   1461 
   1462   @retval EFI_SUCCESS           The packet was successfully queued for transmission.
   1463   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
   1464   @retval EFI_NOT_READY         The previous call to this function has not finished yet. Try to call
   1465                                 this function after collection process completes.
   1466   @retval EFI_NO_MAPPING        The default station address is not available yet.
   1467   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
   1468   @retval Others                Some other unexpected error occurred.
   1469 
   1470 **/
   1471 EFI_STATUS
   1472 EFIAPI
   1473 EfiDhcp4TransmitReceive (
   1474   IN EFI_DHCP4_PROTOCOL                *This,
   1475   IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token
   1476   )
   1477 {
   1478   DHCP_PROTOCOL  *Instance;
   1479   EFI_TPL        OldTpl;
   1480   EFI_STATUS     Status;
   1481   NET_FRAGMENT   Frag;
   1482   NET_BUF        *Wrap;
   1483   UDP_END_POINT  EndPoint;
   1484   IP4_ADDR       Ip;
   1485   DHCP_SERVICE   *DhcpSb;
   1486   EFI_IP_ADDRESS Gateway;
   1487   IP4_ADDR       ClientAddr;
   1488   INTN           Class;
   1489   IP4_ADDR       SubnetMask;
   1490 
   1491   if ((This == NULL) || (Token == NULL) || (Token->Packet == NULL)) {
   1492     return EFI_INVALID_PARAMETER;
   1493   }
   1494 
   1495   Instance = DHCP_INSTANCE_FROM_THIS (This);
   1496   DhcpSb   = Instance->Service;
   1497 
   1498   if (Instance->Token != NULL) {
   1499     //
   1500     // The previous call to TransmitReceive is not finished.
   1501     //
   1502     return EFI_NOT_READY;
   1503   }
   1504 
   1505   if ((Token->Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC)                   ||
   1506       (NTOHL (Token->Packet->Dhcp4.Header.Xid) == Instance->Service->Xid) ||
   1507       (Token->TimeoutValue == 0)                                          ||
   1508       ((Token->ListenPointCount != 0) && (Token->ListenPoints == NULL))   ||
   1509       EFI_ERROR (DhcpValidateOptions (Token->Packet, NULL))               ||
   1510       EFI_IP4_EQUAL (&Token->RemoteAddress, &mZeroIp4Addr)
   1511       ) {
   1512     //
   1513     // The DHCP packet isn't well-formed, the Transaction ID is already used,
   1514     // the timeout value is zero, the ListenPoint is invalid, or the
   1515     // RemoteAddress is zero.
   1516     //
   1517     return EFI_INVALID_PARAMETER;
   1518   }
   1519 
   1520   ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
   1521 
   1522   if (ClientAddr == 0) {
   1523     return EFI_NO_MAPPING;
   1524   }
   1525 
   1526   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
   1527 
   1528   //
   1529   // Save the token and the timeout value.
   1530   //
   1531   Instance->Token   = Token;
   1532   Instance->Timeout = Token->TimeoutValue;
   1533 
   1534   //
   1535   // Create a UDP IO for this transmit receive transaction.
   1536   //
   1537   Status = Dhcp4InstanceCreateUdpIo (Instance);
   1538   if (EFI_ERROR (Status)) {
   1539     goto ON_ERROR;
   1540   }
   1541 
   1542   //
   1543   // Save the Client Address is sent out
   1544   //
   1545   CopyMem (
   1546     &DhcpSb->ClientAddressSendOut[0],
   1547     &Token->Packet->Dhcp4.Header.ClientHwAddr[0],
   1548     Token->Packet->Dhcp4.Header.HwAddrLen
   1549     );
   1550 
   1551   //
   1552   // Wrap the DHCP packet into a net buffer.
   1553   //
   1554   Frag.Bulk = (UINT8 *) &Token->Packet->Dhcp4;
   1555   Frag.Len  = Token->Packet->Length;
   1556   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
   1557   if (Wrap == NULL) {
   1558     Status = EFI_OUT_OF_RESOURCES;
   1559     goto ON_ERROR;
   1560   }
   1561 
   1562   //
   1563   // Set the local address and local port to ZERO.
   1564   //
   1565   ZeroMem (&EndPoint, sizeof (UDP_END_POINT));
   1566 
   1567   //
   1568   // Set the destination address and destination port.
   1569   //
   1570   CopyMem (&Ip, &Token->RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
   1571   EndPoint.RemoteAddr.Addr[0] = NTOHL (Ip);
   1572 
   1573   if (Token->RemotePort == 0) {
   1574     EndPoint.RemotePort = DHCP_SERVER_PORT;
   1575   } else {
   1576     EndPoint.RemotePort = Token->RemotePort;
   1577   }
   1578 
   1579   //
   1580   // Get the gateway.
   1581   //
   1582   Class = NetGetIpClass (ClientAddr);
   1583   ASSERT (Class < IP4_ADDR_CLASSE);
   1584   SubnetMask = gIp4AllMasks[Class << 3];
   1585   ZeroMem (&Gateway, sizeof (Gateway));
   1586   if (!IP4_NET_EQUAL (ClientAddr, EndPoint.RemoteAddr.Addr[0], SubnetMask)) {
   1587     CopyMem (&Gateway.v4, &Token->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
   1588     Gateway.Addr[0] = NTOHL (Gateway.Addr[0]);
   1589   }
   1590 
   1591   //
   1592   // Transmit the DHCP packet.
   1593   //
   1594   Status = UdpIoSendDatagram (Instance->UdpIo, Wrap, &EndPoint, &Gateway, DhcpOnPacketSent, NULL);
   1595   if (EFI_ERROR (Status)) {
   1596     NetbufFree (Wrap);
   1597     goto ON_ERROR;
   1598   }
   1599 
   1600   //
   1601   // Start to receive the DHCP response.
   1602   //
   1603   Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
   1604   if (EFI_ERROR (Status)) {
   1605     goto ON_ERROR;
   1606   }
   1607 
   1608 ON_ERROR:
   1609 
   1610   if (EFI_ERROR (Status) && (Instance->UdpIo != NULL)) {
   1611     UdpIoCleanIo (Instance->UdpIo);
   1612     gBS->CloseProtocol (
   1613            Instance->UdpIo->UdpHandle,
   1614            &gEfiUdp4ProtocolGuid,
   1615            Instance->Service->Image,
   1616            Instance->Handle
   1617            );
   1618     UdpIoFreeIo (Instance->UdpIo);
   1619     Instance->UdpIo = NULL;
   1620     Instance->Token = NULL;
   1621   }
   1622 
   1623   gBS->RestoreTPL (OldTpl);
   1624 
   1625   if (!EFI_ERROR (Status) && (Token->CompletionEvent == NULL)) {
   1626     //
   1627     // Keep polling until timeout if no error happens and the CompletionEvent
   1628     // is NULL.
   1629     //
   1630     while (TRUE) {
   1631       OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
   1632       //
   1633       // Raise TPL to protect the UDPIO in instance, in case that DhcpOnTimerTick
   1634       // free it when timeout.
   1635       //
   1636       if (Instance->Timeout > 0) {
   1637         Instance->UdpIo->Protocol.Udp4->Poll (Instance->UdpIo->Protocol.Udp4);
   1638         gBS->RestoreTPL (OldTpl);
   1639       } else {
   1640         gBS->RestoreTPL (OldTpl);
   1641         break;
   1642       }
   1643     }
   1644   }
   1645 
   1646   return Status;
   1647 }
   1648 
   1649 
   1650 /**
   1651   Callback function for DhcpIterateOptions. This callback sets the
   1652   EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point
   1653   the individual DHCP option in the packet.
   1654 
   1655   @param[in]  Tag                    The DHCP option type
   1656   @param[in]  Len                    Length of the DHCP option data
   1657   @param[in]  Data                   The DHCP option data
   1658   @param[in]  Context                The context, to pass several parameters in.
   1659 
   1660   @retval EFI_SUCCESS            It always returns EFI_SUCCESS
   1661 
   1662 **/
   1663 EFI_STATUS
   1664 Dhcp4ParseCheckOption (
   1665   IN UINT8                  Tag,
   1666   IN UINT8                  Len,
   1667   IN UINT8                  *Data,
   1668   IN VOID                   *Context
   1669   )
   1670 {
   1671   DHCP_PARSE_CONTEXT        *Parse;
   1672 
   1673   Parse = (DHCP_PARSE_CONTEXT *) Context;
   1674   Parse->Index++;
   1675 
   1676   if (Parse->Index <= Parse->OptionCount) {
   1677     //
   1678     // Use BASE_CR to get the memory position of EFI_DHCP4_PACKET_OPTION for
   1679     // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only
   1680     // pass in the point to option data.
   1681     //
   1682     Parse->Option[Parse->Index - 1] = BASE_CR (Data, EFI_DHCP4_PACKET_OPTION, Data);
   1683   }
   1684 
   1685   return EFI_SUCCESS;
   1686 }
   1687 
   1688 
   1689 /**
   1690   Parses the packed DHCP option data.
   1691 
   1692   The Parse() function is used to retrieve the option list from a DHCP packet.
   1693   If *OptionCount isn't zero, and there is enough space for all the DHCP options
   1694   in the Packet, each element of PacketOptionList is set to point to somewhere in
   1695   the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
   1696   the caller should reassemble the parsed DHCP options to get the finial result.
   1697   If *OptionCount is zero or there isn't enough space for all of them, the number
   1698   of DHCP options in the Packet is returned in OptionCount.
   1699 
   1700   @param  This             Pointer to the EFI_DHCP4_PROTOCOL instance.
   1701   @param  Packet           Pointer to packet to be parsed.
   1702   @param  OptionCount      On input, the number of entries in the PacketOptionList.
   1703                            On output, the number of entries that were written into the
   1704                            PacketOptionList.
   1705   @param  PacketOptionList List of packet option entries to be filled in. End option or pad
   1706                            options are not included.
   1707 
   1708   @retval EFI_SUCCESS           The packet was successfully parsed.
   1709   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
   1710   @retval EFI_BUFFER_TOO_SMALL  One or more of the following conditions is TRUE:
   1711                                 1) *OptionCount is smaller than the number of options that
   1712                                 were found in the Packet.
   1713                                 2) PacketOptionList is NULL.
   1714 
   1715 **/
   1716 EFI_STATUS
   1717 EFIAPI
   1718 EfiDhcp4Parse (
   1719   IN EFI_DHCP4_PROTOCOL       *This,
   1720   IN EFI_DHCP4_PACKET         *Packet,
   1721   IN OUT UINT32               *OptionCount,
   1722   OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
   1723   )
   1724 {
   1725   DHCP_PARSE_CONTEXT        Context;
   1726   EFI_STATUS                Status;
   1727 
   1728   //
   1729   // First validate the parameters
   1730   //
   1731   if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {
   1732     return EFI_INVALID_PARAMETER;
   1733   }
   1734 
   1735   if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) ||
   1736       (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
   1737       EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
   1738 
   1739     return EFI_INVALID_PARAMETER;
   1740   }
   1741 
   1742   if ((*OptionCount != 0) && (PacketOptionList == NULL)) {
   1743     return EFI_BUFFER_TOO_SMALL;
   1744   }
   1745 
   1746   ZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
   1747 
   1748   Context.Option      = PacketOptionList;
   1749   Context.OptionCount = *OptionCount;
   1750   Context.Index       = 0;
   1751 
   1752   Status              = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context);
   1753 
   1754   if (EFI_ERROR (Status)) {
   1755     return Status;
   1756   }
   1757 
   1758   *OptionCount = Context.Index;
   1759 
   1760   if (Context.Index > Context.OptionCount) {
   1761     return EFI_BUFFER_TOO_SMALL;
   1762   }
   1763 
   1764   return EFI_SUCCESS;
   1765 }
   1766 
   1767 /**
   1768   Set the elapsed time based on the given instance and the pointer to the
   1769   elapsed time option.
   1770 
   1771   @param[in]      Elapsed       The pointer to the position to append.
   1772   @param[in]      Instance      The pointer to the Dhcp4 instance.
   1773 **/
   1774 VOID
   1775 SetElapsedTime (
   1776   IN     UINT16                 *Elapsed,
   1777   IN     DHCP_PROTOCOL          *Instance
   1778   )
   1779 {
   1780   WriteUnaligned16 (Elapsed, HTONS(Instance->ElaspedTime));
   1781 }
   1782