/*++ 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 */