Home | History | Annotate | Download | only in UefiPxeBcDxe
      1 /** @file
      2   Functions implementation related with DHCPv6 for UefiPxeBc Driver.
      3 
      4   (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
      5   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
      6 
      7   This program and the accompanying materials
      8   are licensed and made available under the terms and conditions of the BSD License
      9   which accompanies this distribution.  The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php.
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 #include "PxeBcImpl.h"
     18 
     19 //
     20 // Well-known multi-cast address defined in section-24.1 of rfc-3315
     21 //
     22 //   ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
     23 //
     24 EFI_IPv6_ADDRESS   mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
     25 
     26 /**
     27   Parse out a DHCPv6 option by OptTag, and find the position in buffer.
     28 
     29   @param[in]  Buffer        The pointer to the option buffer.
     30   @param[in]  Length        Length of the option buffer.
     31   @param[in]  OptTag        The required option tag.
     32 
     33   @retval     NULL          Failed to parse the required option.
     34   @retval     Others        The postion of the required option in buffer.
     35 
     36 **/
     37 EFI_DHCP6_PACKET_OPTION *
     38 PxeBcParseDhcp6Options (
     39   IN UINT8                       *Buffer,
     40   IN UINT32                      Length,
     41   IN UINT16                      OptTag
     42   )
     43 {
     44   EFI_DHCP6_PACKET_OPTION        *Option;
     45   UINT32                         Offset;
     46 
     47   Option  = (EFI_DHCP6_PACKET_OPTION *) Buffer;
     48   Offset  = 0;
     49 
     50   //
     51   // OpLen and OpCode here are both stored in network order.
     52   //
     53   while (Offset < Length) {
     54 
     55     if (NTOHS (Option->OpCode) == OptTag) {
     56 
     57       return Option;
     58     }
     59 
     60     Offset += (NTOHS(Option->OpLen) + 4);
     61     Option  = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);
     62   }
     63 
     64   return NULL;
     65 }
     66 
     67 
     68 /**
     69   Build the options buffer for the DHCPv6 request packet.
     70 
     71   @param[in]  Private             The pointer to PxeBc private data.
     72   @param[out] OptList             The pointer to the option pointer array.
     73   @param[in]  Buffer              The pointer to the buffer to contain the option list.
     74 
     75   @return     Index               The count of the built-in options.
     76 
     77 **/
     78 UINT32
     79 PxeBcBuildDhcp6Options (
     80   IN  PXEBC_PRIVATE_DATA           *Private,
     81   OUT EFI_DHCP6_PACKET_OPTION      **OptList,
     82   IN  UINT8                        *Buffer
     83   )
     84 {
     85   PXEBC_DHCP6_OPTION_ENTRY         OptEnt;
     86   UINT32                           Index;
     87   UINT16                           Value;
     88 
     89   Index       = 0;
     90   OptList[0]  = (EFI_DHCP6_PACKET_OPTION *) Buffer;
     91 
     92   //
     93   // Append client option request option
     94   //
     95   OptList[Index]->OpCode     = HTONS (DHCP6_OPT_ORO);
     96   OptList[Index]->OpLen      = HTONS (6);
     97   OptEnt.Oro                 = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;
     98   OptEnt.Oro->OpCode[0]      = HTONS(DHCP6_OPT_BOOT_FILE_URL);
     99   OptEnt.Oro->OpCode[1]      = HTONS(DHCP6_OPT_BOOT_FILE_PARAM);
    100   OptEnt.Oro->OpCode[2]      = HTONS(DHCP6_OPT_DNS_SERVERS);
    101   Index++;
    102   OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
    103 
    104   //
    105   // Append client network device interface option
    106   //
    107   OptList[Index]->OpCode     = HTONS (DHCP6_OPT_UNDI);
    108   OptList[Index]->OpLen      = HTONS ((UINT16)3);
    109   OptEnt.Undi                = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;
    110 
    111   if (Private->Nii != NULL) {
    112     OptEnt.Undi->Type        = Private->Nii->Type;
    113     OptEnt.Undi->MajorVer    = Private->Nii->MajorVer;
    114     OptEnt.Undi->MinorVer    = Private->Nii->MinorVer;
    115   } else {
    116     OptEnt.Undi->Type        = DEFAULT_UNDI_TYPE;
    117     OptEnt.Undi->MajorVer    = DEFAULT_UNDI_MAJOR;
    118     OptEnt.Undi->MinorVer    = DEFAULT_UNDI_MINOR;
    119   }
    120 
    121   Index++;
    122   OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
    123 
    124   //
    125   // Append client system architecture option
    126   //
    127   OptList[Index]->OpCode     = HTONS (DHCP6_OPT_ARCH);
    128   OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));
    129   OptEnt.Arch                = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;
    130   Value                      = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
    131   CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
    132   Index++;
    133   OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
    134 
    135   //
    136   // Append vendor class option to store the PXE class identifier.
    137   //
    138   OptList[Index]->OpCode       = HTONS (DHCP6_OPT_VENDOR_CLASS);
    139   OptList[Index]->OpLen        = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));
    140   OptEnt.VendorClass           = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;
    141   OptEnt.VendorClass->Vendor   = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);
    142   OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID));
    143   CopyMem (
    144     &OptEnt.VendorClass->ClassId,
    145     DEFAULT_CLASS_ID_DATA,
    146     sizeof (PXEBC_CLASS_ID)
    147     );
    148   PxeBcUintnToAscDecWithFormat (
    149     EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,
    150     OptEnt.VendorClass->ClassId.ArchitectureType,
    151     sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)
    152     );
    153 
    154   if (Private->Nii != NULL) {
    155     CopyMem (
    156       OptEnt.VendorClass->ClassId.InterfaceName,
    157       Private->Nii->StringId,
    158       sizeof (OptEnt.VendorClass->ClassId.InterfaceName)
    159       );
    160     PxeBcUintnToAscDecWithFormat (
    161       Private->Nii->MajorVer,
    162       OptEnt.VendorClass->ClassId.UndiMajor,
    163       sizeof (OptEnt.VendorClass->ClassId.UndiMajor)
    164       );
    165     PxeBcUintnToAscDecWithFormat (
    166       Private->Nii->MinorVer,
    167       OptEnt.VendorClass->ClassId.UndiMinor,
    168       sizeof (OptEnt.VendorClass->ClassId.UndiMinor)
    169       );
    170   }
    171 
    172   Index++;
    173 
    174   return Index;
    175 }
    176 
    177 
    178 /**
    179   Cache the DHCPv6 packet.
    180 
    181   @param[in]  Dst          The pointer to the cache buffer for DHCPv6 packet.
    182   @param[in]  Src          The pointer to the DHCPv6 packet to be cached.
    183 
    184   @retval     EFI_SUCCESS                Packet is copied.
    185   @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.
    186 
    187 **/
    188 EFI_STATUS
    189 PxeBcCacheDhcp6Packet (
    190   IN EFI_DHCP6_PACKET          *Dst,
    191   IN EFI_DHCP6_PACKET          *Src
    192   )
    193 {
    194   if (Dst->Size < Src->Length) {
    195     return EFI_BUFFER_TOO_SMALL;
    196   }
    197 
    198   CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
    199   Dst->Length = Src->Length;
    200 
    201   return EFI_SUCCESS;
    202 }
    203 
    204 
    205 /**
    206   Free all the nodes in the list for boot file.
    207 
    208   @param[in]  Head            The pointer to the head of list.
    209 
    210 **/
    211 VOID
    212 PxeBcFreeBootFileOption (
    213   IN LIST_ENTRY               *Head
    214   )
    215 {
    216   LIST_ENTRY                  *Entry;
    217   LIST_ENTRY                  *NextEntry;
    218   PXEBC_DHCP6_OPTION_NODE     *Node;
    219 
    220   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) {
    221     Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link);
    222     RemoveEntryList (Entry);
    223     FreePool (Node);
    224   }
    225 }
    226 
    227 /**
    228   Retrieve the boot server address using the EFI_DNS6_PROTOCOL.
    229 
    230   @param[in]  Private             Pointer to PxeBc private data.
    231   @param[in]  HostName            Pointer to buffer containing hostname.
    232   @param[out] IpAddress           On output, pointer to buffer containing IPv6 address.
    233 
    234   @retval EFI_SUCCESS             Operation succeeded.
    235   @retval EFI_OUT_OF_RESOURCES    Failed to allocate needed resources.
    236   @retval EFI_DEVICE_ERROR        An unexpected network error occurred.
    237   @retval Others                  Other errors as indicated.
    238 
    239 **/
    240 EFI_STATUS
    241 PxeBcDns6 (
    242   IN PXEBC_PRIVATE_DATA           *Private,
    243   IN     CHAR16                   *HostName,
    244      OUT EFI_IPv6_ADDRESS         *IpAddress
    245   )
    246 {
    247   EFI_STATUS                      Status;
    248   EFI_DNS6_PROTOCOL               *Dns6;
    249   EFI_DNS6_CONFIG_DATA            Dns6ConfigData;
    250   EFI_DNS6_COMPLETION_TOKEN       Token;
    251   EFI_HANDLE                      Dns6Handle;
    252   EFI_IPv6_ADDRESS                *DnsServerList;
    253   BOOLEAN                         IsDone;
    254 
    255   Dns6                = NULL;
    256   Dns6Handle          = NULL;
    257   DnsServerList       = Private->DnsServer;
    258   ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));
    259 
    260   //
    261   // Create a DNSv6 child instance and get the protocol.
    262   //
    263   Status = NetLibCreateServiceChild (
    264              Private->Controller,
    265              Private->Image,
    266              &gEfiDns6ServiceBindingProtocolGuid,
    267              &Dns6Handle
    268              );
    269   if (EFI_ERROR (Status)) {
    270     goto Exit;
    271   }
    272 
    273   Status = gBS->OpenProtocol (
    274                   Dns6Handle,
    275                   &gEfiDns6ProtocolGuid,
    276                   (VOID **) &Dns6,
    277                   Private->Image,
    278                   Private->Controller,
    279                   EFI_OPEN_PROTOCOL_BY_DRIVER
    280                   );
    281   if (EFI_ERROR (Status)) {
    282     goto Exit;
    283   }
    284 
    285   //
    286   // Configure DNS6 instance for the DNS server address and protocol.
    287   //
    288   ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA));
    289   Dns6ConfigData.DnsServerCount = 1;
    290   Dns6ConfigData.DnsServerList  = DnsServerList;
    291   Dns6ConfigData.EnableDnsCache = TRUE;
    292   Dns6ConfigData.Protocol       = EFI_IP_PROTO_UDP;
    293   IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &Private->TmpStationIp.v6);
    294   Status = Dns6->Configure (
    295                    Dns6,
    296                    &Dns6ConfigData
    297                    );
    298   if (EFI_ERROR (Status)) {
    299     goto Exit;
    300   }
    301 
    302   Token.Status = EFI_NOT_READY;
    303   IsDone       = FALSE;
    304   //
    305   // Create event to set the  IsDone flag when name resolution is finished.
    306   //
    307   Status = gBS->CreateEvent (
    308                   EVT_NOTIFY_SIGNAL,
    309                   TPL_NOTIFY,
    310                   PxeBcCommonNotify,
    311                   &IsDone,
    312                   &Token.Event
    313                   );
    314   if (EFI_ERROR (Status)) {
    315     goto Exit;
    316   }
    317 
    318   //
    319   // Start asynchronous name resolution.
    320   //
    321   Status = Dns6->HostNameToIp (Dns6, HostName, &Token);
    322   if (EFI_ERROR (Status)) {
    323     goto Exit;
    324   }
    325 
    326   while (!IsDone) {
    327     Dns6->Poll (Dns6);
    328   }
    329 
    330   //
    331   // Name resolution is done, check result.
    332   //
    333   Status = Token.Status;
    334   if (!EFI_ERROR (Status)) {
    335     if (Token.RspData.H2AData == NULL) {
    336       Status = EFI_DEVICE_ERROR;
    337       goto Exit;
    338     }
    339     if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) {
    340       Status = EFI_DEVICE_ERROR;
    341       goto Exit;
    342     }
    343     //
    344     // We just return the first IPv6 address from DNS protocol.
    345     //
    346     IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);
    347     Status = EFI_SUCCESS;
    348   }
    349 
    350 Exit:
    351   FreePool (HostName);
    352 
    353   if (Token.Event != NULL) {
    354     gBS->CloseEvent (Token.Event);
    355   }
    356   if (Token.RspData.H2AData != NULL) {
    357     if (Token.RspData.H2AData->IpList != NULL) {
    358       FreePool (Token.RspData.H2AData->IpList);
    359     }
    360     FreePool (Token.RspData.H2AData);
    361   }
    362 
    363   if (Dns6 != NULL) {
    364     Dns6->Configure (Dns6, NULL);
    365 
    366     gBS->CloseProtocol (
    367            Dns6Handle,
    368            &gEfiDns6ProtocolGuid,
    369            Private->Image,
    370            Private->Controller
    371            );
    372   }
    373 
    374   if (Dns6Handle != NULL) {
    375     NetLibDestroyServiceChild (
    376       Private->Controller,
    377       Private->Image,
    378       &gEfiDns6ServiceBindingProtocolGuid,
    379       Dns6Handle
    380       );
    381   }
    382 
    383   if (DnsServerList != NULL) {
    384     FreePool (DnsServerList);
    385   }
    386 
    387   return Status;
    388 }
    389 
    390 /**
    391   Parse the Boot File URL option.
    392 
    393   @param[in]      Private      Pointer to PxeBc private data.
    394   @param[out]     FileName     The pointer to the boot file name.
    395   @param[in, out] SrvAddr      The pointer to the boot server address.
    396   @param[in]      BootFile     The pointer to the boot file URL option data.
    397   @param[in]      Length       The length of the boot file URL option data.
    398 
    399   @retval EFI_ABORTED     User cancel operation.
    400   @retval EFI_SUCCESS     Selected the boot menu successfully.
    401   @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.
    402 
    403 **/
    404 EFI_STATUS
    405 PxeBcExtractBootFileUrl (
    406   IN PXEBC_PRIVATE_DATA      *Private,
    407      OUT UINT8               **FileName,
    408   IN OUT EFI_IPv6_ADDRESS    *SrvAddr,
    409   IN     CHAR8               *BootFile,
    410   IN     UINT16              Length
    411   )
    412 {
    413   UINT16                     PrefixLen;
    414   CHAR8                      *BootFileNamePtr;
    415   CHAR8                      *BootFileName;
    416   UINT16                     BootFileNameLen;
    417   CHAR8                      *TmpStr;
    418   CHAR8                      TmpChar;
    419   CHAR8                      *ServerAddressOption;
    420   CHAR8                      *ServerAddress;
    421   CHAR8                      *ModeStr;
    422   CHAR16                     *HostName;
    423   BOOLEAN                    IpExpressedUrl;
    424   UINTN                      Len;
    425   EFI_STATUS                 Status;
    426 
    427   IpExpressedUrl = TRUE;
    428   //
    429   // The format of the Boot File URL option is:
    430   //
    431   //  0                   1                   2                   3
    432   //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    433   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    434   // |       OPT_BOOTFILE_URL        |            option-len         |
    435   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    436   // |                                                               |
    437   // .                  bootfile-url  (variable length)              .
    438   // |                                                               |
    439   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    440   //
    441 
    442   //
    443   // Based upon RFC 5970 and UEFI 2.6, bootfile-url format can be
    444   // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME or tftp://domain_name/BOOTFILE_NAME
    445   // As an example where the BOOTFILE_NAME is the EFI loader and
    446   // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.
    447   //
    448   PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);
    449 
    450   if (Length <= PrefixLen ||
    451       CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) {
    452     return EFI_NOT_FOUND;
    453   }
    454 
    455   BootFile = BootFile + PrefixLen;
    456   Length   = (UINT16) (Length - PrefixLen);
    457 
    458   TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1);
    459   if (TmpStr == NULL) {
    460     return EFI_OUT_OF_RESOURCES;
    461   }
    462 
    463   CopyMem (TmpStr, BootFile, Length);
    464   TmpStr[Length] = '\0';
    465 
    466   //
    467   // Get the part of SERVER_ADDRESS string.
    468   //
    469   ServerAddressOption = TmpStr;
    470   if (*ServerAddressOption == PXEBC_ADDR_START_DELIMITER) {
    471     ServerAddressOption ++;
    472     ServerAddress = ServerAddressOption;
    473     while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {
    474       ServerAddress++;
    475     }
    476 
    477     if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {
    478       FreePool (TmpStr);
    479       return EFI_INVALID_PARAMETER;
    480     }
    481 
    482     *ServerAddress = '\0';
    483 
    484     //
    485     // Convert the string of server address to Ipv6 address format and store it.
    486     //
    487     Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);
    488     if (EFI_ERROR (Status)) {
    489       FreePool (TmpStr);
    490       return Status;
    491     }
    492 
    493   } else {
    494     IpExpressedUrl = FALSE;
    495     ServerAddress = ServerAddressOption;
    496     while (*ServerAddress != '\0' && *ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {
    497       ServerAddress++;
    498     }
    499 
    500     if (*ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {
    501       FreePool (TmpStr);
    502       return EFI_INVALID_PARAMETER;
    503     }
    504     *ServerAddress = '\0';
    505 
    506     Len = AsciiStrSize (ServerAddressOption);
    507     HostName = AllocateZeroPool (Len * sizeof (CHAR16));
    508     if (HostName == NULL) {
    509       FreePool (TmpStr);
    510       return EFI_OUT_OF_RESOURCES;
    511     }
    512     AsciiStrToUnicodeStrS (
    513       ServerAddressOption,
    514       HostName,
    515       Len
    516       );
    517 
    518     //
    519     // Perform DNS resolution.
    520     //
    521     Status = PxeBcDns6 (Private,HostName, SrvAddr);
    522     if (EFI_ERROR (Status)) {
    523       FreePool (TmpStr);
    524       return Status;
    525     }
    526   }
    527 
    528   //
    529   // Get the part of BOOTFILE_NAME string.
    530   //
    531   BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1);
    532   if (IpExpressedUrl) {
    533     if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {
    534       FreePool (TmpStr);
    535       return EFI_INVALID_PARAMETER;
    536     }
    537     ++BootFileNamePtr;
    538   }
    539 
    540   BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);
    541   if (BootFileNameLen != 0 || FileName != NULL) {
    542     //
    543     // Remove trailing mode=octet if present and ignore.  All other modes are
    544     // invalid for netboot6, so reject them.
    545     //
    546     ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet");
    547     if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') {
    548       *ModeStr = '\0';
    549     } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) {
    550       return EFI_INVALID_PARAMETER;
    551     }
    552 
    553     //
    554     // Extract boot file name from URL.
    555     //
    556     BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen);
    557     if (BootFileName == NULL) {
    558       FreePool (TmpStr);
    559       return EFI_OUT_OF_RESOURCES;
    560     }
    561     *FileName = (UINT8*) BootFileName;
    562 
    563     //
    564     // Decode percent-encoding in boot file name.
    565     //
    566     while (*BootFileNamePtr != '\0') {
    567       if (*BootFileNamePtr == '%') {
    568         TmpChar = *(BootFileNamePtr+ 3);
    569         *(BootFileNamePtr+ 3) = '\0';
    570         *BootFileName = (UINT8) AsciiStrHexToUintn ((CHAR8*)(BootFileNamePtr + 1));
    571         BootFileName++;
    572         *(BootFileNamePtr+ 3) = TmpChar;
    573         BootFileNamePtr += 3;
    574       } else {
    575         *BootFileName = *BootFileNamePtr;
    576         BootFileName++;
    577         BootFileNamePtr++;
    578       }
    579     }
    580     *BootFileName = '\0';
    581   }
    582 
    583   FreePool (TmpStr);
    584 
    585   return EFI_SUCCESS;
    586 }
    587 
    588 
    589 /**
    590   Parse the Boot File Parameter option.
    591 
    592   @param[in]  BootFilePara      The pointer to boot file parameter option data.
    593   @param[out] BootFileSize      The pointer to the parsed boot file size.
    594 
    595   @retval EFI_SUCCESS     Successfully obtained the boot file size from parameter option.
    596   @retval EFI_NOT_FOUND   Failed to extract the boot file size from parameter option.
    597 
    598 **/
    599 EFI_STATUS
    600 PxeBcExtractBootFileParam (
    601   IN  CHAR8                  *BootFilePara,
    602   OUT UINT16                 *BootFileSize
    603   )
    604 {
    605   UINT16                     Length;
    606   UINT8                      Index;
    607   UINT8                      Digit;
    608   UINT32                     Size;
    609 
    610   CopyMem (&Length, BootFilePara, sizeof (UINT16));
    611   Length = NTOHS (Length);
    612 
    613   //
    614   // The BootFile Size should be 1~5 byte ASCII strings
    615   //
    616   if (Length < 1 || Length > 5) {
    617     return EFI_NOT_FOUND;
    618   }
    619 
    620   //
    621   // Extract the value of BootFile Size.
    622   //
    623   BootFilePara = BootFilePara + sizeof (UINT16);
    624   Size         = 0;
    625   for (Index = 0; Index < Length; Index++) {
    626     if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {
    627       return EFI_NOT_FOUND;
    628     }
    629 
    630     Size = (Size + Digit) * 10;
    631   }
    632 
    633   Size = Size / 10;
    634   if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {
    635     return EFI_NOT_FOUND;
    636   }
    637 
    638   *BootFileSize = (UINT16) Size;
    639   return EFI_SUCCESS;
    640 }
    641 
    642 
    643 /**
    644   Parse the cached DHCPv6 packet, including all the options.
    645 
    646   @param[in]  Cache6           The pointer to a cached DHCPv6 packet.
    647 
    648   @retval     EFI_SUCCESS      Parsed the DHCPv6 packet successfully.
    649   @retval     EFI_DEVICE_ERROR Failed to parse and invalid the packet.
    650 
    651 **/
    652 EFI_STATUS
    653 PxeBcParseDhcp6Packet (
    654   IN PXEBC_DHCP6_PACKET_CACHE  *Cache6
    655   )
    656 {
    657   EFI_DHCP6_PACKET             *Offer;
    658   EFI_DHCP6_PACKET_OPTION      **Options;
    659   EFI_DHCP6_PACKET_OPTION      *Option;
    660   PXEBC_OFFER_TYPE             OfferType;
    661   BOOLEAN                      IsProxyOffer;
    662   BOOLEAN                      IsPxeOffer;
    663   UINT32                       Offset;
    664   UINT32                       Length;
    665   UINT32                       EnterpriseNum;
    666 
    667   IsProxyOffer = TRUE;
    668   IsPxeOffer   = FALSE;
    669   Offer        = &Cache6->Packet.Offer;
    670   Options      = Cache6->OptList;
    671 
    672   ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));
    673 
    674   Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);
    675   Offset  = 0;
    676   Length  = GET_DHCP6_OPTION_SIZE (Offer);
    677 
    678   //
    679   // OpLen and OpCode here are both stored in network order, since they are from original packet.
    680   //
    681   while (Offset < Length) {
    682 
    683     if (NTOHS (Option->OpCode) == DHCP6_OPT_IA_NA) {
    684       Options[PXEBC_DHCP6_IDX_IA_NA] = Option;
    685     } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_URL) {
    686       //
    687       // The server sends this option to inform the client about an URL to a boot file.
    688       //
    689       Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;
    690     } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_PARAM) {
    691       Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
    692     } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) {
    693       Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;
    694     } else if (NTOHS (Option->OpCode) == DHCP6_OPT_DNS_SERVERS) {
    695       Options[PXEBC_DHCP6_IDX_DNS_SERVER] = Option;
    696     }
    697 
    698     Offset += (NTOHS (Option->OpLen) + 4);
    699     Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);
    700   }
    701 
    702   //
    703   // The offer with assigned client address is NOT a proxy offer.
    704   // An ia_na option, embeded with valid ia_addr option and a status_code of success.
    705   //
    706   Option = Options[PXEBC_DHCP6_IDX_IA_NA];
    707   if (Option != NULL) {
    708     Option = PxeBcParseDhcp6Options (
    709                Option->Data + 12,
    710                NTOHS (Option->OpLen),
    711                DHCP6_OPT_STATUS_CODE
    712                );
    713     if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {
    714       IsProxyOffer = FALSE;
    715     }
    716   }
    717 
    718   //
    719   // The offer with "PXEClient" is a pxe offer.
    720   //
    721   Option        = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];
    722   EnterpriseNum = HTONL(PXEBC_DHCP6_ENTERPRISE_NUM);
    723 
    724   if (Option != NULL &&
    725       NTOHS(Option->OpLen) >= 13 &&
    726       CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&
    727       CompareMem (&Option->Data[6], DEFAULT_CLASS_ID_DATA, 9) == 0) {
    728     IsPxeOffer = TRUE;
    729   }
    730 
    731   //
    732   // Determine offer type of the dhcp6 packet.
    733   //
    734   if (IsPxeOffer) {
    735     //
    736     // It's a binl offer only with PXEClient.
    737     //
    738     OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;
    739   } else {
    740     //
    741     // It's a dhcp only offer, which is a pure dhcp6 offer packet.
    742     //
    743     OfferType = PxeOfferTypeDhcpOnly;
    744   }
    745 
    746   Cache6->OfferType = OfferType;
    747 
    748   return EFI_SUCCESS;
    749 }
    750 
    751 
    752 /**
    753   Cache the DHCPv6 ack packet, and parse it on demand.
    754 
    755   @param[in]  Private             The pointer to PxeBc private data.
    756   @param[in]  Ack                 The pointer to the DHCPv6 ack packet.
    757   @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.
    758 
    759   @retval     EFI_SUCCESS                Cache and parse the packet successfully.
    760   @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.
    761 
    762 **/
    763 EFI_STATUS
    764 PxeBcCopyDhcp6Ack (
    765   IN PXEBC_PRIVATE_DATA   *Private,
    766   IN EFI_DHCP6_PACKET     *Ack,
    767   IN BOOLEAN              Verified
    768   )
    769 {
    770   EFI_PXE_BASE_CODE_MODE  *Mode;
    771   EFI_STATUS              Status;
    772 
    773   Mode = Private->PxeBc.Mode;
    774 
    775   Status = PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);
    776   if (EFI_ERROR (Status)) {
    777     return Status;
    778   }
    779 
    780   if (Verified) {
    781     //
    782     // Parse the ack packet and store it into mode data if needed.
    783     //
    784     PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);
    785     CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);
    786     Mode->DhcpAckReceived = TRUE;
    787   }
    788 
    789   return EFI_SUCCESS;
    790 }
    791 
    792 
    793 /**
    794   Cache the DHCPv6 proxy offer packet according to the received order.
    795 
    796   @param[in]  Private               The pointer to PxeBc private data.
    797   @param[in]  OfferIndex            The received order of offer packets.
    798 
    799   @retval     EFI_SUCCESS                Cache and parse the packet successfully.
    800   @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.
    801 
    802 **/
    803 EFI_STATUS
    804 PxeBcCopyDhcp6Proxy (
    805   IN PXEBC_PRIVATE_DATA     *Private,
    806   IN UINT32                 OfferIndex
    807   )
    808 {
    809   EFI_PXE_BASE_CODE_MODE    *Mode;
    810   EFI_DHCP6_PACKET          *Offer;
    811   EFI_STATUS              Status;
    812 
    813   ASSERT (OfferIndex < Private->OfferNum);
    814   ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);
    815 
    816   Mode  = Private->PxeBc.Mode;
    817   Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;
    818 
    819   //
    820   // Cache the proxy offer packet and parse it.
    821   //
    822   Status = PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);
    823   if (EFI_ERROR(Status)) {
    824     return Status;
    825   }
    826   PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);
    827 
    828   //
    829   // Store this packet into mode data.
    830   //
    831   CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);
    832   Mode->ProxyOfferReceived = TRUE;
    833 
    834   return EFI_SUCCESS;
    835 }
    836 
    837 /**
    838   Seek the address of the first byte of the option header.
    839 
    840   @param[in]  Buf           The pointer to the buffer.
    841   @param[in]  SeekLen       The length to seek.
    842   @param[in]  OptType       The option type.
    843 
    844   @retval     NULL          If it failed to seek the option.
    845   @retval     others        The position to the option.
    846 
    847 **/
    848 UINT8 *
    849 PxeBcDhcp6SeekOption (
    850   IN UINT8           *Buf,
    851   IN UINT32          SeekLen,
    852   IN UINT16          OptType
    853   )
    854 {
    855   UINT8              *Cursor;
    856   UINT8              *Option;
    857   UINT16             DataLen;
    858   UINT16             OpCode;
    859 
    860   Option = NULL;
    861   Cursor = Buf;
    862 
    863   while (Cursor < Buf + SeekLen) {
    864     OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
    865     if (OpCode == HTONS (OptType)) {
    866       Option = Cursor;
    867       break;
    868     }
    869     DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
    870     Cursor += (DataLen + 4);
    871   }
    872 
    873   return Option;
    874 }
    875 
    876 
    877 /**
    878   Build and send out the request packet for the bootfile, and parse the reply.
    879 
    880   @param[in]  Private               The pointer to PxeBc private data.
    881   @param[in]  Index                 PxeBc option boot item type.
    882 
    883   @retval     EFI_SUCCESS           Successfully discovered the boot file.
    884   @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.
    885   @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
    886   @retval     Others                Failed to discover the boot file.
    887 
    888 **/
    889 EFI_STATUS
    890 PxeBcRequestBootService (
    891   IN  PXEBC_PRIVATE_DATA              *Private,
    892   IN  UINT32                          Index
    893   )
    894 {
    895   EFI_PXE_BASE_CODE_UDP_PORT          SrcPort;
    896   EFI_PXE_BASE_CODE_UDP_PORT          DestPort;
    897   EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
    898   EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;
    899   UINTN                               DiscoverLen;
    900   EFI_DHCP6_PACKET                    *Request;
    901   UINTN                               RequestLen;
    902   EFI_DHCP6_PACKET                    *Reply;
    903   UINT8                               *RequestOpt;
    904   UINT8                               *DiscoverOpt;
    905   UINTN                               ReadSize;
    906   UINT16                              OpFlags;
    907   UINT16                              OpCode;
    908   UINT16                              OpLen;
    909   EFI_STATUS                          Status;
    910   EFI_DHCP6_PACKET                    *ProxyOffer;
    911   UINT8                               *Option;
    912 
    913   PxeBc       = &Private->PxeBc;
    914   Request     = Private->Dhcp6Request;
    915   ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer;
    916   SrcPort     = PXEBC_BS_DISCOVER_PORT;
    917   DestPort    = PXEBC_BS_DISCOVER_PORT;
    918   OpFlags     = 0;
    919 
    920   if (Request == NULL) {
    921     return EFI_DEVICE_ERROR;
    922   }
    923 
    924   Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));
    925   if (Discover == NULL) {
    926     return EFI_OUT_OF_RESOURCES;
    927   }
    928 
    929   //
    930   // Build the request packet by the cached request packet before.
    931   //
    932   Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId;
    933   Discover->MessageType   = Request->Dhcp6.Header.MessageType;
    934   RequestOpt              = Request->Dhcp6.Option;
    935   DiscoverOpt             = Discover->DhcpOptions;
    936   DiscoverLen             = sizeof (EFI_DHCP6_HEADER);
    937   RequestLen              = DiscoverLen;
    938 
    939   //
    940   // Find Server ID Option from ProxyOffer.
    941   //
    942   Option = PxeBcDhcp6SeekOption (
    943              ProxyOffer->Dhcp6.Option,
    944              ProxyOffer->Length - 4,
    945              DHCP6_OPT_SERVER_ID
    946              );
    947   if (Option == NULL) {
    948     return EFI_NOT_FOUND;
    949   }
    950 
    951   //
    952   // Add Server ID Option.
    953   //
    954   OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen);
    955   CopyMem (DiscoverOpt, Option, OpLen + 4);
    956   DiscoverOpt += (OpLen + 4);
    957   DiscoverLen += (OpLen + 4);
    958 
    959   while (RequestLen < Request->Length) {
    960     OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);
    961     OpLen  = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);
    962     if (OpCode != EFI_DHCP6_IA_TYPE_NA &&
    963         OpCode != EFI_DHCP6_IA_TYPE_TA &&
    964         OpCode != DHCP6_OPT_SERVER_ID
    965         ) {
    966       //
    967       // Copy all the options except IA option and Server ID
    968       //
    969       CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);
    970       DiscoverOpt += (OpLen + 4);
    971       DiscoverLen += (OpLen + 4);
    972     }
    973     RequestOpt += (OpLen + 4);
    974     RequestLen += (OpLen + 4);
    975   }
    976 
    977   //
    978   // Update Elapsed option in the package
    979   //
    980   Option = PxeBcDhcp6SeekOption (
    981              Discover->DhcpOptions,
    982              (UINT32)(RequestLen - 4),
    983              DHCP6_OPT_ELAPSED_TIME
    984              );
    985   if (Option != NULL) {
    986     CalcElapsedTime (Private);
    987     WriteUnaligned16 ((UINT16*)(Option + 4), HTONS((UINT16) Private->ElapsedTime));
    988   }
    989 
    990   Status = PxeBc->UdpWrite (
    991                     PxeBc,
    992                     OpFlags,
    993                     &Private->ServerIp,
    994                     &DestPort,
    995                     NULL,
    996                     &Private->StationIp,
    997                     &SrcPort,
    998                     NULL,
    999                     NULL,
   1000                     &DiscoverLen,
   1001                     (VOID *) Discover
   1002                     );
   1003 
   1004   if (EFI_ERROR (Status)) {
   1005     return Status;
   1006   }
   1007 
   1008   //
   1009   // Cache the right PXE reply packet here, set valid flag later.
   1010   // Especially for PXE discover packet, store it into mode data here.
   1011   //
   1012   Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
   1013   ReadSize = (UINTN) Reply->Size;
   1014 
   1015   //
   1016   // Start Udp6Read instance
   1017   //
   1018   Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
   1019   if (EFI_ERROR (Status)) {
   1020     return Status;
   1021   }
   1022 
   1023   Status = PxeBc->UdpRead (
   1024                     PxeBc,
   1025                     EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
   1026                     NULL,
   1027                     &SrcPort,
   1028                     &Private->ServerIp,
   1029                     &DestPort,
   1030                     NULL,
   1031                     NULL,
   1032                     &ReadSize,
   1033                     (VOID *) &Reply->Dhcp6
   1034                     );
   1035   //
   1036   // Stop Udp6Read instance
   1037   //
   1038   Private->Udp6Read->Configure (Private->Udp6Read, NULL);
   1039 
   1040   if (EFI_ERROR (Status)) {
   1041     return Status;
   1042   }
   1043 
   1044   //
   1045   // Update length
   1046   //
   1047   Reply->Length = (UINT32) ReadSize;
   1048 
   1049   return EFI_SUCCESS;
   1050 }
   1051 
   1052 
   1053 /**
   1054   Retry to request bootfile name by the BINL offer.
   1055 
   1056   @param[in]  Private              The pointer to PxeBc private data.
   1057   @param[in]  Index                The received order of offer packets.
   1058 
   1059   @retval     EFI_SUCCESS          Successfully retried a request for the bootfile name.
   1060   @retval     EFI_DEVICE_ERROR     Failed to retry the bootfile name.
   1061 
   1062 **/
   1063 EFI_STATUS
   1064 PxeBcRetryDhcp6Binl (
   1065   IN PXEBC_PRIVATE_DATA  *Private,
   1066   IN UINT32              Index
   1067   )
   1068 {
   1069   EFI_PXE_BASE_CODE_MODE    *Mode;
   1070   PXEBC_DHCP6_PACKET_CACHE  *Offer;
   1071   PXEBC_DHCP6_PACKET_CACHE  *Cache6;
   1072   EFI_STATUS                Status;
   1073 
   1074   ASSERT (Index < PXEBC_OFFER_MAX_NUM);
   1075   ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||
   1076           Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl);
   1077 
   1078   Mode                  = Private->PxeBc.Mode;
   1079   Private->IsDoDiscover = FALSE;
   1080   Offer                 = &Private->OfferBuffer[Index].Dhcp6;
   1081   if (Offer->OfferType == PxeOfferTypeDhcpBinl) {
   1082     //
   1083     // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.
   1084     //
   1085     CopyMem (
   1086       &Private->ServerIp.v6,
   1087       &mAllDhcpRelayAndServersAddress,
   1088       sizeof (EFI_IPv6_ADDRESS)
   1089       );
   1090   } else {
   1091     ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
   1092     //
   1093     // Parse out the next server address from the last offer, and store it
   1094     //
   1095     Status = PxeBcExtractBootFileUrl (
   1096                Private,
   1097                &Private->BootFileName,
   1098                &Private->ServerIp.v6,
   1099                (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
   1100                NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
   1101                );
   1102     if (EFI_ERROR (Status)) {
   1103       return Status;
   1104     }
   1105   }
   1106 
   1107   //
   1108   // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.
   1109   //
   1110   Status = PxeBcRequestBootService (Private, Index);
   1111 
   1112   if (EFI_ERROR (Status)) {
   1113     return Status;
   1114   }
   1115 
   1116   Cache6 = &Private->ProxyOffer.Dhcp6;
   1117   Status = PxeBcParseDhcp6Packet (Cache6);
   1118   if (EFI_ERROR (Status)) {
   1119     return Status;
   1120   }
   1121 
   1122   if (Cache6->OfferType != PxeOfferTypeProxyPxe10 &&
   1123       Cache6->OfferType != PxeOfferTypeProxyWfm11a &&
   1124       Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
   1125     //
   1126     // This BINL ack doesn't have discovery option set or multicast option set
   1127     // or bootfile name specified.
   1128     //
   1129     return EFI_DEVICE_ERROR;
   1130   }
   1131 
   1132   Mode->ProxyOfferReceived = TRUE;
   1133   CopyMem (
   1134     &Mode->ProxyOffer.Dhcpv6,
   1135     &Cache6->Packet.Offer.Dhcp6,
   1136     Cache6->Packet.Offer.Length
   1137     );
   1138 
   1139   return EFI_SUCCESS;
   1140 }
   1141 
   1142 
   1143 /**
   1144   Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
   1145 
   1146   @param[in]  Private               The pointer to PXEBC_PRIVATE_DATA.
   1147   @param[in]  RcvdOffer             The pointer to the received offer packet.
   1148 
   1149   @retval     EFI_SUCCESS      Cache and parse the packet successfully.
   1150   @retval     Others           Operation failed.
   1151 **/
   1152 EFI_STATUS
   1153 PxeBcCacheDhcp6Offer (
   1154   IN PXEBC_PRIVATE_DATA     *Private,
   1155   IN EFI_DHCP6_PACKET       *RcvdOffer
   1156   )
   1157 {
   1158   PXEBC_DHCP6_PACKET_CACHE  *Cache6;
   1159   EFI_DHCP6_PACKET          *Offer;
   1160   PXEBC_OFFER_TYPE          OfferType;
   1161   EFI_STATUS                Status;
   1162 
   1163   Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
   1164   Offer  = &Cache6->Packet.Offer;
   1165 
   1166   //
   1167   // Cache the content of DHCPv6 packet firstly.
   1168   //
   1169   Status = PxeBcCacheDhcp6Packet (Offer, RcvdOffer);
   1170   if (EFI_ERROR (Status)) {
   1171     return Status;
   1172   }
   1173 
   1174   //
   1175   // Validate the DHCPv6 packet, and parse the options and offer type.
   1176   //
   1177   if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {
   1178     return EFI_ABORTED;
   1179   }
   1180 
   1181   //
   1182   // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
   1183   //
   1184   OfferType = Cache6->OfferType;
   1185   ASSERT (OfferType < PxeOfferTypeMax);
   1186   ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);
   1187 
   1188   if (IS_PROXY_OFFER (OfferType)) {
   1189     //
   1190     // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
   1191     //
   1192     Private->IsProxyRecved = TRUE;
   1193 
   1194     if (OfferType == PxeOfferTypeProxyBinl) {
   1195       //
   1196       // Cache all proxy BINL offers.
   1197       //
   1198       Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
   1199       Private->OfferCount[OfferType]++;
   1200     } else if (Private->OfferCount[OfferType] > 0) {
   1201       //
   1202       // Only cache the first PXE10/WFM11a offer, and discard the others.
   1203       //
   1204       Private->OfferIndex[OfferType][0] = Private->OfferNum;
   1205       Private->OfferCount[OfferType]    = 1;
   1206     } else {
   1207       return EFI_ABORTED;
   1208     }
   1209   } else {
   1210     //
   1211     // It's a DHCPv6 offer with yiaddr, and cache them all.
   1212     //
   1213     Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
   1214     Private->OfferCount[OfferType]++;
   1215   }
   1216 
   1217   Private->OfferNum++;
   1218 
   1219   return EFI_SUCCESS;
   1220 }
   1221 
   1222 
   1223 /**
   1224   Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.
   1225 
   1226   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
   1227 
   1228 **/
   1229 VOID
   1230 PxeBcSelectDhcp6Offer (
   1231   IN PXEBC_PRIVATE_DATA     *Private
   1232   )
   1233 {
   1234   UINT32                Index;
   1235   UINT32                OfferIndex;
   1236   PXEBC_OFFER_TYPE      OfferType;
   1237 
   1238   Private->SelectIndex = 0;
   1239 
   1240   if (Private->IsOfferSorted) {
   1241     //
   1242     // Select offer by default policy.
   1243     //
   1244     if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {
   1245       //
   1246       // 1. DhcpPxe10 offer
   1247       //
   1248       Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;
   1249 
   1250     } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {
   1251       //
   1252       // 2. DhcpWfm11a offer
   1253       //
   1254       Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;
   1255 
   1256     } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
   1257                Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {
   1258       //
   1259       // 3. DhcpOnly offer and ProxyPxe10 offer.
   1260       //
   1261       Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
   1262       Private->SelectProxyType = PxeOfferTypeProxyPxe10;
   1263 
   1264     } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
   1265                Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {
   1266       //
   1267       // 4. DhcpOnly offer and ProxyWfm11a offer.
   1268       //
   1269       Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
   1270       Private->SelectProxyType = PxeOfferTypeProxyWfm11a;
   1271 
   1272     } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {
   1273       //
   1274       // 5. DhcpBinl offer.
   1275       //
   1276       Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;
   1277 
   1278     } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
   1279                Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {
   1280       //
   1281       // 6. DhcpOnly offer and ProxyBinl offer.
   1282       //
   1283       Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
   1284       Private->SelectProxyType = PxeOfferTypeProxyBinl;
   1285 
   1286     } else {
   1287       //
   1288       // 7. DhcpOnly offer with bootfilename.
   1289       //
   1290       for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {
   1291         OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];
   1292         if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) {
   1293           Private->SelectIndex = OfferIndex + 1;
   1294           break;
   1295         }
   1296       }
   1297     }
   1298   } else {
   1299     //
   1300     // Select offer by received order.
   1301     //
   1302     for (Index = 0; Index < Private->OfferNum; Index++) {
   1303 
   1304       OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
   1305 
   1306       if (IS_PROXY_OFFER (OfferType)) {
   1307         //
   1308         // Skip proxy offers
   1309         //
   1310         continue;
   1311       }
   1312 
   1313       if (!Private->IsProxyRecved &&
   1314           OfferType == PxeOfferTypeDhcpOnly &&
   1315           Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
   1316         //
   1317         // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
   1318         //
   1319         continue;
   1320       }
   1321 
   1322       Private->SelectIndex = Index + 1;
   1323       break;
   1324     }
   1325   }
   1326 }
   1327 
   1328 
   1329 /**
   1330   Handle the DHCPv6 offer packet.
   1331 
   1332   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
   1333 
   1334   @retval     EFI_SUCCESS           Handled the DHCPv6 offer packet successfully.
   1335   @retval     EFI_NO_RESPONSE       No response to the following request packet.
   1336   @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.
   1337   @retval     EFI_BUFFER_TOO_SMALL  Can't cache the offer pacet.
   1338 
   1339 **/
   1340 EFI_STATUS
   1341 PxeBcHandleDhcp6Offer (
   1342   IN PXEBC_PRIVATE_DATA            *Private
   1343   )
   1344 {
   1345   PXEBC_DHCP6_PACKET_CACHE         *Cache6;
   1346   EFI_STATUS                       Status;
   1347   PXEBC_OFFER_TYPE                 OfferType;
   1348   UINT32                           ProxyIndex;
   1349   UINT32                           SelectIndex;
   1350   UINT32                           Index;
   1351 
   1352   ASSERT (Private->SelectIndex > 0);
   1353   SelectIndex = (UINT32) (Private->SelectIndex - 1);
   1354   ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);
   1355   Cache6      = &Private->OfferBuffer[SelectIndex].Dhcp6;
   1356   Status      = EFI_SUCCESS;
   1357 
   1358   //
   1359   // First try to cache DNS server address if DHCP6 offer provides.
   1360   //
   1361   if (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER] != NULL) {
   1362     Private->DnsServer = AllocateZeroPool (NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->OpLen));
   1363     if (Private->DnsServer == NULL) {
   1364       return EFI_OUT_OF_RESOURCES;
   1365     }
   1366     CopyMem (Private->DnsServer, Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->Data, sizeof (EFI_IPv6_ADDRESS));
   1367   }
   1368 
   1369   if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {
   1370     //
   1371     // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
   1372     //
   1373     if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {
   1374       Status = EFI_NO_RESPONSE;
   1375     }
   1376   } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {
   1377 
   1378     if (Private->IsProxyRecved) {
   1379       //
   1380       // DhcpOnly offer is selected, so need try to request bootfilename.
   1381       //
   1382       ProxyIndex = 0;
   1383       if (Private->IsOfferSorted) {
   1384         //
   1385         // The proxy offer should be determined if select by default policy.
   1386         // IsOfferSorted means all offers are labeled by OfferIndex.
   1387         //
   1388         ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);
   1389 
   1390         if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {
   1391           //
   1392           // Try all the cached ProxyBinl offer one by one to request bootfilename.
   1393           //
   1394           for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {
   1395 
   1396             ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];
   1397             if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {
   1398               break;
   1399             }
   1400           }
   1401           if (Index == Private->OfferCount[Private->SelectProxyType]) {
   1402             Status = EFI_NO_RESPONSE;
   1403           }
   1404         } else {
   1405           //
   1406           // For other proxy offers (pxe10 or wfm11a), only one is buffered.
   1407           //
   1408           ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
   1409         }
   1410       } else {
   1411         //
   1412         // The proxy offer should not be determined if select by received order.
   1413         //
   1414         Status = EFI_NO_RESPONSE;
   1415 
   1416         for (Index = 0; Index < Private->OfferNum; Index++) {
   1417 
   1418           OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
   1419 
   1420           if (!IS_PROXY_OFFER (OfferType)) {
   1421             //
   1422             // Skip non proxy dhcp offers.
   1423             //
   1424             continue;
   1425           }
   1426 
   1427           if (OfferType == PxeOfferTypeProxyBinl) {
   1428             //
   1429             // Try all the cached ProxyBinl offer one by one to request bootfilename.
   1430             //
   1431             if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {
   1432               continue;
   1433             }
   1434           }
   1435 
   1436           Private->SelectProxyType = OfferType;
   1437           ProxyIndex               = Index;
   1438           Status                   = EFI_SUCCESS;
   1439           break;
   1440         }
   1441       }
   1442 
   1443       if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {
   1444         //
   1445         // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
   1446         //
   1447         Status = PxeBcCopyDhcp6Proxy (Private, ProxyIndex);
   1448       }
   1449     } else {
   1450       //
   1451       //  Othewise, the bootfilename must be included in DhcpOnly offer.
   1452       //
   1453       ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
   1454     }
   1455   }
   1456 
   1457   if (!EFI_ERROR (Status)) {
   1458     //
   1459     // All PXE boot information is ready by now.
   1460     //
   1461     Status = PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);
   1462     Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;
   1463   }
   1464 
   1465   return Status;
   1466 }
   1467 
   1468 
   1469 /**
   1470   Unregister the address by Ip6Config protocol.
   1471 
   1472   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
   1473 
   1474 **/
   1475 VOID
   1476 PxeBcUnregisterIp6Address (
   1477   IN PXEBC_PRIVATE_DATA           *Private
   1478   )
   1479 {
   1480   if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {
   1481     //
   1482     // PXE driver change the policy of IP6 driver, it's a chance to recover.
   1483     // Keep the point and there is no enough requirements to do recovery.
   1484     //
   1485   }
   1486 }
   1487 
   1488 /**
   1489   Check whether IP driver could route the message which will be sent to ServerIp address.
   1490 
   1491   This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid
   1492   route is found in IP6 route table, the address will be filed in GatewayAddr and return.
   1493 
   1494   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
   1495   @param[in]  TimeOutInSecond     Timeout value in seconds.
   1496   @param[out] GatewayAddr         Pointer to store the gateway IP address.
   1497 
   1498   @retval     EFI_SUCCESS         Found a valid gateway address successfully.
   1499   @retval     EFI_TIMEOUT         The operation is time out.
   1500   @retval     Other               Unexpect error happened.
   1501 
   1502 **/
   1503 EFI_STATUS
   1504 PxeBcCheckRouteTable (
   1505   IN  PXEBC_PRIVATE_DATA            *Private,
   1506   IN  UINTN                         TimeOutInSecond,
   1507   OUT EFI_IPv6_ADDRESS              *GatewayAddr
   1508   )
   1509 {
   1510   EFI_STATUS                       Status;
   1511   EFI_IP6_PROTOCOL                 *Ip6;
   1512   EFI_IP6_MODE_DATA                Ip6ModeData;
   1513   UINTN                            Index;
   1514   EFI_EVENT                        TimeOutEvt;
   1515   UINTN                            RetryCount;
   1516   BOOLEAN                          GatewayIsFound;
   1517 
   1518   ASSERT (GatewayAddr != NULL);
   1519   ASSERT (Private != NULL);
   1520 
   1521   Ip6            = Private->Ip6;
   1522   GatewayIsFound = FALSE;
   1523   RetryCount     = 0;
   1524   TimeOutEvt     = NULL;
   1525   ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));
   1526 
   1527   while (TRUE) {
   1528     Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);
   1529     if (EFI_ERROR (Status)) {
   1530       goto ON_EXIT;
   1531     }
   1532 
   1533     //
   1534     // Find out the gateway address which can route the message which send to ServerIp.
   1535     //
   1536     for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {
   1537       if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {
   1538         IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);
   1539         GatewayIsFound = TRUE;
   1540         break;
   1541       }
   1542     }
   1543 
   1544     if (Ip6ModeData.AddressList != NULL) {
   1545       FreePool (Ip6ModeData.AddressList);
   1546     }
   1547     if (Ip6ModeData.GroupTable != NULL) {
   1548       FreePool (Ip6ModeData.GroupTable);
   1549     }
   1550     if (Ip6ModeData.RouteTable != NULL) {
   1551       FreePool (Ip6ModeData.RouteTable);
   1552     }
   1553     if (Ip6ModeData.NeighborCache != NULL) {
   1554       FreePool (Ip6ModeData.NeighborCache);
   1555     }
   1556     if (Ip6ModeData.PrefixTable != NULL) {
   1557       FreePool (Ip6ModeData.PrefixTable);
   1558     }
   1559     if (Ip6ModeData.IcmpTypeList != NULL) {
   1560       FreePool (Ip6ModeData.IcmpTypeList);
   1561     }
   1562 
   1563     if (GatewayIsFound || RetryCount == TimeOutInSecond) {
   1564       break;
   1565     }
   1566 
   1567     RetryCount++;
   1568 
   1569     //
   1570     // Delay 1 second then recheck it again.
   1571     //
   1572     if (TimeOutEvt == NULL) {
   1573       Status = gBS->CreateEvent (
   1574                       EVT_TIMER,
   1575                       TPL_CALLBACK,
   1576                       NULL,
   1577                       NULL,
   1578                       &TimeOutEvt
   1579                       );
   1580       if (EFI_ERROR (Status)) {
   1581         goto ON_EXIT;
   1582       }
   1583     }
   1584 
   1585     Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);
   1586     if (EFI_ERROR (Status)) {
   1587       goto ON_EXIT;
   1588     }
   1589     while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {
   1590       Ip6->Poll (Ip6);
   1591     }
   1592   }
   1593 
   1594 ON_EXIT:
   1595   if (TimeOutEvt != NULL) {
   1596     gBS->CloseEvent (TimeOutEvt);
   1597   }
   1598 
   1599   if (GatewayIsFound) {
   1600     Status = EFI_SUCCESS;
   1601   } else if (RetryCount == TimeOutInSecond) {
   1602     Status = EFI_TIMEOUT;
   1603   }
   1604 
   1605   return Status;
   1606 }
   1607 
   1608 /**
   1609   Register the ready station address and gateway by Ip6Config protocol.
   1610 
   1611   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
   1612   @param[in]  Address             The pointer to the ready address.
   1613 
   1614   @retval     EFI_SUCCESS         Registered the address succesfully.
   1615   @retval     Others              Failed to register the address.
   1616 
   1617 **/
   1618 EFI_STATUS
   1619 PxeBcRegisterIp6Address (
   1620   IN PXEBC_PRIVATE_DATA            *Private,
   1621   IN EFI_IPv6_ADDRESS              *Address
   1622   )
   1623 {
   1624   EFI_IP6_PROTOCOL                 *Ip6;
   1625   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
   1626   EFI_IP6_CONFIG_POLICY            Policy;
   1627   EFI_IP6_CONFIG_MANUAL_ADDRESS    CfgAddr;
   1628   EFI_IPv6_ADDRESS                 GatewayAddr;
   1629   UINTN                            DataSize;
   1630   EFI_EVENT                        MappedEvt;
   1631   EFI_STATUS                       Status;
   1632   BOOLEAN                          NoGateway;
   1633   EFI_IPv6_ADDRESS                 *Ip6Addr;
   1634   UINTN                            Index;
   1635 
   1636   Status     = EFI_SUCCESS;
   1637   MappedEvt  = NULL;
   1638   Ip6Addr    = NULL;
   1639   DataSize   = sizeof (EFI_IP6_CONFIG_POLICY);
   1640   Ip6Cfg     = Private->Ip6Cfg;
   1641   Ip6        = Private->Ip6;
   1642   NoGateway  = FALSE;
   1643 
   1644   ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
   1645   CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));
   1646 
   1647   Status = Ip6->Configure (Ip6, &Private->Ip6CfgData);
   1648   if (EFI_ERROR (Status)) {
   1649     goto ON_EXIT;
   1650   }
   1651 
   1652   //
   1653   // Retrieve the gateway address from IP6 route table.
   1654   //
   1655   Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);
   1656   if (EFI_ERROR (Status)) {
   1657     NoGateway = TRUE;
   1658   }
   1659 
   1660   //
   1661   // There is no channel between IP6 and PXE driver about address setting,
   1662   // so it has to set the new address by Ip6ConfigProtocol manually.
   1663   //
   1664   Policy = Ip6ConfigPolicyManual;
   1665   Status = Ip6Cfg->SetData (
   1666                      Ip6Cfg,
   1667                      Ip6ConfigDataTypePolicy,
   1668                      sizeof(EFI_IP6_CONFIG_POLICY),
   1669                      &Policy
   1670                      );
   1671   if (EFI_ERROR (Status)) {
   1672     //
   1673     // There is no need to recover later.
   1674     //
   1675     Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
   1676     goto ON_EXIT;
   1677   }
   1678 
   1679   //
   1680   // Create a notify event to set address flag when DAD if IP6 driver succeeded.
   1681   //
   1682   Status = gBS->CreateEvent (
   1683                   EVT_NOTIFY_SIGNAL,
   1684                   TPL_NOTIFY,
   1685                   PxeBcCommonNotify,
   1686                   &Private->IsAddressOk,
   1687                   &MappedEvt
   1688                   );
   1689   if (EFI_ERROR (Status)) {
   1690     goto ON_EXIT;
   1691   }
   1692 
   1693   Private->IsAddressOk = FALSE;
   1694   Status = Ip6Cfg->RegisterDataNotify (
   1695                      Ip6Cfg,
   1696                      Ip6ConfigDataTypeManualAddress,
   1697                      MappedEvt
   1698                      );
   1699   if (EFI_ERROR(Status)) {
   1700     goto ON_EXIT;
   1701   }
   1702 
   1703   Status = Ip6Cfg->SetData (
   1704                      Ip6Cfg,
   1705                      Ip6ConfigDataTypeManualAddress,
   1706                      sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS),
   1707                      &CfgAddr
   1708                      );
   1709   if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {
   1710     goto ON_EXIT;
   1711   } else if (Status == EFI_NOT_READY) {
   1712     //
   1713     // Poll the network until the asynchronous process is finished.
   1714     //
   1715     while (!Private->IsAddressOk) {
   1716       Ip6->Poll (Ip6);
   1717     }
   1718     //
   1719     // Check whether the IP6 address setting is successed.
   1720     //
   1721     DataSize = 0;
   1722     Status = Ip6Cfg->GetData (
   1723                        Ip6Cfg,
   1724                        Ip6ConfigDataTypeManualAddress,
   1725                        &DataSize,
   1726                        NULL
   1727                        );
   1728     if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) {
   1729       Status = EFI_DEVICE_ERROR;
   1730       goto ON_EXIT;
   1731     }
   1732 
   1733     Ip6Addr = AllocatePool (DataSize);
   1734     if (Ip6Addr == NULL) {
   1735       return EFI_OUT_OF_RESOURCES;
   1736     }
   1737     Status = Ip6Cfg->GetData (
   1738                        Ip6Cfg,
   1739                        Ip6ConfigDataTypeManualAddress,
   1740                        &DataSize,
   1741                        (VOID*) Ip6Addr
   1742                        );
   1743     if (EFI_ERROR (Status)) {
   1744       Status = EFI_DEVICE_ERROR;
   1745       goto ON_EXIT;
   1746     }
   1747 
   1748     for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) {
   1749       if (CompareMem (Ip6Addr + Index, Address, sizeof (EFI_IPv6_ADDRESS)) == 0) {
   1750         break;
   1751       }
   1752     }
   1753     if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {
   1754       Status = EFI_ABORTED;
   1755       goto ON_EXIT;
   1756     }
   1757   }
   1758 
   1759   //
   1760   // Set the default gateway address back if needed.
   1761   //
   1762   if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) {
   1763     Status = Ip6Cfg->SetData (
   1764                        Ip6Cfg,
   1765                        Ip6ConfigDataTypeGateway,
   1766                        sizeof (EFI_IPv6_ADDRESS),
   1767                        &GatewayAddr
   1768                        );
   1769     if (EFI_ERROR (Status)) {
   1770       goto ON_EXIT;
   1771     }
   1772   }
   1773 
   1774 ON_EXIT:
   1775   if (MappedEvt != NULL) {
   1776     Ip6Cfg->UnregisterDataNotify (
   1777               Ip6Cfg,
   1778               Ip6ConfigDataTypeManualAddress,
   1779               MappedEvt
   1780               );
   1781     gBS->CloseEvent (MappedEvt);
   1782   }
   1783   if (Ip6Addr != NULL) {
   1784     FreePool (Ip6Addr);
   1785   }
   1786   return Status;
   1787 }
   1788 
   1789 /**
   1790   Set the IP6 policy to Automatic.
   1791 
   1792   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
   1793 
   1794   @retval     EFI_SUCCESS         Switch the IP policy succesfully.
   1795   @retval     Others              Unexpect error happened.
   1796 
   1797 **/
   1798 EFI_STATUS
   1799 PxeBcSetIp6Policy (
   1800   IN PXEBC_PRIVATE_DATA            *Private
   1801   )
   1802 {
   1803   EFI_IP6_CONFIG_POLICY            Policy;
   1804   EFI_STATUS                       Status;
   1805   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
   1806   UINTN                            DataSize;
   1807 
   1808   Ip6Cfg      = Private->Ip6Cfg;
   1809   DataSize    = sizeof (EFI_IP6_CONFIG_POLICY);
   1810 
   1811   //
   1812   // Get and store the current policy of IP6 driver.
   1813   //
   1814   Status = Ip6Cfg->GetData (
   1815                      Ip6Cfg,
   1816                      Ip6ConfigDataTypePolicy,
   1817                      &DataSize,
   1818                      &Private->Ip6Policy
   1819                      );
   1820   if (EFI_ERROR (Status)) {
   1821     return Status;
   1822   }
   1823 
   1824   if (Private->Ip6Policy == Ip6ConfigPolicyManual) {
   1825     Policy = Ip6ConfigPolicyAutomatic;
   1826     Status = Ip6Cfg->SetData (
   1827                        Ip6Cfg,
   1828                        Ip6ConfigDataTypePolicy,
   1829                        sizeof(EFI_IP6_CONFIG_POLICY),
   1830                        &Policy
   1831                        );
   1832     if (EFI_ERROR (Status)) {
   1833       //
   1834       // There is no need to recover later.
   1835       //
   1836       Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
   1837     }
   1838   }
   1839 
   1840   return Status;
   1841 }
   1842 
   1843 /**
   1844   This function will register the station IP address and flush IP instance to start using the new IP address.
   1845 
   1846   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
   1847 
   1848   @retval     EFI_SUCCESS         The new IP address has been configured successfully.
   1849   @retval     Others              Failed to configure the address.
   1850 
   1851 **/
   1852 EFI_STATUS
   1853 PxeBcSetIp6Address (
   1854   IN  PXEBC_PRIVATE_DATA              *Private
   1855   )
   1856 {
   1857   EFI_STATUS                  Status;
   1858   EFI_DHCP6_PROTOCOL          *Dhcp6;
   1859 
   1860   Dhcp6 = Private->Dhcp6;
   1861 
   1862   CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS));
   1863   CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
   1864 
   1865   Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);
   1866   if (EFI_ERROR (Status)) {
   1867     Dhcp6->Stop (Dhcp6);
   1868     return Status;
   1869   }
   1870 
   1871   Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL);
   1872   if (EFI_ERROR (Status)) {
   1873     PxeBcUnregisterIp6Address (Private);
   1874     Dhcp6->Stop (Dhcp6);
   1875     return Status;
   1876   }
   1877 
   1878   AsciiPrint ("\n  Station IP address is ");
   1879   PxeBcShowIp6Addr (&Private->StationIp.v6);
   1880 
   1881   return EFI_SUCCESS;
   1882 }
   1883 
   1884 /**
   1885   EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
   1886   to intercept events that occurred in the configuration process.
   1887 
   1888   @param[in]  This              The pointer to the EFI DHCPv6 Protocol.
   1889   @param[in]  Context           The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
   1890   @param[in]  CurrentState      The current operational state of the EFI DHCPv Protocol driver.
   1891   @param[in]  Dhcp6Event        The event that occurs in the current state, which usually means a
   1892                                 state transition.
   1893   @param[in]  Packet            The DHCPv6 packet that is going to be sent or was already received.
   1894   @param[out] NewPacket         The packet that is used to replace the Packet above.
   1895 
   1896   @retval EFI_SUCCESS           Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
   1897   @retval EFI_NOT_READY         Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
   1898                                 driver will continue to wait for more packets.
   1899   @retval EFI_ABORTED           Told the EFI DHCPv6 Protocol driver to abort the current process.
   1900 
   1901 **/
   1902 EFI_STATUS
   1903 EFIAPI
   1904 PxeBcDhcp6CallBack (
   1905   IN  EFI_DHCP6_PROTOCOL           *This,
   1906   IN  VOID                         *Context,
   1907   IN  EFI_DHCP6_STATE              CurrentState,
   1908   IN  EFI_DHCP6_EVENT              Dhcp6Event,
   1909   IN  EFI_DHCP6_PACKET             *Packet,
   1910   OUT EFI_DHCP6_PACKET             **NewPacket     OPTIONAL
   1911   )
   1912 {
   1913   PXEBC_PRIVATE_DATA                  *Private;
   1914   EFI_PXE_BASE_CODE_MODE              *Mode;
   1915   EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
   1916   EFI_DHCP6_PACKET                    *SelectAd;
   1917   EFI_STATUS                          Status;
   1918   BOOLEAN                             Received;
   1919 
   1920   if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&
   1921       (Dhcp6Event != Dhcp6SelectAdvertise) &&
   1922       (Dhcp6Event != Dhcp6SendSolicit) &&
   1923       (Dhcp6Event != Dhcp6SendRequest) &&
   1924       (Dhcp6Event != Dhcp6RcvdReply)) {
   1925     return EFI_SUCCESS;
   1926   }
   1927 
   1928   ASSERT (Packet != NULL);
   1929 
   1930   Private   = (PXEBC_PRIVATE_DATA *) Context;
   1931   Mode      = Private->PxeBc.Mode;
   1932   Callback  = Private->PxeBcCallback;
   1933 
   1934   //
   1935   // Callback to user when any traffic ocurred if has.
   1936   //
   1937   if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) {
   1938     Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);
   1939     Status = Callback->Callback (
   1940                          Callback,
   1941                          Private->Function,
   1942                          Received,
   1943                          Packet->Length,
   1944                          (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6
   1945                          );
   1946     if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
   1947       return EFI_ABORTED;
   1948     }
   1949   }
   1950 
   1951   Status = EFI_SUCCESS;
   1952 
   1953   switch (Dhcp6Event) {
   1954 
   1955   case Dhcp6SendSolicit:
   1956     if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
   1957       //
   1958       // If the to be sent packet exceeds the maximum length, abort the DHCP process.
   1959       //
   1960       Status = EFI_ABORTED;
   1961       break;
   1962     }
   1963 
   1964     //
   1965     // Record the first Solicate msg time
   1966     //
   1967     if (Private->SolicitTimes == 0) {
   1968       CalcElapsedTime (Private);
   1969       Private->SolicitTimes++;
   1970     }
   1971     //
   1972     // Cache the dhcp discover packet to mode data directly.
   1973     //
   1974     CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);
   1975     break;
   1976 
   1977   case Dhcp6RcvdAdvertise:
   1978     Status = EFI_NOT_READY;
   1979     if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
   1980       //
   1981       // Ignore the incoming packets which exceed the maximum length.
   1982       //
   1983       break;
   1984     }
   1985     if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
   1986       //
   1987       // Cache the dhcp offers to OfferBuffer[] for select later, and record
   1988       // the OfferIndex and OfferCount.
   1989       //
   1990       PxeBcCacheDhcp6Offer (Private, Packet);
   1991     }
   1992     break;
   1993 
   1994   case Dhcp6SendRequest:
   1995     if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
   1996       //
   1997       // If the to be sent packet exceeds the maximum length, abort the DHCP process.
   1998       //
   1999       Status = EFI_ABORTED;
   2000       break;
   2001     }
   2002 
   2003     //
   2004     // Store the request packet as seed packet for discover.
   2005     //
   2006     if (Private->Dhcp6Request != NULL) {
   2007       FreePool (Private->Dhcp6Request);
   2008     }
   2009     Private->Dhcp6Request = AllocateZeroPool (Packet->Size);
   2010     if (Private->Dhcp6Request != NULL) {
   2011       CopyMem (Private->Dhcp6Request, Packet, Packet->Size);
   2012     }
   2013     break;
   2014 
   2015   case Dhcp6SelectAdvertise:
   2016     //
   2017     // Select offer by the default policy or by order, and record the SelectIndex
   2018     // and SelectProxyType.
   2019     //
   2020     PxeBcSelectDhcp6Offer (Private);
   2021 
   2022     if (Private->SelectIndex == 0) {
   2023       Status = EFI_ABORTED;
   2024     } else {
   2025       ASSERT (NewPacket != NULL);
   2026       SelectAd   = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;
   2027       *NewPacket = AllocateZeroPool (SelectAd->Size);
   2028       ASSERT (*NewPacket != NULL);
   2029       CopyMem (*NewPacket, SelectAd, SelectAd->Size);
   2030     }
   2031     break;
   2032 
   2033   case Dhcp6RcvdReply:
   2034     //
   2035     // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
   2036     // without verification.
   2037     //
   2038     ASSERT (Private->SelectIndex != 0);
   2039     Status = PxeBcCopyDhcp6Ack (Private, Packet, FALSE);
   2040     if (EFI_ERROR (Status)) {
   2041       Status = EFI_ABORTED;
   2042     }
   2043     break;
   2044 
   2045   default:
   2046     ASSERT (0);
   2047   }
   2048 
   2049   return Status;
   2050 }
   2051 
   2052 
   2053 /**
   2054   Build and send out the request packet for the bootfile, and parse the reply.
   2055 
   2056   @param[in]  Private               The pointer to PxeBc private data.
   2057   @param[in]  Type                  PxeBc option boot item type.
   2058   @param[in]  Layer                 The pointer to option boot item layer.
   2059   @param[in]  UseBis                Use BIS or not.
   2060   @param[in]  DestIp                The pointer to the server address.
   2061 
   2062   @retval     EFI_SUCCESS           Successfully discovered the boot file.
   2063   @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.
   2064   @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
   2065   @retval     Others                Failed to discover the boot file.
   2066 
   2067 **/
   2068 EFI_STATUS
   2069 PxeBcDhcp6Discover (
   2070   IN  PXEBC_PRIVATE_DATA              *Private,
   2071   IN  UINT16                          Type,
   2072   IN  UINT16                          *Layer,
   2073   IN  BOOLEAN                         UseBis,
   2074   IN  EFI_IP_ADDRESS                  *DestIp
   2075   )
   2076 {
   2077   EFI_PXE_BASE_CODE_UDP_PORT          SrcPort;
   2078   EFI_PXE_BASE_CODE_UDP_PORT          DestPort;
   2079   EFI_PXE_BASE_CODE_MODE              *Mode;
   2080   EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
   2081   EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;
   2082   UINTN                               DiscoverLen;
   2083   EFI_DHCP6_PACKET                    *Request;
   2084   UINTN                               RequestLen;
   2085   EFI_DHCP6_PACKET                    *Reply;
   2086   UINT8                               *RequestOpt;
   2087   UINT8                               *DiscoverOpt;
   2088   UINTN                               ReadSize;
   2089   UINT16                              OpCode;
   2090   UINT16                              OpLen;
   2091   UINT32                              Xid;
   2092   EFI_STATUS                          Status;
   2093 
   2094   PxeBc       = &Private->PxeBc;
   2095   Mode        = PxeBc->Mode;
   2096   Request     = Private->Dhcp6Request;
   2097   SrcPort     = PXEBC_BS_DISCOVER_PORT;
   2098   DestPort    = PXEBC_BS_DISCOVER_PORT;
   2099 
   2100   if (!UseBis && Layer != NULL) {
   2101     *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
   2102   }
   2103 
   2104   if (Request == NULL) {
   2105     return EFI_DEVICE_ERROR;
   2106   }
   2107 
   2108   Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));
   2109   if (Discover == NULL) {
   2110     return EFI_OUT_OF_RESOURCES;
   2111   }
   2112 
   2113   //
   2114   // Build the discover packet by the cached request packet before.
   2115   //
   2116   Xid                     = NET_RANDOM (NetRandomInitSeed ());
   2117   Discover->TransactionId = HTONL (Xid);
   2118   Discover->MessageType   = Request->Dhcp6.Header.MessageType;
   2119   RequestOpt              = Request->Dhcp6.Option;
   2120   DiscoverOpt             = Discover->DhcpOptions;
   2121   DiscoverLen             = sizeof (EFI_DHCP6_HEADER);
   2122   RequestLen              = DiscoverLen;
   2123 
   2124   while (RequestLen < Request->Length) {
   2125     OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);
   2126     OpLen  = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);
   2127     if (OpCode != EFI_DHCP6_IA_TYPE_NA &&
   2128         OpCode != EFI_DHCP6_IA_TYPE_TA) {
   2129       //
   2130       // Copy all the options except IA option.
   2131       //
   2132       CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);
   2133       DiscoverOpt += (OpLen + 4);
   2134       DiscoverLen += (OpLen + 4);
   2135     }
   2136     RequestOpt += (OpLen + 4);
   2137     RequestLen += (OpLen + 4);
   2138   }
   2139 
   2140   Status = PxeBc->UdpWrite (
   2141                     PxeBc,
   2142                     0,
   2143                     &Private->ServerIp,
   2144                     &DestPort,
   2145                     NULL,
   2146                     &Private->StationIp,
   2147                     &SrcPort,
   2148                     NULL,
   2149                     NULL,
   2150                     &DiscoverLen,
   2151                     (VOID *) Discover
   2152                     );
   2153   if (EFI_ERROR (Status)) {
   2154     return Status;
   2155   }
   2156 
   2157   //
   2158   // Cache the right PXE reply packet here, set valid flag later.
   2159   // Especially for PXE discover packet, store it into mode data here.
   2160   //
   2161   if (Private->IsDoDiscover) {
   2162     CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);
   2163     Reply = &Private->PxeReply.Dhcp6.Packet.Ack;
   2164   } else {
   2165     Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
   2166   }
   2167   ReadSize = (UINTN) Reply->Size;
   2168 
   2169   //
   2170   // Start Udp6Read instance
   2171   //
   2172   Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
   2173   if (EFI_ERROR (Status)) {
   2174     return Status;
   2175   }
   2176 
   2177   Status = PxeBc->UdpRead (
   2178                     PxeBc,
   2179                     EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
   2180                     NULL,
   2181                     &SrcPort,
   2182                     &Private->ServerIp,
   2183                     &DestPort,
   2184                     NULL,
   2185                     NULL,
   2186                     &ReadSize,
   2187                     (VOID *) &Reply->Dhcp6
   2188                     );
   2189   //
   2190   // Stop Udp6Read instance
   2191   //
   2192   Private->Udp6Read->Configure (Private->Udp6Read, NULL);
   2193   if (EFI_ERROR (Status)) {
   2194     return Status;
   2195   }
   2196 
   2197   return EFI_SUCCESS;
   2198 }
   2199 
   2200 
   2201 /**
   2202   Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
   2203 
   2204   @param[in]  Private           The pointer to PxeBc private data.
   2205   @param[in]  Dhcp6             The pointer to the EFI_DHCP6_PROTOCOL
   2206 
   2207   @retval EFI_SUCCESS           The S.A.R.R. process successfully finished.
   2208   @retval Others                Failed to finish the S.A.R.R. process.
   2209 
   2210 **/
   2211 EFI_STATUS
   2212 PxeBcDhcp6Sarr (
   2213   IN PXEBC_PRIVATE_DATA            *Private,
   2214   IN EFI_DHCP6_PROTOCOL            *Dhcp6
   2215   )
   2216 {
   2217   EFI_PXE_BASE_CODE_MODE           *PxeMode;
   2218   EFI_DHCP6_CONFIG_DATA            Config;
   2219   EFI_DHCP6_MODE_DATA              Mode;
   2220   EFI_DHCP6_RETRANSMISSION         *Retransmit;
   2221   EFI_DHCP6_PACKET_OPTION          *OptList[PXEBC_DHCP6_OPTION_MAX_NUM];
   2222   UINT8                            Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];
   2223   UINT32                           OptCount;
   2224   EFI_STATUS                       Status;
   2225   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
   2226   EFI_STATUS                       TimerStatus;
   2227   EFI_EVENT                        Timer;
   2228   UINT64                           GetMappingTimeOut;
   2229   UINTN                            DataSize;
   2230   EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS    DadXmits;
   2231 
   2232   Status     = EFI_SUCCESS;
   2233   PxeMode    = Private->PxeBc.Mode;
   2234   Ip6Cfg     = Private->Ip6Cfg;
   2235   Timer      = NULL;
   2236 
   2237   //
   2238   // Build option list for the request packet.
   2239   //
   2240   OptCount   = PxeBcBuildDhcp6Options (Private, OptList, Buffer);
   2241   ASSERT (OptCount> 0);
   2242 
   2243   Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
   2244   if (Retransmit == NULL) {
   2245     return EFI_OUT_OF_RESOURCES;
   2246   }
   2247 
   2248   ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));
   2249   ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
   2250 
   2251   Config.OptionCount           = OptCount;
   2252   Config.OptionList            = OptList;
   2253   Config.Dhcp6Callback         = PxeBcDhcp6CallBack;
   2254   Config.CallbackContext       = Private;
   2255   Config.IaInfoEvent           = NULL;
   2256   Config.RapidCommit           = FALSE;
   2257   Config.ReconfigureAccept     = FALSE;
   2258   Config.IaDescriptor.IaId     = Private->IaId;
   2259   Config.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;
   2260   Config.SolicitRetransmission = Retransmit;
   2261   Retransmit->Irt              = 4;
   2262   Retransmit->Mrc              = 4;
   2263   Retransmit->Mrt              = 32;
   2264   Retransmit->Mrd              = 60;
   2265 
   2266   //
   2267   // Configure the DHCPv6 instance for PXE boot.
   2268   //
   2269   Status = Dhcp6->Configure (Dhcp6, &Config);
   2270   FreePool (Retransmit);
   2271   if (EFI_ERROR (Status)) {
   2272     return Status;
   2273   }
   2274 
   2275   //
   2276   // Initialize the record fields for DHCPv6 offer in private data.
   2277   //
   2278   Private->IsProxyRecved = FALSE;
   2279   Private->OfferNum      = 0;
   2280   Private->SelectIndex   = 0;
   2281   ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
   2282   ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
   2283 
   2284 
   2285   //
   2286   // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
   2287   //
   2288   Status = Dhcp6->Start (Dhcp6);
   2289   if (Status == EFI_NO_MAPPING) {
   2290     //
   2291     // IP6 Linklocal address is not available for use, so stop current Dhcp process
   2292     // and wait for duplicate address detection to finish.
   2293     //
   2294     Dhcp6->Stop (Dhcp6);
   2295 
   2296     //
   2297     // Get Duplicate Address Detection Transmits count.
   2298     //
   2299     DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);
   2300     Status = Ip6Cfg->GetData (
   2301                        Ip6Cfg,
   2302                        Ip6ConfigDataTypeDupAddrDetectTransmits,
   2303                        &DataSize,
   2304                        &DadXmits
   2305                        );
   2306     if (EFI_ERROR (Status)) {
   2307       Dhcp6->Configure (Dhcp6, NULL);
   2308       return Status;
   2309     }
   2310 
   2311     Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
   2312     if (EFI_ERROR (Status)) {
   2313       Dhcp6->Configure (Dhcp6, NULL);
   2314       return Status;
   2315     }
   2316 
   2317     GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY;
   2318     Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);
   2319     if (EFI_ERROR (Status)) {
   2320       gBS->CloseEvent (Timer);
   2321       Dhcp6->Configure (Dhcp6, NULL);
   2322       return Status;
   2323     }
   2324 
   2325     do {
   2326 
   2327       TimerStatus = gBS->CheckEvent (Timer);
   2328       if (!EFI_ERROR (TimerStatus)) {
   2329         Status = Dhcp6->Start (Dhcp6);
   2330       }
   2331     } while (TimerStatus == EFI_NOT_READY);
   2332 
   2333     gBS->CloseEvent (Timer);
   2334   }
   2335   if (EFI_ERROR (Status)) {
   2336     if (Status == EFI_ICMP_ERROR) {
   2337       PxeMode->IcmpErrorReceived = TRUE;
   2338     }
   2339     Dhcp6->Configure (Dhcp6, NULL);
   2340     return Status;
   2341   }
   2342 
   2343   //
   2344   // Get the acquired IPv6 address and store them.
   2345   //
   2346   Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);
   2347   if (EFI_ERROR (Status)) {
   2348     Dhcp6->Stop (Dhcp6);
   2349     return Status;
   2350   }
   2351 
   2352   ASSERT ((Mode.Ia != NULL) && (Mode.Ia->State == Dhcp6Bound));
   2353   //
   2354   // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the
   2355   // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when
   2356   // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as
   2357   // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery
   2358   // to find a valid router address.
   2359   //
   2360   CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));
   2361   if (Mode.ClientId != NULL) {
   2362     FreePool (Mode.ClientId);
   2363   }
   2364   if (Mode.Ia != NULL) {
   2365     FreePool (Mode.Ia);
   2366   }
   2367   //
   2368   // Check the selected offer whether BINL retry is needed.
   2369   //
   2370   Status = PxeBcHandleDhcp6Offer (Private);
   2371   if (EFI_ERROR (Status)) {
   2372     Dhcp6->Stop (Dhcp6);
   2373     return Status;
   2374   }
   2375 
   2376   return EFI_SUCCESS;
   2377 }
   2378