Home | History | Annotate | Download | only in UefiPxeBcDxe
      1 /** @file
      2   Boot functions implementation for UefiPxeBc Driver.
      3 
      4   Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
      5 
      6   This program and the accompanying materials
      7   are licensed and made available under the terms and conditions of the BSD License
      8   which accompanies this distribution.  The full text of the license may be found at
      9   http://opensource.org/licenses/bsd-license.php.
     10 
     11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include "PxeBcImpl.h"
     17 
     18 
     19 /**
     20   Display the string of the boot item.
     21 
     22   If the length of the boot item string beyond 70 Char, just display 70 Char.
     23 
     24   @param[in]  Str           The pointer to the string.
     25   @param[in]  Len           The length of the string.
     26 
     27 **/
     28 VOID
     29 PxeBcDisplayBootItem (
     30   IN UINT8                 *Str,
     31   IN UINT8                 Len
     32   )
     33 {
     34   UINT8                    Tmp;
     35 
     36   //
     37   // Cut off the chars behind 70th.
     38   //
     39   Len       = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);
     40   Tmp       = Str[Len];
     41   Str[Len]  = 0;
     42   AsciiPrint ("%a \n", Str);
     43 
     44   //
     45   // Restore the original 70th char.
     46   //
     47   Str[Len]  = Tmp;
     48 }
     49 
     50 
     51 /**
     52   Select and maintain the boot prompt if needed.
     53 
     54   @param[in]  Private          Pointer to PxeBc private data.
     55 
     56   @retval EFI_SUCCESS          Selected boot prompt done.
     57   @retval EFI_TIMEOUT          Selected boot prompt timed out.
     58   @retval EFI_NOT_FOUND        The proxy offer is not Pxe10.
     59   @retval EFI_ABORTED          User cancelled the operation.
     60   @retval EFI_NOT_READY        Reading the input key from the keyboard has not finish.
     61 
     62 **/
     63 EFI_STATUS
     64 PxeBcSelectBootPrompt (
     65   IN PXEBC_PRIVATE_DATA      *Private
     66   )
     67 {
     68   PXEBC_DHCP_PACKET_CACHE    *Cache;
     69   PXEBC_VENDOR_OPTION        *VendorOpt;
     70   EFI_PXE_BASE_CODE_MODE     *Mode;
     71   EFI_EVENT                  TimeoutEvent;
     72   EFI_EVENT                  DescendEvent;
     73   EFI_INPUT_KEY              InputKey;
     74   EFI_STATUS                 Status;
     75   UINT32                     OfferType;
     76   UINT8                      Timeout;
     77   UINT8                      *Prompt;
     78   UINT8                      PromptLen;
     79   INT32                      SecCol;
     80   INT32                      SecRow;
     81 
     82   TimeoutEvent = NULL;
     83   DescendEvent = NULL;
     84   Mode         = Private->PxeBc.Mode;
     85   Cache        = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
     86   OfferType    = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
     87 
     88   //
     89   // Only DhcpPxe10 and ProxyPxe10 offer needs boot prompt.
     90   //
     91   if (OfferType != PxeOfferTypeProxyPxe10 && OfferType != PxeOfferTypeDhcpPxe10) {
     92     return EFI_NOT_FOUND;
     93   }
     94 
     95   //
     96   // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
     97   //
     98   ASSERT (!Mode->UsingIpv6);
     99 
    100   VendorOpt = &Cache->Dhcp4.VendorOpt;
    101   //
    102   // According to the PXE specification 2.1, Table 2-1 PXE DHCP Options,
    103   // we must not consider a boot prompt or boot menu if all of the following hold:
    104   //   - the PXE_DISCOVERY_CONTROL tag(6) is present inside the Vendor Options(43), and has bit 3 set
    105   //   - a boot file name has been presented in the initial DHCP or ProxyDHCP offer packet.
    106   //
    107   if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) &&
    108       Cache->Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
    109     return EFI_ABORTED;
    110   }
    111 
    112   if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
    113     return EFI_TIMEOUT;
    114   }
    115 
    116   Timeout   = VendorOpt->MenuPrompt->Timeout;
    117   Prompt    = VendorOpt->MenuPrompt->Prompt;
    118   PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);
    119 
    120   //
    121   // The valid scope of Timeout refers to PXE2.1 spec.
    122   //
    123   if (Timeout == 0) {
    124     return EFI_TIMEOUT;
    125   }
    126   if (Timeout == 255) {
    127     return EFI_SUCCESS;
    128   }
    129 
    130   //
    131   // Create and start a timer as timeout event.
    132   //
    133   Status = gBS->CreateEvent (
    134                   EVT_TIMER,
    135                   TPL_CALLBACK,
    136                   NULL,
    137                   NULL,
    138                   &TimeoutEvent
    139                   );
    140   if (EFI_ERROR (Status)) {
    141     return Status;
    142   }
    143 
    144   Status = gBS->SetTimer (
    145                   TimeoutEvent,
    146                   TimerRelative,
    147                   Timeout * TICKS_PER_SECOND
    148                   );
    149   if (EFI_ERROR (Status)) {
    150     goto ON_EXIT;
    151   }
    152 
    153   //
    154   // Create and start a periodic timer as descend event by second.
    155   //
    156   Status = gBS->CreateEvent (
    157                   EVT_TIMER,
    158                   TPL_CALLBACK,
    159                   NULL,
    160                   NULL,
    161                   &DescendEvent
    162                   );
    163   if (EFI_ERROR (Status)) {
    164     goto ON_EXIT;
    165   }
    166 
    167   Status = gBS->SetTimer (
    168                   DescendEvent,
    169                   TimerPeriodic,
    170                   TICKS_PER_SECOND
    171                   );
    172   if (EFI_ERROR (Status)) {
    173     goto ON_EXIT;
    174   }
    175 
    176   //
    177   // Display the boot item and cursor on the screen.
    178   //
    179   SecCol = gST->ConOut->Mode->CursorColumn;
    180   SecRow = gST->ConOut->Mode->CursorRow;
    181 
    182   PxeBcDisplayBootItem (Prompt, PromptLen);
    183 
    184   gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
    185   AsciiPrint ("(%d) ", Timeout--);
    186 
    187   Status = EFI_TIMEOUT;
    188   while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
    189     if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
    190       gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
    191       AsciiPrint ("(%d) ", Timeout--);
    192     }
    193     if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
    194       gBS->Stall (10 * TICKS_PER_MS);
    195       continue;
    196     }
    197     //
    198     // Parse the input key by user.
    199     // If <F8> or <Ctrl> + <M> is pressed, return success to display the boot menu.
    200     //
    201     if (InputKey.ScanCode == 0) {
    202 
    203       switch (InputKey.UnicodeChar) {
    204 
    205       case CTRL ('c'):
    206         Status = EFI_ABORTED;
    207         break;
    208 
    209       case CTRL ('m'):
    210       case 'm':
    211       case 'M':
    212         Status = EFI_SUCCESS;
    213         break;
    214 
    215       default:
    216         continue;
    217       }
    218 
    219     } else {
    220 
    221       switch (InputKey.ScanCode) {
    222 
    223       case SCAN_F8:
    224         Status = EFI_SUCCESS;
    225         break;
    226 
    227       case SCAN_ESC:
    228         Status = EFI_ABORTED;
    229         break;
    230 
    231       default:
    232         continue;
    233       }
    234     }
    235 
    236     break;
    237   }
    238 
    239   //
    240   // Reset the cursor on the screen.
    241   //
    242   gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);
    243 
    244 ON_EXIT:
    245   if (DescendEvent != NULL) {
    246     gBS->CloseEvent (DescendEvent);
    247   }
    248   if (TimeoutEvent != NULL) {
    249     gBS->CloseEvent (TimeoutEvent);
    250   }
    251 
    252   return Status;
    253 }
    254 
    255 
    256 /**
    257   Select the boot menu by user's input.
    258 
    259   @param[in]  Private         Pointer to PxeBc private data.
    260   @param[out] Type            The type of the menu.
    261   @param[in]  UseDefaultItem  Use default item or not.
    262 
    263   @retval EFI_ABORTED     User cancel operation.
    264   @retval EFI_SUCCESS     Select the boot menu success.
    265   @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.
    266 
    267 **/
    268 EFI_STATUS
    269 PxeBcSelectBootMenu (
    270   IN  PXEBC_PRIVATE_DATA              *Private,
    271   OUT UINT16                          *Type,
    272   IN  BOOLEAN                         UseDefaultItem
    273   )
    274 {
    275   EFI_PXE_BASE_CODE_MODE     *Mode;
    276   PXEBC_DHCP_PACKET_CACHE    *Cache;
    277   PXEBC_VENDOR_OPTION        *VendorOpt;
    278   EFI_INPUT_KEY              InputKey;
    279   UINT32                     OfferType;
    280   UINT8                      MenuSize;
    281   UINT8                      MenuNum;
    282   INT32                      TopRow;
    283   UINT16                     Select;
    284   UINT16                     LastSelect;
    285   UINT8                      Index;
    286   BOOLEAN                    Finish;
    287   CHAR8                      Blank[PXEBC_DISPLAY_MAX_LINE];
    288   PXEBC_BOOT_MENU_ENTRY      *MenuItem;
    289   PXEBC_BOOT_MENU_ENTRY      *MenuArray[PXEBC_MENU_MAX_NUM];
    290 
    291   Finish    = FALSE;
    292   Select    = 0;
    293   Index     = 0;
    294   *Type     = 0;
    295   Mode      = Private->PxeBc.Mode;
    296   Cache     = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
    297   OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
    298 
    299   //
    300   // There is no specified DhcpPxe10/ProxyPxe10 for IPv6 in PXE and UEFI spec.
    301   //
    302   ASSERT (!Mode->UsingIpv6);
    303   ASSERT (OfferType == PxeOfferTypeProxyPxe10 || OfferType == PxeOfferTypeDhcpPxe10);
    304 
    305   VendorOpt = &Cache->Dhcp4.VendorOpt;
    306   if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
    307     return EFI_SUCCESS;
    308   }
    309 
    310   //
    311   // Display the boot menu on the screen.
    312   //
    313   SetMem (Blank, sizeof(Blank), ' ');
    314 
    315   MenuSize  = VendorOpt->BootMenuLen;
    316   MenuItem  = VendorOpt->BootMenu;
    317 
    318   if (MenuSize == 0) {
    319     return EFI_DEVICE_ERROR;
    320   }
    321 
    322   while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {
    323     ASSERT (MenuItem != NULL);
    324     MenuArray[Index]  = MenuItem;
    325     MenuSize          = (UINT8) (MenuSize - (MenuItem->DescLen + 3));
    326     MenuItem          = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);
    327     Index++;
    328   }
    329 
    330   if (UseDefaultItem) {
    331     ASSERT (MenuArray[0] != NULL);
    332     CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));
    333     *Type = NTOHS (*Type);
    334     return EFI_SUCCESS;
    335   }
    336 
    337   MenuNum = Index;
    338 
    339   for (Index = 0; Index < MenuNum; Index++) {
    340     ASSERT (MenuArray[Index] != NULL);
    341     PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
    342   }
    343 
    344   TopRow = gST->ConOut->Mode->CursorRow - MenuNum;
    345 
    346   //
    347   // Select the boot item by user in the boot menu.
    348   //
    349   do {
    350     //
    351     // Highlight selected row.
    352     //
    353     gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
    354     gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
    355     ASSERT (Select < PXEBC_MENU_MAX_NUM);
    356     ASSERT (MenuArray[Select] != NULL);
    357     Blank[MenuArray[Select]->DescLen] = 0;
    358     AsciiPrint ("%a\r", Blank);
    359     PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
    360     gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
    361     LastSelect = Select;
    362 
    363     while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
    364       gBS->Stall (10 * TICKS_PER_MS);
    365     }
    366 
    367     if (InputKey.ScanCode == 0) {
    368       switch (InputKey.UnicodeChar) {
    369       case CTRL ('c'):
    370         InputKey.ScanCode = SCAN_ESC;
    371         break;
    372 
    373       case CTRL ('j'):  /* linefeed */
    374       case CTRL ('m'):  /* return */
    375         Finish = TRUE;
    376         break;
    377 
    378       case CTRL ('i'):  /* tab */
    379       case ' ':
    380       case 'd':
    381       case 'D':
    382         InputKey.ScanCode = SCAN_DOWN;
    383         break;
    384 
    385       case CTRL ('h'):  /* backspace */
    386       case 'u':
    387       case 'U':
    388         InputKey.ScanCode = SCAN_UP;
    389         break;
    390 
    391       default:
    392         InputKey.ScanCode = 0;
    393       }
    394     }
    395 
    396     switch (InputKey.ScanCode) {
    397     case SCAN_LEFT:
    398     case SCAN_UP:
    399       if (Select != 0) {
    400         Select--;
    401       }
    402       break;
    403 
    404     case SCAN_DOWN:
    405     case SCAN_RIGHT:
    406       if (++Select == MenuNum) {
    407         Select--;
    408       }
    409       break;
    410 
    411     case SCAN_PAGE_UP:
    412     case SCAN_HOME:
    413       Select = 0;
    414       break;
    415 
    416     case SCAN_PAGE_DOWN:
    417     case SCAN_END:
    418       Select = (UINT16) (MenuNum - 1);
    419       break;
    420 
    421     case SCAN_ESC:
    422       return EFI_ABORTED;
    423     }
    424 
    425     //
    426     // Unhighlight the last selected row.
    427     //
    428     gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
    429     gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
    430     ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);
    431     ASSERT (MenuArray[LastSelect] != NULL);
    432     Blank[MenuArray[LastSelect]->DescLen] = 0;
    433     AsciiPrint ("%a\r", Blank);
    434     PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
    435     gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
    436   } while (!Finish);
    437 
    438   //
    439   // Swap the byte order.
    440   //
    441   ASSERT (Select < PXEBC_MENU_MAX_NUM);
    442   ASSERT (MenuArray[Select] != NULL);
    443   CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
    444   *Type = NTOHS (*Type);
    445 
    446   return EFI_SUCCESS;
    447 }
    448 
    449 
    450 /**
    451   Parse out the boot information from the last Dhcp4 reply packet.
    452 
    453   @param[in, out] Private      Pointer to PxeBc private data.
    454   @param[out]     BufferSize   Size of the boot file to be downloaded.
    455 
    456   @retval EFI_SUCCESS          Successfully parsed out all the boot information.
    457   @retval Others               Failed to parse out the boot information.
    458 
    459 **/
    460 EFI_STATUS
    461 PxeBcDhcp4BootInfo (
    462   IN OUT PXEBC_PRIVATE_DATA   *Private,
    463      OUT UINT64               *BufferSize
    464   )
    465 {
    466   EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
    467   EFI_PXE_BASE_CODE_MODE      *Mode;
    468   EFI_STATUS                  Status;
    469   PXEBC_DHCP4_PACKET_CACHE    *Cache4;
    470   UINT16                      Value;
    471   PXEBC_VENDOR_OPTION         *VendorOpt;
    472   PXEBC_BOOT_SVR_ENTRY        *Entry;
    473 
    474   PxeBc       = &Private->PxeBc;
    475   Mode        = PxeBc->Mode;
    476   Status      = EFI_SUCCESS;
    477   *BufferSize = 0;
    478 
    479   //
    480   // Get the last received Dhcp4 reply packet.
    481   //
    482   if (Mode->PxeReplyReceived) {
    483     Cache4 = &Private->PxeReply.Dhcp4;
    484   } else if (Mode->ProxyOfferReceived) {
    485     Cache4 = &Private->ProxyOffer.Dhcp4;
    486   } else {
    487     Cache4 = &Private->DhcpAck.Dhcp4;
    488   }
    489 
    490   ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
    491 
    492   //
    493   // Parse the boot server address.
    494   // If prompt/discover is disabled, get the first boot server from the boot servers list.
    495   // Otherwise, parse the boot server Ipv4 address from next server address field in DHCP header.
    496   // If all these fields are not available, use option 54 instead.
    497   //
    498   VendorOpt = &Cache4->VendorOpt;
    499   if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) && IS_VALID_BOOT_SERVERS (VendorOpt->BitMap)) {
    500     Entry = VendorOpt->BootSvr;
    501     if (VendorOpt->BootSvrLen >= sizeof (PXEBC_BOOT_SVR_ENTRY) && Entry->IpCnt > 0) {
    502       CopyMem (
    503         &Private->ServerIp,
    504         &Entry->IpAddr[0],
    505         sizeof (EFI_IPv4_ADDRESS)
    506         );
    507     }
    508   }
    509   if (Private->ServerIp.Addr[0] == 0) {
    510     //
    511     // ServerIp.Addr[0] equals zero means we failed to get IP address from boot server list.
    512     // Try to use next server address field.
    513     //
    514     CopyMem (
    515       &Private->ServerIp,
    516       &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,
    517       sizeof (EFI_IPv4_ADDRESS)
    518       );
    519   }
    520   if (Private->ServerIp.Addr[0] == 0) {
    521     //
    522     // Still failed , use the IP address from option 54.
    523     //
    524     CopyMem (
    525       &Private->ServerIp,
    526       Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
    527       sizeof (EFI_IPv4_ADDRESS)
    528       );
    529   }
    530 
    531   //
    532   // Parse the boot file name by option.
    533   //
    534   Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;
    535 
    536   if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {
    537     //
    538     // Parse the boot file size by option.
    539     //
    540     CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));
    541     Value       = NTOHS (Value);
    542     //
    543     // The field of boot file size is 512 bytes in unit.
    544     //
    545     *BufferSize = 512 * Value;
    546   } else {
    547     //
    548     // Get the bootfile size by tftp command if no option available.
    549     //
    550     Status = PxeBc->Mtftp (
    551                       PxeBc,
    552                       EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
    553                       NULL,
    554                       FALSE,
    555                       BufferSize,
    556                       &Private->BlockSize,
    557                       &Private->ServerIp,
    558                       Private->BootFileName,
    559                       NULL,
    560                       FALSE
    561                       );
    562   }
    563 
    564   //
    565   // Save the value of boot file size.
    566   //
    567   Private->BootFileSize = (UINTN) *BufferSize;
    568 
    569   //
    570   // Display all the information: boot server address, boot file name and boot file size.
    571   //
    572   AsciiPrint ("\n  Server IP address is ");
    573   PxeBcShowIp4Addr (&Private->ServerIp.v4);
    574   AsciiPrint ("\n  NBP filename is %a", Private->BootFileName);
    575   AsciiPrint ("\n  NBP filesize is %d Bytes", Private->BootFileSize);
    576 
    577   return Status;
    578 }
    579 
    580 
    581 /**
    582   Parse out the boot information from the last Dhcp6 reply packet.
    583 
    584   @param[in, out] Private      Pointer to PxeBc private data.
    585   @param[out]     BufferSize   Size of the boot file to be downloaded.
    586 
    587   @retval EFI_SUCCESS          Successfully parsed out all the boot information.
    588   @retval EFI_BUFFER_TOO_SMALL
    589   @retval Others               Failed to parse out the boot information.
    590 
    591 **/
    592 EFI_STATUS
    593 PxeBcDhcp6BootInfo (
    594   IN OUT PXEBC_PRIVATE_DATA   *Private,
    595      OUT UINT64               *BufferSize
    596   )
    597 {
    598   EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
    599   EFI_PXE_BASE_CODE_MODE      *Mode;
    600   EFI_STATUS                  Status;
    601   PXEBC_DHCP6_PACKET_CACHE    *Cache6;
    602   UINT16                      Value;
    603 
    604   PxeBc       = &Private->PxeBc;
    605   Mode        = PxeBc->Mode;
    606   Status      = EFI_SUCCESS;
    607   *BufferSize = 0;
    608 
    609   //
    610   // Get the last received Dhcp6 reply packet.
    611   //
    612   if (Mode->PxeReplyReceived) {
    613     Cache6 = &Private->PxeReply.Dhcp6;
    614   } else if (Mode->ProxyOfferReceived) {
    615     Cache6 = &Private->ProxyOffer.Dhcp6;
    616   } else {
    617     Cache6 = &Private->DhcpAck.Dhcp6;
    618   }
    619 
    620   ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
    621 
    622   //
    623   // Parse (m)tftp server ip address and bootfile name.
    624   //
    625   Status = PxeBcExtractBootFileUrl (
    626              &Private->BootFileName,
    627              &Private->ServerIp.v6,
    628              (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
    629              NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
    630              );
    631   if (EFI_ERROR (Status)) {
    632     return Status;
    633   }
    634 
    635   //
    636   // Set the station address to IP layer.
    637   //
    638   Status = PxeBcSetIp6Address (Private);
    639   if (EFI_ERROR (Status)) {
    640     return Status;
    641   }
    642 
    643   //
    644   // Parse the value of boot file size.
    645   //
    646   if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {
    647     //
    648     // Parse it out if have the boot file parameter option.
    649     //
    650     Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);
    651     if (EFI_ERROR (Status)) {
    652       return Status;
    653     }
    654     //
    655     // The field of boot file size is 512 bytes in unit.
    656     //
    657     *BufferSize = 512 * Value;
    658   } else {
    659     //
    660     // Send get file size command by tftp if option unavailable.
    661     //
    662     Status = PxeBc->Mtftp (
    663                       PxeBc,
    664                       EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
    665                       NULL,
    666                       FALSE,
    667                       BufferSize,
    668                       &Private->BlockSize,
    669                       &Private->ServerIp,
    670                       Private->BootFileName,
    671                       NULL,
    672                       FALSE
    673                       );
    674   }
    675 
    676   //
    677   // Save the value of boot file size.
    678   //
    679   Private->BootFileSize = (UINTN) *BufferSize;
    680 
    681   //
    682   // Display all the information: boot server address, boot file name and boot file size.
    683   //
    684   AsciiPrint ("\n  Server IP address is ");
    685   PxeBcShowIp6Addr (&Private->ServerIp.v6);
    686   AsciiPrint ("\n  NBP filename is %a", Private->BootFileName);
    687   AsciiPrint ("\n  NBP filesize is %d Bytes", Private->BootFileSize);
    688 
    689   return Status;
    690 }
    691 
    692 
    693 /**
    694   Extract the discover information and boot server entry from the
    695   cached packets if unspecified.
    696 
    697   @param[in]      Private      Pointer to PxeBc private data.
    698   @param[in]      Type         The type of bootstrap to perform.
    699   @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
    700   @param[out]     BootEntry    Pointer to PXEBC_BOOT_SVR_ENTRY.
    701   @param[out]     SrvList      Pointer to EFI_PXE_BASE_CODE_SRVLIST.
    702 
    703   @retval EFI_SUCCESS       Successfully extracted the information.
    704   @retval EFI_DEVICE_ERROR  Failed to extract the information.
    705 
    706 **/
    707 EFI_STATUS
    708 PxeBcExtractDiscoverInfo (
    709   IN     PXEBC_PRIVATE_DATA               *Private,
    710   IN     UINT16                           Type,
    711   IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO  **DiscoverInfo,
    712      OUT PXEBC_BOOT_SVR_ENTRY             **BootEntry,
    713      OUT EFI_PXE_BASE_CODE_SRVLIST        **SrvList
    714   )
    715 {
    716   EFI_PXE_BASE_CODE_MODE          *Mode;
    717   PXEBC_DHCP4_PACKET_CACHE        *Cache4;
    718   PXEBC_VENDOR_OPTION             *VendorOpt;
    719   PXEBC_BOOT_SVR_ENTRY            *Entry;
    720   BOOLEAN                         IsFound;
    721   EFI_PXE_BASE_CODE_DISCOVER_INFO *Info;
    722   UINT16                          Index;
    723 
    724   Mode = Private->PxeBc.Mode;
    725   Info = *DiscoverInfo;
    726 
    727   if (Mode->UsingIpv6) {
    728     Info->IpCnt    = 1;
    729     Info->UseUCast = TRUE;
    730 
    731     Info->SrvList[0].Type              = Type;
    732     Info->SrvList[0].AcceptAnyResponse = FALSE;
    733 
    734     //
    735     // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.
    736     //
    737     CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
    738 
    739     *SrvList  = Info->SrvList;
    740   } else {
    741     Entry     = NULL;
    742     IsFound   = FALSE;
    743     Cache4    = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;
    744     VendorOpt = &Cache4->VendorOpt;
    745 
    746     if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {
    747       //
    748       // Address is not acquired or no discovery options.
    749       //
    750       return EFI_INVALID_PARAMETER;
    751     }
    752 
    753     //
    754     // Parse the boot server entry from the vendor option in the last cached packet.
    755     //
    756     Info->UseMCast    = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);
    757     Info->UseBCast    = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);
    758     Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);
    759     Info->UseUCast    = (BOOLEAN) IS_VALID_BOOT_SERVERS (VendorOpt->BitMap);
    760 
    761     if (Info->UseMCast) {
    762       //
    763       // Get the multicast discover ip address from vendor option if has.
    764       //
    765       CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));
    766     }
    767 
    768     Info->IpCnt = 0;
    769 
    770     if (Info->UseUCast) {
    771       Entry = VendorOpt->BootSvr;
    772 
    773       while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {
    774         if (Entry->Type == HTONS (Type)) {
    775           IsFound = TRUE;
    776           break;
    777         }
    778         Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);
    779       }
    780 
    781       if (!IsFound) {
    782         return EFI_DEVICE_ERROR;
    783       }
    784 
    785       Info->IpCnt = Entry->IpCnt;
    786       if (Info->IpCnt >= 1) {
    787         *DiscoverInfo = AllocatePool (sizeof (*Info) + (Info->IpCnt - 1) * sizeof (**SrvList));
    788         if (*DiscoverInfo == NULL) {
    789           return EFI_OUT_OF_RESOURCES;
    790         }
    791         CopyMem (*DiscoverInfo, Info, sizeof (*Info));
    792         Info = *DiscoverInfo;
    793       }
    794 
    795       for (Index = 0; Index < Info->IpCnt; Index++) {
    796         CopyMem (&Info->SrvList[Index].IpAddr, &Entry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));
    797         Info->SrvList[Index].AcceptAnyResponse = !Info->MustUseList;
    798         Info->SrvList[Index].Type = NTOHS (Entry->Type);
    799       }
    800     }
    801 
    802     *BootEntry = Entry;
    803     *SrvList   = Info->SrvList;
    804   }
    805 
    806   return EFI_SUCCESS;
    807 }
    808 
    809 
    810 /**
    811   Build the discover packet and send out for boot server.
    812 
    813   @param[in]  Private               Pointer to PxeBc private data.
    814   @param[in]  Type                  PxeBc option boot item type.
    815   @param[in]  Layer                 Pointer to option boot item layer.
    816   @param[in]  UseBis                Use BIS or not.
    817   @param[in]  DestIp                Pointer to the destination address.
    818   @param[in]  IpCount               The count of the server address.
    819   @param[in]  SrvList               Pointer to the server address list.
    820 
    821   @retval     EFI_SUCCESS           Successfully discovered boot file.
    822   @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.
    823   @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
    824   @retval     Others                Failed to discover boot file.
    825 
    826 **/
    827 EFI_STATUS
    828 PxeBcDiscoverBootServer (
    829   IN  PXEBC_PRIVATE_DATA                *Private,
    830   IN  UINT16                            Type,
    831   IN  UINT16                            *Layer,
    832   IN  BOOLEAN                           UseBis,
    833   IN  EFI_IP_ADDRESS                    *DestIp,
    834   IN  UINT16                            IpCount,
    835   IN  EFI_PXE_BASE_CODE_SRVLIST         *SrvList
    836   )
    837 {
    838   if (Private->PxeBc.Mode->UsingIpv6) {
    839     return PxeBcDhcp6Discover (
    840              Private,
    841              Type,
    842              Layer,
    843              UseBis,
    844              DestIp
    845              );
    846   } else {
    847     return PxeBcDhcp4Discover (
    848              Private,
    849              Type,
    850              Layer,
    851              UseBis,
    852              DestIp,
    853              IpCount,
    854              SrvList
    855              );
    856   }
    857 }
    858 
    859 
    860 /**
    861   Discover all the boot information for boot file.
    862 
    863   @param[in, out] Private      Pointer to PxeBc private data.
    864   @param[out]     BufferSize   Size of the boot file to be downloaded.
    865 
    866   @retval EFI_SUCCESS          Successfully obtained all the boot information .
    867   @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
    868   @retval EFI_ABORTED          User cancel current operation.
    869   @retval Others               Failed to parse out the boot information.
    870 
    871 **/
    872 EFI_STATUS
    873 PxeBcDiscoverBootFile (
    874   IN OUT PXEBC_PRIVATE_DATA   *Private,
    875      OUT UINT64               *BufferSize
    876   )
    877 {
    878   EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
    879   EFI_PXE_BASE_CODE_MODE      *Mode;
    880   EFI_STATUS                  Status;
    881   UINT16                      Type;
    882   UINT16                      Layer;
    883   BOOLEAN                     UseBis;
    884 
    885   PxeBc = &Private->PxeBc;
    886   Mode  = PxeBc->Mode;
    887   Type  = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;
    888   Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;
    889 
    890   //
    891   // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
    892   // other pxe boot information.
    893   //
    894   Status = PxeBc->Dhcp (PxeBc, TRUE);
    895   if (EFI_ERROR (Status)) {
    896     return Status;
    897   }
    898 
    899   //
    900   // Select a boot server from boot server list.
    901   //
    902   Status = PxeBcSelectBootPrompt (Private);
    903 
    904   if (Status == EFI_SUCCESS) {
    905     //
    906     // Choose by user's input.
    907     //
    908     Status = PxeBcSelectBootMenu (Private, &Type, FALSE);
    909   } else if (Status == EFI_TIMEOUT) {
    910     //
    911     // Choose by default item.
    912     //
    913     Status = PxeBcSelectBootMenu (Private, &Type, TRUE);
    914   }
    915 
    916   if (!EFI_ERROR (Status)) {
    917 
    918     if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {
    919       //
    920       // Local boot(PXE bootstrap server) need abort
    921       //
    922       return EFI_ABORTED;
    923     }
    924 
    925     //
    926     // Start to discover the boot server to get (m)tftp server ip address, bootfile
    927     // name and bootfile size.
    928     //
    929     UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);
    930     Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);
    931     if (EFI_ERROR (Status)) {
    932       return Status;
    933     }
    934 
    935     if (Mode->PxeReplyReceived && !Mode->ProxyOfferReceived) {
    936       //
    937       // Some network boot loader only search the packet in Mode.ProxyOffer to get its server
    938       // IP address, so we need to store a copy of Mode.PxeReply packet into Mode.ProxyOffer.
    939       //
    940       if (Mode->UsingIpv6) {
    941         CopyMem (
    942           &Mode->ProxyOffer.Dhcpv6,
    943           &Mode->PxeReply.Dhcpv6,
    944           Private->PxeReply.Dhcp6.Packet.Ack.Length
    945           );
    946       } else {
    947         CopyMem (
    948           &Mode->ProxyOffer.Dhcpv4,
    949           &Mode->PxeReply.Dhcpv4,
    950           Private->PxeReply.Dhcp4.Packet.Ack.Length
    951           );
    952       }
    953       Mode->ProxyOfferReceived = TRUE;
    954     }
    955   }
    956 
    957   //
    958   // Parse the boot information.
    959   //
    960   if (Mode->UsingIpv6) {
    961     Status = PxeBcDhcp6BootInfo (Private, BufferSize);
    962   } else {
    963     Status = PxeBcDhcp4BootInfo (Private, BufferSize);
    964   }
    965 
    966   return Status;
    967 }
    968 
    969 
    970 /**
    971   Install PxeBaseCodeCallbackProtocol if not installed before.
    972 
    973   @param[in, out] Private           Pointer to PxeBc private data.
    974   @param[out]     NewMakeCallback   If TRUE, it is a new callback.
    975                                     Otherwise, it is not new callback.
    976   @retval EFI_SUCCESS          PxeBaseCodeCallbackProtocol installed succesfully.
    977   @retval Others               Failed to install PxeBaseCodeCallbackProtocol.
    978 
    979 **/
    980 EFI_STATUS
    981 PxeBcInstallCallback (
    982   IN OUT PXEBC_PRIVATE_DATA   *Private,
    983      OUT BOOLEAN              *NewMakeCallback
    984   )
    985 {
    986   EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
    987   EFI_STATUS                  Status;
    988 
    989   //
    990   // Check whether PxeBaseCodeCallbackProtocol already installed.
    991   //
    992   PxeBc  = &Private->PxeBc;
    993   Status = gBS->HandleProtocol (
    994                   Private->Controller,
    995                   &gEfiPxeBaseCodeCallbackProtocolGuid,
    996                   (VOID **) &Private->PxeBcCallback
    997                   );
    998   if (Status == EFI_UNSUPPORTED) {
    999 
   1000     CopyMem (
   1001       &Private->LoadFileCallback,
   1002       &gPxeBcCallBackTemplate,
   1003       sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)
   1004       );
   1005 
   1006     //
   1007     // Install a default callback if user didn't offer one.
   1008     //
   1009     Status = gBS->InstallProtocolInterface (
   1010                     &Private->Controller,
   1011                     &gEfiPxeBaseCodeCallbackProtocolGuid,
   1012                     EFI_NATIVE_INTERFACE,
   1013                     &Private->LoadFileCallback
   1014                     );
   1015 
   1016     (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);
   1017 
   1018     Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);
   1019     if (EFI_ERROR (Status)) {
   1020       PxeBc->Stop (PxeBc);
   1021       return Status;
   1022     }
   1023   }
   1024 
   1025   return EFI_SUCCESS;
   1026 }
   1027 
   1028 
   1029 /**
   1030   Uninstall PxeBaseCodeCallbackProtocol.
   1031 
   1032   @param[in]  Private           Pointer to PxeBc private data.
   1033   @param[in]  NewMakeCallback   If TRUE, it is a new callback.
   1034                                 Otherwise, it is not new callback.
   1035 
   1036 **/
   1037 VOID
   1038 PxeBcUninstallCallback (
   1039   IN PXEBC_PRIVATE_DATA        *Private,
   1040   IN BOOLEAN                   NewMakeCallback
   1041   )
   1042 {
   1043   EFI_PXE_BASE_CODE_PROTOCOL   *PxeBc;
   1044 
   1045   PxeBc = &Private->PxeBc;
   1046 
   1047   if (NewMakeCallback) {
   1048 
   1049     NewMakeCallback = FALSE;
   1050 
   1051     PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
   1052 
   1053     gBS->UninstallProtocolInterface (
   1054           Private->Controller,
   1055           &gEfiPxeBaseCodeCallbackProtocolGuid,
   1056           &Private->LoadFileCallback
   1057           );
   1058   }
   1059 }
   1060 
   1061 
   1062 /**
   1063   Download one of boot file in the list, and it's special for IPv6.
   1064 
   1065   @param[in]      Private           Pointer to PxeBc private data.
   1066   @param[in, out] BufferSize        Size of user buffer for input;
   1067                                     required buffer size for output.
   1068   @param[in]      Buffer            Pointer to user buffer.
   1069 
   1070   @retval EFI_SUCCESS               Read one of boot file in the list successfully.
   1071   @retval EFI_BUFFER_TOO_SMALL      The buffer size is not enough for boot file.
   1072   @retval EFI_NOT_FOUND             There is no proper boot file available.
   1073   @retval Others                    Failed to download boot file in the list.
   1074 
   1075 **/
   1076 EFI_STATUS
   1077 PxeBcReadBootFileList (
   1078   IN     PXEBC_PRIVATE_DATA           *Private,
   1079   IN OUT UINT64                       *BufferSize,
   1080   IN     VOID                         *Buffer           OPTIONAL
   1081   )
   1082 {
   1083   EFI_STATUS                          Status;
   1084   EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
   1085 
   1086   PxeBc        = &Private->PxeBc;
   1087 
   1088   //
   1089   // Try to download the boot file if everything is ready.
   1090   //
   1091   if (Buffer != NULL) {
   1092     Status = PxeBc->Mtftp (
   1093                       PxeBc,
   1094                       EFI_PXE_BASE_CODE_TFTP_READ_FILE,
   1095                       Buffer,
   1096                       FALSE,
   1097                       BufferSize,
   1098                       &Private->BlockSize,
   1099                       &Private->ServerIp,
   1100                       Private->BootFileName,
   1101                       NULL,
   1102                       FALSE
   1103                       );
   1104 
   1105 
   1106   } else {
   1107     Status      = EFI_BUFFER_TOO_SMALL;
   1108   }
   1109 
   1110   return Status;
   1111 }
   1112 
   1113 
   1114 /**
   1115   Load boot file into user buffer.
   1116 
   1117   @param[in]      Private           Pointer to PxeBc private data.
   1118   @param[in, out] BufferSize        Size of user buffer for input;
   1119                                     required buffer size for output.
   1120   @param[in]      Buffer            Pointer to user buffer.
   1121 
   1122   @retval EFI_SUCCESS          Get all the boot information successfully.
   1123   @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
   1124   @retval EFI_ABORTED          User cancelled the current operation.
   1125   @retval Others               Failed to parse out the boot information.
   1126 
   1127 **/
   1128 EFI_STATUS
   1129 PxeBcLoadBootFile (
   1130   IN     PXEBC_PRIVATE_DATA           *Private,
   1131   IN OUT UINTN                        *BufferSize,
   1132   IN     VOID                         *Buffer         OPTIONAL
   1133   )
   1134 {
   1135   BOOLEAN                             NewMakeCallback;
   1136   UINT64                              RequiredSize;
   1137   UINT64                              CurrentSize;
   1138   EFI_STATUS                          Status;
   1139   EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
   1140   EFI_PXE_BASE_CODE_MODE              *PxeBcMode;
   1141 
   1142   NewMakeCallback = FALSE;
   1143   PxeBc           = &Private->PxeBc;
   1144   PxeBcMode       = &Private->Mode;
   1145   CurrentSize     = *BufferSize;
   1146   RequiredSize    = 0;
   1147 
   1148   //
   1149   // Install pxebc callback protocol if hasn't been installed yet.
   1150   //
   1151   Status = PxeBcInstallCallback (Private, &NewMakeCallback);
   1152   if (EFI_ERROR(Status)) {
   1153     return Status;
   1154   }
   1155 
   1156   if (Private->BootFileSize == 0) {
   1157     //
   1158     // Discover the boot information about the bootfile if hasn't.
   1159     //
   1160     Status = PxeBcDiscoverBootFile (Private, &RequiredSize);
   1161     if (EFI_ERROR (Status)) {
   1162       goto ON_EXIT;
   1163     }
   1164 
   1165     if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {
   1166       //
   1167       // It's error if the required buffer size is beyond the system scope.
   1168       //
   1169       Status = EFI_DEVICE_ERROR;
   1170       goto ON_EXIT;
   1171     } else if (RequiredSize > 0) {
   1172       //
   1173       // Get the right buffer size of the bootfile required.
   1174       //
   1175       if (CurrentSize < RequiredSize || Buffer == NULL) {
   1176         //
   1177         // It's buffer too small if the size of user buffer is smaller than the required.
   1178         //
   1179         CurrentSize = RequiredSize;
   1180         Status      = EFI_BUFFER_TOO_SMALL;
   1181         goto ON_EXIT;
   1182       }
   1183       CurrentSize = RequiredSize;
   1184     } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {
   1185       //
   1186       // Try to download another bootfile in list if failed to get the filesize of the last one.
   1187       // It's special for the case of IPv6.
   1188       //
   1189       Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);
   1190       goto ON_EXIT;
   1191     }
   1192   } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {
   1193     //
   1194     // It's buffer too small if the size of user buffer is smaller than the required.
   1195     //
   1196     CurrentSize = Private->BootFileSize;
   1197     Status      = EFI_BUFFER_TOO_SMALL;
   1198     goto ON_EXIT;
   1199   }
   1200 
   1201   //
   1202   // Begin to download the bootfile if everything is ready.
   1203   //
   1204   AsciiPrint ("\n Downloading NBP file...\n");
   1205   if (PxeBcMode->UsingIpv6) {
   1206     Status = PxeBcReadBootFileList (
   1207                Private,
   1208                &CurrentSize,
   1209                Buffer
   1210                );
   1211   } else {
   1212     Status = PxeBc->Mtftp (
   1213                       PxeBc,
   1214                       EFI_PXE_BASE_CODE_TFTP_READ_FILE,
   1215                       Buffer,
   1216                       FALSE,
   1217                       &CurrentSize,
   1218                       &Private->BlockSize,
   1219                       &Private->ServerIp,
   1220                       Private->BootFileName,
   1221                       NULL,
   1222                       FALSE
   1223                       );
   1224   }
   1225 
   1226 ON_EXIT:
   1227   *BufferSize = (UINTN) CurrentSize;
   1228   PxeBcUninstallCallback(Private, NewMakeCallback);
   1229 
   1230   if (Status == EFI_SUCCESS) {
   1231     AsciiPrint ("\n  Succeed to download NBP file.\n");
   1232     return EFI_SUCCESS;
   1233   } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {
   1234     AsciiPrint ("\n  PXE-E05: Buffer size is smaller than the requested file.\n");
   1235   } else if (Status == EFI_DEVICE_ERROR) {
   1236     AsciiPrint ("\n  PXE-E07: Network device error.\n");
   1237   } else if (Status == EFI_OUT_OF_RESOURCES) {
   1238     AsciiPrint ("\n  PXE-E09: Could not allocate I/O buffers.\n");
   1239   } else if (Status == EFI_NO_MEDIA) {
   1240     AsciiPrint ("\n  PXE-E12: Could not detect network connection.\n");
   1241   } else if (Status == EFI_NO_RESPONSE) {
   1242     AsciiPrint ("\n  PXE-E16: No offer received.\n");
   1243   } else if (Status == EFI_TIMEOUT) {
   1244     AsciiPrint ("\n  PXE-E18: Server response timeout.\n");
   1245   } else if (Status == EFI_ABORTED) {
   1246     AsciiPrint ("\n  PXE-E21: Remote boot cancelled.\n");
   1247   } else if (Status == EFI_ICMP_ERROR) {
   1248     AsciiPrint ("\n  PXE-E22: Client received ICMP error from server.\n");
   1249   } else if (Status == EFI_TFTP_ERROR) {
   1250     AsciiPrint ("\n  PXE-E23: Client received TFTP error from server.\n");
   1251   } else if (Status == EFI_NOT_FOUND) {
   1252     AsciiPrint ("\n  PXE-E53: No boot filename received.\n");
   1253   } else if (Status != EFI_BUFFER_TOO_SMALL) {
   1254     AsciiPrint ("\n  PXE-E99: Unexpected network error.\n");
   1255   }
   1256 
   1257   return Status;
   1258 }
   1259 
   1260