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