]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c
1. Add EFI LOADED IMAGE DEVICE PATH Protocol in LoadImage() service, per UEFI 2.1b.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / PxeBcDxe / Pxe_loadfile.c
CommitLineData
772db4bb 1/** @file\r
2\r
3Copyright (c) 2004 - 2007, Intel Corporation\r
4All rights reserved. This program and the accompanying materials\r
5are licensed and made available under the terms and conditions of the BSD License\r
6which accompanies this distribution. The full text of the license may be found at\r
7http://opensource.org/licenses/bsd-license.php\r
8\r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11\r
12Module Name:\r
13 pxe_loadfile.c\r
14\r
15Abstract:\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
34typedef 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
61STATIC\r
62EFI_PXE_BASE_CODE_CALLBACK_STATUS\r
63EFIAPI\r
64bc_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
152STATIC 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
166STATIC\r
167VOID\r
168PrintIpv4 (\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
186STATIC\r
187VOID\r
188ShowMyInfo (\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
260STATIC\r
261EFI_STATUS\r
262DoPrompt (\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
772db4bb 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
450STATIC\r
451VOID\r
452PrintMenuItem (\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
478STATIC\r
479EFI_STATUS\r
480DoMenu (\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
768STATIC\r
769UINT16\r
770GetValue (\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
792STATIC\r
793UINT8 *\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
840STATIC\r
841UINT8 *\r
842PxeBcFindDhcpOpt (\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
940STATIC\r
941EFI_STATUS\r
942DownloadFile (\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
772db4bb 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
1181STATIC\r
1182EFI_STATUS\r
1183LoadfileStart (\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
1310EFI_STATUS\r
1311EFIAPI\r
1312LoadFile (\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
687a2e5f 1328\r
1329 //\r
1330 // The following line is only used for passing ICC build.\r
1331 //\r
1332 DEBUG ((EFI_D_INFO, "FilePath = %x\n", FilePath));\r
1333\r
772db4bb 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