mirror of https://github.com/acidanthera/audk.git
1171 lines
33 KiB
C
1171 lines
33 KiB
C
/** @file
|
|
Boot functions implementation for UefiPxeBc Driver.
|
|
|
|
Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php.
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "PxeBcImpl.h"
|
|
|
|
|
|
/**
|
|
Display the string of the boot item.
|
|
|
|
If the length of the boot item string beyond 70 Char, just display 70 Char.
|
|
|
|
@param[in] Str The pointer to the string.
|
|
@param[in] Len The length of the string.
|
|
|
|
**/
|
|
VOID
|
|
PxeBcDisplayBootItem (
|
|
IN UINT8 *Str,
|
|
IN UINT8 Len
|
|
)
|
|
{
|
|
UINT8 Tmp;
|
|
|
|
//
|
|
// Cut off the chars behind 70th.
|
|
//
|
|
Len = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);
|
|
Tmp = Str[Len];
|
|
Str[Len] = 0;
|
|
AsciiPrint ("%a \n", Str);
|
|
|
|
//
|
|
// Restore the original 70th char.
|
|
//
|
|
Str[Len] = Tmp;
|
|
}
|
|
|
|
|
|
/**
|
|
Select and maintain the boot prompt if needed.
|
|
|
|
@param[in] Private Pointer to PxeBc private data.
|
|
|
|
@retval EFI_SUCCESS Selected boot prompt done.
|
|
@retval EFI_TIMEOUT Selected boot prompt timed out.
|
|
@retval EFI_NOT_FOUND The proxy offer is not Pxe10.
|
|
@retval EFI_ABORTED User cancelled the operation.
|
|
@retval EFI_NOT_READY Reading the input key from the keyboard has not finish.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcSelectBootPrompt (
|
|
IN PXEBC_PRIVATE_DATA *Private
|
|
)
|
|
{
|
|
PXEBC_DHCP_PACKET_CACHE *Cache;
|
|
PXEBC_VENDOR_OPTION *VendorOpt;
|
|
EFI_PXE_BASE_CODE_MODE *Mode;
|
|
EFI_EVENT TimeoutEvent;
|
|
EFI_EVENT DescendEvent;
|
|
EFI_INPUT_KEY InputKey;
|
|
EFI_STATUS Status;
|
|
UINT32 OfferType;
|
|
UINT8 Timeout;
|
|
UINT8 *Prompt;
|
|
UINT8 PromptLen;
|
|
INT32 SecCol;
|
|
INT32 SecRow;
|
|
|
|
TimeoutEvent = NULL;
|
|
DescendEvent = NULL;
|
|
Mode = Private->PxeBc.Mode;
|
|
Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
|
|
OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
|
|
|
|
//
|
|
// Only ProxyPxe10 offer needs boot prompt.
|
|
//
|
|
if (OfferType != PxeOfferTypeProxyPxe10) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
|
|
//
|
|
ASSERT (!Mode->UsingIpv6);
|
|
|
|
VendorOpt = &Cache->Dhcp4.VendorOpt;
|
|
if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
Timeout = VendorOpt->MenuPrompt->Timeout;
|
|
Prompt = VendorOpt->MenuPrompt->Prompt;
|
|
PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);
|
|
|
|
//
|
|
// The valid scope of Timeout refers to PXE2.1 spec.
|
|
//
|
|
if (Timeout == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
if (Timeout == 255) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
//
|
|
// Create and start a timer as timeout event.
|
|
//
|
|
Status = gBS->CreateEvent (
|
|
EVT_TIMER,
|
|
TPL_CALLBACK,
|
|
NULL,
|
|
NULL,
|
|
&TimeoutEvent
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->SetTimer (
|
|
TimeoutEvent,
|
|
TimerRelative,
|
|
Timeout * TICKS_PER_SECOND
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Create and start a periodic timer as descend event by second.
|
|
//
|
|
Status = gBS->CreateEvent (
|
|
EVT_TIMER,
|
|
TPL_CALLBACK,
|
|
NULL,
|
|
NULL,
|
|
&DescendEvent
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Status = gBS->SetTimer (
|
|
DescendEvent,
|
|
TimerPeriodic,
|
|
TICKS_PER_SECOND
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Display the boot item and cursor on the screen.
|
|
//
|
|
SecCol = gST->ConOut->Mode->CursorColumn;
|
|
SecRow = gST->ConOut->Mode->CursorRow;
|
|
|
|
PxeBcDisplayBootItem (Prompt, PromptLen);
|
|
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
|
|
AsciiPrint ("(%d) ", Timeout--);
|
|
|
|
while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
|
|
if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
|
|
AsciiPrint ("(%d) ", Timeout--);
|
|
}
|
|
if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
|
|
gBS->Stall (10 * TICKS_PER_MS);
|
|
continue;
|
|
}
|
|
//
|
|
// Parse the input key by user.
|
|
//
|
|
if (InputKey.ScanCode == 0) {
|
|
|
|
switch (InputKey.UnicodeChar) {
|
|
|
|
case CTRL ('c'):
|
|
Status = EFI_ABORTED;
|
|
break;
|
|
|
|
case CTRL ('m'):
|
|
case 'm':
|
|
case 'M':
|
|
Status = EFI_TIMEOUT;
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (InputKey.ScanCode) {
|
|
|
|
case SCAN_F8:
|
|
Status = EFI_TIMEOUT;
|
|
break;
|
|
|
|
case SCAN_ESC:
|
|
Status = EFI_ABORTED;
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Reset the cursor on the screen.
|
|
//
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);
|
|
|
|
ON_EXIT:
|
|
if (DescendEvent != NULL) {
|
|
gBS->CloseEvent (DescendEvent);
|
|
}
|
|
if (TimeoutEvent != NULL) {
|
|
gBS->CloseEvent (TimeoutEvent);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Select the boot menu by user's input.
|
|
|
|
@param[in] Private Pointer to PxeBc private data.
|
|
@param[out] Type The type of the menu.
|
|
@param[in] UseDefaultItem Use default item or not.
|
|
|
|
@retval EFI_ABORTED User cancel operation.
|
|
@retval EFI_SUCCESS Select the boot menu success.
|
|
@retval EFI_NOT_READY Read the input key from the keybroad has not finish.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcSelectBootMenu (
|
|
IN PXEBC_PRIVATE_DATA *Private,
|
|
OUT UINT16 *Type,
|
|
IN BOOLEAN UseDefaultItem
|
|
)
|
|
{
|
|
EFI_PXE_BASE_CODE_MODE *Mode;
|
|
PXEBC_DHCP_PACKET_CACHE *Cache;
|
|
PXEBC_VENDOR_OPTION *VendorOpt;
|
|
EFI_INPUT_KEY InputKey;
|
|
UINT32 OfferType;
|
|
UINT8 MenuSize;
|
|
UINT8 MenuNum;
|
|
INT32 TopRow;
|
|
UINT16 Select;
|
|
UINT16 LastSelect;
|
|
UINT8 Index;
|
|
BOOLEAN Finish;
|
|
CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE];
|
|
PXEBC_BOOT_MENU_ENTRY *MenuItem;
|
|
PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM];
|
|
|
|
Finish = FALSE;
|
|
Select = 1;
|
|
Index = 0;
|
|
*Type = 0;
|
|
Mode = Private->PxeBc.Mode;
|
|
Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
|
|
OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
|
|
|
|
//
|
|
// There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
|
|
//
|
|
ASSERT (!Mode->UsingIpv6);
|
|
ASSERT (OfferType == PxeOfferTypeProxyPxe10);
|
|
|
|
VendorOpt = &Cache->Dhcp4.VendorOpt;
|
|
if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Display the boot menu on the screen.
|
|
//
|
|
SetMem (Blank, sizeof(Blank), ' ');
|
|
|
|
MenuSize = VendorOpt->BootMenuLen;
|
|
MenuItem = VendorOpt->BootMenu;
|
|
|
|
if (MenuSize == 0) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {
|
|
ASSERT (MenuItem != NULL);
|
|
MenuArray[Index] = MenuItem;
|
|
MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3));
|
|
MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);
|
|
Index++;
|
|
}
|
|
|
|
if (UseDefaultItem) {
|
|
ASSERT (MenuArray[0] != NULL);
|
|
CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));
|
|
*Type = NTOHS (*Type);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
MenuNum = Index;
|
|
|
|
for (Index = 0; Index < MenuNum; Index++) {
|
|
ASSERT (MenuArray[Index] != NULL);
|
|
PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
|
|
}
|
|
|
|
TopRow = gST->ConOut->Mode->CursorRow - MenuNum;
|
|
|
|
//
|
|
// Select the boot item by user in the boot menu.
|
|
//
|
|
do {
|
|
//
|
|
// Highlight selected row.
|
|
//
|
|
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
|
|
ASSERT (Select < PXEBC_MENU_MAX_NUM);
|
|
ASSERT (MenuArray[Select] != NULL);
|
|
Blank[MenuArray[Select]->DescLen] = 0;
|
|
AsciiPrint ("%a\r", Blank);
|
|
PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
|
|
LastSelect = Select;
|
|
|
|
while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
|
|
gBS->Stall (10 * TICKS_PER_MS);
|
|
}
|
|
|
|
if (InputKey.ScanCode != 0) {
|
|
switch (InputKey.UnicodeChar) {
|
|
case CTRL ('c'):
|
|
InputKey.ScanCode = SCAN_ESC;
|
|
break;
|
|
|
|
case CTRL ('j'): /* linefeed */
|
|
case CTRL ('m'): /* return */
|
|
Finish = TRUE;
|
|
break;
|
|
|
|
case CTRL ('i'): /* tab */
|
|
case ' ':
|
|
case 'd':
|
|
case 'D':
|
|
InputKey.ScanCode = SCAN_DOWN;
|
|
break;
|
|
|
|
case CTRL ('h'): /* backspace */
|
|
case 'u':
|
|
case 'U':
|
|
InputKey.ScanCode = SCAN_UP;
|
|
break;
|
|
|
|
default:
|
|
InputKey.ScanCode = 0;
|
|
}
|
|
}
|
|
|
|
switch (InputKey.ScanCode) {
|
|
case SCAN_LEFT:
|
|
case SCAN_UP:
|
|
if (Select != 0) {
|
|
Select--;
|
|
}
|
|
break;
|
|
|
|
case SCAN_DOWN:
|
|
case SCAN_RIGHT:
|
|
if (++Select == MenuNum) {
|
|
Select--;
|
|
}
|
|
break;
|
|
|
|
case SCAN_PAGE_UP:
|
|
case SCAN_HOME:
|
|
Select = 0;
|
|
break;
|
|
|
|
case SCAN_PAGE_DOWN:
|
|
case SCAN_END:
|
|
Select = (UINT16) (MenuNum - 1);
|
|
break;
|
|
|
|
case SCAN_ESC:
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
//
|
|
// Unhighlight the last selected row.
|
|
//
|
|
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
|
|
ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);
|
|
ASSERT (MenuArray[LastSelect] != NULL);
|
|
Blank[MenuArray[LastSelect]->DescLen] = 0;
|
|
AsciiPrint ("%a\r", Blank);
|
|
PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
|
|
} while (!Finish);
|
|
|
|
//
|
|
// Swap the byte order.
|
|
//
|
|
ASSERT (Select < PXEBC_MENU_MAX_NUM);
|
|
ASSERT (MenuArray[Select] != NULL);
|
|
CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
|
|
*Type = NTOHS (*Type);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Parse out the boot information from the last Dhcp4 reply packet.
|
|
|
|
@param[in, out] Private Pointer to PxeBc private data.
|
|
@param[out] BufferSize Size of the boot file to be downloaded.
|
|
|
|
@retval EFI_SUCCESS Successfully parsed out all the boot information.
|
|
@retval Others Failed to parse out the boot information.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcDhcp4BootInfo (
|
|
IN OUT PXEBC_PRIVATE_DATA *Private,
|
|
OUT UINT64 *BufferSize
|
|
)
|
|
{
|
|
EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
|
|
EFI_PXE_BASE_CODE_MODE *Mode;
|
|
EFI_STATUS Status;
|
|
PXEBC_DHCP4_PACKET_CACHE *Cache4;
|
|
UINT16 Value;
|
|
|
|
PxeBc = &Private->PxeBc;
|
|
Mode = PxeBc->Mode;
|
|
Status = EFI_SUCCESS;
|
|
*BufferSize = 0;
|
|
|
|
//
|
|
// Get the last received Dhcp4 reply packet.
|
|
//
|
|
if (Mode->PxeReplyReceived) {
|
|
Cache4 = &Private->PxeReply.Dhcp4;
|
|
} else if (Mode->ProxyOfferReceived) {
|
|
Cache4 = &Private->ProxyOffer.Dhcp4;
|
|
} else {
|
|
Cache4 = &Private->DhcpAck.Dhcp4;
|
|
}
|
|
|
|
//
|
|
// Parse the boot server Ipv4 address by next server address.
|
|
// If this field isn't available, use option 54 instead.
|
|
//
|
|
CopyMem (
|
|
&Private->ServerIp,
|
|
&Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,
|
|
sizeof (EFI_IPv4_ADDRESS)
|
|
);
|
|
|
|
if (Private->ServerIp.Addr[0] == 0) {
|
|
CopyMem (
|
|
&Private->ServerIp,
|
|
Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
|
|
sizeof (EFI_IPv4_ADDRESS)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Parse the boot file name by option.
|
|
//
|
|
ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
|
|
Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;
|
|
|
|
if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {
|
|
//
|
|
// Parse the boot file size by option.
|
|
//
|
|
CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));
|
|
Value = NTOHS (Value);
|
|
//
|
|
// The field of boot file size is 512 bytes in unit.
|
|
//
|
|
*BufferSize = 512 * Value;
|
|
} else {
|
|
//
|
|
// Get the bootfile size by tftp command if no option available.
|
|
//
|
|
Status = PxeBc->Mtftp (
|
|
PxeBc,
|
|
EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
|
|
NULL,
|
|
FALSE,
|
|
BufferSize,
|
|
&Private->BlockSize,
|
|
&Private->ServerIp,
|
|
Private->BootFileName,
|
|
NULL,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
//
|
|
// Save the value of boot file size.
|
|
//
|
|
Private->BootFileSize = (UINTN) *BufferSize;
|
|
|
|
//
|
|
// Display all the information: boot server address, boot file name and boot file size.
|
|
//
|
|
AsciiPrint ("\n Server IP address is ");
|
|
PxeBcShowIp4Addr (&Private->ServerIp.v4);
|
|
AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
|
|
AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Parse out the boot information from the last Dhcp6 reply packet.
|
|
|
|
@param[in, out] Private Pointer to PxeBc private data.
|
|
@param[out] BufferSize Size of the boot file to be downloaded.
|
|
|
|
@retval EFI_SUCCESS Successfully parsed out all the boot information.
|
|
@retval EFI_BUFFER_TOO_SMALL
|
|
@retval Others Failed to parse out the boot information.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcDhcp6BootInfo (
|
|
IN OUT PXEBC_PRIVATE_DATA *Private,
|
|
OUT UINT64 *BufferSize
|
|
)
|
|
{
|
|
EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
|
|
EFI_PXE_BASE_CODE_MODE *Mode;
|
|
EFI_STATUS Status;
|
|
PXEBC_DHCP6_PACKET_CACHE *Cache6;
|
|
UINT16 Value;
|
|
|
|
PxeBc = &Private->PxeBc;
|
|
Mode = PxeBc->Mode;
|
|
Status = EFI_SUCCESS;
|
|
*BufferSize = 0;
|
|
|
|
//
|
|
// Get the last received Dhcp6 reply packet.
|
|
//
|
|
if (Mode->PxeReplyReceived) {
|
|
Cache6 = &Private->PxeReply.Dhcp6;
|
|
} else if (Mode->ProxyOfferReceived) {
|
|
Cache6 = &Private->ProxyOffer.Dhcp6;
|
|
} else {
|
|
Cache6 = &Private->DhcpAck.Dhcp6;
|
|
}
|
|
|
|
ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
|
|
|
|
//
|
|
// Parse (m)tftp server ip address and bootfile name.
|
|
//
|
|
Status = PxeBcExtractBootFileUrl (
|
|
&Private->BootFileName,
|
|
&Private->ServerIp.v6,
|
|
(CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
|
|
NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Parse the value of boot file size.
|
|
//
|
|
if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {
|
|
//
|
|
// Parse it out if have the boot file parameter option.
|
|
//
|
|
Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
//
|
|
// The field of boot file size is 512 bytes in unit.
|
|
//
|
|
*BufferSize = 512 * Value;
|
|
} else {
|
|
//
|
|
// Send get file size command by tftp if option unavailable.
|
|
//
|
|
Status = PxeBc->Mtftp (
|
|
PxeBc,
|
|
EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
|
|
NULL,
|
|
FALSE,
|
|
BufferSize,
|
|
&Private->BlockSize,
|
|
&Private->ServerIp,
|
|
Private->BootFileName,
|
|
NULL,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
//
|
|
// Save the value of boot file size.
|
|
//
|
|
Private->BootFileSize = (UINTN) *BufferSize;
|
|
|
|
//
|
|
// Display all the information: boot server address, boot file name and boot file size.
|
|
//
|
|
AsciiPrint ("\n Server IP address is ");
|
|
PxeBcShowIp6Addr (&Private->ServerIp.v6);
|
|
AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
|
|
AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Extract the discover information and boot server entry from the
|
|
cached packets if unspecified.
|
|
|
|
@param[in] Private Pointer to PxeBc private data.
|
|
@param[in] Type The type of bootstrap to perform.
|
|
@param[in, out] Info Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
|
|
@param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.
|
|
@param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.
|
|
|
|
@retval EFI_SUCCESS Successfully extracted the information.
|
|
@retval EFI_DEVICE_ERROR Failed to extract the information.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcExtractDiscoverInfo (
|
|
IN PXEBC_PRIVATE_DATA *Private,
|
|
IN UINT16 Type,
|
|
IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info,
|
|
OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,
|
|
OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList
|
|
)
|
|
{
|
|
EFI_PXE_BASE_CODE_MODE *Mode;
|
|
PXEBC_DHCP4_PACKET_CACHE *Cache4;
|
|
PXEBC_VENDOR_OPTION *VendorOpt;
|
|
PXEBC_BOOT_SVR_ENTRY *Entry;
|
|
BOOLEAN IsFound;
|
|
|
|
Mode = Private->PxeBc.Mode;
|
|
|
|
if (Mode->UsingIpv6) {
|
|
Info->IpCnt = 1;
|
|
Info->UseUCast = TRUE;
|
|
|
|
Info->SrvList[0].Type = Type;
|
|
Info->SrvList[0].AcceptAnyResponse = FALSE;
|
|
|
|
//
|
|
// There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.
|
|
//
|
|
CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
|
|
|
|
*SrvList = Info->SrvList;
|
|
} else {
|
|
Entry = NULL;
|
|
IsFound = FALSE;
|
|
Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;
|
|
VendorOpt = &Cache4->VendorOpt;
|
|
|
|
if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {
|
|
//
|
|
// Address is not acquired or no discovery options.
|
|
//
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Parse the boot server entry from the vendor option in the last cached packet.
|
|
//
|
|
Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);
|
|
Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);
|
|
Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);
|
|
Info->UseUCast = Info->MustUseList;
|
|
|
|
if (Info->UseMCast) {
|
|
//
|
|
// Get the multicast discover ip address from vendor option if has.
|
|
//
|
|
CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));
|
|
}
|
|
|
|
Info->IpCnt = 0;
|
|
|
|
if (Info->MustUseList) {
|
|
Entry = VendorOpt->BootSvr;
|
|
|
|
while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {
|
|
if (Entry->Type == HTONS (Type)) {
|
|
IsFound = TRUE;
|
|
break;
|
|
}
|
|
Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);
|
|
}
|
|
|
|
if (!IsFound) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
Info->IpCnt = Entry->IpCnt;
|
|
}
|
|
|
|
*BootEntry = Entry;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Build the discover packet and send out for boot server.
|
|
|
|
@param[in] Private Pointer to PxeBc private data.
|
|
@param[in] Type PxeBc option boot item type.
|
|
@param[in] Layer Pointer to option boot item layer.
|
|
@param[in] UseBis Use BIS or not.
|
|
@param[in] DestIp Pointer to the destination address.
|
|
@param[in] IpCount The count of the server address.
|
|
@param[in] SrvList Pointer to the server address list.
|
|
|
|
@retval EFI_SUCCESS Successfully discovered boot file.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
|
|
@retval EFI_NOT_FOUND Can't get the PXE reply packet.
|
|
@retval Others Failed to discover boot file.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcDiscoverBootServer (
|
|
IN PXEBC_PRIVATE_DATA *Private,
|
|
IN UINT16 Type,
|
|
IN UINT16 *Layer,
|
|
IN BOOLEAN UseBis,
|
|
IN EFI_IP_ADDRESS *DestIp,
|
|
IN UINT16 IpCount,
|
|
IN EFI_PXE_BASE_CODE_SRVLIST *SrvList
|
|
)
|
|
{
|
|
if (Private->PxeBc.Mode->UsingIpv6) {
|
|
return PxeBcDhcp6Discover (
|
|
Private,
|
|
Type,
|
|
Layer,
|
|
UseBis,
|
|
DestIp
|
|
);
|
|
} else {
|
|
return PxeBcDhcp4Discover (
|
|
Private,
|
|
Type,
|
|
Layer,
|
|
UseBis,
|
|
DestIp,
|
|
IpCount,
|
|
SrvList
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Discover all the boot information for boot file.
|
|
|
|
@param[in, out] Private Pointer to PxeBc private data.
|
|
@param[out] BufferSize Size of the boot file to be downloaded.
|
|
|
|
@retval EFI_SUCCESS Successfully obtained all the boot information .
|
|
@retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
|
|
@retval EFI_ABORTED User cancel current operation.
|
|
@retval Others Failed to parse out the boot information.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcDiscoverBootFile (
|
|
IN OUT PXEBC_PRIVATE_DATA *Private,
|
|
OUT UINT64 *BufferSize
|
|
)
|
|
{
|
|
EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
|
|
EFI_PXE_BASE_CODE_MODE *Mode;
|
|
EFI_STATUS Status;
|
|
UINT16 Type;
|
|
UINT16 Layer;
|
|
BOOLEAN UseBis;
|
|
|
|
PxeBc = &Private->PxeBc;
|
|
Mode = PxeBc->Mode;
|
|
Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;
|
|
Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;
|
|
|
|
//
|
|
// Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
|
|
// other pxe boot information.
|
|
//
|
|
Status = PxeBc->Dhcp (PxeBc, TRUE);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Select a boot server from boot server list.
|
|
//
|
|
Status = PxeBcSelectBootPrompt (Private);
|
|
|
|
if (Status == EFI_SUCCESS) {
|
|
//
|
|
// Choose by user's input.
|
|
//
|
|
Status = PxeBcSelectBootMenu (Private, &Type, TRUE);
|
|
} else if (Status == EFI_TIMEOUT) {
|
|
//
|
|
// Choose by default item.
|
|
//
|
|
Status = PxeBcSelectBootMenu (Private, &Type, FALSE);
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
|
|
if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {
|
|
//
|
|
// Local boot(PXE bootstrap server) need abort
|
|
//
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
//
|
|
// Start to discover the boot server to get (m)tftp server ip address, bootfile
|
|
// name and bootfile size.
|
|
//
|
|
UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);
|
|
Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Parse the boot information.
|
|
//
|
|
if (Mode->UsingIpv6) {
|
|
Status = PxeBcDhcp6BootInfo (Private, BufferSize);
|
|
} else {
|
|
Status = PxeBcDhcp4BootInfo (Private, BufferSize);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Install PxeBaseCodeCallbackProtocol if not installed before.
|
|
|
|
@param[in, out] Private Pointer to PxeBc private data.
|
|
@param[out] NewMakeCallback If TRUE, it is a new callback.
|
|
Otherwise, it is not new callback.
|
|
@retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully.
|
|
@retval Others Failed to install PxeBaseCodeCallbackProtocol.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcInstallCallback (
|
|
IN OUT PXEBC_PRIVATE_DATA *Private,
|
|
OUT BOOLEAN *NewMakeCallback
|
|
)
|
|
{
|
|
EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Check whether PxeBaseCodeCallbackProtocol already installed.
|
|
//
|
|
PxeBc = &Private->PxeBc;
|
|
Status = gBS->HandleProtocol (
|
|
Private->Controller,
|
|
&gEfiPxeBaseCodeCallbackProtocolGuid,
|
|
(VOID **) &Private->PxeBcCallback
|
|
);
|
|
if (Status == EFI_UNSUPPORTED) {
|
|
|
|
CopyMem (
|
|
&Private->LoadFileCallback,
|
|
&gPxeBcCallBackTemplate,
|
|
sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)
|
|
);
|
|
|
|
//
|
|
// Install a default callback if user didn't offer one.
|
|
//
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Private->Controller,
|
|
&gEfiPxeBaseCodeCallbackProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&Private->LoadFileCallback
|
|
);
|
|
|
|
(*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);
|
|
|
|
Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);
|
|
if (EFI_ERROR (Status)) {
|
|
PxeBc->Stop (PxeBc);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Uninstall PxeBaseCodeCallbackProtocol.
|
|
|
|
@param[in] Private Pointer to PxeBc private data.
|
|
@param[in] NewMakeCallback If TRUE, it is a new callback.
|
|
Otherwise, it is not new callback.
|
|
|
|
**/
|
|
VOID
|
|
PxeBcUninstallCallback (
|
|
IN PXEBC_PRIVATE_DATA *Private,
|
|
IN BOOLEAN NewMakeCallback
|
|
)
|
|
{
|
|
EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
|
|
|
|
PxeBc = &Private->PxeBc;
|
|
|
|
if (NewMakeCallback) {
|
|
|
|
NewMakeCallback = FALSE;
|
|
|
|
PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
|
|
|
|
gBS->UninstallProtocolInterface (
|
|
Private->Controller,
|
|
&gEfiPxeBaseCodeCallbackProtocolGuid,
|
|
&Private->LoadFileCallback
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Download one of boot file in the list, and it's special for IPv6.
|
|
|
|
@param[in] Private Pointer to PxeBc private data.
|
|
@param[in, out] BufferSize Size of user buffer for input;
|
|
required buffer size for output.
|
|
@param[in] Buffer Pointer to user buffer.
|
|
|
|
@retval EFI_SUCCESS Read one of boot file in the list successfully.
|
|
@retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
|
|
@retval EFI_NOT_FOUND There is no proper boot file available.
|
|
@retval Others Failed to download boot file in the list.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcReadBootFileList (
|
|
IN PXEBC_PRIVATE_DATA *Private,
|
|
IN OUT UINT64 *BufferSize,
|
|
IN VOID *Buffer OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
|
|
|
|
PxeBc = &Private->PxeBc;
|
|
|
|
//
|
|
// Try to download the boot file if everything is ready.
|
|
//
|
|
if (Buffer != NULL) {
|
|
Status = PxeBc->Mtftp (
|
|
PxeBc,
|
|
EFI_PXE_BASE_CODE_TFTP_READ_FILE,
|
|
Buffer,
|
|
FALSE,
|
|
BufferSize,
|
|
&Private->BlockSize,
|
|
&Private->ServerIp,
|
|
Private->BootFileName,
|
|
NULL,
|
|
FALSE
|
|
);
|
|
|
|
|
|
} else {
|
|
Status = EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Load boot file into user buffer.
|
|
|
|
@param[in] Private Pointer to PxeBc private data.
|
|
@param[in, out] BufferSize Size of user buffer for input;
|
|
required buffer size for output.
|
|
@param[in] Buffer Pointer to user buffer.
|
|
|
|
@retval EFI_SUCCESS Get all the boot information successfully.
|
|
@retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
|
|
@retval EFI_ABORTED User cancelled the current operation.
|
|
@retval Others Failed to parse out the boot information.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PxeBcLoadBootFile (
|
|
IN PXEBC_PRIVATE_DATA *Private,
|
|
IN OUT UINTN *BufferSize,
|
|
IN VOID *Buffer OPTIONAL
|
|
)
|
|
{
|
|
BOOLEAN NewMakeCallback;
|
|
UINT64 RequiredSize;
|
|
UINT64 CurrentSize;
|
|
EFI_STATUS Status;
|
|
EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
|
|
EFI_PXE_BASE_CODE_MODE *PxeBcMode;
|
|
|
|
NewMakeCallback = FALSE;
|
|
PxeBc = &Private->PxeBc;
|
|
PxeBcMode = &Private->Mode;
|
|
CurrentSize = *BufferSize;
|
|
RequiredSize = 0;
|
|
|
|
//
|
|
// Install pxebc callback protocol if hasn't been installed yet.
|
|
//
|
|
Status = PxeBcInstallCallback (Private, &NewMakeCallback);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (Private->BootFileSize == 0) {
|
|
//
|
|
// Discover the boot information about the bootfile if hasn't.
|
|
//
|
|
Status = PxeBcDiscoverBootFile (Private, &RequiredSize);
|
|
|
|
if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {
|
|
//
|
|
// It's error if the required buffer size is beyond the system scope.
|
|
//
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto ON_EXIT;
|
|
} else if (RequiredSize > 0) {
|
|
//
|
|
// Get the right buffer size of the bootfile required.
|
|
//
|
|
if (CurrentSize < RequiredSize || Buffer == NULL) {
|
|
//
|
|
// It's buffer too small if the size of user buffer is smaller than the required.
|
|
//
|
|
CurrentSize = RequiredSize;
|
|
Status = EFI_BUFFER_TOO_SMALL;
|
|
goto ON_EXIT;
|
|
}
|
|
CurrentSize = RequiredSize;
|
|
} else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {
|
|
//
|
|
// Try to download another bootfile in list if failed to get the filesize of the last one.
|
|
// It's special for the case of IPv6.
|
|
//
|
|
Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);
|
|
goto ON_EXIT;
|
|
}
|
|
} else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {
|
|
//
|
|
// It's buffer too small if the size of user buffer is smaller than the required.
|
|
//
|
|
CurrentSize = Private->BootFileSize;
|
|
Status = EFI_BUFFER_TOO_SMALL;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Begin to download the bootfile if everything is ready.
|
|
//
|
|
AsciiPrint ("\n Downloading NBP file...\n");
|
|
if (PxeBcMode->UsingIpv6) {
|
|
Status = PxeBcReadBootFileList (
|
|
Private,
|
|
&CurrentSize,
|
|
Buffer
|
|
);
|
|
} else {
|
|
Status = PxeBc->Mtftp (
|
|
PxeBc,
|
|
EFI_PXE_BASE_CODE_TFTP_READ_FILE,
|
|
Buffer,
|
|
FALSE,
|
|
&CurrentSize,
|
|
&Private->BlockSize,
|
|
&Private->ServerIp,
|
|
Private->BootFileName,
|
|
NULL,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
ON_EXIT:
|
|
*BufferSize = (UINTN) CurrentSize;
|
|
PxeBcUninstallCallback(Private, NewMakeCallback);
|
|
|
|
if (Status == EFI_SUCCESS) {
|
|
AsciiPrint ("\n Succeed to download NBP file.\n");
|
|
return EFI_SUCCESS;
|
|
} else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {
|
|
AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n");
|
|
} else if (Status == EFI_DEVICE_ERROR) {
|
|
AsciiPrint ("\n PXE-E07: Network device error.\n");
|
|
} else if (Status == EFI_OUT_OF_RESOURCES) {
|
|
AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n");
|
|
} else if (Status == EFI_NO_MEDIA) {
|
|
AsciiPrint ("\n PXE-E12: Could not detect network connection.\n");
|
|
} else if (Status == EFI_NO_RESPONSE) {
|
|
AsciiPrint ("\n PXE-E16: No offer received.\n");
|
|
} else if (Status == EFI_TIMEOUT) {
|
|
AsciiPrint ("\n PXE-E18: Server response timeout.\n");
|
|
} else if (Status == EFI_ABORTED) {
|
|
AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n");
|
|
} else if (Status == EFI_ICMP_ERROR) {
|
|
AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n");
|
|
} else if (Status == EFI_TFTP_ERROR) {
|
|
AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n");
|
|
} else if (Status != EFI_BUFFER_TOO_SMALL) {
|
|
AsciiPrint ("\n PXE-E99: Unexpected network error.\n");
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|