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