2 Boot functions implementation for UefiPxeBc Driver.
4 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include "PxeBcImpl.h"
14 Display the string of the boot item.
16 If the length of the boot item string beyond 70 Char, just display 70 Char.
18 @param[in] Str The pointer to the string.
19 @param[in] Len The length of the string.
23 PxeBcDisplayBootItem (
31 // Cut off the chars behind 70th.
33 Len
= (UINT8
)MIN (PXEBC_DISPLAY_MAX_LINE
, Len
);
36 AsciiPrint ("%a \n", Str
);
39 // Restore the original 70th char.
45 Select and maintain the boot prompt if needed.
47 @param[in] Private Pointer to PxeBc private data.
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.
57 PxeBcSelectBootPrompt (
58 IN PXEBC_PRIVATE_DATA
*Private
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
;
77 Mode
= Private
->PxeBc
.Mode
;
78 Cache
= Mode
->ProxyOfferReceived
? &Private
->ProxyOffer
: &Private
->DhcpAck
;
79 OfferType
= Mode
->UsingIpv6
? Cache
->Dhcp6
.OfferType
: Cache
->Dhcp4
.OfferType
;
82 // Only DhcpPxe10 and ProxyPxe10 offer needs boot prompt.
84 if ((OfferType
!= PxeOfferTypeProxyPxe10
) && (OfferType
!= PxeOfferTypeDhcpPxe10
)) {
89 // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
91 ASSERT (!Mode
->UsingIpv6
);
93 VendorOpt
= &Cache
->Dhcp4
.VendorOpt
;
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.
100 if (IS_DISABLE_PROMPT_MENU (VendorOpt
->DiscoverCtrl
) &&
101 (Cache
->Dhcp4
.OptList
[PXEBC_DHCP4_TAG_INDEX_BOOTFILE
] != NULL
))
106 if (!IS_VALID_BOOT_PROMPT (VendorOpt
->BitMap
)) {
110 Timeout
= VendorOpt
->MenuPrompt
->Timeout
;
111 Prompt
= VendorOpt
->MenuPrompt
->Prompt
;
112 PromptLen
= (UINT8
)(VendorOpt
->MenuPromptLen
- 1);
115 // The valid scope of Timeout refers to PXE2.1 spec.
121 if (Timeout
== 255) {
126 // Create and start a timer as timeout event.
128 Status
= gBS
->CreateEvent (
135 if (EFI_ERROR (Status
)) {
139 Status
= gBS
->SetTimer (
142 MultU64x32 (Timeout
, TICKS_PER_SECOND
)
144 if (EFI_ERROR (Status
)) {
149 // Create and start a periodic timer as descend event by second.
151 Status
= gBS
->CreateEvent (
158 if (EFI_ERROR (Status
)) {
162 Status
= gBS
->SetTimer (
167 if (EFI_ERROR (Status
)) {
172 // Display the boot item and cursor on the screen.
174 SecCol
= gST
->ConOut
->Mode
->CursorColumn
;
175 SecRow
= gST
->ConOut
->Mode
->CursorRow
;
177 PxeBcDisplayBootItem (Prompt
, PromptLen
);
179 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, SecCol
+ PromptLen
, SecRow
);
180 AsciiPrint ("(%d) ", Timeout
--);
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
--);
189 if (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &InputKey
) == EFI_NOT_READY
) {
190 gBS
->Stall (10 * TICKS_PER_MS
);
195 // Parse the input key by user.
196 // If <F8> or <Ctrl> + <M> is pressed, return success to display the boot menu.
198 if (InputKey
.ScanCode
== 0) {
199 switch (InputKey
.UnicodeChar
) {
201 Status
= EFI_ABORTED
;
207 Status
= EFI_SUCCESS
;
214 switch (InputKey
.ScanCode
) {
216 Status
= EFI_SUCCESS
;
220 Status
= EFI_ABORTED
;
232 // Reset the cursor on the screen.
234 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, SecRow
+ 1);
237 if (DescendEvent
!= NULL
) {
238 gBS
->CloseEvent (DescendEvent
);
241 if (TimeoutEvent
!= NULL
) {
242 gBS
->CloseEvent (TimeoutEvent
);
249 Select the boot menu by user's input.
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.
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.
261 PxeBcSelectBootMenu (
262 IN PXEBC_PRIVATE_DATA
*Private
,
264 IN BOOLEAN UseDefaultItem
267 EFI_PXE_BASE_CODE_MODE
*Mode
;
268 PXEBC_DHCP_PACKET_CACHE
*Cache
;
269 PXEBC_VENDOR_OPTION
*VendorOpt
;
270 EFI_INPUT_KEY InputKey
;
279 CHAR8 Blank
[PXEBC_DISPLAY_MAX_LINE
];
280 PXEBC_BOOT_MENU_ENTRY
*MenuItem
;
281 PXEBC_BOOT_MENU_ENTRY
*MenuArray
[PXEBC_MENU_MAX_NUM
];
287 Mode
= Private
->PxeBc
.Mode
;
288 Cache
= Mode
->ProxyOfferReceived
? &Private
->ProxyOffer
: &Private
->DhcpAck
;
289 OfferType
= Mode
->UsingIpv6
? Cache
->Dhcp6
.OfferType
: Cache
->Dhcp4
.OfferType
;
292 // There is no specified DhcpPxe10/ProxyPxe10 for IPv6 in PXE and UEFI spec.
294 ASSERT (!Mode
->UsingIpv6
);
295 ASSERT (OfferType
== PxeOfferTypeProxyPxe10
|| OfferType
== PxeOfferTypeDhcpPxe10
);
297 VendorOpt
= &Cache
->Dhcp4
.VendorOpt
;
298 if (!IS_VALID_BOOT_MENU (VendorOpt
->BitMap
)) {
303 // Display the boot menu on the screen.
305 SetMem (Blank
, sizeof (Blank
), ' ');
307 MenuSize
= VendorOpt
->BootMenuLen
;
308 MenuItem
= VendorOpt
->BootMenu
;
311 return EFI_DEVICE_ERROR
;
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);
322 if (UseDefaultItem
) {
323 ASSERT (MenuArray
[0] != NULL
);
324 CopyMem (Type
, &MenuArray
[0]->Type
, sizeof (UINT16
));
325 *Type
= NTOHS (*Type
);
331 for (Index
= 0; Index
< MenuNum
; Index
++) {
332 ASSERT (MenuArray
[Index
] != NULL
);
333 PxeBcDisplayBootItem (MenuArray
[Index
]->DescStr
, MenuArray
[Index
]->DescLen
);
336 TopRow
= gST
->ConOut
->Mode
->CursorRow
- MenuNum
;
339 // Select the boot item by user in the boot menu.
343 // Highlight selected row.
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
);
355 while (gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &InputKey
) == EFI_NOT_READY
) {
356 gBS
->Stall (10 * TICKS_PER_MS
);
359 if (InputKey
.ScanCode
== 0) {
360 switch (InputKey
.UnicodeChar
) {
362 InputKey
.ScanCode
= SCAN_ESC
;
365 case CTRL ('j'): /* linefeed */
366 case CTRL ('m'): /* return */
370 case CTRL ('i'): /* tab */
374 InputKey
.ScanCode
= SCAN_DOWN
;
377 case CTRL ('h'): /* backspace */
380 InputKey
.ScanCode
= SCAN_UP
;
384 InputKey
.ScanCode
= 0;
388 switch (InputKey
.ScanCode
) {
399 if (++Select
== MenuNum
) {
412 Select
= (UINT16
)(MenuNum
- 1);
420 // Unhighlight the last selected row.
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
);
433 // Swap the byte order.
435 ASSERT (Select
< PXEBC_MENU_MAX_NUM
);
436 ASSERT (MenuArray
[Select
] != NULL
);
437 CopyMem (Type
, &MenuArray
[Select
]->Type
, sizeof (UINT16
));
438 *Type
= NTOHS (*Type
);
444 Parse out the boot information from the last Dhcp4 reply packet.
446 @param[in, out] Private Pointer to PxeBc private data.
447 @param[out] BufferSize Size of the boot file to be downloaded.
449 @retval EFI_SUCCESS Successfully parsed out all the boot information.
450 @retval Others Failed to parse out the boot information.
455 IN OUT PXEBC_PRIVATE_DATA
*Private
,
456 OUT UINT64
*BufferSize
459 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
460 EFI_PXE_BASE_CODE_MODE
*Mode
;
462 PXEBC_DHCP4_PACKET_CACHE
*Cache4
;
464 PXEBC_VENDOR_OPTION
*VendorOpt
;
465 PXEBC_BOOT_SVR_ENTRY
*Entry
;
467 PxeBc
= &Private
->PxeBc
;
469 Status
= EFI_SUCCESS
;
473 // Get the last received Dhcp4 reply packet.
475 if (Mode
->PxeReplyReceived
) {
476 Cache4
= &Private
->PxeReply
.Dhcp4
;
477 } else if (Mode
->ProxyOfferReceived
) {
478 Cache4
= &Private
->ProxyOffer
.Dhcp4
;
480 Cache4
= &Private
->DhcpAck
.Dhcp4
;
483 if (Cache4
->OptList
[PXEBC_DHCP4_TAG_INDEX_BOOTFILE
] == NULL
) {
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.
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.
495 return EFI_PROTOCOL_ERROR
;
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.
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)) {
511 sizeof (EFI_IPv4_ADDRESS
)
516 if (Private
->ServerIp
.Addr
[0] == 0) {
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.
523 &Cache4
->Packet
.Offer
.Dhcp4
.Header
.ServerAddr
,
524 sizeof (EFI_IPv4_ADDRESS
)
528 if (Private
->ServerIp
.Addr
[0] == 0) {
530 // Still failed , use the IP address from option 54.
534 Cache4
->OptList
[PXEBC_DHCP4_TAG_INDEX_SERVER_ID
]->Data
,
535 sizeof (EFI_IPv4_ADDRESS
)
540 // Parse the boot file name by option.
542 Private
->BootFileName
= Cache4
->OptList
[PXEBC_DHCP4_TAG_INDEX_BOOTFILE
]->Data
;
544 if (Cache4
->OptList
[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN
] != NULL
) {
546 // Parse the boot file size by option.
548 CopyMem (&Value
, Cache4
->OptList
[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN
]->Data
, sizeof (Value
));
549 Value
= NTOHS (Value
);
551 // The field of boot file size is 512 bytes in unit.
553 *BufferSize
= 512 * Value
;
556 // Get the bootfile size by tftp command if no option available.
558 Status
= PxeBc
->Mtftp (
560 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
566 Private
->BootFileName
,
573 // Save the value of boot file size.
575 Private
->BootFileSize
= (UINTN
)*BufferSize
;
578 // Display all the information: boot server address, boot file name and boot file size.
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
);
589 Parse out the boot information from the last Dhcp6 reply packet.
591 @param[in, out] Private Pointer to PxeBc private data.
592 @param[out] BufferSize Size of the boot file to be downloaded.
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.
601 IN OUT PXEBC_PRIVATE_DATA
*Private
,
602 OUT UINT64
*BufferSize
605 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
606 EFI_PXE_BASE_CODE_MODE
*Mode
;
608 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
611 PxeBc
= &Private
->PxeBc
;
613 Status
= EFI_SUCCESS
;
617 // Get the last received Dhcp6 reply packet.
619 if (Mode
->PxeReplyReceived
) {
620 Cache6
= &Private
->PxeReply
.Dhcp6
;
621 } else if (Mode
->ProxyOfferReceived
) {
622 Cache6
= &Private
->ProxyOffer
.Dhcp6
;
624 Cache6
= &Private
->DhcpAck
.Dhcp6
;
627 if (Cache6
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] == NULL
) {
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.
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.
639 return EFI_PROTOCOL_ERROR
;
643 // Set the station address to IP layer.
645 Status
= PxeBcSetIp6Address (Private
);
646 if (EFI_ERROR (Status
)) {
651 // Parse (m)tftp server ip address and bootfile name.
653 Status
= PxeBcExtractBootFileUrl (
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
)
660 if (EFI_ERROR (Status
)) {
665 // Parse the value of boot file size.
667 if (Cache6
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM
] != NULL
) {
669 // Parse it out if have the boot file parameter option.
671 Status
= PxeBcExtractBootFileParam ((CHAR8
*)Cache6
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM
]->Data
, &Value
);
672 if (EFI_ERROR (Status
)) {
677 // The field of boot file size is 512 bytes in unit.
679 *BufferSize
= 512 * Value
;
682 // Send get file size command by tftp if option unavailable.
684 Status
= PxeBc
->Mtftp (
686 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
692 Private
->BootFileName
,
699 // Save the value of boot file size.
701 Private
->BootFileSize
= (UINTN
)*BufferSize
;
704 // Display all the information: boot server address, boot file name and boot file size.
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
);
715 Extract the discover information and boot server entry from the
716 cached packets if unspecified.
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.
724 @retval EFI_SUCCESS Successfully extracted the information.
725 @retval EFI_DEVICE_ERROR Failed to extract the information.
729 PxeBcExtractDiscoverInfo (
730 IN PXEBC_PRIVATE_DATA
*Private
,
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
737 EFI_PXE_BASE_CODE_MODE
*Mode
;
738 PXEBC_DHCP4_PACKET_CACHE
*Cache4
;
739 PXEBC_VENDOR_OPTION
*VendorOpt
;
740 PXEBC_BOOT_SVR_ENTRY
*Entry
;
742 EFI_PXE_BASE_CODE_DISCOVER_INFO
*Info
;
745 Mode
= Private
->PxeBc
.Mode
;
746 Info
= *DiscoverInfo
;
748 if (Mode
->UsingIpv6
) {
750 Info
->UseUCast
= TRUE
;
752 Info
->SrvList
[0].Type
= Type
;
753 Info
->SrvList
[0].AcceptAnyResponse
= FALSE
;
756 // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.
758 CopyMem (&Info
->SrvList
[0].IpAddr
, &Private
->ServerIp
, sizeof (EFI_IP_ADDRESS
));
760 *SrvList
= Info
->SrvList
;
764 Cache4
= (Mode
->ProxyOfferReceived
) ? &Private
->ProxyOffer
.Dhcp4
: &Private
->DhcpAck
.Dhcp4
;
765 VendorOpt
= &Cache4
->VendorOpt
;
767 if (!Mode
->DhcpAckReceived
|| !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt
->BitMap
)) {
769 // Address is not acquired or no discovery options.
771 return EFI_INVALID_PARAMETER
;
775 // Parse the boot server entry from the vendor option in the last cached packet.
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
);
782 if (Info
->UseMCast
) {
784 // Get the multicast discover ip address from vendor option if has.
786 CopyMem (&Info
->ServerMCastIp
.v4
, &VendorOpt
->DiscoverMcastIp
, sizeof (EFI_IPv4_ADDRESS
));
791 if (Info
->UseUCast
) {
792 Entry
= VendorOpt
->BootSvr
;
794 while (((UINT8
)(Entry
- VendorOpt
->BootSvr
)) < VendorOpt
->BootSvrLen
) {
795 if (Entry
->Type
== HTONS (Type
)) {
800 Entry
= GET_NEXT_BOOT_SVR_ENTRY (Entry
);
804 return EFI_DEVICE_ERROR
;
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
;
814 CopyMem (*DiscoverInfo
, Info
, sizeof (*Info
));
815 Info
= *DiscoverInfo
;
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
);
826 *SrvList
= Info
->SrvList
;
833 Build the discover packet and send out for boot server.
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.
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.
850 PxeBcDiscoverBootServer (
851 IN PXEBC_PRIVATE_DATA
*Private
,
855 IN EFI_IP_ADDRESS
*DestIp
,
857 IN EFI_PXE_BASE_CODE_SRVLIST
*SrvList
860 if (Private
->PxeBc
.Mode
->UsingIpv6
) {
861 return PxeBcDhcp6Discover (
869 return PxeBcDhcp4Discover (
882 Discover all the boot information for boot file.
884 @param[in, out] Private Pointer to PxeBc private data.
885 @param[out] BufferSize Size of the boot file to be downloaded.
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.
894 PxeBcDiscoverBootFile (
895 IN OUT PXEBC_PRIVATE_DATA
*Private
,
896 OUT UINT64
*BufferSize
899 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
900 EFI_PXE_BASE_CODE_MODE
*Mode
;
906 PxeBc
= &Private
->PxeBc
;
908 Type
= EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP
;
909 Layer
= EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL
;
912 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
913 // other pxe boot information.
915 Status
= PxeBc
->Dhcp (PxeBc
, TRUE
);
916 if (EFI_ERROR (Status
)) {
921 // Select a boot server from boot server list.
923 Status
= PxeBcSelectBootPrompt (Private
);
925 if (Status
== EFI_SUCCESS
) {
927 // Choose by user's input.
929 Status
= PxeBcSelectBootMenu (Private
, &Type
, FALSE
);
930 } else if (Status
== EFI_TIMEOUT
) {
932 // Choose by default item.
934 Status
= PxeBcSelectBootMenu (Private
, &Type
, TRUE
);
937 if (!EFI_ERROR (Status
)) {
938 if (Type
== EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP
) {
940 // Local boot(PXE bootstrap server) need abort
946 // Start to discover the boot server to get (m)tftp server ip address, bootfile
947 // name and bootfile size.
949 UseBis
= (BOOLEAN
)(Mode
->BisSupported
&& Mode
->BisDetected
);
950 Status
= PxeBc
->Discover (PxeBc
, Type
, &Layer
, UseBis
, NULL
);
951 if (EFI_ERROR (Status
)) {
955 if (Mode
->PxeReplyReceived
&& !Mode
->ProxyOfferReceived
) {
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.
960 if (Mode
->UsingIpv6
) {
962 &Mode
->ProxyOffer
.Dhcpv6
,
963 &Mode
->PxeReply
.Dhcpv6
,
964 Private
->PxeReply
.Dhcp6
.Packet
.Ack
.Length
968 &Mode
->ProxyOffer
.Dhcpv4
,
969 &Mode
->PxeReply
.Dhcpv4
,
970 Private
->PxeReply
.Dhcp4
.Packet
.Ack
.Length
974 Mode
->ProxyOfferReceived
= TRUE
;
979 // Parse the boot information.
981 if (Mode
->UsingIpv6
) {
982 Status
= PxeBcDhcp6BootInfo (Private
, BufferSize
);
984 Status
= PxeBcDhcp4BootInfo (Private
, BufferSize
);
991 Install PxeBaseCodeCallbackProtocol if not installed before.
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.
1001 PxeBcInstallCallback (
1002 IN OUT PXEBC_PRIVATE_DATA
*Private
,
1003 OUT BOOLEAN
*NewMakeCallback
1006 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
1010 // Check whether PxeBaseCodeCallbackProtocol already installed.
1012 PxeBc
= &Private
->PxeBc
;
1013 Status
= gBS
->HandleProtocol (
1014 Private
->Mode
.UsingIpv6
? Private
->Ip6Nic
->Controller
: Private
->Ip4Nic
->Controller
,
1015 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1016 (VOID
**)&Private
->PxeBcCallback
1018 if (Status
== EFI_UNSUPPORTED
) {
1020 &Private
->LoadFileCallback
,
1021 &gPxeBcCallBackTemplate
,
1022 sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
)
1026 // Install a default callback if user didn't offer one.
1028 Status
= gBS
->InstallProtocolInterface (
1029 Private
->Mode
.UsingIpv6
? &Private
->Ip6Nic
->Controller
: &Private
->Ip4Nic
->Controller
,
1030 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1031 EFI_NATIVE_INTERFACE
,
1032 &Private
->LoadFileCallback
1035 (*NewMakeCallback
) = (BOOLEAN
)(Status
== EFI_SUCCESS
);
1037 Status
= PxeBc
->SetParameters (PxeBc
, NULL
, NULL
, NULL
, NULL
, NewMakeCallback
);
1038 if (EFI_ERROR (Status
)) {
1039 PxeBc
->Stop (PxeBc
);
1048 Uninstall PxeBaseCodeCallbackProtocol.
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.
1056 PxeBcUninstallCallback (
1057 IN PXEBC_PRIVATE_DATA
*Private
,
1058 IN BOOLEAN NewMakeCallback
1061 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
1063 PxeBc
= &Private
->PxeBc
;
1065 if (NewMakeCallback
) {
1066 NewMakeCallback
= FALSE
;
1068 PxeBc
->SetParameters (PxeBc
, NULL
, NULL
, NULL
, NULL
, &NewMakeCallback
);
1070 gBS
->UninstallProtocolInterface (
1071 Private
->Mode
.UsingIpv6
? Private
->Ip6Nic
->Controller
: Private
->Ip4Nic
->Controller
,
1072 &gEfiPxeBaseCodeCallbackProtocolGuid
,
1073 &Private
->LoadFileCallback
1079 Download one of boot file in the list, and it's special for IPv6.
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.
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.
1093 PxeBcReadBootFileList (
1094 IN PXEBC_PRIVATE_DATA
*Private
,
1095 IN OUT UINT64
*BufferSize
,
1096 IN VOID
*Buffer OPTIONAL
1100 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
1102 PxeBc
= &Private
->PxeBc
;
1105 // Try to download the boot file if everything is ready.
1107 if (Buffer
!= NULL
) {
1108 Status
= PxeBc
->Mtftp (
1110 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1114 &Private
->BlockSize
,
1116 Private
->BootFileName
,
1121 Status
= EFI_BUFFER_TOO_SMALL
;
1128 Load boot file into user buffer.
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.
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.
1143 IN PXEBC_PRIVATE_DATA
*Private
,
1144 IN OUT UINTN
*BufferSize
,
1145 IN VOID
*Buffer OPTIONAL
1148 BOOLEAN NewMakeCallback
;
1149 UINT64 RequiredSize
;
1152 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
1153 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
1155 NewMakeCallback
= FALSE
;
1156 PxeBc
= &Private
->PxeBc
;
1157 PxeBcMode
= &Private
->Mode
;
1158 CurrentSize
= *BufferSize
;
1162 // Install pxebc callback protocol if hasn't been installed yet.
1164 Status
= PxeBcInstallCallback (Private
, &NewMakeCallback
);
1165 if (EFI_ERROR (Status
)) {
1169 if (Private
->BootFileSize
== 0) {
1171 // Discover the boot information about the bootfile if hasn't.
1173 Status
= PxeBcDiscoverBootFile (Private
, &RequiredSize
);
1174 if (EFI_ERROR (Status
)) {
1178 if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize
)) {
1180 // It's error if the required buffer size is beyond the system scope.
1182 Status
= EFI_DEVICE_ERROR
;
1184 } else if (RequiredSize
> 0) {
1186 // Get the right buffer size of the bootfile required.
1188 if ((CurrentSize
< RequiredSize
) || (Buffer
== NULL
)) {
1190 // It's buffer too small if the size of user buffer is smaller than the required.
1192 CurrentSize
= RequiredSize
;
1193 Status
= EFI_BUFFER_TOO_SMALL
;
1197 CurrentSize
= RequiredSize
;
1198 } else if ((RequiredSize
== 0) && PxeBcMode
->UsingIpv6
) {
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.
1203 Status
= PxeBcReadBootFileList (Private
, &CurrentSize
, Buffer
);
1206 } else if ((CurrentSize
< Private
->BootFileSize
) || (Buffer
== NULL
)) {
1208 // It's buffer too small if the size of user buffer is smaller than the required.
1210 CurrentSize
= Private
->BootFileSize
;
1211 Status
= EFI_BUFFER_TOO_SMALL
;
1216 // Begin to download the bootfile if everything is ready.
1218 AsciiPrint ("\n Downloading NBP file...\n");
1219 if (PxeBcMode
->UsingIpv6
) {
1220 Status
= PxeBcReadBootFileList (
1226 Status
= PxeBc
->Mtftp (
1228 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1232 &Private
->BlockSize
,
1234 Private
->BootFileName
,
1241 *BufferSize
= (UINTN
)CurrentSize
;
1242 PxeBcUninstallCallback (Private
, NewMakeCallback
);
1244 if (Status
== EFI_SUCCESS
) {
1245 AsciiPrint ("\n NBP file downloaded successfully.\n");
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");