• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
PxeBcDisplayBootItem(IN UINT8 * Str,IN UINT8 Len)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
PxeBcSelectBootPrompt(IN PXEBC_PRIVATE_DATA * Private)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
PxeBcSelectBootMenu(IN PXEBC_PRIVATE_DATA * Private,OUT UINT16 * Type,IN BOOLEAN UseDefaultItem)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
PxeBcDhcp4BootInfo(IN OUT PXEBC_PRIVATE_DATA * Private,OUT UINT64 * BufferSize)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
PxeBcDhcp6BootInfo(IN OUT PXEBC_PRIVATE_DATA * Private,OUT UINT64 * BufferSize)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
PxeBcExtractDiscoverInfo(IN PXEBC_PRIVATE_DATA * Private,IN UINT16 Type,IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO ** DiscoverInfo,OUT PXEBC_BOOT_SVR_ENTRY ** BootEntry,OUT EFI_PXE_BASE_CODE_SRVLIST ** SrvList)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
PxeBcDiscoverBootServer(IN PXEBC_PRIVATE_DATA * Private,IN UINT16 Type,IN UINT16 * Layer,IN BOOLEAN UseBis,IN EFI_IP_ADDRESS * DestIp,IN UINT16 IpCount,IN EFI_PXE_BASE_CODE_SRVLIST * SrvList)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
PxeBcDiscoverBootFile(IN OUT PXEBC_PRIVATE_DATA * Private,OUT UINT64 * BufferSize)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
PxeBcInstallCallback(IN OUT PXEBC_PRIVATE_DATA * Private,OUT BOOLEAN * NewMakeCallback)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
PxeBcUninstallCallback(IN PXEBC_PRIVATE_DATA * Private,IN BOOLEAN NewMakeCallback)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
PxeBcReadBootFileList(IN PXEBC_PRIVATE_DATA * Private,IN OUT UINT64 * BufferSize,IN VOID * Buffer OPTIONAL)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
PxeBcLoadBootFile(IN PXEBC_PRIVATE_DATA * Private,IN OUT UINTN * BufferSize,IN VOID * Buffer OPTIONAL)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