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