Patch to remove STATIC modifier. This is on longer recommended by EFI Framework codin...
[mirror_edk2.git] / MdeModulePkg / Universal / Network / PxeBcDxe / Pxe_loadfile.c
1 /** @file\r
2 \r
3 Copyright (c) 2004 - 2007, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 Module Name:\r
13   pxe_loadfile.c\r
14 \r
15 Abstract:\r
16   An implementation of the load file protocol for network devices.\r
17 \r
18 \r
19 **/\r
20 \r
21 \r
22 #include "Bc.h"\r
23 \r
24 #define DO_MENU     (EFI_SUCCESS)\r
25 #define NO_MENU     (DO_MENU + 1)\r
26 #define LOCAL_BOOT  (EFI_ABORTED)\r
27 #define AUTO_SELECT (NO_MENU)\r
28 \r
29 #define NUMBER_ROWS   25  // we set to mode 0\r
30 #define MAX_MENULIST  23\r
31 \r
32 #define Ctl(x)  (0x1F & (x))\r
33 \r
34 typedef union {\r
35   DHCPV4_OP_STRUCT          *OpPtr;\r
36   PXE_BOOT_MENU_ENTRY       *CurrentMenuItemPtr;\r
37   PXE_OP_DISCOVERY_CONTROL  *DiscCtlOpStr;\r
38   PXE_OP_BOOT_MENU          *MenuPtr;\r
39   UINT8                     *BytePtr;\r
40 } UNION_PTR;\r
41 \r
42 \r
43 \r
44 /**\r
45   PxeBc callback routine for status updates and aborts.\r
46 \r
47   @param  This                                        Pointer to PxeBcCallback\r
48                                                       interface\r
49   @param  Function                                    PxeBc function ID#\r
50   @param  Received                                    Receive/transmit flag\r
51   @param  PacketLength                                Length of received packet (0\r
52                                                       == idle callback)\r
53   @param  PacketPtr                                   Pointer to received packet\r
54                                                       (NULL == idle callback)\r
55 \r
56   @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE\r
57                                                       EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT\r
58                                                       -\r
59 \r
60 **/\r
61 EFI_PXE_BASE_CODE_CALLBACK_STATUS\r
62 EFIAPI\r
63 bc_callback (\r
64   IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  * This,\r
65   IN EFI_PXE_BASE_CODE_FUNCTION           Function,\r
66   IN BOOLEAN                              Received,\r
67   IN UINT32                               PacketLength,\r
68   IN EFI_PXE_BASE_CODE_PACKET             * PacketPtr OPTIONAL\r
69   )\r
70 {\r
71   STATIC UINTN  Propeller;\r
72 \r
73   EFI_INPUT_KEY Key;\r
74   UINTN         Row;\r
75   UINTN         Col;\r
76 \r
77   Propeller = 0;\r
78   //\r
79   // Resolve Warning 4 unreferenced parameter problem\r
80   //\r
81   This = This;\r
82 \r
83   //\r
84   // Check for user abort.\r
85   //\r
86   if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_SUCCESS) {\r
87     if (!Key.ScanCode) {\r
88       if (Key.UnicodeChar == Ctl ('c')) {\r
89         return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;\r
90       }\r
91     } else if (Key.ScanCode == SCAN_ESC) {\r
92       return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;\r
93     }\r
94   }\r
95   //\r
96   // Do nothing if this is a receive.\r
97   //\r
98   if (Received) {\r
99     return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
100   }\r
101   //\r
102   // The display code is only for these functions.\r
103   //\r
104   switch (Function) {\r
105   case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:\r
106     //\r
107     // If this is a transmit and not a M/TFTP open request,\r
108     // return now.  Do not print a dot for each M/TFTP packet\r
109     // that is sent, only for the open packets.\r
110     //\r
111     if (PacketLength != 0 && PacketPtr != NULL) {\r
112       if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {\r
113         return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
114       }\r
115     }\r
116 \r
117     break;\r
118 \r
119   case EFI_PXE_BASE_CODE_FUNCTION_DHCP:\r
120   case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:\r
121     break;\r
122 \r
123   default:\r
124     return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
125   }\r
126   //\r
127   // Display routines\r
128   //\r
129   if (PacketLength != 0 && PacketPtr != NULL) {\r
130     //\r
131     // Display a '.' when a packet is transmitted.\r
132     //\r
133     AsciiPrint (".");\r
134   } else if (PacketLength == 0 && PacketPtr == NULL) {\r
135     //\r
136     // Display a propeller when waiting for packets if at\r
137     // least 200 ms have passed.\r
138     //\r
139     Row = gST->ConOut->Mode->CursorRow;\r
140     Col = gST->ConOut->Mode->CursorColumn;\r
141 \r
142     AsciiPrint ("%c", "/-\\|"[Propeller]);\r
143     gST->ConOut->SetCursorPosition (gST->ConOut, Col, Row);\r
144 \r
145     Propeller = (Propeller + 1) & 3;\r
146   }\r
147 \r
148   return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
149 }\r
150 \r
151 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  _bc_callback = {\r
152   EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,\r
153   &bc_callback\r
154 };\r
155 \r
156 \r
157 /**\r
158   Display an IPv4 address in dot notation.\r
159 \r
160   @param  Ptr                                         Pointer to IPv4 address.\r
161 \r
162   @return None\r
163 \r
164 **/\r
165 VOID\r
166 PrintIpv4 (\r
167   UINT8 *Ptr\r
168   )\r
169 {\r
170   if (Ptr != NULL) {\r
171     AsciiPrint ("%d.%d.%d.%d", Ptr[0], Ptr[1], Ptr[2], Ptr[3]);\r
172   }\r
173 }\r
174 \r
175 \r
176 /**\r
177   Display client and server IP information.\r
178 \r
179   @param  Private                                     Pointer to PxeBc interface\r
180 \r
181   @return None\r
182 \r
183 **/\r
184 VOID\r
185 ShowMyInfo (\r
186   IN PXE_BASECODE_DEVICE *Private\r
187   )\r
188 {\r
189   EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
190   UINTN                   Index;\r
191 \r
192   //\r
193   // Do nothing if a NULL pointer is passed in.\r
194   //\r
195   if (Private == NULL) {\r
196     return ;\r
197   }\r
198   //\r
199   // Get pointer to PXE BaseCode mode structure\r
200   //\r
201   PxeBcMode = Private->EfiBc.Mode;\r
202 \r
203   //\r
204   // Display client IP address\r
205   //\r
206   AsciiPrint ("\rCLIENT IP: ");\r
207   PrintIpv4 (PxeBcMode->StationIp.v4.Addr);\r
208 \r
209   //\r
210   // Display subnet mask\r
211   //\r
212   AsciiPrint ("  MASK: ");\r
213   PrintIpv4 (PxeBcMode->SubnetMask.v4.Addr);\r
214 \r
215   //\r
216   // Display DHCP and proxyDHCP IP addresses\r
217   //\r
218   if (PxeBcMode->ProxyOfferReceived) {\r
219     AsciiPrint ("\nDHCP IP: ");\r
220     PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);\r
221 \r
222     AsciiPrint ("  PROXY IP: ");\r
223     PrintIpv4 (((DHCPV4_OP_SERVER_IP *) PXE_OFFER_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);\r
224   } else {\r
225     AsciiPrint ("  DHCP IP: ");\r
226     PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);\r
227   }\r
228   //\r
229   // Display gateway IP addresses\r
230   //\r
231   for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {\r
232     if ((Index % 3) == 0) {\r
233       AsciiPrint ("\r\nGATEWAY IP:");\r
234     }\r
235 \r
236     AsciiPrint (" ");\r
237     PrintIpv4 (PxeBcMode->RouteTable[Index].GwAddr.v4.Addr);\r
238     AsciiPrint (" ");\r
239   }\r
240 \r
241   AsciiPrint ("\n");\r
242 }\r
243 \r
244 \r
245 /**\r
246   Display prompt and wait for input.\r
247 \r
248   @param  Private                                     Pointer to PxeBc interface\r
249   @param  BootPromptPtr                               Pointer to PXE boot prompt\r
250                                                       option\r
251 \r
252   @retval AUTO_SELECT                                 DO_MENU -\r
253   @retval NO_MENU\r
254   @retval LOCAL_BOOT\r
255 \r
256 **/\r
257 EFI_STATUS\r
258 DoPrompt (\r
259   PXE_BASECODE_DEVICE *Private,\r
260   PXE_OP_BOOT_PROMPT  *BootPromptPtr\r
261   )\r
262 {\r
263   EFI_STATUS  Status;\r
264   EFI_EVENT   TimeoutEvent;\r
265   EFI_EVENT   SecondsEvent;\r
266   INT32       SecColumn;\r
267   INT32       SecRow;\r
268   UINT8       SaveChar;\r
269   UINT8       SecsLeft;\r
270 \r
271   //\r
272   // if auto select, just get right to it\r
273   //\r
274   if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_AUTO_SELECT) {\r
275     return AUTO_SELECT;\r
276   }\r
277   //\r
278   // if no timeout, go directly to display of menu\r
279   //\r
280   if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_NO_TIMEOUT) {\r
281     return DO_MENU;\r
282   }\r
283   //\r
284   //\r
285   //\r
286   Status = gBS->CreateEvent (\r
287                   EVT_TIMER,\r
288                   TPL_CALLBACK,\r
289                   NULL,\r
290                   NULL,\r
291                   &TimeoutEvent\r
292                   );\r
293 \r
294   if (EFI_ERROR (Status)) {\r
295     return DO_MENU;\r
296   }\r
297 \r
298   Status = gBS->SetTimer (\r
299                   TimeoutEvent,\r
300                   TimerRelative,\r
301                   BootPromptPtr->Timeout * 10000000 + 100000\r
302                   );\r
303 \r
304   if (EFI_ERROR (Status)) {\r
305     gBS->CloseEvent (TimeoutEvent);\r
306     return DO_MENU;\r
307   }\r
308   //\r
309   //\r
310   //\r
311   Status = gBS->CreateEvent (\r
312                   EVT_TIMER,\r
313                   TPL_CALLBACK,\r
314                   NULL,\r
315                   NULL,\r
316                   &SecondsEvent\r
317                   );\r
318 \r
319   if (EFI_ERROR (Status)) {\r
320     gBS->CloseEvent (TimeoutEvent);\r
321     return DO_MENU;\r
322   }\r
323 \r
324   Status = gBS->SetTimer (\r
325                   SecondsEvent,\r
326                   TimerPeriodic,\r
327                   10000000\r
328                   );  /* 1 second */\r
329 \r
330   if (EFI_ERROR (Status)) {\r
331     gBS->CloseEvent (SecondsEvent);\r
332     gBS->CloseEvent (TimeoutEvent);\r
333     return DO_MENU;\r
334   }\r
335   //\r
336   // display the prompt\r
337   // IMPORTANT!  This prompt is an ASCII character string that may\r
338   // not be terminated with a NULL byte.\r
339   //\r
340   SaveChar  = BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1];\r
341   BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = 0;\r
342 \r
343   AsciiPrint ("%a ", BootPromptPtr->Prompt);\r
344   BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = SaveChar;\r
345 \r
346   //\r
347   // wait until time expires or selection made - menu or local\r
348   //\r
349   SecColumn = gST->ConOut->Mode->CursorColumn;\r
350   SecRow    = gST->ConOut->Mode->CursorRow;\r
351   SecsLeft  = BootPromptPtr->Timeout;\r
352 \r
353   gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);\r
354   AsciiPrint ("(%d) ", SecsLeft);\r
355 \r
356   //\r
357   // set the default action to be AUTO_SELECT\r
358   //\r
359   Status = AUTO_SELECT;\r
360 \r
361   while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
362     EFI_INPUT_KEY Key;\r
363 \r
364     if (!EFI_ERROR (gBS->CheckEvent (SecondsEvent))) {\r
365       --SecsLeft;\r
366       gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);\r
367       AsciiPrint ("(%d) ", SecsLeft);\r
368     }\r
369 \r
370     if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {\r
371       UINT8       Buffer[512];\r
372       UINTN       BufferSize;\r
373 \r
374       BufferSize = sizeof Buffer;\r
375 \r
376       Status = Private->EfiBc.UdpRead (\r
377                                 &Private->EfiBc,\r
378                                 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |\r
379                                 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |\r
380                                 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,\r
381                                 NULL, /* dest ip */\r
382                                 NULL, /* dest port */\r
383                                 NULL, /* src ip */\r
384                                 NULL, /* src port */\r
385                                 NULL, /* hdr size */\r
386                                 NULL, /* hdr ptr */\r
387                                 &BufferSize,\r
388                                 Buffer\r
389                                 );\r
390 \r
391       continue;\r
392     }\r
393 \r
394     if (Key.ScanCode == 0) {\r
395       switch (Key.UnicodeChar) {\r
396       case Ctl ('c'):\r
397         Status = LOCAL_BOOT;\r
398         break;\r
399 \r
400       case Ctl ('m'):\r
401       case 'm':\r
402       case 'M':\r
403         Status = DO_MENU;\r
404         break;\r
405 \r
406       default:\r
407         continue;\r
408       }\r
409     } else {\r
410       switch (Key.ScanCode) {\r
411       case SCAN_F8:\r
412         Status = DO_MENU;\r
413         break;\r
414 \r
415       case SCAN_ESC:\r
416         Status = LOCAL_BOOT;\r
417         break;\r
418 \r
419       default:\r
420         continue;\r
421       }\r
422     }\r
423 \r
424     break;\r
425   }\r
426 \r
427   gBS->CloseEvent (SecondsEvent);\r
428   gBS->CloseEvent (TimeoutEvent);\r
429 \r
430   gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);\r
431   AsciiPrint ("     ");\r
432 \r
433   return Status;\r
434 }\r
435 \r
436 \r
437 /**\r
438   Display one menu item.\r
439 \r
440   @param  MenuItemPtr                                 Pointer to PXE menu item\r
441                                                       option.\r
442 \r
443   @return None\r
444 \r
445 **/\r
446 VOID\r
447 PrintMenuItem (\r
448   PXE_BOOT_MENU_ENTRY *MenuItemPtr\r
449   )\r
450 {\r
451   UINT8 Length;\r
452   UINT8 SaveChar;\r
453 \r
454   Length                    = (UINT8) MIN (70, MenuItemPtr->DataLen);\r
455   SaveChar                  = MenuItemPtr->Data[Length];\r
456 \r
457   MenuItemPtr->Data[Length] = 0;\r
458   AsciiPrint ("     %a\n", MenuItemPtr->Data);\r
459   MenuItemPtr->Data[Length] = SaveChar;\r
460 }\r
461 \r
462 \r
463 /**\r
464   Display and process menu.\r
465 \r
466   @param  Private                                     Pointer to PxeBc interface\r
467   @param  RxBufferPtr                                 Pointer to receive buffer\r
468 \r
469   @retval NO_MENU\r
470   @retval LOCAL_BOOT\r
471 \r
472 **/\r
473 EFI_STATUS\r
474 DoMenu (\r
475   PXE_BASECODE_DEVICE *Private,\r
476   DHCP_RECEIVE_BUFFER *RxBufferPtr\r
477   )\r
478 {\r
479   PXE_OP_DISCOVERY_CONTROL  *DiscoveryControlPtr;\r
480   PXE_BOOT_MENU_ENTRY       *MenuItemPtrs[MAX_MENULIST];\r
481   EFI_STATUS                Status;\r
482   UNION_PTR                 Ptr;\r
483   UINTN                     SaveNumRte;\r
484   UINTN                     TopRow;\r
485   UINTN                     MenuLth;\r
486   UINTN                     NumMenuItems;\r
487   UINTN                     Index;\r
488   UINTN                     Longest;\r
489   UINTN                     Selected;\r
490   UINT16                    Type;\r
491   UINT16                    Layer;\r
492   BOOLEAN                   Done;\r
493 \r
494   Selected  = 0;\r
495   Layer     = 0;\r
496 \r
497   DEBUG ((DEBUG_WARN, "\nDoMenu()  Enter."));\r
498 \r
499   /* see if we have a menu/prompt */\r
500   if (!(RxBufferPtr->OpAdds.Status & DISCOVER_TYPE)) {\r
501     DEBUG (\r
502       (DEBUG_WARN,\r
503       "\nDoMenu()  No menu/prompt info.  OpAdds.Status == %xh  ",\r
504       RxBufferPtr->OpAdds.Status)\r
505       );\r
506 \r
507     return NO_MENU;\r
508   }\r
509 \r
510   DiscoveryControlPtr = (PXE_OP_DISCOVERY_CONTROL *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];\r
511 \r
512   //\r
513   // if not USE_BOOTFILE or no bootfile given, must have menu stuff\r
514   //\r
515   if ((DiscoveryControlPtr->ControlBits & USE_BOOTFILE) && RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {\r
516     DEBUG ((DEBUG_WARN, "\nDoMenu()  DHCP w/ bootfile.  "));\r
517     return NO_MENU;\r
518   }\r
519   //\r
520   // do prompt & menu if necessary\r
521   //\r
522   Status = DoPrompt (Private, (PXE_OP_BOOT_PROMPT *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]);\r
523 \r
524   if (Status == LOCAL_BOOT) {\r
525     DEBUG ((DEBUG_WARN, "\nDoMenu()  DoPrompt() returned LOCAL_BOOT.  "));\r
526 \r
527     return Status;\r
528   }\r
529 \r
530   Ptr.BytePtr             = (UINT8 *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1];\r
531 \r
532   MenuLth                 = Ptr.MenuPtr->Header.Length;\r
533   Ptr.CurrentMenuItemPtr  = Ptr.MenuPtr->MenuItem;\r
534 \r
535   //\r
536   // build menu items array\r
537   //\r
538   for (Longest = NumMenuItems = Index = 0; Index < MenuLth && NumMenuItems < MAX_MENULIST;) {\r
539     UINTN lth;\r
540 \r
541     lth = Ptr.CurrentMenuItemPtr->DataLen + sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data);\r
542 \r
543     MenuItemPtrs[NumMenuItems++] = Ptr.CurrentMenuItemPtr;\r
544 \r
545     if (lth > Longest) {\r
546       //\r
547       // check if too long\r
548       //\r
549       if ((Longest = lth) > 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))) {\r
550         Longest = 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data));\r
551       }\r
552     }\r
553 \r
554     Index += lth;\r
555     Ptr.BytePtr += lth;\r
556   }\r
557 \r
558   if (Status != AUTO_SELECT) {\r
559     UINT8 BlankBuf[75];\r
560 \r
561     SetMem (BlankBuf, sizeof BlankBuf, ' ');\r
562     BlankBuf[Longest + 5 - (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))] = 0;\r
563     AsciiPrint ("\n");\r
564 \r
565     //\r
566     // now put up menu\r
567     //\r
568     for (Index = 0; Index < NumMenuItems; ++Index) {\r
569       PrintMenuItem (MenuItemPtrs[Index]);\r
570     }\r
571 \r
572     TopRow = gST->ConOut->Mode->CursorRow - NumMenuItems;\r
573 \r
574     //\r
575     // now wait for a selection\r
576     //\r
577     Done = FALSE;\r
578     do {\r
579       //\r
580       // highlight selection\r
581       //\r
582       EFI_INPUT_KEY Key;\r
583       UINTN         NewSelected;\r
584 \r
585       NewSelected = Selected;\r
586 \r
587       //\r
588       // highlight selected row\r
589       //\r
590       gST->ConOut->SetAttribute (\r
591                     gST->ConOut,\r
592                     EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)\r
593                     );\r
594       gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Selected);\r
595 \r
596       AsciiPrint (" --->%a\r", BlankBuf);\r
597 \r
598       PrintMenuItem (MenuItemPtrs[Selected]);\r
599       gST->ConOut->SetAttribute (\r
600                     gST->ConOut,\r
601                     EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)\r
602                     );\r
603       gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + NumMenuItems);\r
604 \r
605       //\r
606       // wait for a keystroke\r
607       //\r
608       while (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {\r
609         UINT8 TmpBuf[512];\r
610         UINTN TmpBufLen;\r
611 \r
612         TmpBufLen = sizeof TmpBuf;\r
613 \r
614         Private->EfiBc.UdpRead (\r
615                         &Private->EfiBc,\r
616                         EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |\r
617                         EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |\r
618                         EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,\r
619                         NULL, /* dest ip */\r
620                         NULL, /* dest port */\r
621                         NULL, /* src ip */\r
622                         NULL, /* src port */\r
623                         NULL, /* hdr size */\r
624                         NULL, /* hdr ptr */\r
625                         &TmpBufLen,\r
626                         TmpBuf\r
627                         );\r
628       }\r
629 \r
630       if (!Key.ScanCode) {\r
631         switch (Key.UnicodeChar) {\r
632         case Ctl ('c'):\r
633           Key.ScanCode = SCAN_ESC;\r
634           break;\r
635 \r
636         case Ctl ('j'): /* linefeed */\r
637         case Ctl ('m'): /* return */\r
638           Done = TRUE;\r
639           break;\r
640 \r
641         case Ctl ('i'): /* tab */\r
642         case ' ':\r
643         case 'd':\r
644         case 'D':\r
645           Key.ScanCode = SCAN_DOWN;\r
646           break;\r
647 \r
648         case Ctl ('h'): /* backspace */\r
649         case 'u':\r
650         case 'U':\r
651           Key.ScanCode = SCAN_UP;\r
652           break;\r
653 \r
654         default:\r
655           Key.ScanCode = 0;\r
656         }\r
657       }\r
658 \r
659       switch (Key.ScanCode) {\r
660       case SCAN_LEFT:\r
661       case SCAN_UP:\r
662         if (NewSelected) {\r
663           --NewSelected;\r
664         }\r
665 \r
666         break;\r
667 \r
668       case SCAN_DOWN:\r
669       case SCAN_RIGHT:\r
670         if (++NewSelected == NumMenuItems) {\r
671           --NewSelected;\r
672         }\r
673 \r
674         break;\r
675 \r
676       case SCAN_PAGE_UP:\r
677       case SCAN_HOME:\r
678         NewSelected = 0;\r
679         break;\r
680 \r
681       case SCAN_PAGE_DOWN:\r
682       case SCAN_END:\r
683         NewSelected = NumMenuItems - 1;\r
684         break;\r
685 \r
686       case SCAN_ESC:\r
687         return LOCAL_BOOT;\r
688       }\r
689 \r
690       /* unhighlight last selected row */\r
691       gST->ConOut->SetCursorPosition (gST->ConOut, 5, TopRow + Selected);\r
692 \r
693       AsciiPrint ("%a\r", BlankBuf);\r
694 \r
695       PrintMenuItem (MenuItemPtrs[Selected]);\r
696 \r
697       Selected = NewSelected;\r
698     } while (!Done);\r
699   }\r
700 \r
701   SaveNumRte  = Private->EfiBc.Mode->RouteTableEntries;\r
702 \r
703   Type        = NTOHS (MenuItemPtrs[Selected]->Type);\r
704 \r
705   if (Type == 0) {\r
706     DEBUG ((DEBUG_WARN, "\nDoMenu()  Local boot selected.  "));\r
707     return LOCAL_BOOT;\r
708   }\r
709 \r
710   AsciiPrint ("Discover");\r
711 \r
712   Status = Private->EfiBc.Discover (\r
713                             &Private->EfiBc,\r
714                             Type,\r
715                             &Layer,\r
716                             (BOOLEAN) (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected),\r
717                             0\r
718                             );\r
719 \r
720   if (EFI_ERROR (Status)) {\r
721     AsciiPrint ("\r                    \r");\r
722 \r
723     DEBUG (\r
724       (DEBUG_WARN,\r
725       "\nDoMenu()  Return w/ %xh (%r).",\r
726       Status,\r
727       Status)\r
728       );\r
729 \r
730     return Status;\r
731   }\r
732 \r
733   AsciiPrint ("\rBOOT_SERVER_IP: ");\r
734   PrintIpv4 ((UINT8 *) &Private->ServerIp);\r
735 \r
736   for (Index = SaveNumRte; Index < Private->EfiBc.Mode->RouteTableEntries; ++Index) {\r
737     if ((Index % 3) == 0) {\r
738       AsciiPrint ("\r\nGATEWAY IP:");\r
739     }\r
740 \r
741     AsciiPrint (" ");\r
742     PrintIpv4 ((UINT8 *) &Private->EfiBc.Mode->RouteTable[Index].GwAddr);\r
743     AsciiPrint (" ");\r
744   }\r
745 \r
746   AsciiPrint ("\n");\r
747 \r
748   DEBUG ((DEBUG_WARN, "\nDoMenu()  Return w/ EFI_SUCCESS.  "));\r
749 \r
750   return EFI_SUCCESS;\r
751 }\r
752 \r
753 \r
754 /**\r
755   Get value 8- or 16-bit value from DHCP option.\r
756 \r
757   @param  OpPtr                                       Pointer to DHCP option\r
758 \r
759   @return Value from DHCP option\r
760 \r
761 **/\r
762 UINT16\r
763 GetValue (\r
764   DHCPV4_OP_STRUCT *OpPtr\r
765   )\r
766 {\r
767   if (OpPtr->Header.Length == 1) {\r
768     return OpPtr->Data[0];\r
769   } else {\r
770     return NTOHS (OpPtr->Data);\r
771   }\r
772 }\r
773 \r
774 \r
775 /**\r
776   Locate opcode in buffer.\r
777 \r
778   @param  BufferPtr                                   Pointer to buffer\r
779   @param  BufferLen                                   Length of buffer\r
780   @param  OpCode                                      Option number\r
781 \r
782   @return Pointer to opcode, may be NULL\r
783 \r
784 **/\r
785 UINT8 *\r
786 _PxeBcFindOpt (\r
787   UINT8 *BufferPtr,\r
788   UINTN BufferLen,\r
789   UINT8 OpCode\r
790   )\r
791 {\r
792   if (BufferPtr == NULL) {\r
793     return NULL;\r
794   }\r
795 \r
796   while (BufferLen != 0) {\r
797     if (*BufferPtr == OpCode) {\r
798       return BufferPtr;\r
799     }\r
800 \r
801     switch (*BufferPtr) {\r
802     case OP_END:\r
803       return NULL;\r
804 \r
805     case OP_PAD:\r
806       ++BufferPtr;\r
807       --BufferLen;\r
808       continue;\r
809     }\r
810 \r
811     if ((UINTN) BufferLen <= (UINTN) 2 + BufferPtr[1]) {\r
812       return NULL;\r
813     }\r
814 \r
815     BufferLen -= 2 + BufferPtr[1];\r
816     BufferPtr += 2 + BufferPtr[1];\r
817   }\r
818 \r
819   return NULL;\r
820 }\r
821 \r
822 \r
823 /**\r
824   Find option in packet\r
825 \r
826   @param  PacketPtr                                   Pointer to packet\r
827   @param  OpCode                                      option number\r
828 \r
829   @return Pointer to option in packet\r
830 \r
831 **/\r
832 UINT8 *\r
833 PxeBcFindDhcpOpt (\r
834   EFI_PXE_BASE_CODE_PACKET  *PacketPtr,\r
835   UINT8                     OpCode\r
836   )\r
837 {\r
838   UINTN PacketLen;\r
839   UINT8 Overload;\r
840   UINT8 *OptionBufferPtr;\r
841 \r
842   //\r
843   //\r
844   //\r
845   PacketLen = 380;\r
846   Overload  = 0;\r
847 \r
848   //\r
849   // Figure size of DHCP option space.\r
850   //\r
851   OptionBufferPtr = _PxeBcFindOpt (\r
852                       PacketPtr->Dhcpv4.DhcpOptions,\r
853                       380,\r
854                       OP_DHCP_MAX_MESSAGE_SZ\r
855                       );\r
856 \r
857   if (OptionBufferPtr != NULL) {\r
858     if (OptionBufferPtr[1] == 2) {\r
859       UINT16  n;\r
860 \r
861       CopyMem (&n, &OptionBufferPtr[2], 2);\r
862       PacketLen = HTONS (n);\r
863 \r
864       if (PacketLen < sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET)) {\r
865         PacketLen = 380;\r
866       } else {\r
867         PacketLen -= (PacketPtr->Dhcpv4.DhcpOptions - &PacketPtr->Dhcpv4.BootpOpcode) + 28;\r
868       }\r
869     }\r
870   }\r
871   //\r
872   // Look for option overloading.\r
873   //\r
874   OptionBufferPtr = _PxeBcFindOpt (\r
875                       PacketPtr->Dhcpv4.DhcpOptions,\r
876                       PacketLen,\r
877                       OP_DHCP_OPTION_OVERLOAD\r
878                       );\r
879 \r
880   if (OptionBufferPtr != NULL) {\r
881     if (OptionBufferPtr[1] == 1) {\r
882       Overload = OptionBufferPtr[2];\r
883     }\r
884   }\r
885   //\r
886   // Look for caller's option.\r
887   //\r
888   OptionBufferPtr = _PxeBcFindOpt (\r
889                       PacketPtr->Dhcpv4.DhcpOptions,\r
890                       PacketLen,\r
891                       OpCode\r
892                       );\r
893 \r
894   if (OptionBufferPtr != NULL) {\r
895     return OptionBufferPtr;\r
896   }\r
897 \r
898   if (Overload & OVLD_FILE) {\r
899     OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpBootFile, 128, OpCode);\r
900 \r
901     if (OptionBufferPtr != NULL) {\r
902       return OptionBufferPtr;\r
903     }\r
904   }\r
905 \r
906   if (Overload & OVLD_SRVR_NAME) {\r
907     OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpSrvName, 64, OpCode);\r
908 \r
909     if (OptionBufferPtr != NULL) {\r
910       return OptionBufferPtr;\r
911     }\r
912   }\r
913 \r
914   return NULL;\r
915 }\r
916 \r
917 \r
918 /**\r
919   Download file into buffer\r
920 \r
921   @param  Private                                     Pointer to PxeBc interface\r
922   @param  BufferSize                                  pointer to size of download\r
923                                                       buffer\r
924   @param  Buffer                                      Pointer to buffer\r
925 \r
926   @return EFI_BUFFER_TOO_SMALL -\r
927   @return EFI_NOT_FOUND -\r
928   @return EFI_PROTOCOL_ERROR -\r
929 \r
930 **/\r
931 EFI_STATUS\r
932 DownloadFile (\r
933   IN PXE_BASECODE_DEVICE  *Private,\r
934   IN OUT UINT64           *BufferSize,\r
935   IN VOID                 *Buffer\r
936   )\r
937 {\r
938   EFI_PXE_BASE_CODE_MTFTP_INFO  MtftpInfo;\r
939   EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode;\r
940   DHCP_RECEIVE_BUFFER           *RxBuf;\r
941   EFI_STATUS                    Status;\r
942   UINTN                         BlockSize;\r
943 \r
944   RxBuf     = (DHCP_RECEIVE_BUFFER *) Private->BootServerReceiveBuffer;\r
945   BlockSize = 0x8000;\r
946 \r
947   DEBUG ((EFI_D_WARN, "\nDownloadFile()  Enter."));\r
948 \r
949   if (Buffer == NULL || *BufferSize == 0 || *BufferSize < Private->FileSize) {\r
950     if (Private->FileSize != 0) {\r
951       *BufferSize = Private->FileSize;\r
952       return EFI_BUFFER_TOO_SMALL;\r
953     }\r
954 \r
955     AsciiPrint ("\nTSize");\r
956 \r
957     OpCode = EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE;\r
958   } else if (RxBuf->OpAdds.Status & WfM11a_TYPE) {\r
959     OpCode = EFI_PXE_BASE_CODE_MTFTP_READ_FILE;\r
960 \r
961     ZeroMem (&MtftpInfo, sizeof MtftpInfo);\r
962 \r
963     *(IPV4_ADDR *) &MtftpInfo.MCastIp = *(IPV4_ADDR *) RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_IP - 1]->Data;\r
964 \r
965     CopyMem (\r
966       &MtftpInfo.CPort,\r
967       RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_CPORT - 1]->Data,\r
968       sizeof MtftpInfo.CPort\r
969       );\r
970 \r
971     CopyMem (\r
972       &MtftpInfo.SPort,\r
973       RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_SPORT - 1]->Data,\r
974       sizeof MtftpInfo.SPort\r
975       );\r
976 \r
977     MtftpInfo.ListenTimeout   = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_TMOUT - 1]);\r
978 \r
979     MtftpInfo.TransmitTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_DELAY - 1]);\r
980 \r
981     AsciiPrint ("\nMTFTP");\r
982   } else {\r
983     AsciiPrint ("\nTFTP");\r
984 \r
985     OpCode = EFI_PXE_BASE_CODE_TFTP_READ_FILE;\r
986   }\r
987 \r
988   Private->FileSize = 0;\r
989 \r
990   RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data[RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length] = 0;\r
991 \r
992   Status = Private->EfiBc.Mtftp (\r
993                             &Private->EfiBc,\r
994                             OpCode,\r
995                             Buffer,\r
996                             FALSE,\r
997                             BufferSize,\r
998                             &BlockSize,\r
999                             &Private->ServerIp,\r
1000                             (UINT8 *) RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data,\r
1001                             &MtftpInfo,\r
1002                             FALSE\r
1003                             );\r
1004 \r
1005   if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {\r
1006     DEBUG ((DEBUG_WARN, "\nDownloadFile()  Exit #1 %Xh", Status));\r
1007     return Status;\r
1008   }\r
1009 \r
1010   if (sizeof (UINTN) < sizeof (UINT64) && *BufferSize > 0xFFFFFFFF) {\r
1011     Private->FileSize = 0xFFFFFFFF;\r
1012   } else {\r
1013     Private->FileSize = (UINTN) *BufferSize;\r
1014   }\r
1015 \r
1016   if (OpCode == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) {\r
1017     DEBUG ((DEBUG_WARN, "\nDownloadFile()  Exit #2"));\r
1018     return EFI_BUFFER_TOO_SMALL;\r
1019   }\r
1020 \r
1021   if (EFI_ERROR (Status)) {\r
1022     DEBUG ((DEBUG_WARN, "\nDownloadFile()  Exit #3 %Xh", Status));\r
1023     return Status;\r
1024   }\r
1025 \r
1026   if (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected && Private->EfiBc.Mode->PxeBisReplyReceived) {\r
1027     UINT64  CredentialLen;\r
1028     UINT8   CredentialFilename[256];\r
1029     UINT8   *op;\r
1030     VOID    *CredentialBuffer;\r
1031 \r
1032     //\r
1033     // Get name of credential file.  It may be in the BOOTP\r
1034     // bootfile field or a DHCP option.\r
1035     //\r
1036     ZeroMem (CredentialFilename, sizeof CredentialFilename);\r
1037 \r
1038     op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_DHCP_BOOTFILE);\r
1039 \r
1040     if (op != NULL) {\r
1041       if (op[1] == 0) {\r
1042         /* No credential filename */\r
1043         return EFI_NOT_FOUND;\r
1044       }\r
1045 \r
1046       CopyMem (CredentialFilename, &op[2], op[1]);\r
1047     } else {\r
1048       if (Private->EfiBc.Mode->PxeBisReply.Dhcpv4.BootpBootFile[0] == 0) {\r
1049         /* No credential filename */\r
1050         return EFI_NOT_FOUND;\r
1051       }\r
1052 \r
1053       CopyMem (CredentialFilename, &op[2], 128);\r
1054     }\r
1055     //\r
1056     // Get size of credential file.  It may be available as a\r
1057     // DHCP option.  If not, use the TFTP get file size.\r
1058     //\r
1059     CredentialLen = 0;\r
1060 \r
1061     op            = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_BOOT_FILE_SZ);\r
1062 \r
1063     if (op != NULL) {\r
1064       /*\r
1065        * This is actually the size of the credential file\r
1066        * buffer.  The actual credential file size will be\r
1067        * returned when we download the file.\r
1068        */\r
1069       if (op[1] == 2) {\r
1070         UINT16  n;\r
1071 \r
1072         CopyMem (&n, &op[2], 2);\r
1073         CredentialLen = HTONS (n) * 512;\r
1074       }\r
1075     }\r
1076 \r
1077     if (CredentialLen == 0) {\r
1078       BlockSize = 8192;\r
1079 \r
1080       Status = Private->EfiBc.Mtftp (\r
1081                                 &Private->EfiBc,\r
1082                                 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
1083                                 NULL,\r
1084                                 FALSE,\r
1085                                 &CredentialLen,\r
1086                                 &BlockSize,\r
1087                                 &Private->ServerIp,\r
1088                                 CredentialFilename,\r
1089                                 NULL,\r
1090                                 FALSE\r
1091                                 );\r
1092 \r
1093       if (EFI_ERROR (Status)) {\r
1094         return Status;\r
1095       }\r
1096 \r
1097       if (CredentialLen == 0) {\r
1098         //\r
1099         // %%TBD -- EFI error for invalid credential\r
1100         // file.\r
1101         //\r
1102         return EFI_PROTOCOL_ERROR;\r
1103       }\r
1104     }\r
1105     //\r
1106     // Allocate credential file buffer.\r
1107     //\r
1108     Status = gBS->AllocatePool (\r
1109                     EfiBootServicesData,\r
1110                     (UINTN) CredentialLen,\r
1111                     &CredentialBuffer\r
1112                     );\r
1113 \r
1114     if (EFI_ERROR (Status)) {\r
1115       return Status;\r
1116     }\r
1117     //\r
1118     // Download credential file.\r
1119     //\r
1120     BlockSize = 8192;\r
1121 \r
1122     Status = Private->EfiBc.Mtftp (\r
1123                               &Private->EfiBc,\r
1124                               EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
1125                               CredentialBuffer,\r
1126                               FALSE,\r
1127                               &CredentialLen,\r
1128                               &BlockSize,\r
1129                               &Private->ServerIp,\r
1130                               CredentialFilename,\r
1131                               NULL,\r
1132                               FALSE\r
1133                               );\r
1134 \r
1135     if (EFI_ERROR (Status)) {\r
1136       gBS->FreePool (CredentialBuffer);\r
1137       return Status;\r
1138     }\r
1139     //\r
1140     // Verify credentials.\r
1141     //\r
1142     if (PxebcBisVerify (Private, Buffer, Private->FileSize, CredentialBuffer, (UINTN) CredentialLen)) {\r
1143       Status = EFI_SUCCESS;\r
1144     } else {\r
1145       //\r
1146       // %%TBD -- An EFI error code for failing credential verification.\r
1147       //\r
1148       Status = EFI_PROTOCOL_ERROR;\r
1149     }\r
1150 \r
1151     gBS->FreePool (CredentialBuffer);\r
1152   }\r
1153 \r
1154   return Status;\r
1155 }\r
1156 \r
1157 \r
1158 /**\r
1159   Start PXE DHCP.  Get DHCP and proxyDHCP information.\r
1160   Display remote boot menu and prompt.  Select item from menu.\r
1161 \r
1162   @param  Private                                     Pointer to PxeBc interface\r
1163   @param  BufferSize                                  Pointer to download buffer\r
1164                                                       size\r
1165   @param  Buffer                                      Pointer to download buffer\r
1166 \r
1167   @retval EFI_SUCCESS\r
1168   @retval EFI_NOT_READY\r
1169 \r
1170 **/\r
1171 EFI_STATUS\r
1172 LoadfileStart (\r
1173   IN PXE_BASECODE_DEVICE  *Private,\r
1174   IN OUT UINT64           *BufferSize,\r
1175   IN VOID                 *Buffer\r
1176   )\r
1177 {\r
1178   EFI_PXE_BASE_CODE_MODE      *PxeBcMode;\r
1179   EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
1180   EFI_SIMPLE_NETWORK_MODE     *SnpMode;\r
1181   EFI_STATUS                  Status;\r
1182   VOID                        *RxBuf;\r
1183 \r
1184   DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Enter."));\r
1185 \r
1186   //\r
1187   // Try to start BaseCode, for now only IPv4 is supported\r
1188   // so don't try to start using IPv6.\r
1189   //\r
1190   Status = Private->EfiBc.Start (&Private->EfiBc, FALSE);\r
1191 \r
1192   if (EFI_ERROR (Status)) {\r
1193     if (Status != EFI_ALREADY_STARTED) {\r
1194       DEBUG ((DEBUG_NET, "\nLoadfileStart()  Exit  BC.Start() == %xh", Status));\r
1195       return Status;\r
1196     }\r
1197   }\r
1198   //\r
1199   // Get pointers to PXE mode structure, SNP protocol structure\r
1200   // and SNP mode structure.\r
1201   //\r
1202   PxeBcMode = Private->EfiBc.Mode;\r
1203   Snp       = Private->SimpleNetwork;\r
1204   SnpMode   = Snp->Mode;\r
1205 \r
1206   //\r
1207   // Display client MAC address, like 16-bit PXE ROMs\r
1208   //\r
1209   AsciiPrint ("\nCLIENT MAC ADDR: ");\r
1210 \r
1211   {\r
1212     UINTN Index;\r
1213     UINTN hlen;\r
1214 \r
1215     hlen = SnpMode->HwAddressSize;\r
1216 \r
1217     for (Index = 0; Index < hlen; ++Index) {\r
1218       AsciiPrint ("%02x ", SnpMode->CurrentAddress.Addr[Index]);\r
1219     }\r
1220   }\r
1221 \r
1222   AsciiPrint ("\nDHCP");\r
1223 \r
1224   Status = Private->EfiBc.Dhcp (&Private->EfiBc, TRUE);\r
1225 \r
1226   if (EFI_ERROR (Status)) {\r
1227     DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Exit  BC.Dhcp() == %Xh", Status));\r
1228     AsciiPrint ("\r               \r");\r
1229     return Status;\r
1230   }\r
1231 \r
1232   ShowMyInfo (Private);\r
1233 \r
1234   RxBuf = PxeBcMode->ProxyOfferReceived ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER;\r
1235 #define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)\r
1236 \r
1237   Status = DoMenu (Private, RxBufferPtr);\r
1238 \r
1239   if (Status == EFI_SUCCESS) {\r
1240     //\r
1241     // did a discovery - take info from discovery packet\r
1242     //\r
1243     RxBuf = &PXE_ACK_BUFFER;\r
1244   } else if (Status == NO_MENU) {\r
1245     //\r
1246     // did not do a discovery - take info from rxbuf\r
1247     //\r
1248     Private->ServerIp.Addr[0] = RxBufferPtr->u.Dhcpv4.siaddr;\r
1249 \r
1250     if (!(Private->ServerIp.Addr[0])) {\r
1251       *(IPV4_ADDR *) &Private->ServerIp = *(IPV4_ADDR *) RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]->Data;\r
1252     }\r
1253   } else {\r
1254     DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Exit  DoMenu() == %Xh", Status));\r
1255     return Status;\r
1256   }\r
1257 \r
1258   if (!RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {\r
1259     DEBUG ((DEBUG_WARN, "\nLoadfileStart()  Exit  Not ready?"));\r
1260     return EFI_NOT_READY;\r
1261   }\r
1262   //\r
1263   // check for file size option sent\r
1264   //\r
1265   if (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]) {\r
1266     Private->FileSize = 512 * NTOHS (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]->Data);\r
1267   }\r
1268 \r
1269   Private->BootServerReceiveBuffer  = RxBufferPtr;\r
1270 \r
1271   Status = DownloadFile (Private, BufferSize, Buffer);\r
1272 \r
1273   DEBUG (\r
1274     (DEBUG_WARN,\r
1275     "\nLoadfileStart()  Exit.  DownloadFile() = %Xh",\r
1276     Status)\r
1277     );\r
1278 \r
1279   return Status;\r
1280 }\r
1281 \r
1282 \r
1283 /**\r
1284   Loadfile interface for PxeBc interface\r
1285 \r
1286   @param  This                                        Pointer to Loadfile interface\r
1287   @param  FilePath                                    Not used and not checked\r
1288   @param  BootPolicy                                  Must be TRUE\r
1289   @param  BufferSize                                  Pointer to buffer size\r
1290   @param  Buffer                                      Pointer to download buffer or\r
1291                                                       NULL\r
1292 \r
1293   @return EFI_INVALID_PARAMETER -\r
1294   @return EFI_UNSUPPORTED -\r
1295   @return EFI_SUCCESS -\r
1296   @return EFI_BUFFER_TOO_SMALL -\r
1297 \r
1298 **/\r
1299 EFI_STATUS\r
1300 EFIAPI\r
1301 LoadFile (\r
1302   IN EFI_LOAD_FILE_PROTOCOL           *This,\r
1303   IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,\r
1304   IN BOOLEAN                          BootPolicy,\r
1305   IN OUT UINTN                        *BufferSize,\r
1306   IN OUT VOID                         *Buffer\r
1307   )\r
1308 {\r
1309   LOADFILE_DEVICE *LoadfilePtr;\r
1310   UINT64          TmpBufSz;\r
1311   INT32           OrigMode;\r
1312   INT32           OrigAttribute;\r
1313   BOOLEAN         RemoveCallback;\r
1314   BOOLEAN         NewMakeCallback;\r
1315   EFI_STATUS      Status;\r
1316   EFI_STATUS      TempStatus;\r
1317 \r
1318   //\r
1319   // The following line is only used for passing ICC build.\r
1320   //\r
1321   DEBUG ((EFI_D_INFO, "FilePath = %p\n", FilePath));\r
1322 \r
1323   //\r
1324   //\r
1325   //\r
1326   OrigMode        = gST->ConOut->Mode->Mode;\r
1327   OrigAttribute   = gST->ConOut->Mode->Attribute;\r
1328   RemoveCallback  = FALSE;\r
1329 \r
1330   AsciiPrint ("Running LoadFile()\n");\r
1331 \r
1332   //\r
1333   // Resolve Warning 4 unreferenced parameter problem\r
1334   //\r
1335   FilePath = NULL;\r
1336 \r
1337   //\r
1338   // If either if these parameters are NULL, we cannot continue.\r
1339   //\r
1340   if (This == NULL || BufferSize == NULL) {\r
1341     DEBUG ((DEBUG_WARN, "\nLoadFile()  This or BufferSize == NULL"));\r
1342     return EFI_INVALID_PARAMETER;\r
1343   }\r
1344   //\r
1345   // We only support BootPolicy == TRUE\r
1346   //\r
1347   if (!BootPolicy) {\r
1348     DEBUG ((DEBUG_WARN, "\nLoadFile()  BootPolicy == FALSE"));\r
1349     return EFI_UNSUPPORTED;\r
1350   }\r
1351   //\r
1352   // Get pointer to LoadFile protocol structure.\r
1353   //\r
1354   LoadfilePtr = CR (This, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE);\r
1355 \r
1356   if (LoadfilePtr == NULL) {\r
1357     DEBUG (\r
1358       (DEBUG_NET,\r
1359       "\nLoadFile()  Could not get pointer to LoadFile structure")\r
1360       );\r
1361     return EFI_INVALID_PARAMETER;\r
1362   }\r
1363   //\r
1364   // Lock interface\r
1365   //\r
1366   EfiAcquireLock (&LoadfilePtr->Lock);\r
1367 \r
1368   //\r
1369   // Set console output mode and display attribute\r
1370   //\r
1371   if (OrigMode != 0) {\r
1372     gST->ConOut->SetMode (gST->ConOut, 0);\r
1373   }\r
1374 \r
1375   gST->ConOut->SetAttribute (\r
1376                 gST->ConOut,\r
1377                 EFI_TEXT_ATTR (EFI_LIGHTGRAY,EFI_BLACK)\r
1378                 );\r
1379 \r
1380   //\r
1381   // See if BaseCode already has a Callback protocol attached.\r
1382   // If there is none, attach our own Callback protocol.\r
1383   //\r
1384   Status = gBS->HandleProtocol (\r
1385                   LoadfilePtr->Private->Handle,\r
1386                   &gEfiPxeBaseCodeCallbackProtocolGuid,\r
1387                   (VOID *) &LoadfilePtr->Private->CallbackProtocolPtr\r
1388                   );\r
1389 \r
1390   if (Status == EFI_SUCCESS) {\r
1391     //\r
1392     // There is already a callback routine.  Do nothing.\r
1393     //\r
1394     DEBUG ((DEBUG_WARN, "\nLoadFile()  BC callback exists."));\r
1395 \r
1396   } else if (Status == EFI_UNSUPPORTED) {\r
1397     //\r
1398     // No BaseCode Callback protocol found.  Add our own.\r
1399     //\r
1400     Status = gBS->InstallProtocolInterface (\r
1401                     &LoadfilePtr->Private->Handle,\r
1402                     &gEfiPxeBaseCodeCallbackProtocolGuid,\r
1403                     EFI_NATIVE_INTERFACE,\r
1404                     &_bc_callback\r
1405                     );\r
1406 \r
1407     DEBUG ((DEBUG_WARN, "\nLoadFile()  Callback install status == %xh", Status));\r
1408 \r
1409     RemoveCallback = (BOOLEAN) (Status == EFI_SUCCESS);\r
1410 \r
1411     if (LoadfilePtr->Private->EfiBc.Mode != NULL && LoadfilePtr->Private->EfiBc.Mode->Started) {\r
1412       NewMakeCallback = TRUE;\r
1413       LoadfilePtr->Private->EfiBc.SetParameters (\r
1414                                     &LoadfilePtr->Private->EfiBc,\r
1415                                     NULL,\r
1416                                     NULL,\r
1417                                     NULL,\r
1418                                     NULL,\r
1419                                     &NewMakeCallback\r
1420                                     );\r
1421     }\r
1422 \r
1423   } else {\r
1424     DEBUG ((DEBUG_WARN, "\nLoadFile()  Callback check status == %xh", Status));\r
1425   }\r
1426   //\r
1427   // Check for starting or for continuing after already getting\r
1428   // the file size.\r
1429   //\r
1430   if (LoadfilePtr->Private->FileSize == 0) {\r
1431     TmpBufSz  = 0;\r
1432     Status    = LoadfileStart (LoadfilePtr->Private, &TmpBufSz, Buffer);\r
1433 \r
1434     if (sizeof (UINTN) < sizeof (UINT64) && TmpBufSz > 0xFFFFFFFF) {\r
1435       *BufferSize = 0xFFFFFFFF;\r
1436     } else {\r
1437       *BufferSize = (UINTN) TmpBufSz;\r
1438     }\r
1439 \r
1440     if (Status == EFI_BUFFER_TOO_SMALL) {\r
1441       //\r
1442       // This is done so loadfile will work even if the boot manager\r
1443       // did not make the first call with Buffer == NULL.\r
1444       //\r
1445       Buffer = NULL;\r
1446     }\r
1447   } else if (Buffer == NULL) {\r
1448     DEBUG ((DEBUG_WARN, "\nLoadfile()  Get buffer size"));\r
1449 \r
1450     //\r
1451     // Continuing from previous LoadFile request.  Make sure there\r
1452     // is a buffer and that it is big enough.\r
1453     //\r
1454     *BufferSize = LoadfilePtr->Private->FileSize;\r
1455     Status      = EFI_BUFFER_TOO_SMALL;\r
1456   } else {\r
1457     DEBUG ((DEBUG_WARN, "\nLoadFile()  Download file"));\r
1458 \r
1459     //\r
1460     // Everything looks good, try to download the file.\r
1461     //\r
1462     TmpBufSz  = *BufferSize;\r
1463     Status    = DownloadFile (LoadfilePtr->Private, &TmpBufSz, Buffer);\r
1464 \r
1465     //\r
1466     // Next call to loadfile will start DHCP process again.\r
1467     //\r
1468     LoadfilePtr->Private->FileSize = 0;\r
1469   }\r
1470   //\r
1471   // If we added a callback protocol, now is the time to remove it.\r
1472   //\r
1473   if (RemoveCallback) {\r
1474     NewMakeCallback = FALSE;\r
1475     TempStatus = LoadfilePtr->Private->EfiBc.SetParameters (\r
1476                                           &LoadfilePtr->Private->EfiBc,\r
1477                                           NULL,\r
1478                                           NULL,\r
1479                                           NULL,\r
1480                                           NULL,\r
1481                                           &NewMakeCallback\r
1482                                           );\r
1483 \r
1484     if (TempStatus == EFI_SUCCESS) {\r
1485       gBS->UninstallProtocolInterface (\r
1486             LoadfilePtr->Private->Handle,\r
1487             &gEfiPxeBaseCodeCallbackProtocolGuid,\r
1488             &_bc_callback\r
1489             );\r
1490     }\r
1491   }\r
1492   //\r
1493   // Restore display mode and attribute\r
1494   //\r
1495   if (OrigMode != 0) {\r
1496     gST->ConOut->SetMode (gST->ConOut, OrigMode);\r
1497   }\r
1498 \r
1499   gST->ConOut->SetAttribute (gST->ConOut, OrigAttribute);\r
1500 \r
1501   //\r
1502   // Unlock interface\r
1503   //\r
1504   EfiReleaseLock (&LoadfilePtr->Lock);\r
1505 \r
1506   DEBUG ((DEBUG_WARN, "\nBC.Loadfile()  Status == %xh\n", Status));\r
1507 \r
1508   if (Status == EFI_SUCCESS) {\r
1509     return EFI_SUCCESS;\r
1510 \r
1511   } else if (Status == EFI_BUFFER_TOO_SMALL) {\r
1512     //\r
1513     // Error is only displayed when we are actually trying to\r
1514     // download the boot image.\r
1515     //\r
1516     if (Buffer == NULL) {\r
1517       return EFI_BUFFER_TOO_SMALL;\r
1518     }\r
1519 \r
1520     AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");\r
1521 \r
1522   } else if (Status == EFI_DEVICE_ERROR) {\r
1523     AsciiPrint ("\nPXE-E07: Network device error.  Check network connection.\n");\r
1524 \r
1525   } else if (Status == EFI_OUT_OF_RESOURCES) {\r
1526     AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");\r
1527 \r
1528   } else if (Status == EFI_NO_MEDIA) {\r
1529     AsciiPrint ("\nPXE-E12: Could not detect network connection.  Check cable.\n");\r
1530 \r
1531   } else if (Status == EFI_NO_RESPONSE) {\r
1532     AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");\r
1533 \r
1534   } else if (Status == EFI_TIMEOUT) {\r
1535     AsciiPrint ("\nPXE-E18: Timeout.  Server did not respond.\n");\r
1536 \r
1537   } else if (Status == EFI_ABORTED) {\r
1538     AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");\r
1539 \r
1540   } else if (Status == EFI_ICMP_ERROR) {\r
1541     AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");\r
1542 \r
1543     if (LoadfilePtr->Private->EfiBc.Mode != NULL) {\r
1544       if (LoadfilePtr->Private->EfiBc.Mode->IcmpErrorReceived) {\r
1545 \r
1546       AsciiPrint (\r
1547           "PXE-E98: Type: %xh  Code: %xh  ",\r
1548           LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type,\r
1549           LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code\r
1550           );\r
1551 \r
1552         switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type) {\r
1553         case 0x03:\r
1554           switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code) {\r
1555           case 0x00:              /* net unreachable */\r
1556           AsciiPrint ("Net unreachable");\r
1557             break;\r
1558 \r
1559           case 0x01:              /* host unreachable */\r
1560           AsciiPrint ("Host unreachable");\r
1561             break;\r
1562 \r
1563           case 0x02:              /* protocol unreachable */\r
1564           AsciiPrint ("Protocol unreachable");\r
1565             break;\r
1566 \r
1567           case 0x03:              /* port unreachable */\r
1568           AsciiPrint ("Port unreachable");\r
1569             break;\r
1570 \r
1571           case 0x04:              /* Fragmentation needed */\r
1572           AsciiPrint ("Fragmentation needed");\r
1573             break;\r
1574 \r
1575           case 0x05:              /* Source route failed */\r
1576           AsciiPrint ("Source route failed");\r
1577             break;\r
1578           }\r
1579 \r
1580           break;\r
1581         }\r
1582 \r
1583       AsciiPrint ("\n");\r
1584       }\r
1585     }\r
1586 \r
1587   } else if (Status == EFI_TFTP_ERROR) {\r
1588     AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");\r
1589 \r
1590     if (LoadfilePtr->Private->EfiBc.Mode != NULL) {\r
1591       if (LoadfilePtr->Private->EfiBc.Mode->TftpErrorReceived) {\r
1592       AsciiPrint (\r
1593           "PXE-E98: Code: %xh  %a\n",\r
1594           LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorCode,\r
1595           LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorString\r
1596           );\r
1597       }\r
1598     }\r
1599 \r
1600   } else {\r
1601     AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status);\r
1602   }\r
1603 \r
1604   LoadfilePtr->Private->EfiBc.Stop (&LoadfilePtr->Private->EfiBc);\r
1605 \r
1606   return Status;\r
1607 }\r