/*++ | |
Copyright (c) 2006, 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" | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
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 ( | |
EFI_EVENT_TIMER, | |
EFI_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 | |
); | |
} | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
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; | |
} | |
// | |
// special !!! do not leave enabled in saved version on Source Safe | |
// Following code put in in order to create a special version for regression | |
// test of MTFTP server to make sure it handles mulitple opens correctly. | |
// This code should NOT be enabled normally. | |
// | |
#ifdef SpecialNowaitVersion | |
#pragma message ("This is special version for MTFTP regression test") | |
if (StartBlock || !LastBlock) | |
#endif | |
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; | |
} | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
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; | |
EFI_STATUS Status; | |
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) { | |
Status = gBS->AllocatePool ( | |
EfiBootServicesData, | |
BUFFER_ALLOCATE_SIZE, | |
(VOID **) &BufferPtrLocal | |
); | |
if (EFI_ERROR (Status) || 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) { | |
gBS->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 */ |