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