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
PxeBcDisplayBootItem(IN UINT8 * Str,IN UINT8 Len)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
PxeBcSelectBootPrompt(IN PXEBC_PRIVATE_DATA * Private)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
PxeBcSelectBootMenu(IN PXEBC_PRIVATE_DATA * Private,OUT UINT16 * Type,IN BOOLEAN UseDefaultItem)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
PxeBcDhcp4BootInfo(IN OUT PXEBC_PRIVATE_DATA * Private,OUT UINT64 * BufferSize)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
PxeBcDhcp6BootInfo(IN OUT PXEBC_PRIVATE_DATA * Private,OUT UINT64 * BufferSize)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
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)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
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)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
PxeBcDiscoverBootFile(IN OUT PXEBC_PRIVATE_DATA * Private,OUT UINT64 * BufferSize)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
PxeBcInstallCallback(IN OUT PXEBC_PRIVATE_DATA * Private,OUT BOOLEAN * NewMakeCallback)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
PxeBcUninstallCallback(IN PXEBC_PRIVATE_DATA * Private,IN BOOLEAN NewMakeCallback)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
PxeBcReadBootFileList(IN PXEBC_PRIVATE_DATA * Private,IN OUT UINT64 * BufferSize,IN VOID * Buffer OPTIONAL)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
PxeBcLoadBootFile(IN PXEBC_PRIVATE_DATA * Private,IN OUT UINTN * BufferSize,IN VOID * Buffer OPTIONAL)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