mirror of https://github.com/acidanthera/audk.git
2380 lines
61 KiB
C
2380 lines
61 KiB
C
/*++
|
|
|
|
Copyright (c) 2006 - 2007, 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_bc_mtftp.c
|
|
|
|
Abstract:
|
|
TFTP and MTFTP (multicast TFTP) implementation.
|
|
|
|
Revision History
|
|
|
|
--*/
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
//
|
|
// The following #define is used to create a version that does not wait to
|
|
// open after a listen. This is just for a special regression test of MTFTP
|
|
// server to make sure multiple opens are handled correctly. Normally this
|
|
// next line should be a comment.
|
|
// #define SpecialNowaitVersion // comment out for normal operation
|
|
//
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
|
|
#include "Bc.h"
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
UINT64
|
|
Swap64 (
|
|
UINT64 n
|
|
)
|
|
{
|
|
union {
|
|
UINT64 n;
|
|
UINT8 b[8];
|
|
} u;
|
|
|
|
UINT8 t;
|
|
|
|
u.n = n;
|
|
|
|
t = u.b[0];
|
|
u.b[0] = u.b[7];
|
|
u.b[7] = t;
|
|
|
|
t = u.b[1];
|
|
u.b[1] = u.b[6];
|
|
u.b[6] = t;
|
|
|
|
t = u.b[2];
|
|
u.b[2] = u.b[5];
|
|
u.b[5] = t;
|
|
|
|
t = u.b[3];
|
|
u.b[3] = u.b[4];
|
|
u.b[4] = t;
|
|
|
|
return u.n;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
TftpUdpRead (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
UINT16 Operation,
|
|
VOID *HeaderPtr,
|
|
UINTN *BufferSizePtr,
|
|
VOID *BufferPtr,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
|
|
EFI_IP_ADDRESS *OurIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
|
|
UINT16 Timeout
|
|
)
|
|
/*++
|
|
Routine description:
|
|
Read TFTP packet. If TFTP ERROR packet is read, fill in TFTP error
|
|
information in Mode structure and return TFTP_ERROR status.
|
|
|
|
Parameters:
|
|
Private :=
|
|
Operation :=
|
|
HeaderPtr :=
|
|
BufferSizePtr :=
|
|
BufferPtr :=
|
|
ServerIpPtr :=
|
|
ServerPortPtr :=
|
|
OurIpPtr :=
|
|
OurPortPtr :=
|
|
Timeout :=
|
|
|
|
Returns:
|
|
EFI_SUCCESS :=
|
|
EFI_TFTP_ERROR :=
|
|
other :=
|
|
--*/
|
|
{
|
|
EFI_PXE_BASE_CODE_MODE *PxeBcMode;
|
|
EFI_STATUS Status;
|
|
EFI_EVENT TimeoutEvent;
|
|
UINTN HeaderSize;
|
|
|
|
//
|
|
//
|
|
//
|
|
Status = gBS->CreateEvent (
|
|
EVT_TIMER,
|
|
TPL_CALLBACK,
|
|
NULL,
|
|
NULL,
|
|
&TimeoutEvent
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->SetTimer (
|
|
TimeoutEvent,
|
|
TimerRelative,
|
|
Timeout * 10000000 + 1000000
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->CloseEvent (TimeoutEvent);
|
|
return Status;
|
|
}
|
|
//
|
|
//
|
|
//
|
|
HeaderSize = Private->BigBlkNumFlag ? sizeof (struct Tftpv4Ack8) : sizeof (struct Tftpv4Ack);
|
|
|
|
#define ERROR_MESSAGE_PTR ((struct Tftpv4Error *) HeaderPtr)
|
|
|
|
Status = UdpRead (
|
|
Private,
|
|
Operation,
|
|
OurIpPtr,
|
|
OurPortPtr,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
&HeaderSize,
|
|
HeaderPtr,
|
|
BufferSizePtr,
|
|
BufferPtr,
|
|
TimeoutEvent
|
|
);
|
|
|
|
if (Status != EFI_SUCCESS || ERROR_MESSAGE_PTR->OpCode != HTONS (TFTP_ERROR)) {
|
|
gBS->CloseEvent (TimeoutEvent);
|
|
return Status;
|
|
}
|
|
//
|
|
// got an error packet
|
|
// write one byte error code followed by error message
|
|
//
|
|
PxeBcMode = Private->EfiBc.Mode;
|
|
PxeBcMode->TftpErrorReceived = TRUE;
|
|
PxeBcMode->TftpError.ErrorCode = (UINT8) NTOHS (ERROR_MESSAGE_PTR->ErrCode);
|
|
HeaderSize = EFI_MIN (*BufferSizePtr, sizeof PxeBcMode->TftpError.ErrorString);
|
|
CopyMem (PxeBcMode->TftpError.ErrorString, BufferPtr, HeaderSize);
|
|
|
|
gBS->CloseEvent (TimeoutEvent);
|
|
return EFI_TFTP_ERROR;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
VOID
|
|
SendError (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr
|
|
)
|
|
/*++
|
|
Routine description:
|
|
Send TFTP ERROR message to TFTP server
|
|
|
|
Parameters:
|
|
Private :=
|
|
ServerIpPtr :=
|
|
ServerPortPtr :=
|
|
OurPortPtr :=
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
struct Tftpv4Error *ErrStr;
|
|
UINTN Len;
|
|
|
|
ErrStr = (VOID *) Private->TftpErrorBuffer;
|
|
Len = sizeof *ErrStr;
|
|
|
|
ErrStr->OpCode = HTONS (TFTP_ERROR);
|
|
ErrStr->ErrCode = HTONS (TFTP_ERR_OPTION);
|
|
ErrStr->ErrMsg[0] = 0;
|
|
|
|
UdpWrite (
|
|
Private,
|
|
EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
0,
|
|
0,
|
|
OurPortPtr,
|
|
0,
|
|
0,
|
|
&Len,
|
|
ErrStr
|
|
);
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
SendAckAndGetData (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
|
|
EFI_IP_ADDRESS *ReplyIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
|
|
UINT16 Timeout,
|
|
UINTN *ReplyLenPtr,
|
|
UINT8 *PxeBcMode,
|
|
UINT64 *BlockNumPtr,
|
|
BOOLEAN AckOnly
|
|
)
|
|
/*++
|
|
Routine description:
|
|
Send TFTP ACK packet to server and read next DATA packet.
|
|
|
|
Parameters:
|
|
Private := Pointer to PxeBc interface
|
|
ServerIpPtr := Pointer to TFTP server IP address
|
|
ServerPortPtr := Pointer to TFTP server UDP port
|
|
ReplyIpPtr := Pointer to TFTP DATA packet destination IP address
|
|
OurPortPtr := Pointer to TFTP client UDP port
|
|
Timeout :=
|
|
ReplyLenPtr := Pointer to packet length
|
|
PxeBcMode := Pointer to packet buffer
|
|
BlockNumPtr := Pointer to block number
|
|
AckOnly := TRUE == Send last ack - do not wait for reply
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
struct Tftpv4Data DataBuffer;
|
|
struct Tftpv4Ack *Ack2Ptr;
|
|
struct Tftpv4Ack8 *Ack8Ptr;
|
|
EFI_STATUS Status;
|
|
UINTN Len;
|
|
|
|
Ack2Ptr = (VOID *) Private->TftpAckBuffer;
|
|
Ack8Ptr = (VOID *) Private->TftpAckBuffer;
|
|
|
|
if (Private->BigBlkNumFlag) {
|
|
Len = sizeof (struct Tftpv4Ack8);
|
|
|
|
Ack8Ptr->OpCode = HTONS (TFTP_ACK8);
|
|
Ack8Ptr->BlockNum = Swap64 (*BlockNumPtr);
|
|
|
|
Status = UdpWrite (
|
|
Private,
|
|
EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
0,
|
|
0,
|
|
OurPortPtr,
|
|
0,
|
|
0,
|
|
&Len,
|
|
Ack8Ptr
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
} else {
|
|
Len = sizeof (struct Tftpv4Ack);
|
|
|
|
Ack2Ptr->OpCode = HTONS (TFTP_ACK);
|
|
Ack2Ptr->BlockNum = HTONS ((UINT16) *BlockNumPtr);
|
|
|
|
Status = UdpWrite (
|
|
Private,
|
|
EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
0,
|
|
0,
|
|
OurPortPtr,
|
|
0,
|
|
0,
|
|
&Len,
|
|
Ack2Ptr
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (AckOnly) {
|
|
//
|
|
// ACK of last packet. This is just a courtesy.
|
|
// Do not wait for response.
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
//
|
|
// read reply
|
|
//
|
|
Status = TftpUdpRead (
|
|
Private,
|
|
0,
|
|
&DataBuffer,
|
|
ReplyLenPtr,
|
|
PxeBcMode,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
ReplyIpPtr,
|
|
OurPortPtr,
|
|
Timeout
|
|
);
|
|
|
|
if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
|
|
return Status;
|
|
}
|
|
//
|
|
// got a good reply (so far)
|
|
// check for next data packet
|
|
//
|
|
if (!Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA)) {
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);
|
|
}
|
|
|
|
*BlockNumPtr = NTOHS (DataBuffer.Header.BlockNum);
|
|
return Status;
|
|
}
|
|
|
|
if (Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA8)) {
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);
|
|
}
|
|
|
|
*BlockNumPtr = Swap64 (*(UINT64 *) &DataBuffer.Header.BlockNum);
|
|
return Status;
|
|
}
|
|
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
LockStepReceive (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
UINTN PacketSize,
|
|
UINT64 *BufferSizePtr,
|
|
UINT64 Offset,
|
|
UINT8 *BufferPtr,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
|
|
EFI_IP_ADDRESS *ReplyIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
|
|
UINT64 LastBlock,
|
|
UINT16 Timeout,
|
|
IN BOOLEAN DontUseBuffer
|
|
)
|
|
/*++
|
|
Routine description:
|
|
Read rest of file after successfull M/TFTP request.
|
|
|
|
Parameters:
|
|
Private := Pointer to PxeBc interface
|
|
PacketSize := Pointer to packet size
|
|
BufferSizePtr := Pointer to buffer (file) size
|
|
Offset := Offset into buffer of next packet
|
|
BufferPtr := Pointer to receive buffer
|
|
ServerIpPtr := Pointer to TFTP server IP address
|
|
ServerPortPtr := Pointer to TFTP server UDP port
|
|
ReplyIpPtr := Pointer to TFTP DATA packet destination IP address
|
|
OurPortPtr := Pointer to TFTP client UDP port
|
|
LastBlock := Last block number received
|
|
Timeout :=
|
|
DontUseBuffer := TRUE == throw away data, just count # of bytes
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 BlockNum;
|
|
UINT64 BufferSize;
|
|
UINTN Retries;
|
|
UINTN SaveLen;
|
|
UINTN ReplyLen;
|
|
|
|
ReplyLen = PacketSize;
|
|
BlockNum = LastBlock;
|
|
|
|
DEBUG ((EFI_D_INFO, "\nLockStepReceive() PacketSize = %d", PacketSize));
|
|
|
|
if (DontUseBuffer) {
|
|
BufferSize = PacketSize;
|
|
} else {
|
|
BufferSize = *BufferSizePtr - Offset;
|
|
BufferPtr += Offset;
|
|
}
|
|
|
|
while (ReplyLen >= 512 && ReplyLen == PacketSize) {
|
|
if (BufferSize < PacketSize) {
|
|
ReplyLen = (UINTN) ((BufferSize > 0) ? BufferSize : 0);
|
|
}
|
|
|
|
SaveLen = ReplyLen;
|
|
|
|
//
|
|
// write an ack packet and get data - retry up to NUM_ACK_RETRIES on timeout
|
|
//
|
|
Retries = NUM_ACK_RETRIES;
|
|
|
|
do {
|
|
ReplyLen = SaveLen;
|
|
|
|
Status = SendAckAndGetData (
|
|
Private,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
ReplyIpPtr,
|
|
OurPortPtr,
|
|
Timeout,
|
|
(UINTN *) &ReplyLen,
|
|
BufferPtr,
|
|
&BlockNum,
|
|
FALSE
|
|
);
|
|
|
|
if (!EFI_ERROR (Status) || Status == EFI_BUFFER_TOO_SMALL) {
|
|
if (BlockNum == LastBlock) {
|
|
DEBUG ((EFI_D_NET, "\nresend"));
|
|
//
|
|
// a resend - continue
|
|
//
|
|
Status = EFI_TIMEOUT;
|
|
} else if (Private->BigBlkNumFlag) {
|
|
if (BlockNum != ++LastBlock) {
|
|
DEBUG ((EFI_D_NET, "\nLockStepReceive() Exit #1a"));
|
|
//
|
|
// not correct blocknum - error
|
|
//
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
} else {
|
|
LastBlock = (LastBlock + 1) & 0xFFFF;
|
|
if (BlockNum != LastBlock) {
|
|
DEBUG ((EFI_D_NET, "\nLockStepReceive() Exit #1b"));
|
|
return EFI_PROTOCOL_ERROR;
|
|
//
|
|
// not correct blocknum - error
|
|
//
|
|
}
|
|
}
|
|
}
|
|
} while (Status == EFI_TIMEOUT && --Retries);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status != EFI_BUFFER_TOO_SMALL) {
|
|
SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (DontUseBuffer) {
|
|
BufferSize += ReplyLen;
|
|
} else {
|
|
BufferPtr += ReplyLen;
|
|
BufferSize -= ReplyLen;
|
|
}
|
|
}
|
|
//
|
|
// while (ReplyLen == PacketSize);
|
|
//
|
|
if (DontUseBuffer) {
|
|
if (BufferSizePtr != NULL) {
|
|
*BufferSizePtr = (BufferSize - PacketSize);
|
|
}
|
|
} else {
|
|
*BufferSizePtr -= BufferSize;
|
|
}
|
|
|
|
/* Send ACK of last packet. */
|
|
ReplyLen = 0;
|
|
|
|
SendAckAndGetData (
|
|
Private,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
ReplyIpPtr,
|
|
OurPortPtr,
|
|
Timeout,
|
|
(UINTN *) &ReplyLen,
|
|
BufferPtr,
|
|
&BlockNum,
|
|
TRUE
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
//
|
|
// some literals
|
|
//
|
|
STATIC UINT8 Mode[] = MODE_BINARY;
|
|
STATIC UINT8 BlockSizeOp[] = OP_BLKSIZE;
|
|
STATIC UINT8 TsizeOp[] = OP_TFRSIZE;
|
|
STATIC UINT8 OverwriteOp[] = OP_OVERWRITE;
|
|
STATIC UINT8 BigBlkNumOp[] = OP_BIGBLKNUM;
|
|
STATIC EFI_PXE_BASE_CODE_UDP_PORT TftpRequestPort = TFTP_OPEN_PORT;
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
UINT8 *
|
|
FindOption (
|
|
UINT8 *OptionPtr,
|
|
INTN OpLen,
|
|
UINT8 *OackPtr,
|
|
INTN OackSize
|
|
)
|
|
/*++
|
|
Routine description:
|
|
Check TFTP OACK packet for option.
|
|
|
|
Parameters:
|
|
OptionPtr := Pointer to option string to find
|
|
OpLen := Length of option string
|
|
OackPtr := Pointer to OACK data
|
|
OackSize := Length of OACK data
|
|
|
|
Returns:
|
|
Pointer to value field if option found or NULL if not found.
|
|
--*/
|
|
{
|
|
if ((OackSize -= OpLen) <= 0) {
|
|
return NULL;
|
|
}
|
|
|
|
do {
|
|
if (!CompareMem (OackPtr, OptionPtr, OpLen)) {
|
|
return OackPtr + OpLen;
|
|
}
|
|
|
|
++OackPtr;
|
|
} while (--OackSize);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
#define BKSZOP 1 // block size
|
|
#define TSIZEOP 2 // transfer size
|
|
#define OVERWRITEOP 4 // overwrite
|
|
#define BIGBLKNUMOP 8 // big block numbers
|
|
STATIC
|
|
EFI_STATUS
|
|
TftpRwReq (
|
|
UINT16 Req,
|
|
UINT16 Options,
|
|
PXE_BASECODE_DEVICE *Private,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
|
|
UINT8 *FilenamePtr,
|
|
UINTN *PacketSizePtr,
|
|
VOID *Buffer
|
|
)
|
|
/*++
|
|
Routine description:
|
|
Send TFTP RRQ/WRQ packet.
|
|
|
|
Parameters:
|
|
Req := Type of request to send
|
|
Options := One or more of the #define values above
|
|
Private := Pointer to PxeBc interface
|
|
ServerIpPtr := Pointer to TFTP server IP address
|
|
ServerPortPtr := Pointer to TFTP server UDP port
|
|
OurPortPtr := Pointer to TFTP client UDP port
|
|
FilenamePtr := Pointer to TFTP file or directory name
|
|
PacketSizePtr := Pointer to block size
|
|
Buffer :=
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
union {
|
|
UINT8 Data[514];
|
|
struct Tftpv4Req ReqStr;
|
|
} *u;
|
|
|
|
UINT16 OpFlags;
|
|
INTN Len;
|
|
INTN TotalLen;
|
|
UINT8 *Ptr;
|
|
|
|
if (*OurPortPtr == 0) {
|
|
OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT;
|
|
} else {
|
|
OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT;
|
|
}
|
|
//
|
|
// build the basic request - opcode, filename, mode
|
|
//
|
|
u = Buffer;
|
|
u->ReqStr.OpCode = HTONS (Req);
|
|
TotalLen = sizeof (Mode) + sizeof (u->ReqStr.OpCode) + (Len = 1 + AsciiStrLen ((CHAR8 *)FilenamePtr));
|
|
|
|
CopyMem (u->ReqStr.FileName, FilenamePtr, Len);
|
|
Ptr = (UINT8 *) (u->ReqStr.FileName + Len);
|
|
|
|
CopyMem (Ptr, Mode, sizeof (Mode));
|
|
Ptr += sizeof (Mode);
|
|
|
|
if (Options & BKSZOP) {
|
|
CopyMem (Ptr, BlockSizeOp, sizeof (BlockSizeOp));
|
|
UtoA10 (*PacketSizePtr, Ptr + sizeof (BlockSizeOp));
|
|
|
|
TotalLen += (Len = 1 + AsciiStrLen ((CHAR8 *)Ptr + sizeof (BlockSizeOp)) + sizeof (BlockSizeOp));
|
|
|
|
Ptr += Len;
|
|
}
|
|
|
|
if (Options & TSIZEOP) {
|
|
CopyMem (Ptr, TsizeOp, sizeof (TsizeOp));
|
|
CopyMem (Ptr + sizeof (TsizeOp), "0", 2);
|
|
TotalLen += sizeof (TsizeOp) + 2;
|
|
Ptr += sizeof (TsizeOp) + 2;
|
|
}
|
|
|
|
if (Options & OVERWRITEOP) {
|
|
CopyMem (Ptr, OverwriteOp, sizeof (OverwriteOp));
|
|
CopyMem (Ptr + sizeof (OverwriteOp), "1", 2);
|
|
TotalLen += sizeof (OverwriteOp) + 2;
|
|
Ptr += sizeof (OverwriteOp) + 2;
|
|
}
|
|
|
|
if (Options & BIGBLKNUMOP) {
|
|
CopyMem (Ptr, BigBlkNumOp, sizeof (BigBlkNumOp));
|
|
CopyMem (Ptr + sizeof (BigBlkNumOp), "8", 2);
|
|
TotalLen += sizeof (BigBlkNumOp) + 2;
|
|
Ptr += sizeof (BigBlkNumOp) + 2;
|
|
}
|
|
//
|
|
// send it
|
|
//
|
|
return UdpWrite (
|
|
Private,
|
|
OpFlags,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
0,
|
|
0,
|
|
OurPortPtr,
|
|
0,
|
|
0,
|
|
(UINTN *) &TotalLen,
|
|
u
|
|
);
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
TftpRwReqwResp (
|
|
UINT16 Req,
|
|
UINT16 Options,
|
|
PXE_BASECODE_DEVICE *Private,
|
|
VOID *HeaderPtr,
|
|
UINTN *PacketSizePtr,
|
|
UINTN *ReplyLenPtr,
|
|
VOID *BufferPtr,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *ServerReplyPortPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr,
|
|
UINT8 *FilenamePtr,
|
|
UINT16 Timeout
|
|
)
|
|
/*++
|
|
Routine description:
|
|
Start TFTP session. Issue request and wait for response.
|
|
Retry three times on error. If failed using options,
|
|
retry three times w/o options on error.
|
|
|
|
Parameters:
|
|
Req := TFTP request type
|
|
Options := TFTP option bits
|
|
Private := Pointer to PxeBc interface
|
|
HeaderPtr :=
|
|
PacketSizePtr := Pointer to block size
|
|
ReplyLenPtr :=
|
|
BufferPtr :=
|
|
ServerIpPtr := Pointer to TFTP server IP address
|
|
ServerPortPtr := Pointer to TFTP server UDP port
|
|
ServerReplyPortPtr :=
|
|
OurPortPtr := Pointer to TFTP client UDP Port
|
|
FilenamePtr := Pointer to file or directory name
|
|
Timeout :=
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN SaveReplyLen;
|
|
INTN Retries;
|
|
UINT8 Buffer[514];
|
|
|
|
SaveReplyLen = *ReplyLenPtr;
|
|
Retries = 3;
|
|
Private->BigBlkNumFlag = FALSE;
|
|
*OurPortPtr = 0;
|
|
//
|
|
// generate random
|
|
//
|
|
do {
|
|
if (*OurPortPtr != 0) {
|
|
if (++ *OurPortPtr == 0) {
|
|
*OurPortPtr = PXE_RND_PORT_LOW;
|
|
}
|
|
}
|
|
//
|
|
// send request from our Ip = StationIp
|
|
//
|
|
if ((Status = TftpRwReq (
|
|
Req,
|
|
Options,
|
|
Private,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
OurPortPtr,
|
|
FilenamePtr,
|
|
PacketSizePtr,
|
|
Buffer
|
|
)) != EFI_SUCCESS) {
|
|
DEBUG (
|
|
(EFI_D_WARN,
|
|
"\nTftpRwReqwResp() Exit #1 %xh (%r)",
|
|
Status,
|
|
Status)
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
//
|
|
// read reply to our Ip = StationIp
|
|
//
|
|
*ReplyLenPtr = SaveReplyLen;
|
|
|
|
Status = TftpUdpRead (
|
|
Private,
|
|
EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
|
|
HeaderPtr,
|
|
ReplyLenPtr,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
ServerReplyPortPtr,
|
|
0,
|
|
OurPortPtr,
|
|
Timeout
|
|
);
|
|
} while (Status == EFI_TIMEOUT && --Retries);
|
|
|
|
if (!Options || Status != EFI_TFTP_ERROR) {
|
|
DEBUG (
|
|
(EFI_D_WARN,
|
|
"\nTftpRwReqwResp() Exit #2 %xh (%r)",
|
|
Status,
|
|
Status)
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
Status = TftpRwReqwResp (
|
|
Req,
|
|
0,
|
|
Private,
|
|
HeaderPtr,
|
|
PacketSizePtr,
|
|
ReplyLenPtr,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
ServerPortPtr,
|
|
ServerReplyPortPtr,
|
|
OurPortPtr,
|
|
FilenamePtr,
|
|
Timeout
|
|
);
|
|
|
|
DEBUG ((EFI_D_WARN, "\nTftpRwReqwResp() Exit #3 %xh (%r)", Status, Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
//
|
|
// mtftp listen
|
|
// read on mcast ip, cport, from sport, for data packet
|
|
// returns success if gets multicast last packet or all up to last block
|
|
// if not missing, then finished
|
|
//
|
|
STATIC
|
|
EFI_STATUS
|
|
MtftpListen (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
UINT64 *BufferSizePtr,
|
|
UINT8 *BufferPtr,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr,
|
|
UINT64 *StartBlockPtr,
|
|
UINTN *NumMissedPtr,
|
|
UINT16 TransTimeout,
|
|
UINT16 ListenTimeout,
|
|
UINT64 FinalBlock,
|
|
IN BOOLEAN DontUseBuffer
|
|
)
|
|
/*++
|
|
Routine description:
|
|
Listen for MTFTP traffic and save desired packets.
|
|
|
|
Parameters:
|
|
Private := Pointer to PxeBc interface
|
|
BufferSizePtr :=
|
|
BufferPtr :=
|
|
ServerIpPtr := Pointer to TFTP server IP address
|
|
MtftpInfoPtr := Pointer to MTFTP session information
|
|
StartBlockPtr := IN=first block we are looking for OUT=first block received
|
|
NumMissedPtr := Number of blocks missed
|
|
TransTimeout :=
|
|
ListenTimeout :=
|
|
FinalBlock :=
|
|
DontUseBuffer := TRUE == throw packets away, just count bytes
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
struct Tftpv4Ack Header;
|
|
UINT64 Offset;
|
|
UINT64 BlockNum;
|
|
UINT64 LastBlockNum;
|
|
UINT64 BufferSize;
|
|
UINTN NumMissed;
|
|
UINTN PacketSize;
|
|
UINTN SaveReplyLen;
|
|
UINTN ReplyLen;
|
|
UINT16 Timeout;
|
|
|
|
LastBlockNum = *StartBlockPtr;
|
|
Timeout = ListenTimeout;
|
|
*NumMissedPtr = 0;
|
|
PacketSize = 0;
|
|
BufferSize = *BufferSizePtr;
|
|
ReplyLen = MAX_TFTP_PKT_SIZE;;
|
|
|
|
//
|
|
// receive
|
|
//
|
|
do {
|
|
if ((SaveReplyLen = ReplyLen) > BufferSize) {
|
|
SaveReplyLen = (UINTN) BufferSize;
|
|
}
|
|
|
|
/* %%TBD - add big block number support */
|
|
|
|
//
|
|
// get data - loop on resends
|
|
//
|
|
do {
|
|
ReplyLen = SaveReplyLen;
|
|
|
|
if ((Status = TftpUdpRead (
|
|
Private,
|
|
0,
|
|
&Header,
|
|
&ReplyLen,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
&MtftpInfoPtr->SPort,
|
|
&MtftpInfoPtr->MCastIp,
|
|
&MtftpInfoPtr->CPort,
|
|
Timeout
|
|
)) != EFI_SUCCESS) {
|
|
return Status;
|
|
}
|
|
//
|
|
// make sure a data packet
|
|
//
|
|
if (Header.OpCode != HTONS (TFTP_DATA)) {
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
} while ((BlockNum = NTOHS (Header.BlockNum)) == LastBlockNum);
|
|
|
|
//
|
|
// make sure still going up
|
|
//
|
|
if (LastBlockNum > BlockNum) {
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
if (BlockNum - LastBlockNum > 0xFFFFFFFF) {
|
|
return EFI_PROTOCOL_ERROR;
|
|
} else {
|
|
NumMissed = (UINTN) (BlockNum - LastBlockNum - 1);
|
|
}
|
|
|
|
LastBlockNum = BlockNum;
|
|
|
|
//
|
|
// if first time through, some reinitialization
|
|
//
|
|
if (!PacketSize) {
|
|
*StartBlockPtr = BlockNum;
|
|
PacketSize = ReplyLen;
|
|
Timeout = TransTimeout;
|
|
} else {
|
|
*NumMissedPtr = (UINT16) (*NumMissedPtr + NumMissed);
|
|
}
|
|
//
|
|
// if missed packets, update start block,
|
|
// etc. and move packet to proper place in buffer
|
|
//
|
|
if (NumMissed) {
|
|
*StartBlockPtr = BlockNum;
|
|
if (!DontUseBuffer) {
|
|
Offset = NumMissed * PacketSize;
|
|
CopyMem (BufferPtr + Offset, BufferPtr, ReplyLen);
|
|
BufferPtr += Offset;
|
|
BufferSize -= Offset;
|
|
}
|
|
}
|
|
|
|
if (!DontUseBuffer) {
|
|
BufferPtr += ReplyLen;
|
|
BufferSize -= ReplyLen;
|
|
}
|
|
} while (ReplyLen == PacketSize && BlockNum != FinalBlock);
|
|
|
|
*BufferSizePtr = BufferSize;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
MtftpOpen (
|
|
PXE_BASECODE_DEVICE * Private,
|
|
UINT64 *BufferSizePtr,
|
|
UINT8 *BufferPtr,
|
|
UINTN *PacketSizePtr,
|
|
EFI_IP_ADDRESS * ServerIpPtr,
|
|
UINT8 *FilenamePtr,
|
|
EFI_PXE_BASE_CODE_MTFTP_INFO * MtftpInfoPtr,
|
|
UINT8 *CompletionStatusPtr,
|
|
#define GOTUNI 1
|
|
#define GOTMULTI 2
|
|
IN BOOLEAN DontUseBuffer
|
|
)
|
|
/*++
|
|
Routine description:
|
|
Open MTFTP session.
|
|
|
|
Parameters:
|
|
Private := Pointer to PxeBc interface
|
|
BufferSizePtr := IN=buffer size OUT=transfer size
|
|
BufferPtr :=
|
|
PacketSizePtr :=
|
|
ServerIpPtr :=
|
|
FilenamePtr :=
|
|
MtftpInfoPtr :=
|
|
CompletionStatusPtr :=
|
|
DontUseBuffer :=
|
|
|
|
Returns:
|
|
// mtftp open session
|
|
// return code EFI_SUCCESS
|
|
// and *CompletionStatusPtr = GOTUNI | GOTMULTI means done
|
|
// and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest
|
|
// and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all
|
|
// (do not get = GOTUNI - returns NO_DATA go will go to TFTP session)
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_IP_ADDRESS OurReplyIp;
|
|
struct Tftpv4Ack Header;
|
|
INTN ReplyLen;
|
|
INTN Retries;
|
|
UINT8 *BufferPtr2;
|
|
UINT8 TmpBuf[514];
|
|
|
|
Retries = NUM_MTFTP_OPEN_RETRIES;
|
|
BufferPtr2 = BufferPtr;
|
|
*PacketSizePtr = (UINTN) (EFI_MIN (*BufferSizePtr, MAX_TFTP_PKT_SIZE));
|
|
|
|
do {
|
|
//
|
|
// send a read request
|
|
//
|
|
*CompletionStatusPtr = 0;
|
|
|
|
if ((Status = TftpRwReq (
|
|
TFTP_RRQ,
|
|
0,
|
|
Private,
|
|
ServerIpPtr,
|
|
&MtftpInfoPtr->SPort,
|
|
&MtftpInfoPtr->CPort,
|
|
FilenamePtr,
|
|
PacketSizePtr,
|
|
TmpBuf
|
|
)) != EFI_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
for (;;) {
|
|
//
|
|
// read reply
|
|
//
|
|
ZeroMem (&OurReplyIp, Private->IpLength);
|
|
ReplyLen = *PacketSizePtr;
|
|
|
|
if ((Status = TftpUdpRead (
|
|
Private,
|
|
EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER,
|
|
&Header,
|
|
(UINTN *) &ReplyLen,
|
|
BufferPtr2,
|
|
ServerIpPtr,
|
|
&MtftpInfoPtr->SPort,
|
|
&OurReplyIp,
|
|
&MtftpInfoPtr->CPort,
|
|
MtftpInfoPtr->TransmitTimeout
|
|
)) == EFI_SUCCESS) {
|
|
//
|
|
// check for first data packet
|
|
//
|
|
if (Header.OpCode != HTONS (TFTP_DATA)) {
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
//
|
|
// check block num
|
|
//
|
|
if (Header.BlockNum != HTONS (1)) {
|
|
//
|
|
// it's not first
|
|
// if we are not the primary client,
|
|
// we probably got first and now second
|
|
// multicast but no unicast, so
|
|
// *CompletionStatusPtr = GOTMULTI - if this is
|
|
// the second, can just go on to listen
|
|
// starting with 2 as the last block
|
|
// received
|
|
//
|
|
if (Header.BlockNum != HTONS (2)) {
|
|
//
|
|
// not second
|
|
//
|
|
*CompletionStatusPtr = 0;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// now actual
|
|
//
|
|
*PacketSizePtr = ReplyLen;
|
|
//
|
|
// see if a unicast data packet
|
|
//
|
|
if (!CompareMem (
|
|
&OurReplyIp,
|
|
&Private->EfiBc.Mode->StationIp,
|
|
Private->IpLength
|
|
)) {
|
|
*CompletionStatusPtr |= GOTUNI;
|
|
//
|
|
// it is
|
|
// if already got multicast packet,
|
|
// got em both
|
|
//
|
|
if (*CompletionStatusPtr & GOTMULTI) {
|
|
break;
|
|
}
|
|
} else if (!CompareMem (
|
|
&OurReplyIp,
|
|
&MtftpInfoPtr->MCastIp,
|
|
Private->IpLength
|
|
)) {
|
|
//
|
|
// otherwise see if a multicast data packet
|
|
//
|
|
*CompletionStatusPtr |= GOTMULTI;
|
|
//
|
|
// it is
|
|
// got first - bump pointer so that if
|
|
// second multi comes along, we're OK
|
|
//
|
|
if (!DontUseBuffer) {
|
|
BufferPtr2 = (UINT8 *) BufferPtr + ReplyLen;
|
|
}
|
|
//
|
|
// if already got unicast packet,
|
|
// got em both
|
|
//
|
|
if (*CompletionStatusPtr & GOTUNI) {
|
|
break;
|
|
}
|
|
} else {
|
|
//
|
|
// else protocol error
|
|
//
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
} else if (Status == EFI_TIMEOUT) {
|
|
//
|
|
// bad return code - if timed out, retry
|
|
//
|
|
break;
|
|
} else {
|
|
//
|
|
// else just bad - failed MTFTP open
|
|
//
|
|
return Status;
|
|
}
|
|
}
|
|
} while (Status == EFI_TIMEOUT && --Retries);
|
|
|
|
if (Status != EFI_SUCCESS) {
|
|
//
|
|
// open failed
|
|
//
|
|
return Status;
|
|
}
|
|
//
|
|
// got em both - go into receive mode
|
|
// routine to read rest of file after a successful open (TFTP or MTFTP)
|
|
// sends ACK and gets next data packet until short packet arrives,
|
|
// then sends ACK and (hopefully) times out
|
|
//
|
|
return LockStepReceive (
|
|
Private,
|
|
(UINT16) ReplyLen,
|
|
BufferSizePtr,
|
|
ReplyLen,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
&MtftpInfoPtr->SPort,
|
|
&MtftpInfoPtr->MCastIp,
|
|
&MtftpInfoPtr->CPort,
|
|
1,
|
|
MtftpInfoPtr->TransmitTimeout,
|
|
DontUseBuffer
|
|
);
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
MtftpDownload (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
UINT64 *BufferSizePtr,
|
|
UINT8 *BufferPtr,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
UINT8 *FilenamePtr,
|
|
EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr,
|
|
IN BOOLEAN DontUseBuffer
|
|
)
|
|
/*++
|
|
Routine description:
|
|
// mtftp
|
|
// loop
|
|
// listen
|
|
// if did not get any packets, try MTFTP open
|
|
// if got all packets, return
|
|
// compute listen timeout and loop
|
|
|
|
Parameters:
|
|
Private := Pointer to PxeBc interface
|
|
BufferSizePtr :=
|
|
BufferPtr :=
|
|
ServerIpPtr :=
|
|
FilenamePtr :=
|
|
MtftpInfoPtr :=
|
|
DontUseBuffer :=
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
EFI_PXE_BASE_CODE_IP_FILTER Filter;
|
|
EFI_STATUS Status;
|
|
UINT64 StartBlock;
|
|
UINT64 LastBlock;
|
|
UINT64 LastStartBlock;
|
|
UINT64 BufferSize;
|
|
UINTN Offset;
|
|
UINTN NumMissed;
|
|
UINT16 TransTimeout;
|
|
UINT16 ListenTimeout;
|
|
UINT8 *BufferPtrLocal;
|
|
|
|
TransTimeout = MtftpInfoPtr->TransmitTimeout;
|
|
ListenTimeout = MtftpInfoPtr->ListenTimeout;
|
|
LastBlock = 0;
|
|
LastStartBlock = 0;
|
|
Offset = 0;
|
|
|
|
Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;
|
|
Filter.IpCnt = 2;
|
|
CopyMem (&Filter.IpList[0], &Private->EfiBc.Mode->StationIp, sizeof (EFI_IP_ADDRESS));
|
|
CopyMem (&Filter.IpList[1], &MtftpInfoPtr->MCastIp, sizeof (EFI_IP_ADDRESS));
|
|
|
|
if ((Status = IpFilter (Private, &Filter)) != EFI_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
for (;;) {
|
|
StartBlock = LastStartBlock;
|
|
BufferSize = *BufferSizePtr - Offset;
|
|
|
|
if (DontUseBuffer) {
|
|
//
|
|
// overwrie the temp buf
|
|
//
|
|
BufferPtrLocal = BufferPtr;
|
|
} else {
|
|
BufferPtrLocal = BufferPtr + Offset;
|
|
|
|
}
|
|
|
|
if (((Status = MtftpListen (
|
|
Private,
|
|
&BufferSize,
|
|
BufferPtrLocal,
|
|
ServerIpPtr,
|
|
MtftpInfoPtr,
|
|
&StartBlock,
|
|
&NumMissed,
|
|
TransTimeout,
|
|
ListenTimeout,
|
|
LastBlock,
|
|
DontUseBuffer
|
|
)) != EFI_SUCCESS) && (Status != EFI_TIMEOUT)) {
|
|
return Status;
|
|
//
|
|
// failed
|
|
//
|
|
}
|
|
//
|
|
// if none were received, start block is not reset
|
|
//
|
|
if (StartBlock == LastStartBlock) {
|
|
UINT8 CompStat;
|
|
|
|
//
|
|
// timed out with none received - try MTFTP open
|
|
//
|
|
if ((Status = MtftpOpen (
|
|
Private,
|
|
BufferSizePtr,
|
|
BufferPtr,
|
|
&Offset,
|
|
ServerIpPtr,
|
|
FilenamePtr,
|
|
MtftpInfoPtr,
|
|
&CompStat,
|
|
DontUseBuffer
|
|
)) != EFI_SUCCESS) {
|
|
//
|
|
// open failure - try TFTP
|
|
//
|
|
return Status;
|
|
}
|
|
//
|
|
// return code EFI_SUCCESS
|
|
// and *CompletionStatusPtr = GOTUNI | GOTMULTI means done
|
|
// and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest
|
|
// and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all
|
|
// (do not get = GOTUNI - returns NO_DATA go will go to TFTP session)
|
|
//
|
|
if (CompStat == (GOTUNI | GOTMULTI)) {
|
|
//
|
|
// finished - got it all
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
if (CompStat) {
|
|
//
|
|
// offset is two packet lengths
|
|
//
|
|
Offset <<= 1;
|
|
//
|
|
// last block received
|
|
//
|
|
LastStartBlock = 2;
|
|
} else {
|
|
Offset = 0;
|
|
LastStartBlock = 0;
|
|
}
|
|
|
|
ListenTimeout = TransTimeout;
|
|
continue;
|
|
}
|
|
//
|
|
// did we get the last block
|
|
//
|
|
if (Status == EFI_SUCCESS) {
|
|
//
|
|
// yes - set the file size if this was first time
|
|
//
|
|
if (!LastBlock) {
|
|
*BufferSizePtr -= BufferSize;
|
|
}
|
|
//
|
|
// if buffer was too small, finished
|
|
//
|
|
if (!DontUseBuffer) {
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
//
|
|
// if we got them all, finished
|
|
//
|
|
if (!NumMissed && StartBlock == LastStartBlock + 1) {
|
|
return Status;
|
|
}
|
|
//
|
|
// did not get them all - set last block
|
|
//
|
|
LastBlock = (UINT16) (StartBlock - 1);
|
|
}
|
|
//
|
|
// compute listen timeout
|
|
//
|
|
ListenTimeout = (UINT16) ((NumMissed > MtftpInfoPtr->ListenTimeout) ? 0 : (MtftpInfoPtr->ListenTimeout - NumMissed));
|
|
|
|
//
|
|
// reset
|
|
//
|
|
Offset = 0;
|
|
LastStartBlock = 0;
|
|
}
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
TftpInfo (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
UINT64 *BufferSizePtr,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT SrvPort,
|
|
UINT8 *FilenamePtr,
|
|
UINTN *PacketSizePtr
|
|
)
|
|
/*++
|
|
Routine description:
|
|
// TFTP info request routine
|
|
// send read request with block size and transfer size options
|
|
// get reply
|
|
// send error to terminate session
|
|
// if OACK received, set info
|
|
|
|
Parameters:
|
|
Private :=
|
|
BufferSizePtr :=
|
|
ServerIpPtr :=
|
|
SrvPort :=
|
|
FilenamePtr :=
|
|
PacketSizePtr :=
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
EFI_PXE_BASE_CODE_UDP_PORT OurPort;
|
|
EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort;
|
|
EFI_STATUS Status;
|
|
UINT64 BlockNum;
|
|
UINTN Offset;
|
|
UINTN ReplyLen;
|
|
UINT8 *Ptr;
|
|
|
|
union {
|
|
struct Tftpv4Oack OAck2Ptr;
|
|
struct Tftpv4Ack Ack2Ptr;
|
|
struct Tftpv4Data Datastr;
|
|
} u;
|
|
|
|
OurPort = 0;
|
|
ServerReplyPort = 0;
|
|
ReplyLen = sizeof (u.Datastr.Data);
|
|
|
|
//
|
|
// send a write request with the blocksize option -
|
|
// sets our IP and port - and receive reply - sets his port
|
|
// will retry operation up to 3 times if no response,
|
|
// and will retry without options on an error reply
|
|
//
|
|
if ((Status = TftpRwReqwResp (
|
|
TFTP_RRQ,
|
|
/* BIGBLKNUMOP | */BKSZOP | TSIZEOP,
|
|
Private,
|
|
&u,
|
|
PacketSizePtr,
|
|
&ReplyLen,
|
|
u.Datastr.Data,
|
|
ServerIpPtr,
|
|
&SrvPort,
|
|
&ServerReplyPort,
|
|
&OurPort,
|
|
FilenamePtr,
|
|
REQ_RESP_TIMEOUT
|
|
)) != EFI_SUCCESS) {
|
|
DEBUG ((EFI_D_WARN, "\nTftpInfo() Exit #1"));
|
|
return Status;
|
|
}
|
|
//
|
|
// check for good OACK
|
|
//
|
|
if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {
|
|
//
|
|
// now parse it for options
|
|
// bigblk#
|
|
//
|
|
Ptr = FindOption (
|
|
BigBlkNumOp,
|
|
sizeof (BigBlkNumOp),
|
|
u.OAck2Ptr.OpAck[0].Option,
|
|
ReplyLen + sizeof (u.Ack2Ptr.BlockNum)
|
|
);
|
|
|
|
if (Ptr != NULL) {
|
|
if (AtoU (Ptr) == 8) {
|
|
Private->BigBlkNumFlag = TRUE;
|
|
} else {
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
//
|
|
// blksize
|
|
//
|
|
Ptr = FindOption (
|
|
BlockSizeOp,
|
|
sizeof (BlockSizeOp),
|
|
u.OAck2Ptr.OpAck[0].Option,
|
|
ReplyLen += sizeof (u.Ack2Ptr.BlockNum)
|
|
);
|
|
|
|
*PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512;
|
|
|
|
//
|
|
// tsize
|
|
//
|
|
Ptr = FindOption (
|
|
TsizeOp,
|
|
sizeof (TsizeOp),
|
|
u.OAck2Ptr.OpAck[0].Option,
|
|
ReplyLen
|
|
);
|
|
|
|
if (Ptr != NULL) {
|
|
*BufferSizePtr = AtoU64 (Ptr);
|
|
|
|
//
|
|
// teminate session with error
|
|
//
|
|
SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
Offset = 0;
|
|
BlockNum = 0;
|
|
} else {
|
|
//
|
|
// if MTFTP get filesize, return unsupported
|
|
//
|
|
if (SrvPort != TftpRequestPort) {
|
|
SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort);
|
|
DEBUG ((EFI_D_WARN, "\nTftpInfo() Exit #3"));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Offset = ReplyLen;
|
|
//
|
|
// last block received
|
|
//
|
|
BlockNum = 1;
|
|
}
|
|
//
|
|
// does not support the option - do a download with no buffer
|
|
//
|
|
*BufferSizePtr = 0;
|
|
|
|
Status = LockStepReceive (
|
|
Private,
|
|
(UINT16) ReplyLen,
|
|
BufferSizePtr,
|
|
Offset,
|
|
(UINT8 *) &u,
|
|
ServerIpPtr,
|
|
&ServerReplyPort,
|
|
&Private->EfiBc.Mode->StationIp,
|
|
&OurPort,
|
|
BlockNum,
|
|
ACK_TIMEOUT,
|
|
TRUE
|
|
);
|
|
|
|
if (Status != EFI_SUCCESS) {
|
|
DEBUG ((EFI_D_WARN, "\nTftpInfo() LockStepReceive() == %Xh", Status));
|
|
}
|
|
|
|
if (Status != EFI_BUFFER_TOO_SMALL) {
|
|
return Status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
TftpDownload (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
UINT64 *BufferSizePtr,
|
|
UINT8 *BufferPtr,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
UINT8 *FilenamePtr,
|
|
UINTN *PacketSizePtr,
|
|
EFI_PXE_BASE_CODE_UDP_PORT SrvPort,
|
|
UINT16 Req,
|
|
IN BOOLEAN DontUseBuffer
|
|
)
|
|
/*++
|
|
Routine description:
|
|
// tftp read session
|
|
// send read request
|
|
// [get OACK
|
|
// send ACK]
|
|
// loop
|
|
// get data
|
|
// send ACK
|
|
// while data size is max
|
|
|
|
Parameters:
|
|
Private :=
|
|
BufferSizePtr :=
|
|
BufferPtr :=
|
|
ServerIpPtr :=
|
|
FilenamePtr :=
|
|
PacketSizePtr :=
|
|
SrvPort :=
|
|
Req :=
|
|
DontUseBuffer :=
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
EFI_PXE_BASE_CODE_UDP_PORT OurPort;
|
|
EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort;
|
|
EFI_STATUS Status;
|
|
UINT64 Offset;
|
|
UINT64 BlockNum;
|
|
UINTN ReplyLen;
|
|
UINT8 *Ptr;
|
|
|
|
union {
|
|
struct Tftpv4Ack Ack2Ptr;
|
|
struct Tftpv4Oack OAck2Ptr;
|
|
struct Tftpv4Data Data;
|
|
struct Tftpv4Ack8 Ack8Ptr;
|
|
struct Tftpv4Data8 Data8;
|
|
} U;
|
|
|
|
OurPort = 0;
|
|
ServerReplyPort = 0;
|
|
ReplyLen = (UINTN) ((*BufferSizePtr > 0xFFFF) ? 0xFFFF : *BufferSizePtr);
|
|
|
|
//
|
|
// send a read request with the blocksize option - sets our IP and port
|
|
// - and receive reply - sets his port will retry operation up to 3
|
|
// times if no response, and will retry without options on an error
|
|
// reply
|
|
//
|
|
if ((Status = TftpRwReqwResp (
|
|
Req,
|
|
/* BIGBLKNUMOP | */BKSZOP,
|
|
Private,
|
|
&U,
|
|
PacketSizePtr,
|
|
&ReplyLen,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
&SrvPort,
|
|
&ServerReplyPort,
|
|
&OurPort,
|
|
FilenamePtr,
|
|
REQ_RESP_TIMEOUT
|
|
)) != EFI_SUCCESS) {
|
|
DEBUG ((EFI_D_WARN, "\nTftpDownload() Exit #1 %xh (%r)", Status, Status));
|
|
return Status;
|
|
}
|
|
//
|
|
// check for OACK
|
|
//
|
|
if (U.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {
|
|
//
|
|
// get the OACK
|
|
//
|
|
CopyMem (U.Data.Data, BufferPtr, ReplyLen);
|
|
|
|
Ptr = FindOption (
|
|
BigBlkNumOp,
|
|
sizeof (BigBlkNumOp),
|
|
U.OAck2Ptr.OpAck[0].Option,
|
|
ReplyLen + sizeof (U.Ack2Ptr.BlockNum)
|
|
);
|
|
|
|
if (Ptr != NULL) {
|
|
if (AtoU (Ptr) == 8) {
|
|
Private->BigBlkNumFlag = TRUE;
|
|
} else {
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
//
|
|
// now parse it for blocksize option
|
|
//
|
|
Ptr = FindOption (
|
|
BlockSizeOp,
|
|
sizeof (BlockSizeOp),
|
|
U.OAck2Ptr.OpAck[0].Option,
|
|
ReplyLen += sizeof (U.Ack2Ptr.BlockNum)
|
|
);
|
|
|
|
ReplyLen = (Ptr != NULL) ? AtoU (Ptr) : 512;
|
|
|
|
Offset = 0;
|
|
//
|
|
// last block received
|
|
//
|
|
BlockNum = 0;
|
|
} else if (U.Ack2Ptr.OpCode != HTONS (TFTP_DATA) || U.Ack2Ptr.BlockNum != HTONS (1)) {
|
|
//
|
|
// or data
|
|
//
|
|
DEBUG ((EFI_D_WARN, "\nTftpDownload() Exit #2 %xh (%r)", Status, Status));
|
|
|
|
return EFI_PROTOCOL_ERROR;
|
|
} else {
|
|
//
|
|
// got good data packet
|
|
//
|
|
Offset = ReplyLen;
|
|
//
|
|
// last block received
|
|
//
|
|
BlockNum = 1;
|
|
}
|
|
|
|
if (PacketSizePtr != NULL) {
|
|
*PacketSizePtr = ReplyLen;
|
|
}
|
|
//
|
|
// routine to read rest of file after a successful open (TFTP or MTFTP)
|
|
// sends ACK and gets next data packet until short packet arrives, then sends
|
|
// ACK and (hopefully) times out
|
|
// if first packet has been read, BufferPtr and BufferSize must reflect fact
|
|
//
|
|
Status = LockStepReceive (
|
|
Private,
|
|
ReplyLen,
|
|
BufferSizePtr,
|
|
Offset,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
&ServerReplyPort,
|
|
&Private->EfiBc.Mode->StationIp,
|
|
&OurPort,
|
|
BlockNum,
|
|
ACK_TIMEOUT,
|
|
DontUseBuffer
|
|
);
|
|
|
|
if (Status != EFI_SUCCESS) {
|
|
DEBUG ((EFI_D_WARN, "\nTftpDownload() Exit #3 %xh (%r)", Status, Status));
|
|
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
Status = TftpInfo (
|
|
Private,
|
|
BufferSizePtr,
|
|
ServerIpPtr,
|
|
SrvPort,
|
|
FilenamePtr,
|
|
PacketSizePtr
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
TftpUpload (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
UINT64 *BufferSizePtr,
|
|
VOID *BufferPtr,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
UINT8 *FilenamePtr,
|
|
UINTN *PacketSizePtr,
|
|
BOOLEAN Overwrite
|
|
)
|
|
/*++
|
|
Routine description:
|
|
// tftp write session
|
|
// send write request
|
|
// get OACK or ACK
|
|
// loop
|
|
// send min (rest of data, max data packet)
|
|
// get ACK
|
|
// while data size is max
|
|
|
|
Parameters:
|
|
Private :=
|
|
BufferSizePtr :=
|
|
BufferPtr :=
|
|
ServerIpPtr :=
|
|
FilenamePtr :=
|
|
PacketSizePtr :=
|
|
Overwrite :=
|
|
|
|
Returns:
|
|
--*/
|
|
{
|
|
struct Tftpv4Ack Header;
|
|
EFI_PXE_BASE_CODE_UDP_PORT OurPort;
|
|
EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort;
|
|
EFI_STATUS Status;
|
|
UINT64 BlockNum;
|
|
UINT64 TransferSize;
|
|
UINTN ReplyLen;
|
|
UINTN TransferLen;
|
|
UINT16 Options;
|
|
UINT8 *Ptr;
|
|
|
|
union {
|
|
struct Tftpv4Oack OAck2Ptr;
|
|
struct Tftpv4Ack Ack2Ptr;
|
|
struct Tftpv4Data Datastr;
|
|
} u;
|
|
|
|
OurPort = 0;
|
|
ServerReplyPort = 0;
|
|
TransferSize = *BufferSizePtr;
|
|
ReplyLen = sizeof (u.Datastr.Data);
|
|
Options = (UINT16) ((Overwrite) ? OVERWRITEOP | BKSZOP : BKSZOP);
|
|
|
|
//
|
|
// send a write request with the blocksize option - sets our IP and port -
|
|
// and receive reply - sets his port
|
|
// will retry operation up to 3 times if no response, and will retry without
|
|
// options on an error reply
|
|
//
|
|
if ((Status = TftpRwReqwResp (
|
|
TFTP_WRQ,
|
|
Options,
|
|
Private,
|
|
&u,
|
|
PacketSizePtr,
|
|
&ReplyLen,
|
|
u.Datastr.Data,
|
|
ServerIpPtr,
|
|
&TftpRequestPort,
|
|
&ServerReplyPort,
|
|
&OurPort,
|
|
FilenamePtr,
|
|
REQ_RESP_TIMEOUT
|
|
)) != EFI_SUCCESS) {
|
|
return Status;
|
|
}
|
|
//
|
|
// check for OACK
|
|
//
|
|
if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {
|
|
//
|
|
// parse it for blocksize option
|
|
//
|
|
Ptr = FindOption (
|
|
BlockSizeOp,
|
|
sizeof (BlockSizeOp),
|
|
u.OAck2Ptr.OpAck[0].Option,
|
|
ReplyLen += sizeof (u.Ack2Ptr.BlockNum)
|
|
);
|
|
*PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512;
|
|
}
|
|
//
|
|
// or ACK
|
|
//
|
|
else if (u.Ack2Ptr.OpCode == HTONS (TFTP_ACK)) {
|
|
//
|
|
// option was not supported
|
|
//
|
|
*PacketSizePtr = 512;
|
|
} else {
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
//
|
|
// loop
|
|
//
|
|
Header.OpCode = HTONS (TFTP_DATA);
|
|
BlockNum = 1;
|
|
Header.BlockNum = HTONS (1);
|
|
|
|
do {
|
|
UINTN HeaderSize;
|
|
INTN Retries;
|
|
|
|
Retries = NUM_ACK_RETRIES;
|
|
HeaderSize = sizeof (Header);
|
|
TransferLen = (UINTN) (EFI_MIN (*PacketSizePtr, TransferSize));
|
|
|
|
//
|
|
// write a data packet and get an ack
|
|
//
|
|
do {
|
|
//
|
|
// write
|
|
//
|
|
if ((Status = UdpWrite (
|
|
Private,
|
|
EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
|
|
ServerIpPtr,
|
|
&ServerReplyPort,
|
|
0,
|
|
0,
|
|
&OurPort,
|
|
&HeaderSize,
|
|
&Header,
|
|
&TransferLen,
|
|
BufferPtr
|
|
)) != EFI_SUCCESS) {
|
|
return Status;
|
|
}
|
|
//
|
|
// read reply
|
|
//
|
|
ReplyLen = sizeof (u.Datastr.Data);
|
|
|
|
if ((Status = TftpUdpRead (
|
|
Private,
|
|
0,
|
|
&u,
|
|
&ReplyLen,
|
|
u.Datastr.Data,
|
|
ServerIpPtr,
|
|
&ServerReplyPort,
|
|
0,
|
|
&OurPort,
|
|
ACK_TIMEOUT
|
|
)) == EFI_SUCCESS) {
|
|
//
|
|
// check for ACK for this data packet
|
|
//
|
|
if (u.Ack2Ptr.OpCode != HTONS (TFTP_ACK)) {
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
if (u.Ack2Ptr.BlockNum != Header.BlockNum) {
|
|
//
|
|
// not for this packet - continue
|
|
//
|
|
Status = EFI_TIMEOUT;
|
|
}
|
|
}
|
|
} while (Status == EFI_TIMEOUT && --Retries);
|
|
|
|
if (Status != EFI_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
BufferPtr = (VOID *) ((UINT8 *) (BufferPtr) + TransferLen);
|
|
TransferSize -= TransferLen;
|
|
++BlockNum;
|
|
Header.BlockNum = HTONS ((UINT16) BlockNum);
|
|
} while (TransferLen == *PacketSizePtr);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
STATIC
|
|
EFI_STATUS
|
|
PxeBcMtftp (
|
|
PXE_BASECODE_DEVICE *Private,
|
|
IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation,
|
|
UINT64 *BufferSizePtr,
|
|
VOID *BufferPtr,
|
|
EFI_IP_ADDRESS *ServerIpPtr,
|
|
UINT8 *FilenamePtr,
|
|
UINTN *PacketSizePtr,
|
|
IN EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr, OPTIONAL
|
|
IN BOOLEAN Overwrite,
|
|
IN BOOLEAN DontUseBuffer
|
|
)
|
|
/*++
|
|
Routine description:
|
|
MTFTP API entry point
|
|
|
|
Parameters:
|
|
Private :=
|
|
Operation :=
|
|
BufferSizePtr :=
|
|
BufferPtr :=
|
|
ServerIpPtr :=
|
|
FilenamePtr :=
|
|
PacketSizePtr :=
|
|
MtftpInfoPtr :=
|
|
Overwrite :=
|
|
DontUseBuffer :=
|
|
|
|
Returns:
|
|
* EFI_INVALID_PARAMETER
|
|
* EFI_OUT_OF_RESOURCES
|
|
* EFI_BAD_BUFFER_SIZE
|
|
* Status is also returned from IpFilter(), TftpInfo(), MtftpDownload(),
|
|
* TftpDownload() and TftpUpload().
|
|
--*/
|
|
{
|
|
EFI_PXE_BASE_CODE_IP_FILTER Filter;
|
|
EFI_STATUS StatCode;
|
|
UINT64 BufferSizeLocal;
|
|
UINTN PacketSize;
|
|
UINT8 *BufferPtrLocal;
|
|
|
|
Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
|
|
Filter.IpCnt = 0;
|
|
Filter.reserved = 0;
|
|
|
|
/* No error has occurred, yet. */
|
|
Private->EfiBc.Mode->TftpErrorReceived = FALSE;
|
|
|
|
/* We must at least have an MTFTP server IP address and
|
|
* a pointer to the buffer size.
|
|
*/
|
|
if (!ServerIpPtr || !BufferSizePtr) {
|
|
DEBUG ((EFI_D_WARN, "\nPxeBcMtftp() Exit #1"));
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Private->Function = EFI_PXE_BASE_CODE_FUNCTION_MTFTP;
|
|
|
|
//
|
|
// make sure filter set to unicast at start
|
|
//
|
|
if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {
|
|
DEBUG (
|
|
(EFI_D_NET,
|
|
"\nPxeBcMtftp() Exit IpFilter() == %Xh",
|
|
StatCode)
|
|
);
|
|
|
|
return StatCode;
|
|
}
|
|
//
|
|
// set unset parms to default values
|
|
//
|
|
if (!PacketSizePtr) {
|
|
*(PacketSizePtr = &PacketSize) = MAX_TFTP_PKT_SIZE;
|
|
}
|
|
|
|
if (*PacketSizePtr > *BufferSizePtr) {
|
|
*PacketSizePtr = (UINTN) *BufferSizePtr;
|
|
}
|
|
|
|
if (*PacketSizePtr < MIN_TFTP_PKT_SIZE) {
|
|
*PacketSizePtr = MIN_TFTP_PKT_SIZE;
|
|
}
|
|
|
|
if (*PacketSizePtr > BUFFER_ALLOCATE_SIZE) {
|
|
*PacketSizePtr = BUFFER_ALLOCATE_SIZE;
|
|
}
|
|
|
|
if (*PacketSizePtr > MAX_TFTP_PKT_SIZE) {
|
|
*PacketSizePtr = MAX_TFTP_PKT_SIZE;
|
|
}
|
|
|
|
if (Operation == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) {
|
|
StatCode = TftpInfo (
|
|
Private,
|
|
BufferSizePtr,
|
|
ServerIpPtr,
|
|
TftpRequestPort,
|
|
FilenamePtr,
|
|
PacketSizePtr
|
|
);
|
|
|
|
if (StatCode != EFI_SUCCESS) {
|
|
DEBUG (
|
|
(EFI_D_WARN,
|
|
"\nPxeBcMtftp() Exit TftpInfo() == %Xh",
|
|
StatCode)
|
|
);
|
|
}
|
|
|
|
return StatCode;
|
|
}
|
|
|
|
if (Operation == EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE) {
|
|
if (!MtftpInfoPtr || !MtftpInfoPtr->SPort) {
|
|
DEBUG ((EFI_D_WARN, "\nPxeBcMtftp() Exit #2"));
|
|
return EFI_INVALID_PARAMETER;
|
|
} else {
|
|
StatCode = TftpInfo (
|
|
Private,
|
|
BufferSizePtr,
|
|
ServerIpPtr,
|
|
MtftpInfoPtr->SPort,
|
|
FilenamePtr,
|
|
PacketSizePtr
|
|
);
|
|
|
|
gBS->Stall (10000);
|
|
|
|
if (StatCode != EFI_SUCCESS) {
|
|
DEBUG (
|
|
(EFI_D_WARN,
|
|
"\nPxeBcMtftp() Exit TftpInfo() == %Xh",
|
|
StatCode)
|
|
);
|
|
}
|
|
|
|
return StatCode;
|
|
}
|
|
}
|
|
|
|
if (!BufferPtr && !DontUseBuffer) {
|
|
//
|
|
// if dontusebuffer is false and no buffer???
|
|
//
|
|
DEBUG ((EFI_D_WARN, "\nPxeBcMtftp() Exit #3"));
|
|
//
|
|
// DontUseBuffer can be true only for read_file operation
|
|
//
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (DontUseBuffer) {
|
|
BufferPtrLocal = AllocatePool (BUFFER_ALLOCATE_SIZE);
|
|
if (BufferPtrLocal == NULL) {
|
|
DEBUG ((EFI_D_NET, "\nPxeBcMtftp() Exit #4"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
BufferSizeLocal = BUFFER_ALLOCATE_SIZE;
|
|
} else {
|
|
if (!*BufferSizePtr && Operation != EFI_PXE_BASE_CODE_TFTP_WRITE_FILE) {
|
|
DEBUG ((EFI_D_WARN, "\nPxeBcMtftp() Exit #5"));
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
BufferPtrLocal = BufferPtr;
|
|
BufferSizeLocal = *BufferSizePtr;
|
|
}
|
|
|
|
switch (Operation) {
|
|
case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:
|
|
if (FilenamePtr == NULL ||
|
|
MtftpInfoPtr == NULL ||
|
|
MtftpInfoPtr->MCastIp.Addr[0] == 0 ||
|
|
MtftpInfoPtr->SPort == 0 ||
|
|
MtftpInfoPtr->CPort == 0 ||
|
|
MtftpInfoPtr->ListenTimeout == 0 ||
|
|
MtftpInfoPtr->TransmitTimeout == 0
|
|
) {
|
|
StatCode = EFI_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
//
|
|
// try MTFTP - if fails, drop into TFTP read
|
|
//
|
|
if ((StatCode = MtftpDownload (
|
|
Private,
|
|
&BufferSizeLocal,
|
|
BufferPtrLocal,
|
|
ServerIpPtr,
|
|
FilenamePtr,
|
|
MtftpInfoPtr,
|
|
DontUseBuffer
|
|
)) == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {
|
|
if (BufferSizePtr /* %% !DontUseBuffer */ ) {
|
|
*BufferSizePtr = BufferSizeLocal;
|
|
}
|
|
|
|
break;
|
|
}
|
|
//
|
|
// go back to unicast
|
|
//
|
|
if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
/* fall thru */
|
|
case EFI_PXE_BASE_CODE_TFTP_READ_FILE:
|
|
if (FilenamePtr == NULL) {
|
|
StatCode = EFI_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
StatCode = TftpDownload (
|
|
Private,
|
|
&BufferSizeLocal,
|
|
BufferPtrLocal,
|
|
ServerIpPtr,
|
|
FilenamePtr,
|
|
PacketSizePtr,
|
|
TftpRequestPort,
|
|
TFTP_RRQ,
|
|
DontUseBuffer
|
|
);
|
|
|
|
if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {
|
|
if (BufferSizePtr /* !DontUseBuffer */ ) {
|
|
*BufferSizePtr = BufferSizeLocal;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:
|
|
if (FilenamePtr == NULL || DontUseBuffer) {
|
|
//
|
|
// not a valid option
|
|
//
|
|
StatCode = EFI_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
StatCode = TftpUpload (
|
|
Private,
|
|
BufferSizePtr,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
FilenamePtr,
|
|
PacketSizePtr,
|
|
Overwrite
|
|
);
|
|
|
|
if (StatCode != EFI_SUCCESS) {
|
|
DEBUG (
|
|
(EFI_D_WARN,
|
|
"\nPxeBcMtftp() Exit #6 %xh (%r)",
|
|
StatCode,
|
|
StatCode)
|
|
);
|
|
}
|
|
|
|
return StatCode;
|
|
|
|
case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:
|
|
if (FilenamePtr == NULL || DontUseBuffer) {
|
|
//
|
|
// not a valid option
|
|
//
|
|
StatCode = EFI_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
StatCode = TftpDownload (
|
|
Private,
|
|
BufferSizePtr,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
FilenamePtr,
|
|
PacketSizePtr,
|
|
TftpRequestPort,
|
|
TFTP_DIR,
|
|
DontUseBuffer
|
|
);
|
|
|
|
if (StatCode != EFI_SUCCESS) {
|
|
DEBUG (
|
|
(EFI_D_WARN,
|
|
"\nPxeBcMtftp() Exit #7 %xh (%r)",
|
|
StatCode,
|
|
StatCode)
|
|
);
|
|
}
|
|
|
|
return StatCode;
|
|
|
|
case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:
|
|
if (DontUseBuffer) {
|
|
StatCode = EFI_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
if (MtftpInfoPtr == NULL || !MtftpInfoPtr->SPort) {
|
|
DEBUG (
|
|
(EFI_D_WARN,
|
|
"\nPxeBcMtftp() Exit #9 %xh (%r)",
|
|
EFI_INVALID_PARAMETER,
|
|
EFI_INVALID_PARAMETER)
|
|
);
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
StatCode = TftpDownload (
|
|
Private,
|
|
BufferSizePtr,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
(UINT8 *) "/",
|
|
PacketSizePtr,
|
|
MtftpInfoPtr->SPort,
|
|
TFTP_DIR,
|
|
DontUseBuffer
|
|
);
|
|
|
|
break;
|
|
|
|
default:
|
|
StatCode = EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (DontUseBuffer) {
|
|
FreePool (BufferPtrLocal);
|
|
}
|
|
|
|
if (StatCode != EFI_SUCCESS) {
|
|
DEBUG (
|
|
(EFI_D_WARN,
|
|
"\nPxeBcMtftp() Exit #8 %xh (%r)",
|
|
StatCode,
|
|
StatCode)
|
|
);
|
|
}
|
|
|
|
gBS->Stall (10000);
|
|
|
|
return StatCode;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BcMtftp (
|
|
IN EFI_PXE_BASE_CODE_PROTOCOL * This,
|
|
IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation,
|
|
IN OUT VOID *BufferPtr,
|
|
IN BOOLEAN Overwrite,
|
|
IN OUT UINT64 *BufferSizePtr,
|
|
IN UINTN *BlockSizePtr OPTIONAL,
|
|
IN EFI_IP_ADDRESS * ServerIpPtr,
|
|
IN UINT8 *FilenamePtr,
|
|
IN EFI_PXE_BASE_CODE_MTFTP_INFO * MtftpInfoPtr OPTIONAL,
|
|
IN BOOLEAN DontUseBuffer
|
|
)
|
|
/*++
|
|
Routine description:
|
|
MTFTP API entry point.
|
|
|
|
Parameters:
|
|
This :=
|
|
Operation :=
|
|
BufferPtr :=
|
|
Overwrite :=
|
|
BufferSizePtr :=
|
|
BlockSizePtr :=
|
|
ServerIpPtr :=
|
|
FilenamePtr :=
|
|
MtftpInfoPtr :=
|
|
DontUseBuffer :=
|
|
|
|
Returns:
|
|
* EFI_INVALID_PARAMETER
|
|
* Status is also returned from PxeBcMtftp();
|
|
--*/
|
|
{
|
|
EFI_PXE_BASE_CODE_IP_FILTER Filter;
|
|
EFI_STATUS StatCode;
|
|
PXE_BASECODE_DEVICE *Private;
|
|
|
|
//
|
|
// Lock the instance data and make sure started
|
|
//
|
|
StatCode = EFI_SUCCESS;
|
|
|
|
if (This == NULL) {
|
|
DEBUG ((EFI_D_ERROR, "BC *This pointer == NULL"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);
|
|
|
|
if (Private == NULL) {
|
|
DEBUG ((EFI_D_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
EfiAcquireLock (&Private->Lock);
|
|
|
|
if (This->Mode == NULL || !This->Mode->Started) {
|
|
DEBUG ((EFI_D_ERROR, "BC was not started."));
|
|
EfiReleaseLock (&Private->Lock);
|
|
return EFI_NOT_STARTED;
|
|
}
|
|
//
|
|
// Issue BC command
|
|
//
|
|
Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
|
|
Filter.IpCnt = 0;
|
|
Filter.reserved = 0;
|
|
|
|
DEBUG ((EFI_D_WARN, "\nBcMtftp() Op=%d Buf=%Xh", Operation, BufferPtr));
|
|
|
|
StatCode = PxeBcMtftp (
|
|
Private,
|
|
Operation,
|
|
BufferSizePtr,
|
|
BufferPtr,
|
|
ServerIpPtr,
|
|
FilenamePtr,
|
|
BlockSizePtr,
|
|
MtftpInfoPtr,
|
|
Overwrite,
|
|
DontUseBuffer
|
|
);
|
|
|
|
//
|
|
// restore to unicast
|
|
//
|
|
IpFilter (Private, &Filter);
|
|
|
|
//
|
|
// Unlock the instance data
|
|
//
|
|
EfiReleaseLock (&Private->Lock);
|
|
return StatCode;
|
|
}
|
|
|
|
/* eof - PxeBcMtftp.c */
|