]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
Reconfigure the UdpRead instance if an error occurred in Dhcp, Discover and Mtftp...
[mirror_edk2.git] / NetworkPkg / UefiPxeBcDxe / PxeBcBoot.c
CommitLineData
a3bcde70
HT
1/** @file\r
2 Boot functions implementation for UefiPxeBc Driver.\r
3\r
278c6019 4 Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
a3bcde70
HT
5\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php.\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "PxeBcImpl.h"\r
17\r
18\r
19/**\r
20 Display the string of the boot item.\r
21\r
22 If the length of the boot item string beyond 70 Char, just display 70 Char.\r
23\r
24 @param[in] Str The pointer to the string.\r
25 @param[in] Len The length of the string.\r
26\r
27**/\r
28VOID\r
29PxeBcDisplayBootItem (\r
30 IN UINT8 *Str,\r
31 IN UINT8 Len\r
32 )\r
33{\r
34 UINT8 Tmp;\r
35\r
36 //\r
37 // Cut off the chars behind 70th.\r
38 //\r
39 Len = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);\r
40 Tmp = Str[Len];\r
41 Str[Len] = 0;\r
42 AsciiPrint ("%a \n", Str);\r
43\r
44 //\r
45 // Restore the original 70th char.\r
46 //\r
47 Str[Len] = Tmp;\r
48}\r
49\r
50\r
51/**\r
52 Select and maintain the boot prompt if needed.\r
53\r
54 @param[in] Private Pointer to PxeBc private data.\r
55\r
56 @retval EFI_SUCCESS Selected boot prompt done.\r
57 @retval EFI_TIMEOUT Selected boot prompt timed out.\r
58 @retval EFI_NOT_FOUND The proxy offer is not Pxe10.\r
59 @retval EFI_ABORTED User cancelled the operation.\r
60 @retval EFI_NOT_READY Reading the input key from the keyboard has not finish.\r
61\r
62**/\r
63EFI_STATUS\r
64PxeBcSelectBootPrompt (\r
65 IN PXEBC_PRIVATE_DATA *Private\r
66 )\r
67{\r
68 PXEBC_DHCP_PACKET_CACHE *Cache;\r
69 PXEBC_VENDOR_OPTION *VendorOpt;\r
70 EFI_PXE_BASE_CODE_MODE *Mode;\r
71 EFI_EVENT TimeoutEvent;\r
72 EFI_EVENT DescendEvent;\r
73 EFI_INPUT_KEY InputKey;\r
74 EFI_STATUS Status;\r
75 UINT32 OfferType;\r
76 UINT8 Timeout;\r
77 UINT8 *Prompt;\r
78 UINT8 PromptLen;\r
79 INT32 SecCol;\r
80 INT32 SecRow;\r
81\r
82 TimeoutEvent = NULL;\r
83 DescendEvent = NULL;\r
84 Mode = Private->PxeBc.Mode;\r
85 Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;\r
86 OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;\r
87\r
88 //\r
89 // Only ProxyPxe10 offer needs boot prompt.\r
90 //\r
91 if (OfferType != PxeOfferTypeProxyPxe10) {\r
92 return EFI_NOT_FOUND;\r
93 }\r
94\r
95 //\r
96 // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.\r
97 //\r
98 ASSERT (!Mode->UsingIpv6);\r
99\r
100 VendorOpt = &Cache->Dhcp4.VendorOpt;\r
101 if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {\r
102 return EFI_SUCCESS;\r
103 }\r
104\r
105 Timeout = VendorOpt->MenuPrompt->Timeout;\r
106 Prompt = VendorOpt->MenuPrompt->Prompt;\r
107 PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);\r
108\r
109 //\r
110 // The valid scope of Timeout refers to PXE2.1 spec.\r
111 //\r
112 if (Timeout == 0) {\r
113 return EFI_SUCCESS;\r
114 }\r
115 if (Timeout == 255) {\r
116 return EFI_TIMEOUT;\r
117 }\r
118\r
119 //\r
120 // Create and start a timer as timeout event.\r
121 //\r
122 Status = gBS->CreateEvent (\r
123 EVT_TIMER,\r
124 TPL_CALLBACK,\r
125 NULL,\r
126 NULL,\r
127 &TimeoutEvent\r
128 );\r
129 if (EFI_ERROR (Status)) {\r
130 return Status;\r
131 }\r
132\r
133 Status = gBS->SetTimer (\r
134 TimeoutEvent,\r
135 TimerRelative,\r
136 Timeout * TICKS_PER_SECOND\r
137 );\r
138 if (EFI_ERROR (Status)) {\r
139 goto ON_EXIT;\r
140 }\r
141\r
142 //\r
143 // Create and start a periodic timer as descend event by second.\r
144 //\r
145 Status = gBS->CreateEvent (\r
146 EVT_TIMER,\r
147 TPL_CALLBACK,\r
148 NULL,\r
149 NULL,\r
150 &DescendEvent\r
151 );\r
152 if (EFI_ERROR (Status)) {\r
153 goto ON_EXIT;\r
154 }\r
155\r
156 Status = gBS->SetTimer (\r
157 DescendEvent,\r
158 TimerPeriodic,\r
159 TICKS_PER_SECOND\r
160 );\r
161 if (EFI_ERROR (Status)) {\r
162 goto ON_EXIT;\r
163 }\r
164\r
165 //\r
166 // Display the boot item and cursor on the screen.\r
167 //\r
168 SecCol = gST->ConOut->Mode->CursorColumn;\r
169 SecRow = gST->ConOut->Mode->CursorRow;\r
170\r
171 PxeBcDisplayBootItem (Prompt, PromptLen);\r
172\r
173 gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);\r
174 AsciiPrint ("(%d) ", Timeout--);\r
175\r
176 while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
177 if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {\r
178 gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);\r
179 AsciiPrint ("(%d) ", Timeout--);\r
180 }\r
181 if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {\r
182 gBS->Stall (10 * TICKS_PER_MS);\r
183 continue;\r
184 }\r
185 //\r
186 // Parse the input key by user.\r
187 //\r
188 if (InputKey.ScanCode == 0) {\r
189\r
190 switch (InputKey.UnicodeChar) {\r
191\r
192 case CTRL ('c'):\r
193 Status = EFI_ABORTED;\r
194 break;\r
195\r
196 case CTRL ('m'):\r
197 case 'm':\r
198 case 'M':\r
199 Status = EFI_TIMEOUT;\r
200 break;\r
201\r
202 default:\r
203 continue;\r
204 }\r
205\r
206 } else {\r
207\r
208 switch (InputKey.ScanCode) {\r
209\r
210 case SCAN_F8:\r
211 Status = EFI_TIMEOUT;\r
212 break;\r
213\r
214 case SCAN_ESC:\r
215 Status = EFI_ABORTED;\r
216 break;\r
217\r
218 default:\r
219 continue;\r
220 }\r
221 }\r
222\r
223 break;\r
224 }\r
225\r
226 //\r
227 // Reset the cursor on the screen.\r
228 //\r
229 gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);\r
230\r
231ON_EXIT:\r
232 if (DescendEvent != NULL) {\r
233 gBS->CloseEvent (DescendEvent);\r
234 }\r
235 if (TimeoutEvent != NULL) {\r
236 gBS->CloseEvent (TimeoutEvent);\r
237 }\r
238\r
239 return Status;\r
240}\r
241\r
242\r
243/**\r
244 Select the boot menu by user's input.\r
245\r
246 @param[in] Private Pointer to PxeBc private data.\r
247 @param[out] Type The type of the menu.\r
248 @param[in] UseDefaultItem Use default item or not.\r
249\r
250 @retval EFI_ABORTED User cancel operation.\r
251 @retval EFI_SUCCESS Select the boot menu success.\r
252 @retval EFI_NOT_READY Read the input key from the keybroad has not finish.\r
253\r
254**/\r
255EFI_STATUS\r
256PxeBcSelectBootMenu (\r
257 IN PXEBC_PRIVATE_DATA *Private,\r
258 OUT UINT16 *Type,\r
259 IN BOOLEAN UseDefaultItem\r
260 )\r
261{\r
262 EFI_PXE_BASE_CODE_MODE *Mode;\r
263 PXEBC_DHCP_PACKET_CACHE *Cache;\r
264 PXEBC_VENDOR_OPTION *VendorOpt;\r
265 EFI_INPUT_KEY InputKey;\r
266 UINT32 OfferType;\r
267 UINT8 MenuSize;\r
268 UINT8 MenuNum;\r
269 INT32 TopRow;\r
270 UINT16 Select;\r
271 UINT16 LastSelect;\r
272 UINT8 Index;\r
273 BOOLEAN Finish;\r
274 CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE];\r
275 PXEBC_BOOT_MENU_ENTRY *MenuItem;\r
276 PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM];\r
277\r
278 Finish = FALSE;\r
279 Select = 1;\r
280 Index = 0;\r
281 *Type = 0;\r
282 Mode = Private->PxeBc.Mode;\r
283 Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;\r
284 OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;\r
285\r
286 //\r
287 // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.\r
288 //\r
289 ASSERT (!Mode->UsingIpv6);\r
290 ASSERT (OfferType == PxeOfferTypeProxyPxe10);\r
291\r
292 VendorOpt = &Cache->Dhcp4.VendorOpt;\r
293 if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {\r
294 return EFI_SUCCESS;\r
295 }\r
296\r
297 //\r
298 // Display the boot menu on the screen.\r
299 //\r
300 SetMem (Blank, sizeof(Blank), ' ');\r
301\r
302 MenuSize = VendorOpt->BootMenuLen;\r
303 MenuItem = VendorOpt->BootMenu;\r
304\r
305 if (MenuSize == 0) {\r
306 return EFI_DEVICE_ERROR;\r
307 }\r
308\r
309 while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {\r
310 ASSERT (MenuItem != NULL);\r
311 MenuArray[Index] = MenuItem;\r
312 MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3));\r
313 MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);\r
314 Index++;\r
315 }\r
316\r
317 if (UseDefaultItem) {\r
318 ASSERT (MenuArray[0] != NULL);\r
319 CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));\r
320 *Type = NTOHS (*Type);\r
321 return EFI_SUCCESS;\r
322 }\r
323\r
324 MenuNum = Index;\r
325\r
326 for (Index = 0; Index < MenuNum; Index++) {\r
327 ASSERT (MenuArray[Index] != NULL);\r
328 PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);\r
329 }\r
330\r
331 TopRow = gST->ConOut->Mode->CursorRow - MenuNum;\r
332\r
333 //\r
334 // Select the boot item by user in the boot menu.\r
335 //\r
336 do {\r
337 //\r
338 // Highlight selected row.\r
339 //\r
340 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
341 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);\r
342 ASSERT (Select < PXEBC_MENU_MAX_NUM);\r
343 ASSERT (MenuArray[Select] != NULL);\r
344 Blank[MenuArray[Select]->DescLen] = 0;\r
345 AsciiPrint ("%a\r", Blank);\r
346 PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);\r
347 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);\r
348 LastSelect = Select;\r
349\r
350 while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {\r
351 gBS->Stall (10 * TICKS_PER_MS);\r
352 }\r
353\r
354 if (InputKey.ScanCode != 0) {\r
355 switch (InputKey.UnicodeChar) {\r
356 case CTRL ('c'):\r
357 InputKey.ScanCode = SCAN_ESC;\r
358 break;\r
359\r
360 case CTRL ('j'): /* linefeed */\r
361 case CTRL ('m'): /* return */\r
362 Finish = TRUE;\r
363 break;\r
364\r
365 case CTRL ('i'): /* tab */\r
366 case ' ':\r
367 case 'd':\r
368 case 'D':\r
369 InputKey.ScanCode = SCAN_DOWN;\r
370 break;\r
371\r
372 case CTRL ('h'): /* backspace */\r
373 case 'u':\r
374 case 'U':\r
375 InputKey.ScanCode = SCAN_UP;\r
376 break;\r
377\r
378 default:\r
379 InputKey.ScanCode = 0;\r
380 }\r
381 }\r
382\r
383 switch (InputKey.ScanCode) {\r
384 case SCAN_LEFT:\r
385 case SCAN_UP:\r
386 if (Select != 0) {\r
387 Select--;\r
388 }\r
389 break;\r
390\r
391 case SCAN_DOWN:\r
392 case SCAN_RIGHT:\r
393 if (++Select == MenuNum) {\r
394 Select--;\r
395 }\r
396 break;\r
397\r
398 case SCAN_PAGE_UP:\r
399 case SCAN_HOME:\r
400 Select = 0;\r
401 break;\r
402\r
403 case SCAN_PAGE_DOWN:\r
404 case SCAN_END:\r
405 Select = (UINT16) (MenuNum - 1);\r
406 break;\r
407\r
408 case SCAN_ESC:\r
409 return EFI_ABORTED;\r
410 }\r
411\r
412 //\r
413 // Unhighlight the last selected row.\r
414 //\r
415 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
416 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);\r
417 ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);\r
418 ASSERT (MenuArray[LastSelect] != NULL);\r
419 Blank[MenuArray[LastSelect]->DescLen] = 0;\r
420 AsciiPrint ("%a\r", Blank);\r
421 PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);\r
422 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);\r
423 } while (!Finish);\r
424\r
425 //\r
426 // Swap the byte order.\r
427 //\r
428 ASSERT (Select < PXEBC_MENU_MAX_NUM);\r
429 ASSERT (MenuArray[Select] != NULL);\r
430 CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));\r
431 *Type = NTOHS (*Type);\r
432\r
433 return EFI_SUCCESS;\r
434}\r
435\r
436\r
437/**\r
438 Parse out the boot information from the last Dhcp4 reply packet.\r
439\r
440 @param[in, out] Private Pointer to PxeBc private data.\r
441 @param[out] BufferSize Size of the boot file to be downloaded.\r
442\r
443 @retval EFI_SUCCESS Successfully parsed out all the boot information.\r
444 @retval Others Failed to parse out the boot information.\r
445\r
446**/\r
447EFI_STATUS\r
448PxeBcDhcp4BootInfo (\r
449 IN OUT PXEBC_PRIVATE_DATA *Private,\r
450 OUT UINT64 *BufferSize\r
451 )\r
452{\r
453 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
454 EFI_PXE_BASE_CODE_MODE *Mode;\r
455 EFI_STATUS Status;\r
456 PXEBC_DHCP4_PACKET_CACHE *Cache4;\r
457 UINT16 Value;\r
458\r
459 PxeBc = &Private->PxeBc;\r
460 Mode = PxeBc->Mode;\r
461 Status = EFI_SUCCESS;\r
462 *BufferSize = 0;\r
463\r
464 //\r
465 // Get the last received Dhcp4 reply packet.\r
466 //\r
467 if (Mode->PxeReplyReceived) {\r
468 Cache4 = &Private->PxeReply.Dhcp4;\r
469 } else if (Mode->ProxyOfferReceived) {\r
470 Cache4 = &Private->ProxyOffer.Dhcp4;\r
471 } else {\r
472 Cache4 = &Private->DhcpAck.Dhcp4;\r
473 }\r
474\r
475 //\r
476 // Parse the boot server Ipv4 address by next server address.\r
477 // If this field isn't available, use option 54 instead.\r
478 //\r
479 CopyMem (\r
480 &Private->ServerIp,\r
481 &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,\r
482 sizeof (EFI_IPv4_ADDRESS)\r
483 );\r
484\r
485 if (Private->ServerIp.Addr[0] == 0) {\r
486 CopyMem (\r
487 &Private->ServerIp,\r
488 Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,\r
489 sizeof (EFI_IPv4_ADDRESS)\r
490 );\r
491 }\r
492\r
493 //\r
494 // Parse the boot file name by option.\r
495 //\r
496 ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);\r
497 Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;\r
498\r
499 if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {\r
500 //\r
501 // Parse the boot file size by option.\r
502 //\r
503 CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));\r
504 Value = NTOHS (Value);\r
505 //\r
506 // The field of boot file size is 512 bytes in unit.\r
507 //\r
508 *BufferSize = 512 * Value;\r
509 } else {\r
510 //\r
511 // Get the bootfile size by tftp command if no option available.\r
512 //\r
513 Status = PxeBc->Mtftp (\r
514 PxeBc,\r
515 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
516 NULL,\r
517 FALSE,\r
518 BufferSize,\r
519 &Private->BlockSize,\r
520 &Private->ServerIp,\r
521 Private->BootFileName,\r
522 NULL,\r
523 FALSE\r
524 );\r
525 }\r
526\r
527 //\r
528 // Save the value of boot file size.\r
529 //\r
530 Private->BootFileSize = (UINTN) *BufferSize;\r
531\r
532 //\r
533 // Display all the information: boot server address, boot file name and boot file size.\r
534 //\r
535 AsciiPrint ("\n Server IP address is ");\r
536 PxeBcShowIp4Addr (&Private->ServerIp.v4);\r
537 AsciiPrint ("\n NBP filename is %a", Private->BootFileName);\r
538 AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);\r
539\r
540 return Status;\r
541}\r
542\r
543\r
544/**\r
545 Parse out the boot information from the last Dhcp6 reply packet.\r
546\r
547 @param[in, out] Private Pointer to PxeBc private data.\r
548 @param[out] BufferSize Size of the boot file to be downloaded.\r
549\r
550 @retval EFI_SUCCESS Successfully parsed out all the boot information.\r
551 @retval EFI_BUFFER_TOO_SMALL\r
552 @retval Others Failed to parse out the boot information.\r
553\r
554**/\r
555EFI_STATUS\r
556PxeBcDhcp6BootInfo (\r
557 IN OUT PXEBC_PRIVATE_DATA *Private,\r
558 OUT UINT64 *BufferSize\r
559 )\r
560{\r
561 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
562 EFI_PXE_BASE_CODE_MODE *Mode;\r
563 EFI_STATUS Status;\r
564 PXEBC_DHCP6_PACKET_CACHE *Cache6;\r
565 UINT16 Value;\r
566\r
567 PxeBc = &Private->PxeBc;\r
568 Mode = PxeBc->Mode;\r
569 Status = EFI_SUCCESS;\r
570 *BufferSize = 0;\r
571\r
572 //\r
573 // Get the last received Dhcp6 reply packet.\r
574 //\r
575 if (Mode->PxeReplyReceived) {\r
576 Cache6 = &Private->PxeReply.Dhcp6;\r
577 } else if (Mode->ProxyOfferReceived) {\r
578 Cache6 = &Private->ProxyOffer.Dhcp6;\r
579 } else {\r
580 Cache6 = &Private->DhcpAck.Dhcp6;\r
581 }\r
582\r
583 ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
584\r
585 //\r
586 // Parse (m)tftp server ip address and bootfile name.\r
587 //\r
588 Status = PxeBcExtractBootFileUrl (\r
589 &Private->BootFileName,\r
590 &Private->ServerIp.v6,\r
591 (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
592 NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
593 );\r
594 if (EFI_ERROR (Status)) {\r
595 return Status;\r
596 }\r
597\r
598 //\r
599 // Parse the value of boot file size.\r
600 //\r
601 if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {\r
602 //\r
603 // Parse it out if have the boot file parameter option.\r
604 //\r
605 Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);\r
606 if (EFI_ERROR (Status)) {\r
607 return Status;\r
608 }\r
609 //\r
610 // The field of boot file size is 512 bytes in unit.\r
611 //\r
612 *BufferSize = 512 * Value;\r
613 } else {\r
614 //\r
615 // Send get file size command by tftp if option unavailable.\r
616 //\r
617 Status = PxeBc->Mtftp (\r
618 PxeBc,\r
619 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
620 NULL,\r
621 FALSE,\r
622 BufferSize,\r
623 &Private->BlockSize,\r
624 &Private->ServerIp,\r
625 Private->BootFileName,\r
626 NULL,\r
627 FALSE\r
628 );\r
629 }\r
630\r
631 //\r
632 // Save the value of boot file size.\r
633 //\r
634 Private->BootFileSize = (UINTN) *BufferSize;\r
635\r
636 //\r
637 // Display all the information: boot server address, boot file name and boot file size.\r
638 //\r
639 AsciiPrint ("\n Server IP address is ");\r
640 PxeBcShowIp6Addr (&Private->ServerIp.v6);\r
641 AsciiPrint ("\n NBP filename is %a", Private->BootFileName);\r
642 AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);\r
643\r
644 return Status;\r
645}\r
646\r
647\r
648/**\r
649 Extract the discover information and boot server entry from the\r
650 cached packets if unspecified.\r
651\r
652 @param[in] Private Pointer to PxeBc private data.\r
653 @param[in] Type The type of bootstrap to perform.\r
654 @param[in, out] Info Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.\r
655 @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.\r
656 @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.\r
657\r
658 @retval EFI_SUCCESS Successfully extracted the information.\r
659 @retval EFI_DEVICE_ERROR Failed to extract the information.\r
660\r
661**/\r
662EFI_STATUS\r
663PxeBcExtractDiscoverInfo (\r
664 IN PXEBC_PRIVATE_DATA *Private,\r
665 IN UINT16 Type,\r
666 IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info,\r
667 OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,\r
668 OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList\r
669 )\r
670{\r
671 EFI_PXE_BASE_CODE_MODE *Mode;\r
672 PXEBC_DHCP4_PACKET_CACHE *Cache4;\r
673 PXEBC_VENDOR_OPTION *VendorOpt;\r
674 PXEBC_BOOT_SVR_ENTRY *Entry;\r
675 BOOLEAN IsFound;\r
676\r
677 Mode = Private->PxeBc.Mode;\r
678\r
679 if (Mode->UsingIpv6) {\r
680 Info->IpCnt = 1;\r
681 Info->UseUCast = TRUE;\r
682\r
683 Info->SrvList[0].Type = Type;\r
684 Info->SrvList[0].AcceptAnyResponse = FALSE;\r
685\r
686 //\r
687 // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.\r
688 //\r
689 CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));\r
690\r
691 *SrvList = Info->SrvList;\r
692 } else {\r
693 Entry = NULL;\r
694 IsFound = FALSE;\r
695 Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;\r
696 VendorOpt = &Cache4->VendorOpt;\r
697\r
698 if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {\r
699 //\r
700 // Address is not acquired or no discovery options.\r
701 //\r
702 return EFI_INVALID_PARAMETER;\r
703 }\r
704\r
705 //\r
706 // Parse the boot server entry from the vendor option in the last cached packet.\r
707 //\r
708 Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);\r
709 Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);\r
710 Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);\r
711 Info->UseUCast = Info->MustUseList;\r
712\r
713 if (Info->UseMCast) {\r
714 //\r
715 // Get the multicast discover ip address from vendor option if has.\r
716 //\r
717 CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));\r
718 }\r
719\r
720 Info->IpCnt = 0;\r
721\r
722 if (Info->MustUseList) {\r
723 Entry = VendorOpt->BootSvr;\r
724\r
725 while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {\r
726 if (Entry->Type == HTONS (Type)) {\r
727 IsFound = TRUE;\r
728 break;\r
729 }\r
730 Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);\r
731 }\r
732\r
733 if (!IsFound) {\r
734 return EFI_DEVICE_ERROR;\r
735 }\r
736\r
737 Info->IpCnt = Entry->IpCnt;\r
738 }\r
739\r
740 *BootEntry = Entry;\r
741 }\r
742\r
743 return EFI_SUCCESS;\r
744}\r
745\r
746\r
747/**\r
748 Build the discover packet and send out for boot server.\r
749\r
750 @param[in] Private Pointer to PxeBc private data.\r
751 @param[in] Type PxeBc option boot item type.\r
752 @param[in] Layer Pointer to option boot item layer.\r
753 @param[in] UseBis Use BIS or not.\r
754 @param[in] DestIp Pointer to the destination address.\r
755 @param[in] IpCount The count of the server address.\r
756 @param[in] SrvList Pointer to the server address list.\r
757\r
758 @retval EFI_SUCCESS Successfully discovered boot file.\r
759 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.\r
760 @retval EFI_NOT_FOUND Can't get the PXE reply packet.\r
761 @retval Others Failed to discover boot file.\r
762\r
763**/\r
764EFI_STATUS\r
765PxeBcDiscoverBootServer (\r
766 IN PXEBC_PRIVATE_DATA *Private,\r
767 IN UINT16 Type,\r
768 IN UINT16 *Layer,\r
769 IN BOOLEAN UseBis,\r
770 IN EFI_IP_ADDRESS *DestIp,\r
771 IN UINT16 IpCount,\r
772 IN EFI_PXE_BASE_CODE_SRVLIST *SrvList\r
773 )\r
774{\r
775 if (Private->PxeBc.Mode->UsingIpv6) {\r
776 return PxeBcDhcp6Discover (\r
777 Private,\r
778 Type,\r
779 Layer,\r
780 UseBis,\r
781 DestIp\r
782 );\r
783 } else {\r
784 return PxeBcDhcp4Discover (\r
785 Private,\r
786 Type,\r
787 Layer,\r
788 UseBis,\r
789 DestIp,\r
790 IpCount,\r
791 SrvList\r
792 );\r
793 }\r
794}\r
795\r
796\r
797/**\r
798 Discover all the boot information for boot file.\r
799\r
800 @param[in, out] Private Pointer to PxeBc private data.\r
801 @param[out] BufferSize Size of the boot file to be downloaded.\r
802\r
803 @retval EFI_SUCCESS Successfully obtained all the boot information .\r
804 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
805 @retval EFI_ABORTED User cancel current operation.\r
806 @retval Others Failed to parse out the boot information.\r
807\r
808**/\r
809EFI_STATUS\r
810PxeBcDiscoverBootFile (\r
811 IN OUT PXEBC_PRIVATE_DATA *Private,\r
812 OUT UINT64 *BufferSize\r
813 )\r
814{\r
815 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
816 EFI_PXE_BASE_CODE_MODE *Mode;\r
817 EFI_STATUS Status;\r
818 UINT16 Type;\r
819 UINT16 Layer;\r
820 BOOLEAN UseBis;\r
821\r
822 PxeBc = &Private->PxeBc;\r
823 Mode = PxeBc->Mode;\r
824 Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;\r
825 Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;\r
826\r
827 //\r
828 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and\r
829 // other pxe boot information.\r
830 //\r
831 Status = PxeBc->Dhcp (PxeBc, TRUE);\r
832 if (EFI_ERROR (Status)) {\r
833 return Status;\r
834 }\r
835\r
836 //\r
837 // Select a boot server from boot server list.\r
838 //\r
839 Status = PxeBcSelectBootPrompt (Private);\r
840\r
841 if (Status == EFI_SUCCESS) {\r
842 //\r
843 // Choose by user's input.\r
844 //\r
845 Status = PxeBcSelectBootMenu (Private, &Type, TRUE);\r
846 } else if (Status == EFI_TIMEOUT) {\r
847 //\r
848 // Choose by default item.\r
849 //\r
850 Status = PxeBcSelectBootMenu (Private, &Type, FALSE);\r
851 }\r
852\r
853 if (!EFI_ERROR (Status)) {\r
854\r
855 if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {\r
856 //\r
857 // Local boot(PXE bootstrap server) need abort\r
858 //\r
859 return EFI_ABORTED;\r
860 }\r
861\r
862 //\r
863 // Start to discover the boot server to get (m)tftp server ip address, bootfile\r
864 // name and bootfile size.\r
865 //\r
866 UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);\r
867 Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);\r
868 if (EFI_ERROR (Status)) {\r
869 return Status;\r
870 }\r
871 }\r
872\r
873 //\r
874 // Parse the boot information.\r
875 //\r
876 if (Mode->UsingIpv6) {\r
877 Status = PxeBcDhcp6BootInfo (Private, BufferSize);\r
878 } else {\r
879 Status = PxeBcDhcp4BootInfo (Private, BufferSize);\r
880 }\r
881\r
882 return Status;\r
883}\r
884\r
885\r
886/**\r
887 Install PxeBaseCodeCallbackProtocol if not installed before.\r
888\r
889 @param[in, out] Private Pointer to PxeBc private data.\r
890 @param[out] NewMakeCallback If TRUE, it is a new callback.\r
891 Otherwise, it is not new callback.\r
892 @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully.\r
893 @retval Others Failed to install PxeBaseCodeCallbackProtocol.\r
894\r
895**/\r
896EFI_STATUS\r
897PxeBcInstallCallback (\r
898 IN OUT PXEBC_PRIVATE_DATA *Private,\r
899 OUT BOOLEAN *NewMakeCallback\r
900 )\r
901{\r
902 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
903 EFI_STATUS Status;\r
904\r
905 //\r
906 // Check whether PxeBaseCodeCallbackProtocol already installed.\r
907 //\r
908 PxeBc = &Private->PxeBc;\r
909 Status = gBS->HandleProtocol (\r
910 Private->Controller,\r
911 &gEfiPxeBaseCodeCallbackProtocolGuid,\r
912 (VOID **) &Private->PxeBcCallback\r
913 );\r
914 if (Status == EFI_UNSUPPORTED) {\r
915\r
916 CopyMem (\r
917 &Private->LoadFileCallback,\r
918 &gPxeBcCallBackTemplate,\r
919 sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)\r
920 );\r
921\r
922 //\r
923 // Install a default callback if user didn't offer one.\r
924 //\r
925 Status = gBS->InstallProtocolInterface (\r
926 &Private->Controller,\r
927 &gEfiPxeBaseCodeCallbackProtocolGuid,\r
928 EFI_NATIVE_INTERFACE,\r
929 &Private->LoadFileCallback\r
930 );\r
931\r
932 (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);\r
933\r
934 Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);\r
935 if (EFI_ERROR (Status)) {\r
936 PxeBc->Stop (PxeBc);\r
937 return Status;\r
938 }\r
939 }\r
940\r
941 return EFI_SUCCESS;\r
942}\r
943\r
944\r
945/**\r
946 Uninstall PxeBaseCodeCallbackProtocol.\r
947\r
948 @param[in] Private Pointer to PxeBc private data.\r
949 @param[in] NewMakeCallback If TRUE, it is a new callback.\r
950 Otherwise, it is not new callback.\r
951\r
952**/\r
953VOID\r
954PxeBcUninstallCallback (\r
955 IN PXEBC_PRIVATE_DATA *Private,\r
956 IN BOOLEAN NewMakeCallback\r
957 )\r
958{\r
959 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
960\r
961 PxeBc = &Private->PxeBc;\r
962\r
963 if (NewMakeCallback) {\r
964\r
965 NewMakeCallback = FALSE;\r
966\r
967 PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);\r
968\r
969 gBS->UninstallProtocolInterface (\r
970 Private->Controller,\r
971 &gEfiPxeBaseCodeCallbackProtocolGuid,\r
972 &Private->LoadFileCallback\r
973 );\r
974 }\r
975}\r
976\r
977\r
978/**\r
979 Download one of boot file in the list, and it's special for IPv6.\r
980\r
981 @param[in] Private Pointer to PxeBc private data.\r
982 @param[in, out] BufferSize Size of user buffer for input;\r
983 required buffer size for output.\r
984 @param[in] Buffer Pointer to user buffer.\r
985\r
986 @retval EFI_SUCCESS Read one of boot file in the list successfully.\r
987 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
988 @retval EFI_NOT_FOUND There is no proper boot file available.\r
989 @retval Others Failed to download boot file in the list.\r
990\r
991**/\r
992EFI_STATUS\r
993PxeBcReadBootFileList (\r
994 IN PXEBC_PRIVATE_DATA *Private,\r
995 IN OUT UINT64 *BufferSize,\r
996 IN VOID *Buffer OPTIONAL\r
997 )\r
998{\r
999 EFI_STATUS Status;\r
1000 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
1001\r
1002 PxeBc = &Private->PxeBc;\r
1003\r
1004 //\r
1005 // Try to download the boot file if everything is ready.\r
1006 //\r
1007 if (Buffer != NULL) {\r
1008 Status = PxeBc->Mtftp (\r
1009 PxeBc,\r
1010 EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
1011 Buffer,\r
1012 FALSE,\r
1013 BufferSize,\r
1014 &Private->BlockSize,\r
1015 &Private->ServerIp,\r
1016 Private->BootFileName,\r
1017 NULL,\r
1018 FALSE\r
1019 );\r
1020\r
1021\r
1022 } else {\r
1023 Status = EFI_BUFFER_TOO_SMALL;\r
1024 }\r
1025\r
1026 return Status;\r
1027}\r
1028\r
1029\r
1030/**\r
1031 Load boot file into user buffer.\r
1032\r
1033 @param[in] Private Pointer to PxeBc private data.\r
1034 @param[in, out] BufferSize Size of user buffer for input;\r
1035 required buffer size for output.\r
1036 @param[in] Buffer Pointer to user buffer.\r
1037\r
1038 @retval EFI_SUCCESS Get all the boot information successfully.\r
1039 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
1040 @retval EFI_ABORTED User cancelled the current operation.\r
1041 @retval Others Failed to parse out the boot information.\r
1042\r
1043**/\r
1044EFI_STATUS\r
1045PxeBcLoadBootFile (\r
1046 IN PXEBC_PRIVATE_DATA *Private,\r
1047 IN OUT UINTN *BufferSize,\r
1048 IN VOID *Buffer OPTIONAL\r
1049 )\r
1050{\r
1051 BOOLEAN NewMakeCallback;\r
1052 UINT64 RequiredSize;\r
1053 UINT64 CurrentSize;\r
1054 EFI_STATUS Status;\r
1055 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
1056 EFI_PXE_BASE_CODE_MODE *PxeBcMode;\r
1057\r
1058 NewMakeCallback = FALSE;\r
1059 PxeBc = &Private->PxeBc;\r
1060 PxeBcMode = &Private->Mode;\r
1061 CurrentSize = *BufferSize;\r
1062 RequiredSize = 0;\r
1063\r
1064 //\r
1065 // Install pxebc callback protocol if hasn't been installed yet.\r
1066 //\r
1067 Status = PxeBcInstallCallback (Private, &NewMakeCallback);\r
1068 if (EFI_ERROR(Status)) {\r
1069 return Status;\r
1070 }\r
1071\r
1072 if (Private->BootFileSize == 0) {\r
1073 //\r
1074 // Discover the boot information about the bootfile if hasn't.\r
1075 //\r
1076 Status = PxeBcDiscoverBootFile (Private, &RequiredSize);\r
278c6019 1077 if (EFI_ERROR (Status)) {\r
1078 goto ON_EXIT;\r
1079 }\r
a3bcde70
HT
1080\r
1081 if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {\r
1082 //\r
1083 // It's error if the required buffer size is beyond the system scope.\r
1084 //\r
1085 Status = EFI_DEVICE_ERROR;\r
1086 goto ON_EXIT;\r
1087 } else if (RequiredSize > 0) {\r
1088 //\r
1089 // Get the right buffer size of the bootfile required.\r
1090 //\r
1091 if (CurrentSize < RequiredSize || Buffer == NULL) {\r
1092 //\r
1093 // It's buffer too small if the size of user buffer is smaller than the required.\r
1094 //\r
1095 CurrentSize = RequiredSize;\r
1096 Status = EFI_BUFFER_TOO_SMALL;\r
1097 goto ON_EXIT;\r
1098 }\r
1099 CurrentSize = RequiredSize;\r
1100 } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {\r
1101 //\r
1102 // Try to download another bootfile in list if failed to get the filesize of the last one.\r
1103 // It's special for the case of IPv6.\r
1104 //\r
1105 Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);\r
1106 goto ON_EXIT;\r
1107 }\r
1108 } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {\r
1109 //\r
1110 // It's buffer too small if the size of user buffer is smaller than the required.\r
1111 //\r
1112 CurrentSize = Private->BootFileSize;\r
1113 Status = EFI_BUFFER_TOO_SMALL;\r
1114 goto ON_EXIT;\r
1115 }\r
1116\r
1117 //\r
1118 // Begin to download the bootfile if everything is ready.\r
1119 //\r
1120 AsciiPrint ("\n Downloading NBP file...\n");\r
1121 if (PxeBcMode->UsingIpv6) {\r
1122 Status = PxeBcReadBootFileList (\r
1123 Private,\r
1124 &CurrentSize,\r
1125 Buffer\r
1126 );\r
1127 } else {\r
1128 Status = PxeBc->Mtftp (\r
1129 PxeBc,\r
1130 EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
1131 Buffer,\r
1132 FALSE,\r
1133 &CurrentSize,\r
1134 &Private->BlockSize,\r
1135 &Private->ServerIp,\r
1136 Private->BootFileName,\r
1137 NULL,\r
1138 FALSE\r
1139 );\r
1140 }\r
1141\r
1142ON_EXIT:\r
1143 *BufferSize = (UINTN) CurrentSize;\r
1144 PxeBcUninstallCallback(Private, NewMakeCallback);\r
1145\r
1146 if (Status == EFI_SUCCESS) {\r
1147 AsciiPrint ("\n Succeed to download NBP file.\n");\r
1148 return EFI_SUCCESS;\r
1149 } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {\r
1150 AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n");\r
1151 } else if (Status == EFI_DEVICE_ERROR) {\r
1152 AsciiPrint ("\n PXE-E07: Network device error.\n");\r
1153 } else if (Status == EFI_OUT_OF_RESOURCES) {\r
1154 AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n");\r
1155 } else if (Status == EFI_NO_MEDIA) {\r
1156 AsciiPrint ("\n PXE-E12: Could not detect network connection.\n");\r
1157 } else if (Status == EFI_NO_RESPONSE) {\r
1158 AsciiPrint ("\n PXE-E16: No offer received.\n");\r
1159 } else if (Status == EFI_TIMEOUT) {\r
1160 AsciiPrint ("\n PXE-E18: Server response timeout.\n");\r
1161 } else if (Status == EFI_ABORTED) {\r
1162 AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n");\r
1163 } else if (Status == EFI_ICMP_ERROR) {\r
1164 AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n");\r
1165 } else if (Status == EFI_TFTP_ERROR) {\r
1166 AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n");\r
4496ff75 1167 } else if (Status == EFI_NOT_FOUND) {\r
1168 AsciiPrint ("\n PXE-E53: No boot filename received.\n");\r
a3bcde70
HT
1169 } else if (Status != EFI_BUFFER_TOO_SMALL) {\r
1170 AsciiPrint ("\n PXE-E99: Unexpected network error.\n");\r
1171 }\r
1172\r
1173 return Status;\r
1174}\r
1175\r