audk/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c

1606 lines
43 KiB
C
Raw Normal View History

/** @file
Copyright (c) 2004 - 2008, Intel Corporation
All rights reserved. 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.
Module Name:
pxe_loadfile.c
Abstract:
An implementation of the load file protocol for network devices.
**/
#include "Bc.h"
#define DO_MENU (EFI_SUCCESS)
#define NO_MENU (DO_MENU + 1)
#define LOCAL_BOOT (EFI_ABORTED)
#define AUTO_SELECT (NO_MENU)
#define NUMBER_ROWS 25 // we set to mode 0
#define MAX_MENULIST 23
#define Ctl(x) (0x1F & (x))
typedef union {
DHCPV4_OP_STRUCT *OpPtr;
PXE_BOOT_MENU_ENTRY *CurrentMenuItemPtr;
PXE_OP_DISCOVERY_CONTROL *DiscCtlOpStr;
PXE_OP_BOOT_MENU *MenuPtr;
UINT8 *BytePtr;
} UNION_PTR;
UINTN mPropeller;
/**
PxeBc callback routine for status updates and aborts.
@param This Pointer to PxeBcCallback
interface
@param Function PxeBc function ID#
@param Received Receive/transmit flag
@param PacketLength Length of received packet (0
== idle callback)
@param PacketPtr Pointer to received packet
(NULL == idle callback)
@retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT
-
**/
EFI_PXE_BASE_CODE_CALLBACK_STATUS
EFIAPI
bc_callback (
IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL * This,
IN EFI_PXE_BASE_CODE_FUNCTION Function,
IN BOOLEAN Received,
IN UINT32 PacketLength,
IN EFI_PXE_BASE_CODE_PACKET * PacketPtr OPTIONAL
)
{
EFI_INPUT_KEY Key;
UINTN Row;
UINTN Col;
mPropeller = 0;
//
// Resolve Warning 4 unreferenced parameter problem
//
This = This;
//
// Check for user abort.
//
if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_SUCCESS) {
if (!Key.ScanCode) {
if (Key.UnicodeChar == Ctl ('c')) {
return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
}
} else if (Key.ScanCode == SCAN_ESC) {
return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
}
}
//
// Do nothing if this is a receive.
//
if (Received) {
return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
}
//
// The display code is only for these functions.
//
switch (Function) {
case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:
//
// If this is a transmit and not a M/TFTP open request,
// return now. Do not print a dot for each M/TFTP packet
// that is sent, only for the open packets.
//
if (PacketLength != 0 && PacketPtr != NULL) {
if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {
return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
}
}
break;
case EFI_PXE_BASE_CODE_FUNCTION_DHCP:
case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:
break;
default:
return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
}
//
// Display routines
//
if (PacketLength != 0 && PacketPtr != NULL) {
//
// Display a '.' when a packet is transmitted.
//
AsciiPrint (".");
} else if (PacketLength == 0 && PacketPtr == NULL) {
//
// Display a propeller when waiting for packets if at
// least 200 ms have passed.
//
Row = gST->ConOut->Mode->CursorRow;
Col = gST->ConOut->Mode->CursorColumn;
AsciiPrint ("%c", "/-\\|"[mPropeller]);
gST->ConOut->SetCursorPosition (gST->ConOut, Col, Row);
mPropeller = (mPropeller + 1) & 3;
}
return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
}
EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL _bc_callback = {
EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,
&bc_callback
};
/**
Display an IPv4 address in dot notation.
@param Ptr Pointer to IPv4 address.
@return None
**/
VOID
PrintIpv4 (
UINT8 *Ptr
)
{
if (Ptr != NULL) {
AsciiPrint ("%d.%d.%d.%d", Ptr[0], Ptr[1], Ptr[2], Ptr[3]);
}
}
/**
Display client and server IP information.
@param Private Pointer to PxeBc interface
@return None
**/
VOID
ShowMyInfo (
IN PXE_BASECODE_DEVICE *Private
)
{
EFI_PXE_BASE_CODE_MODE *PxeBcMode;
UINTN Index;
//
// Do nothing if a NULL pointer is passed in.
//
if (Private == NULL) {
return ;
}
//
// Get pointer to PXE BaseCode mode structure
//
PxeBcMode = Private->EfiBc.Mode;
//
// Display client IP address
//
AsciiPrint ("\rCLIENT IP: ");
PrintIpv4 (PxeBcMode->StationIp.v4.Addr);
//
// Display subnet mask
//
AsciiPrint (" MASK: ");
PrintIpv4 (PxeBcMode->SubnetMask.v4.Addr);
//
// Display DHCP and proxyDHCP IP addresses
//
if (PxeBcMode->ProxyOfferReceived) {
AsciiPrint ("\nDHCP IP: ");
PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
AsciiPrint (" PROXY IP: ");
PrintIpv4 (((DHCPV4_OP_SERVER_IP *) PXE_OFFER_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
} else {
AsciiPrint (" DHCP IP: ");
PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr);
}
//
// Display gateway IP addresses
//
for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) {
if ((Index % 3) == 0) {
AsciiPrint ("\r\nGATEWAY IP:");
}
AsciiPrint (" ");
PrintIpv4 (PxeBcMode->RouteTable[Index].GwAddr.v4.Addr);
AsciiPrint (" ");
}
AsciiPrint ("\n");
}
/**
Display prompt and wait for input.
@param Private Pointer to PxeBc interface
@param BootPromptPtr Pointer to PXE boot prompt
option
@retval AUTO_SELECT DO_MENU -
@retval NO_MENU
@retval LOCAL_BOOT
**/
EFI_STATUS
DoPrompt (
PXE_BASECODE_DEVICE *Private,
PXE_OP_BOOT_PROMPT *BootPromptPtr
)
{
EFI_STATUS Status;
EFI_EVENT TimeoutEvent;
EFI_EVENT SecondsEvent;
INT32 SecColumn;
INT32 SecRow;
UINT8 SaveChar;
UINT8 SecsLeft;
//
// if auto select, just get right to it
//
if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_AUTO_SELECT) {
return AUTO_SELECT;
}
//
// if no timeout, go directly to display of menu
//
if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_NO_TIMEOUT) {
return DO_MENU;
}
//
//
//
Status = gBS->CreateEvent (
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&TimeoutEvent
);
if (EFI_ERROR (Status)) {
return DO_MENU;
}
Status = gBS->SetTimer (
TimeoutEvent,
TimerRelative,
BootPromptPtr->Timeout * 10000000 + 100000
);
if (EFI_ERROR (Status)) {
gBS->CloseEvent (TimeoutEvent);
return DO_MENU;
}
//
//
//
Status = gBS->CreateEvent (
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&SecondsEvent
);
if (EFI_ERROR (Status)) {
gBS->CloseEvent (TimeoutEvent);
return DO_MENU;
}
Status = gBS->SetTimer (
SecondsEvent,
TimerPeriodic,
10000000
); /* 1 second */
if (EFI_ERROR (Status)) {
gBS->CloseEvent (SecondsEvent);
gBS->CloseEvent (TimeoutEvent);
return DO_MENU;
}
//
// display the prompt
// IMPORTANT! This prompt is an ASCII character string that may
// not be terminated with a NULL byte.
//
SaveChar = BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1];
BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = 0;
AsciiPrint ("%a ", BootPromptPtr->Prompt);
BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = SaveChar;
//
// wait until time expires or selection made - menu or local
//
SecColumn = gST->ConOut->Mode->CursorColumn;
SecRow = gST->ConOut->Mode->CursorRow;
SecsLeft = BootPromptPtr->Timeout;
gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);
AsciiPrint ("(%d) ", SecsLeft);
//
// set the default action to be AUTO_SELECT
//
Status = AUTO_SELECT;
while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
EFI_INPUT_KEY Key;
if (!EFI_ERROR (gBS->CheckEvent (SecondsEvent))) {
--SecsLeft;
gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);
AsciiPrint ("(%d) ", SecsLeft);
}
if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {
UINT8 Buffer[512];
UINTN BufferSize;
BufferSize = sizeof Buffer;
Status = Private->EfiBc.UdpRead (
&Private->EfiBc,
EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |
EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |
EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,
NULL, /* dest ip */
NULL, /* dest port */
NULL, /* src ip */
NULL, /* src port */
NULL, /* hdr size */
NULL, /* hdr ptr */
&BufferSize,
Buffer
);
continue;
}
if (Key.ScanCode == 0) {
switch (Key.UnicodeChar) {
case Ctl ('c'):
Status = LOCAL_BOOT;
break;
case Ctl ('m'):
case 'm':
case 'M':
Status = DO_MENU;
break;
default:
continue;
}
} else {
switch (Key.ScanCode) {
case SCAN_F8:
Status = DO_MENU;
break;
case SCAN_ESC:
Status = LOCAL_BOOT;
break;
default:
continue;
}
}
break;
}
gBS->CloseEvent (SecondsEvent);
gBS->CloseEvent (TimeoutEvent);
gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow);
AsciiPrint (" ");
return Status;
}
/**
Display one menu item.
@param MenuItemPtr Pointer to PXE menu item
option.
@return None
**/
VOID
PrintMenuItem (
PXE_BOOT_MENU_ENTRY *MenuItemPtr
)
{
UINT8 Length;
UINT8 SaveChar;
Length = (UINT8) MIN (70, MenuItemPtr->DataLen);
SaveChar = MenuItemPtr->Data[Length];
MenuItemPtr->Data[Length] = 0;
AsciiPrint (" %a\n", MenuItemPtr->Data);
MenuItemPtr->Data[Length] = SaveChar;
}
/**
Display and process menu.
@param Private Pointer to PxeBc interface
@param RxBufferPtr Pointer to receive buffer
@retval NO_MENU
@retval LOCAL_BOOT
**/
EFI_STATUS
DoMenu (
PXE_BASECODE_DEVICE *Private,
DHCP_RECEIVE_BUFFER *RxBufferPtr
)
{
PXE_OP_DISCOVERY_CONTROL *DiscoveryControlPtr;
PXE_BOOT_MENU_ENTRY *MenuItemPtrs[MAX_MENULIST];
EFI_STATUS Status;
UNION_PTR Ptr;
UINTN SaveNumRte;
UINTN TopRow;
UINTN MenuLth;
UINTN NumMenuItems;
UINTN Index;
UINTN Longest;
UINTN Selected;
UINT16 Type;
UINT16 Layer;
BOOLEAN Done;
Selected = 0;
Layer = 0;
DEBUG ((DEBUG_WARN, "\nDoMenu() Enter."));
/* see if we have a menu/prompt */
if (!(RxBufferPtr->OpAdds.Status & DISCOVER_TYPE)) {
DEBUG (
(DEBUG_WARN,
"\nDoMenu() No menu/prompt info. OpAdds.Status == %xh ",
RxBufferPtr->OpAdds.Status)
);
return NO_MENU;
}
DiscoveryControlPtr = (PXE_OP_DISCOVERY_CONTROL *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1];
//
// if not USE_BOOTFILE or no bootfile given, must have menu stuff
//
if ((DiscoveryControlPtr->ControlBits & USE_BOOTFILE) && RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
DEBUG ((DEBUG_WARN, "\nDoMenu() DHCP w/ bootfile. "));
return NO_MENU;
}
//
// do prompt & menu if necessary
//
Status = DoPrompt (Private, (PXE_OP_BOOT_PROMPT *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]);
if (Status == LOCAL_BOOT) {
DEBUG ((DEBUG_WARN, "\nDoMenu() DoPrompt() returned LOCAL_BOOT. "));
return Status;
}
Ptr.BytePtr = (UINT8 *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1];
MenuLth = Ptr.MenuPtr->Header.Length;
Ptr.CurrentMenuItemPtr = Ptr.MenuPtr->MenuItem;
//
// build menu items array
//
for (Longest = NumMenuItems = Index = 0; Index < MenuLth && NumMenuItems < MAX_MENULIST;) {
UINTN lth;
lth = Ptr.CurrentMenuItemPtr->DataLen + sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data);
MenuItemPtrs[NumMenuItems++] = Ptr.CurrentMenuItemPtr;
if (lth > Longest) {
//
// check if too long
//
if ((Longest = lth) > 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))) {
Longest = 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data));
}
}
Index += lth;
Ptr.BytePtr += lth;
}
if (Status != AUTO_SELECT) {
UINT8 BlankBuf[75];
SetMem (BlankBuf, sizeof BlankBuf, ' ');
BlankBuf[Longest + 5 - (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))] = 0;
AsciiPrint ("\n");
//
// now put up menu
//
for (Index = 0; Index < NumMenuItems; ++Index) {
PrintMenuItem (MenuItemPtrs[Index]);
}
TopRow = gST->ConOut->Mode->CursorRow - NumMenuItems;
//
// now wait for a selection
//
Done = FALSE;
do {
//
// highlight selection
//
EFI_INPUT_KEY Key;
UINTN NewSelected;
NewSelected = Selected;
//
// highlight selected row
//
gST->ConOut->SetAttribute (
gST->ConOut,
EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)
);
gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Selected);
AsciiPrint (" --->%a\r", BlankBuf);
PrintMenuItem (MenuItemPtrs[Selected]);
gST->ConOut->SetAttribute (
gST->ConOut,
EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)
);
gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + NumMenuItems);
//
// wait for a keystroke
//
while (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) {
UINT8 TmpBuf[512];
UINTN TmpBufLen;
TmpBufLen = sizeof TmpBuf;
Private->EfiBc.UdpRead (
&Private->EfiBc,
EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP |
EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT |
EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT,
NULL, /* dest ip */
NULL, /* dest port */
NULL, /* src ip */
NULL, /* src port */
NULL, /* hdr size */
NULL, /* hdr ptr */
&TmpBufLen,
TmpBuf
);
}
if (!Key.ScanCode) {
switch (Key.UnicodeChar) {
case Ctl ('c'):
Key.ScanCode = SCAN_ESC;
break;
case Ctl ('j'): /* linefeed */
case Ctl ('m'): /* return */
Done = TRUE;
break;
case Ctl ('i'): /* tab */
case ' ':
case 'd':
case 'D':
Key.ScanCode = SCAN_DOWN;
break;
case Ctl ('h'): /* backspace */
case 'u':
case 'U':
Key.ScanCode = SCAN_UP;
break;
default:
Key.ScanCode = 0;
}
}
switch (Key.ScanCode) {
case SCAN_LEFT:
case SCAN_UP:
if (NewSelected) {
--NewSelected;
}
break;
case SCAN_DOWN:
case SCAN_RIGHT:
if (++NewSelected == NumMenuItems) {
--NewSelected;
}
break;
case SCAN_PAGE_UP:
case SCAN_HOME:
NewSelected = 0;
break;
case SCAN_PAGE_DOWN:
case SCAN_END:
NewSelected = NumMenuItems - 1;
break;
case SCAN_ESC:
return LOCAL_BOOT;
}
/* unhighlight last selected row */
gST->ConOut->SetCursorPosition (gST->ConOut, 5, TopRow + Selected);
AsciiPrint ("%a\r", BlankBuf);
PrintMenuItem (MenuItemPtrs[Selected]);
Selected = NewSelected;
} while (!Done);
}
SaveNumRte = Private->EfiBc.Mode->RouteTableEntries;
Type = NTOHS (MenuItemPtrs[Selected]->Type);
if (Type == 0) {
DEBUG ((DEBUG_WARN, "\nDoMenu() Local boot selected. "));
return LOCAL_BOOT;
}
AsciiPrint ("Discover");
Status = Private->EfiBc.Discover (
&Private->EfiBc,
Type,
&Layer,
(BOOLEAN) (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected),
0
);
if (EFI_ERROR (Status)) {
AsciiPrint ("\r \r");
DEBUG (
(DEBUG_WARN,
"\nDoMenu() Return w/ %xh (%r).",
Status,
Status)
);
return Status;
}
AsciiPrint ("\rBOOT_SERVER_IP: ");
PrintIpv4 ((UINT8 *) &Private->ServerIp);
for (Index = SaveNumRte; Index < Private->EfiBc.Mode->RouteTableEntries; ++Index) {
if ((Index % 3) == 0) {
AsciiPrint ("\r\nGATEWAY IP:");
}
AsciiPrint (" ");
PrintIpv4 ((UINT8 *) &Private->EfiBc.Mode->RouteTable[Index].GwAddr);
AsciiPrint (" ");
}
AsciiPrint ("\n");
DEBUG ((DEBUG_WARN, "\nDoMenu() Return w/ EFI_SUCCESS. "));
return EFI_SUCCESS;
}
/**
Get value 8- or 16-bit value from DHCP option.
@param OpPtr Pointer to DHCP option
@return Value from DHCP option
**/
UINT16
GetValue (
DHCPV4_OP_STRUCT *OpPtr
)
{
if (OpPtr->Header.Length == 1) {
return OpPtr->Data[0];
} else {
return NTOHS (OpPtr->Data);
}
}
/**
Locate opcode in buffer.
@param BufferPtr Pointer to buffer
@param BufferLen Length of buffer
@param OpCode Option number
@return Pointer to opcode, may be NULL
**/
UINT8 *
_PxeBcFindOpt (
UINT8 *BufferPtr,
UINTN BufferLen,
UINT8 OpCode
)
{
if (BufferPtr == NULL) {
return NULL;
}
while (BufferLen != 0) {
if (*BufferPtr == OpCode) {
return BufferPtr;
}
switch (*BufferPtr) {
case OP_END:
return NULL;
case OP_PAD:
++BufferPtr;
--BufferLen;
continue;
}
if ((UINTN) BufferLen <= (UINTN) 2 + BufferPtr[1]) {
return NULL;
}
BufferLen -= 2 + BufferPtr[1];
BufferPtr += 2 + BufferPtr[1];
}
return NULL;
}
/**
Find option in packet
@param PacketPtr Pointer to packet
@param OpCode option number
@return Pointer to option in packet
**/
UINT8 *
PxeBcFindDhcpOpt (
EFI_PXE_BASE_CODE_PACKET *PacketPtr,
UINT8 OpCode
)
{
UINTN PacketLen;
UINT8 Overload;
UINT8 *OptionBufferPtr;
//
//
//
PacketLen = 380;
Overload = 0;
//
// Figure size of DHCP option space.
//
OptionBufferPtr = _PxeBcFindOpt (
PacketPtr->Dhcpv4.DhcpOptions,
380,
OP_DHCP_MAX_MESSAGE_SZ
);
if (OptionBufferPtr != NULL) {
if (OptionBufferPtr[1] == 2) {
UINT16 n;
CopyMem (&n, &OptionBufferPtr[2], 2);
PacketLen = HTONS (n);
if (PacketLen < sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET)) {
PacketLen = 380;
} else {
PacketLen -= (PacketPtr->Dhcpv4.DhcpOptions - &PacketPtr->Dhcpv4.BootpOpcode) + 28;
}
}
}
//
// Look for option overloading.
//
OptionBufferPtr = _PxeBcFindOpt (
PacketPtr->Dhcpv4.DhcpOptions,
PacketLen,
OP_DHCP_OPTION_OVERLOAD
);
if (OptionBufferPtr != NULL) {
if (OptionBufferPtr[1] == 1) {
Overload = OptionBufferPtr[2];
}
}
//
// Look for caller's option.
//
OptionBufferPtr = _PxeBcFindOpt (
PacketPtr->Dhcpv4.DhcpOptions,
PacketLen,
OpCode
);
if (OptionBufferPtr != NULL) {
return OptionBufferPtr;
}
if (Overload & OVLD_FILE) {
OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpBootFile, 128, OpCode);
if (OptionBufferPtr != NULL) {
return OptionBufferPtr;
}
}
if (Overload & OVLD_SRVR_NAME) {
OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpSrvName, 64, OpCode);
if (OptionBufferPtr != NULL) {
return OptionBufferPtr;
}
}
return NULL;
}
/**
Download file into buffer
@param Private Pointer to PxeBc interface
@param BufferSize pointer to size of download
buffer
@param Buffer Pointer to buffer
@return EFI_BUFFER_TOO_SMALL -
@return EFI_NOT_FOUND -
@return EFI_PROTOCOL_ERROR -
**/
EFI_STATUS
DownloadFile (
IN PXE_BASECODE_DEVICE *Private,
IN OUT UINT64 *BufferSize,
IN VOID *Buffer
)
{
EFI_PXE_BASE_CODE_MTFTP_INFO MtftpInfo;
EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode;
DHCP_RECEIVE_BUFFER *RxBuf;
EFI_STATUS Status;
UINTN BlockSize;
RxBuf = (DHCP_RECEIVE_BUFFER *) Private->BootServerReceiveBuffer;
BlockSize = 0x8000;
DEBUG ((EFI_D_WARN, "\nDownloadFile() Enter."));
if (Buffer == NULL || *BufferSize == 0 || *BufferSize < Private->FileSize) {
if (Private->FileSize != 0) {
*BufferSize = Private->FileSize;
return EFI_BUFFER_TOO_SMALL;
}
AsciiPrint ("\nTSize");
OpCode = EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE;
} else if (RxBuf->OpAdds.Status & WfM11a_TYPE) {
OpCode = EFI_PXE_BASE_CODE_MTFTP_READ_FILE;
ZeroMem (&MtftpInfo, sizeof MtftpInfo);
*(IPV4_ADDR *) &MtftpInfo.MCastIp = *(IPV4_ADDR *) RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_IP - 1]->Data;
CopyMem (
&MtftpInfo.CPort,
RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_CPORT - 1]->Data,
sizeof MtftpInfo.CPort
);
CopyMem (
&MtftpInfo.SPort,
RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_SPORT - 1]->Data,
sizeof MtftpInfo.SPort
);
MtftpInfo.ListenTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_TMOUT - 1]);
MtftpInfo.TransmitTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_DELAY - 1]);
AsciiPrint ("\nMTFTP");
} else {
AsciiPrint ("\nTFTP");
OpCode = EFI_PXE_BASE_CODE_TFTP_READ_FILE;
}
Private->FileSize = 0;
RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data[RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length] = 0;
Status = Private->EfiBc.Mtftp (
&Private->EfiBc,
OpCode,
Buffer,
FALSE,
BufferSize,
&BlockSize,
&Private->ServerIp,
(UINT8 *) RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data,
&MtftpInfo,
FALSE
);
if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {
DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #1 %Xh", Status));
return Status;
}
if (sizeof (UINTN) < sizeof (UINT64) && *BufferSize > 0xFFFFFFFF) {
Private->FileSize = 0xFFFFFFFF;
} else {
Private->FileSize = (UINTN) *BufferSize;
}
if (OpCode == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) {
DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #2"));
return EFI_BUFFER_TOO_SMALL;
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #3 %Xh", Status));
return Status;
}
if (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected && Private->EfiBc.Mode->PxeBisReplyReceived) {
UINT64 CredentialLen;
UINT8 CredentialFilename[256];
UINT8 *op;
VOID *CredentialBuffer;
//
// Get name of credential file. It may be in the BOOTP
// bootfile field or a DHCP option.
//
ZeroMem (CredentialFilename, sizeof CredentialFilename);
op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_DHCP_BOOTFILE);
if (op != NULL) {
if (op[1] == 0) {
/* No credential filename */
return EFI_NOT_FOUND;
}
CopyMem (CredentialFilename, &op[2], op[1]);
} else {
if (Private->EfiBc.Mode->PxeBisReply.Dhcpv4.BootpBootFile[0] == 0) {
/* No credential filename */
return EFI_NOT_FOUND;
}
CopyMem (CredentialFilename, &op[2], 128);
}
//
// Get size of credential file. It may be available as a
// DHCP option. If not, use the TFTP get file size.
//
CredentialLen = 0;
op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_BOOT_FILE_SZ);
if (op != NULL) {
/*
* This is actually the size of the credential file
* buffer. The actual credential file size will be
* returned when we download the file.
*/
if (op[1] == 2) {
UINT16 n;
CopyMem (&n, &op[2], 2);
CredentialLen = HTONS (n) * 512;
}
}
if (CredentialLen == 0) {
BlockSize = 8192;
Status = Private->EfiBc.Mtftp (
&Private->EfiBc,
EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
NULL,
FALSE,
&CredentialLen,
&BlockSize,
&Private->ServerIp,
CredentialFilename,
NULL,
FALSE
);
if (EFI_ERROR (Status)) {
return Status;
}
if (CredentialLen == 0) {
//
// %%TBD -- EFI error for invalid credential
// file.
//
return EFI_PROTOCOL_ERROR;
}
}
//
// Allocate credential file buffer.
//
Status = gBS->AllocatePool (
EfiBootServicesData,
(UINTN) CredentialLen,
&CredentialBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Download credential file.
//
BlockSize = 8192;
Status = Private->EfiBc.Mtftp (
&Private->EfiBc,
EFI_PXE_BASE_CODE_TFTP_READ_FILE,
CredentialBuffer,
FALSE,
&CredentialLen,
&BlockSize,
&Private->ServerIp,
CredentialFilename,
NULL,
FALSE
);
if (EFI_ERROR (Status)) {
gBS->FreePool (CredentialBuffer);
return Status;
}
//
// Verify credentials.
//
if (PxebcBisVerify (Private, Buffer, Private->FileSize, CredentialBuffer, (UINTN) CredentialLen)) {
Status = EFI_SUCCESS;
} else {
//
// %%TBD -- An EFI error code for failing credential verification.
//
Status = EFI_PROTOCOL_ERROR;
}
gBS->FreePool (CredentialBuffer);
}
return Status;
}
/**
Start PXE DHCP. Get DHCP and proxyDHCP information.
Display remote boot menu and prompt. Select item from menu.
@param Private Pointer to PxeBc interface
@param BufferSize Pointer to download buffer
size
@param Buffer Pointer to download buffer
@retval EFI_SUCCESS
@retval EFI_NOT_READY
**/
EFI_STATUS
LoadfileStart (
IN PXE_BASECODE_DEVICE *Private,
IN OUT UINT64 *BufferSize,
IN VOID *Buffer
)
{
EFI_PXE_BASE_CODE_MODE *PxeBcMode;
EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
EFI_SIMPLE_NETWORK_MODE *SnpMode;
EFI_STATUS Status;
VOID *RxBuf;
DEBUG ((DEBUG_WARN, "\nLoadfileStart() Enter."));
//
// Try to start BaseCode, for now only IPv4 is supported
// so don't try to start using IPv6.
//
Status = Private->EfiBc.Start (&Private->EfiBc, FALSE);
if (EFI_ERROR (Status)) {
if (Status != EFI_ALREADY_STARTED) {
DEBUG ((DEBUG_NET, "\nLoadfileStart() Exit BC.Start() == %xh", Status));
return Status;
}
}
//
// Get pointers to PXE mode structure, SNP protocol structure
// and SNP mode structure.
//
PxeBcMode = Private->EfiBc.Mode;
Snp = Private->SimpleNetwork;
SnpMode = Snp->Mode;
//
// Display client MAC address, like 16-bit PXE ROMs
//
AsciiPrint ("\nCLIENT MAC ADDR: ");
{
UINTN Index;
UINTN hlen;
hlen = SnpMode->HwAddressSize;
for (Index = 0; Index < hlen; ++Index) {
AsciiPrint ("%02x ", SnpMode->CurrentAddress.Addr[Index]);
}
}
AsciiPrint ("\nDHCP");
Status = Private->EfiBc.Dhcp (&Private->EfiBc, TRUE);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status));
AsciiPrint ("\r \r");
return Status;
}
ShowMyInfo (Private);
RxBuf = PxeBcMode->ProxyOfferReceived ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER;
#define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf)
Status = DoMenu (Private, RxBufferPtr);
if (Status == EFI_SUCCESS) {
//
// did a discovery - take info from discovery packet
//
RxBuf = &PXE_ACK_BUFFER;
} else if (Status == NO_MENU) {
//
// did not do a discovery - take info from rxbuf
//
Private->ServerIp.Addr[0] = RxBufferPtr->u.Dhcpv4.siaddr;
if (!(Private->ServerIp.Addr[0])) {
*(IPV4_ADDR *) &Private->ServerIp = *(IPV4_ADDR *) RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]->Data;
}
} else {
DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit DoMenu() == %Xh", Status));
return Status;
}
if (!RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) {
DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit Not ready?"));
return EFI_NOT_READY;
}
//
// check for file size option sent
//
if (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]) {
Private->FileSize = 512 * NTOHS (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]->Data);
}
Private->BootServerReceiveBuffer = RxBufferPtr;
Status = DownloadFile (Private, BufferSize, Buffer);
DEBUG (
(DEBUG_WARN,
"\nLoadfileStart() Exit. DownloadFile() = %Xh",
Status)
);
return Status;
}
/**
Loadfile interface for PxeBc interface
@param This Pointer to Loadfile interface
@param FilePath Not used and not checked
@param BootPolicy Must be TRUE
@param BufferSize Pointer to buffer size
@param Buffer Pointer to download buffer or
NULL
@return EFI_INVALID_PARAMETER -
@return EFI_UNSUPPORTED -
@return EFI_SUCCESS -
@return EFI_BUFFER_TOO_SMALL -
**/
EFI_STATUS
EFIAPI
LoadFile (
IN EFI_LOAD_FILE_PROTOCOL *This,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN BOOLEAN BootPolicy,
IN OUT UINTN *BufferSize,
IN OUT VOID *Buffer
)
{
LOADFILE_DEVICE *LoadfilePtr;
UINT64 TmpBufSz;
INT32 OrigMode;
INT32 OrigAttribute;
BOOLEAN RemoveCallback;
BOOLEAN NewMakeCallback;
EFI_STATUS Status;
EFI_STATUS TempStatus;
//
// The following line is only used for passing ICC build.
//
DEBUG ((EFI_D_INFO, "FilePath = %p\n", FilePath));
//
//
//
OrigMode = gST->ConOut->Mode->Mode;
OrigAttribute = gST->ConOut->Mode->Attribute;
RemoveCallback = FALSE;
AsciiPrint ("Running LoadFile()\n");
//
// Resolve Warning 4 unreferenced parameter problem
//
FilePath = NULL;
//
// If either if these parameters are NULL, we cannot continue.
//
if (This == NULL || BufferSize == NULL) {
DEBUG ((DEBUG_WARN, "\nLoadFile() This or BufferSize == NULL"));
return EFI_INVALID_PARAMETER;
}
//
// We only support BootPolicy == TRUE
//
if (!BootPolicy) {
DEBUG ((DEBUG_WARN, "\nLoadFile() BootPolicy == FALSE"));
return EFI_UNSUPPORTED;
}
//
// Get pointer to LoadFile protocol structure.
//
LoadfilePtr = CR (This, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE);
if (LoadfilePtr == NULL) {
DEBUG (
(DEBUG_NET,
"\nLoadFile() Could not get pointer to LoadFile structure")
);
return EFI_INVALID_PARAMETER;
}
//
// Lock interface
//
EfiAcquireLock (&LoadfilePtr->Lock);
//
// Set console output mode and display attribute
//
if (OrigMode != 0) {
gST->ConOut->SetMode (gST->ConOut, 0);
}
gST->ConOut->SetAttribute (
gST->ConOut,
EFI_TEXT_ATTR (EFI_LIGHTGRAY,EFI_BLACK)
);
//
// See if BaseCode already has a Callback protocol attached.
// If there is none, attach our own Callback protocol.
//
Status = gBS->HandleProtocol (
LoadfilePtr->Private->Handle,
&gEfiPxeBaseCodeCallbackProtocolGuid,
(VOID *) &LoadfilePtr->Private->CallbackProtocolPtr
);
if (Status == EFI_SUCCESS) {
//
// There is already a callback routine. Do nothing.
//
DEBUG ((DEBUG_WARN, "\nLoadFile() BC callback exists."));
} else if (Status == EFI_UNSUPPORTED) {
//
// No BaseCode Callback protocol found. Add our own.
//
Status = gBS->InstallProtocolInterface (
&LoadfilePtr->Private->Handle,
&gEfiPxeBaseCodeCallbackProtocolGuid,
EFI_NATIVE_INTERFACE,
&_bc_callback
);
DEBUG ((DEBUG_WARN, "\nLoadFile() Callback install status == %xh", Status));
RemoveCallback = (BOOLEAN) (Status == EFI_SUCCESS);
if (LoadfilePtr->Private->EfiBc.Mode != NULL && LoadfilePtr->Private->EfiBc.Mode->Started) {
NewMakeCallback = TRUE;
LoadfilePtr->Private->EfiBc.SetParameters (
&LoadfilePtr->Private->EfiBc,
NULL,
NULL,
NULL,
NULL,
&NewMakeCallback
);
}
} else {
DEBUG ((DEBUG_WARN, "\nLoadFile() Callback check status == %xh", Status));
}
//
// Check for starting or for continuing after already getting
// the file size.
//
if (LoadfilePtr->Private->FileSize == 0) {
TmpBufSz = 0;
Status = LoadfileStart (LoadfilePtr->Private, &TmpBufSz, Buffer);
if (sizeof (UINTN) < sizeof (UINT64) && TmpBufSz > 0xFFFFFFFF) {
*BufferSize = 0xFFFFFFFF;
} else {
*BufferSize = (UINTN) TmpBufSz;
}
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// This is done so loadfile will work even if the boot manager
// did not make the first call with Buffer == NULL.
//
Buffer = NULL;
}
} else if (Buffer == NULL) {
DEBUG ((DEBUG_WARN, "\nLoadfile() Get buffer size"));
//
// Continuing from previous LoadFile request. Make sure there
// is a buffer and that it is big enough.
//
*BufferSize = LoadfilePtr->Private->FileSize;
Status = EFI_BUFFER_TOO_SMALL;
} else {
DEBUG ((DEBUG_WARN, "\nLoadFile() Download file"));
//
// Everything looks good, try to download the file.
//
TmpBufSz = *BufferSize;
Status = DownloadFile (LoadfilePtr->Private, &TmpBufSz, Buffer);
//
// Next call to loadfile will start DHCP process again.
//
LoadfilePtr->Private->FileSize = 0;
}
//
// If we added a callback protocol, now is the time to remove it.
//
if (RemoveCallback) {
NewMakeCallback = FALSE;
TempStatus = LoadfilePtr->Private->EfiBc.SetParameters (
&LoadfilePtr->Private->EfiBc,
NULL,
NULL,
NULL,
NULL,
&NewMakeCallback
);
if (TempStatus == EFI_SUCCESS) {
gBS->UninstallProtocolInterface (
LoadfilePtr->Private->Handle,
&gEfiPxeBaseCodeCallbackProtocolGuid,
&_bc_callback
);
}
}
//
// Restore display mode and attribute
//
if (OrigMode != 0) {
gST->ConOut->SetMode (gST->ConOut, OrigMode);
}
gST->ConOut->SetAttribute (gST->ConOut, OrigAttribute);
//
// Unlock interface
//
EfiReleaseLock (&LoadfilePtr->Lock);
DEBUG ((DEBUG_WARN, "\nBC.Loadfile() Status == %xh\n", Status));
if (Status == EFI_SUCCESS) {
return EFI_SUCCESS;
} else if (Status == EFI_BUFFER_TOO_SMALL) {
//
// Error is only displayed when we are actually trying to
// download the boot image.
//
if (Buffer == NULL) {
return EFI_BUFFER_TOO_SMALL;
}
AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n");
} else if (Status == EFI_DEVICE_ERROR) {
AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n");
} else if (Status == EFI_OUT_OF_RESOURCES) {
AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n");
} else if (Status == EFI_NO_MEDIA) {
AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n");
} else if (Status == EFI_NO_RESPONSE) {
AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n");
} else if (Status == EFI_TIMEOUT) {
AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n");
} else if (Status == EFI_ABORTED) {
AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n");
} else if (Status == EFI_ICMP_ERROR) {
AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n");
if (LoadfilePtr->Private->EfiBc.Mode != NULL) {
if (LoadfilePtr->Private->EfiBc.Mode->IcmpErrorReceived) {
AsciiPrint (
"PXE-E98: Type: %xh Code: %xh ",
LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type,
LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code
);
switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type) {
case 0x03:
switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code) {
case 0x00: /* net unreachable */
AsciiPrint ("Net unreachable");
break;
case 0x01: /* host unreachable */
AsciiPrint ("Host unreachable");
break;
case 0x02: /* protocol unreachable */
AsciiPrint ("Protocol unreachable");
break;
case 0x03: /* port unreachable */
AsciiPrint ("Port unreachable");
break;
case 0x04: /* Fragmentation needed */
AsciiPrint ("Fragmentation needed");
break;
case 0x05: /* Source route failed */
AsciiPrint ("Source route failed");
break;
}
break;
}
AsciiPrint ("\n");
}
}
} else if (Status == EFI_TFTP_ERROR) {
AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n");
if (LoadfilePtr->Private->EfiBc.Mode != NULL) {
if (LoadfilePtr->Private->EfiBc.Mode->TftpErrorReceived) {
AsciiPrint (
"PXE-E98: Code: %xh %a\n",
LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorCode,
LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorString
);
}
}
} else {
AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status);
}
LoadfilePtr->Private->EfiBc.Stop (&LoadfilePtr->Private->EfiBc);
return Status;
}