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