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